← Back to posts

12 min read

Your Coding Agent Installed 23 Packages in a Minute. Your SBOM Saw Zero.

Filed under Supply Chain

In a write-up on dev.to, a developer describes 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.

Strip the vulnerabilities out and the story gets worse, not better. Even if all 23 packages had been clean, none of them passed through an approval, an inventory entry, or a human decision. That is the part your current tooling does not cover, and it is the part almost nobody is writing about.

The pattern underneath the headlines

A widely shared thread recently described Claude, Codex, and Hermes installing unowned code inside corporate networks. Treat the specific incident details as unconfirmed; the reporting chain behind them is thin. The pattern underneath, though, is well documented.

An arXiv preprint published July 17, 2026 by Aadesh Bagmar and Pushkar Saraf tested frontier coding agents across twelve scenarios in five attack classes, all grounded in documented incidents. The headline result: editing a single README, requirements file, or Makefile is enough to redirect an agent toward untrusted registries and known-vulnerable dependencies. Agents reliably catch blatant typosquats like requ3sts, but separator-confusion names such as azurecore instead of azure-core slip through at rates that depend on the specific harness-model pairing. Source redirection is the worst class. Point the agent at an attacker-controlled registry instead of swapping a package name, and across npm and Cargo nearly every model tested installed the dependency without flagging it.

The preprint has not been peer-reviewed and its "first systematic evaluation" claim is self-assessed. Fair. But the in-the-wild evidence does not depend on it:

  • ReversingLabs documented PromptMink, a campaign attributed to Famous Chollima, a North Korean group focused on cryptocurrency and fintech developers, active since at least September 2025. The bait package @solana-launchpad/sdk used READMEs crafted to look authoritative to an LLM resolving dependencies, a technique ReversingLabs calls LLM Optimization abuse. The payload chain included infostealers, SSH key deployment for persistence, and data archiving, rotating across npm, PyPI, and Rust before pivoting to compiled Rust payloads via NAPI-RS.
  • In January 2026, researchers found a legitimate Solana Graveyard Hackathon project that had pulled in @solana-launchpad/sdk through a commit co-authored by Claude Opus.
  • USENIX Security 2025 research tested 16 models across 576,000 samples and found roughly 19.7% of AI-generated package recommendations referenced packages that do not exist. Aikido's Charlie Eriksen named the exploitation of that habit "slopsquatting": register the name the model hallucinates and wait. In January 2026 he registered react-codeshift, a name a model invented by conflating jscodeshift and react-codemod.
  • Related TechTimes coverage carries the headline "North Korea Backdoored 144 AI Packages in 88 Minutes." That is the speed you are governing against.

Socket's Brad Arkin put the operational problem plainly in late June: agents pull packages into environments no scanner is watching, resolving dependencies before security teams can see them, at a pace human review was never designed to match. If you have read our piece on Grok decrypting its own attack instructions, the PromptMink READMEs will look familiar. The documentation is written for the model, not for you.

Why your SBOM never sees any of this

The workflow your SBOM assumes vs. what agents actually do

Your software inventory was built around a human workflow: write a manifest, commit it, scan it in CI, generate the SBOM at build time. Agents break every assumption in that chain.

Installs happen before review, not after. The agent resolves and installs dependencies while it works, on a laptop or runner holding prod-adjacent credentials. By the time a PR exists, the package has already executed in your environment.

Install scripts run at install time. npm lifecycle scripts, pip's setup.py, Rust's build.rs all execute during installation. A scanner that flags the package in CI three hours later is writing an incident report, not preventing one. PromptMink deployed SSH keys through this exact path. You cannot un-run a postinstall script with a Jira ticket.

The lockfile lies by omission. A manifest records a name and a version. It does not record where the bytes came from. When an agent gets redirected to an untrusted registry, the lockfile looks completely clean: right name, right version, wrong source. Your manifest-based SBOM will happily attest to a package you never vetted. This is precisely the attack class the preprint found agents miss almost everywhere, and it maps one-to-one onto an inventory blind spot.

The environments are ephemeral and the protection is variable. Agent sandboxes, worktrees, and throwaway containers rarely carry your EDR or inventory agent. Worse, the preprint's core finding is that install-time security is a property of the harness-model combination, not the model. Swap harnesses while keeping the same model and you gain or lose protection without anyone noticing. You cannot certify your way out of this at the model layer. It is worth asking the harder question of what your environment actually trusts, which is exactly the exercise in our trust hierarchy audit.

The approval layer, built properly

The approval layer as a request path

The fix the preprint authors propose is the right one: a deterministic pre-install check that verifies package names, sources, and versions before any code runs. Not a model fix. Infrastructure. Here is what that looks like when you build it.

1. Put a registry proxy in front of everything

Every package manager the agent can touch points at an internal proxy (Verdaccio, Artifactory, Nexus, or your cloud artifact registry). Bake it into repo-level config and base images so the agent inherits it automatically:

