Last month a software consultancy called Softjourn came a couple of clicks away from installing malware into a client project. The story, reported in The Register, is worth sitting with because nothing about it is exotic. An engineer asked an AI coding agent to recommend a package for a routine task. The agent suggested one with a plausible, familiar-looking name. At most shops that would have been the end of it — the package gets installed, and everyone moves on.
What saved Softjourn was a policy: check anything an AI recommends before installing it. The engineer opened the package's source on GitHub, saw that it had almost no downloads and had been created only days earlier, and backed away. That was the whole defense. A human, following a habit, noticing two things looked off.
I build a package firewall, so I think about this failure mode a lot. And the part that stuck with me isn't that the attack worked — it's that the thing that stopped it was manual, and manual controls fail the moment someone's in a hurry.
Why the agent makes this worse
The specific attack here is what people have started calling slopsquatting, and it only exists because of how coding agents behave. Language models sometimes invent package names — names that sound real, follow the right conventions, and don't correspond to anything that exists. Attackers noticed. Now they register those hallucinated names ahead of time and wait. The agent confidently recommends a package; a developer under deadline pressure installs it; the payload runs.
The uncomfortable detail is that the agent is a trusted recommender. When a teammate suggests a library you give it a second look. When the tool you've wired into your editor suggests one, most people just run the install. The agent has borrowed the trust we used to extend to a colleague, without earning it, and attackers are pricing that in.
The check was really two checks
Look again at what the Softjourn engineer actually noticed: the package was too new, and its reputation was too thin. That's it. Those are the two signals that caught a real attack before it landed.
Both of them are mechanical. "How old is this release" is a lookup. "How many downloads, how established is the maintainer" is a lookup. There is nothing about either check that requires human judgment — a person did it here only because no automated gate was standing in the way. And because a person did it, it was slow, it depended on that person remembering, and it would have been skipped the first afternoon someone was racing a deadline. The Register piece ends on exactly that note: skipping the check once is how a team ends up explaining a supply-chain compromise instead of shipping on time.
Machines should do mechanical checks. Humans are bad at doing the same lookup consistently a hundred times; that's precisely the kind of work you automate.
What automating it looks like
Three things, roughly in order of how much they'd have helped here:
Cooldown period. Don't install anything younger than some threshold — say seven days. Most of these campaigns are live for a short window before someone reports them and the registry pulls them. A newly published malicious version is often gone before a seven-day window would ever let it through. This one control would have stopped the Softjourn package, which was days old. It's the highest-value, lowest-effort policy in the whole space and almost nobody enforces it by default.
Reputation thresholds. Downloads, maintainer history, whether the linked repository actually exists and matches the package. None of these is decisive alone, and plenty of legitimate new packages are small — but combined with youth, thin reputation is a strong signal, and it's the other thing the engineer noticed by eye.
Enforcement at install time, not in CI. This is the part teams get wrong. Dependency scanning that runs in your pipeline fires after the package is already on a developer's machine — after any install script has already executed. If the defense runs later than the install, it isn't a defense against this attack; it's a report about it. The check has to sit at the moment of installation, between the request and the registry.
You can approximate a lot of this without buying anything. A private index or proxy (Artifactory, Verdaccio, or similar) in front of the public registries gives you one place to enforce an age policy across pip, npm, and everything else, instead of fighting each client's configuration. uv has an exclude-newer option. It's more work to assemble and maintain than most teams admit, but it's real, and if you'd rather roll it yourself, roll it yourself.
Our package firewall enforces exactly these checks at the proxy layer — cooldown, reputation, and other signals — so it covers every client uniformly with no per-tool config. But the point of this post isn't the tool. The point is that the control that saved Softjourn was the right idea implemented the wrong way.
Human in the loop, in the right place
The Register's takeaway was "keep a human in the loop," and that's correct as far as it goes. But there's a version of human-in-the-loop that scales and a version that doesn't. Asking a person to open GitHub and eyeball downloads and creation dates for every dependency an agent suggests is the version that doesn't — it works right up until the day it's inconvenient, and then it quietly stops working.
The version that scales is: the mechanical checks run automatically on every install, and a human reviews only the things that trip a threshold. Let the machine handle "is this package a week old with twelve downloads." Save the human for the judgment calls. That's the same instinct Softjourn had, moved to where it can actually hold under pressure.
The agents aren't going to stop recommending packages, and they aren't going to stop occasionally recommending ones that don't exist. That's now part of the threat model. The fix isn't to distrust the agent case by case with human diligence you have to remember to apply. It's to put a gate at the install step that doesn't get tired and doesn't skip the check when there's a deadline.