DEV Community

Jason Miller
Jason Miller

Posted on Originally published at axeploit.com

Your coding agent installed 23 packages in a minute. Your SBOM saw zero.

A developer on this site described asking Claude Code to scaffold an Express API with auth. The agent installed 23 packages in under a minute. Three carried known critical vulnerabilities, one at CVSS 9.8, and the agent never mentioned any of it.

Now delete the vulnerabilities from that story. It gets worse. Even if all 23 packages had been clean, none of them passed through an approval, an inventory entry, or a human decision. Your dependency governance assumes a person writes a manifest, commits it, and CI scans it. Agents broke that sequence. They resolve and install while they work, on machines holding prod-adjacent credentials, and by the time a PR exists the code has already executed.

Why the SBOM misses it

Install scripts run at install time. npm lifecycle hooks, pip's setup.py, Rust's build.rs. A scanner flagging the package in CI three hours later is writing an incident report, not preventing one. ReversingLabs documented the PromptMink campaign deploying SSH keys through exactly this path. You cannot un-run a postinstall script with a Jira ticket.

The lockfile lies by omission. It records a name and a version, never where the bytes came from. Point an agent at an attacker-controlled registry and the lockfile stays clean: right name, right version, wrong source. An arXiv preprint from July (Bagmar and Saraf, not yet peer-reviewed, so apply salt) tested frontier agents across twelve scenarios and found this is the worst class. Edit one Makefile or requirements file to redirect the source, and nearly every model installed the dependency without flagging it. The same agents reliably caught blatant typosquats like requ3sts.

The environments are ephemeral. Agent sandboxes and throwaway containers rarely carry your EDR or inventory agent. And install-time security turned out to be a property of the harness-model pairing, not the model. Swap harnesses, keep the model, and you gain or lose protection without anyone noticing.

The approval layer that actually works

The fix is infrastructure, not prompting. A deterministic check that verifies name, source, and version before any code runs.

Put a registry proxy in front of everything and make the agent inherit it through repo config and base images:

# .npmrc, checked into the repo
registry=https://artifacts.corp.example/npm/
Enter fullscreen mode Exit fullscreen mode

Then close the direct route. Deny egress from developer and agent subnets to registry.npmjs.org, pypi.org, files.pythonhosted.org, crates.io, and static.crates.io, with an allow rule for the proxy host only. That single rule kills the README-redirect attack: a Makefile pointing at an attacker registry now fails closed instead of failing silent.

Enforce policy at the proxy. Build the baseline allowlist from the union of every lockfile in your repos, so the review queue only fires on genuinely new dependencies, which is exactly when you want human eyes. Useful rules: minimum package age of 14 days, no install scripts unless allowlisted, resolved source must equal your proxy. Add a normalized-name collision check (strip hyphens and underscores) to catch the azurecore vs azure-core class, and default to ignore-scripts for agent-facing installs.

"We use a good model" is not a control

The preprint tested security-oriented prompting directly. It helps only on the dimension the prompt names. Told to watch package names, agents still never checked whether the registry URL was legitimate. Provenance is not something a model can verify from the inside, and since behavior varies by harness, model choice is a hope. Hopes are not controls.

If you do anything this week:

  • Block egress to public registries from dev and agent subnets, allow only your internal proxy. This is the one rule that matters most.
  • Bake the proxy into repo-level config and base images so agents inherit it automatically.
  • Build your allowlist from existing lockfiles so humans only review net-new deps.
  • Set ignore-scripts as the global default for agent installs, allowlist the exceptions.

Honest question for the comments: is cutting dev subnets off from npm directly overkill, or is it table stakes at this point? And if you've done it, who owns the new-dependency review queue, security or the team that asked for the package?

Longer writeup if you want the full argument: https://axeploit.com/blog/your-coding-agent-installed-23-packages-in-a-minute-your-sbom-saw-zero

Top comments (0)