ini
# .npmrc, checked into the repo
registry=https://artifacts.corp.example/npm/
ini
# pip.conf in the agent's base image
[global]
index-url = https://artifacts.corp.example/pypi/simple
toml
# .cargo/config.toml
[source.crates-io]
replace-with = "corp-mirror"

[source.corp-mirror]
registry = "https://artifacts.corp.example/cargo/"

2. Close the direct route

The proxy is a suggestion until you block the bypass. Deny egress from developer and agent subnets to registry.npmjs.org, pypi.org, files.pythonhosted.org, crates.io, static.crates.io, rubygems.org, and proxy.golang.org, with an allow rule for the proxy host only. This single rule kills the README-redirect attack class: a Makefile pointing at an attacker registry now fails closed instead of failing silent. If you do only one thing from this article, do this.

3. Write the approval rules as code

Build your baseline allowlist from the union of every lockfile in your repos. That set covers the large majority of day-to-day installs, so the review queue fires only on genuinely new dependencies, which is exactly the moment you want a second pair of eyes. Enforce policy at the proxy:

rego
deny[msg] {
    input.age_days < 14
    msg := sprintf("%s published %d days ago; minimum age is 14", [input.name, input.age_days])
}

deny[msg] {
    input.has_install_scripts
    not input.allowlisted
    msg := sprintf("%s runs install scripts and is not approved", [input.name])
}

deny[msg] {
    input.resolved_from != "https://artifacts.corp.example/npm/"
    msg := sprintf("%s resolved from unapproved source %s", [input.name, input.resolved_from])
}

Add a normalized-name collision check against your top packages (strip hyphens and underscores before comparing) to catch the azurecore class, and set ignore-scripts as the global default for agent-facing installs, allowlisting the exceptions. Agent-pulled components are already a proven malware vector; five of the top seven skills we analyzed in the OWASP Agentic Skills Top 10 guide were malicious, and packages are no different.

4. Scan at the tool call, not after the fact

If your agents install through MCP tools or harness shell access, wrap the install command itself. A pre-tool hook resolves what would be installed (dry run, or diff the lockfile the command produces) and submits that set to the policy engine before execution. MCP servers are their own attack surface now, so govern them like it; our CircleCI MCP playbook shows what happens when nobody does.

Why prompts and better models will not save you

The preprint tested security-oriented prompting directly. It helps only on the dimension the prompt names: told to watch package names, the agent still never checks whether the registry URL is legitimate. Provenance is not something a model can verify from the inside, and a related preprint on instructional-text-induced data leakage shows the same trust pathway exposes sensitive data, not just dependencies. Since install-time behavior varies by harness and model pairing, "we use a good model" is not a control. It is a hope.

"My developers will route around it" and other objections

"The friction will make devs bypass it." Once egress is closed, bypassing stops being an accident and becomes a deliberate, logged policy violation. Different problem, easier problem. And the baseline allowlist means the queue only fires on new deps; a ten-minute median review on those is cheap insurance against a 9.8.

"We already run npm audit and Snyk in CI." Keep them. They are your second layer. But they run after install scripts have executed, against whatever got committed, and as the dev.to account shows, agents do not run them by default. Detective controls do not help against an infostealer that exfiltrates during install.

"The preprint is unreviewed and models will improve." Both true, neither relevant. PromptMink is documented in the wild since at least September 2025, and the 19.7% hallucination figure is USENIX-published. You should have had a registry boundary for humans anyway.

Your first 30 days

  • Week 1: Inventory where agents run (harness, model, network access). Export the union of all lockfiles as your baseline allowlist. Turn on DNS and egress logging for the registry domains above.
  • Week 2: Stand up the proxy in audit mode with one pilot team. Measure what fraction of requests the baseline auto-approves; expect the large majority.
  • Week 3: Enforce the policy rules, close egress for agent subnets, set ignore-scripts defaults.
  • Week 4: Add CI checks that fail builds on lockfile entries not resolved through the proxy, alert on new dependencies in PRs, and generate your dev-side SBOM from the proxy transaction log, so the inventory of what agents actually fetched exists at all. Finish with a tabletop: plant a slopsquat-style name in a test repo and watch whether anything catches it.

One honest note: an approval layer governs what enters the environment. What your application does with those packages afterward, the auth flaws and injection paths they flow into, is a separate question. If you want the same rigor pointed at the app that consumes those packages, start at Axeploit.

Key takeaways

  • Agent installs happen outside the commit-and-scan workflow, so manifest-based SBOMs and CI scanners see nothing until after install scripts have already run.
  • Source redirection is the highest-risk attack class and produces a clean-looking lockfile. Only an enforced registry boundary catches it.
  • Prompts and model choice are not controls; install-time safety varies by harness-model pairing. Verify name, source, and version deterministically before execution.
  • The working architecture is a registry proxy, closed egress, policy-as-code approval rules, and scanning at the tool call, with a baseline allowlist from existing lockfiles to keep friction near zero.
  • Start this week with egress logging and a lockfile baseline. You are governing against campaigns that move 144 packages in 88 minutes.
Get started

Integrate Axeploit into your workflow today