Infrastructure Review Stack

Dependency Poisoning in AI-Generated Package Imports

Hallucinated package names recur predictably, letting attackers register them first.

Columnist · · 9 min read · Updated
Cover illustration for “Dependency Poisoning in AI-Generated Package Imports”
Running AI-Generated Code Safely · August 12, 2026 · 9 min read · 2,103 words

The most operationally significant finding from the USENIX dataset is not the volume of hallucinated names. It is their recurrence. Fifty-eight percent of hallucinated packages reappeared within ten iterations of the same prompt on the same model. Forty-three percent surfaced repeatedly across distinct prompts. These are not noise; they are structural features of how a model learned to interpolate package identifiers from training data, drawing on the same learned associations each time and producing the same fictitious outputs with a consistency that should unsettle anyone who assumed hallucinations were random.

Cross-model convergence narrows the target list further. Across a large corpus of prompts in the 2026 re-evaluation, 127 package names were hallucinated identically by all five frontier models tested, and 53 of those names remained registrable as of April 2026. That is an open inventory, visible to anyone willing to monitor model output systematically.

This recurrence is what separates slopsquatting from a lottery. An attacker does not need to guess which names developers will request. They can instrument a handful of models against representative prompts, compile a watch-list of reliably hallucinated identifiers, and register the most promising ones before most developers have any reason to be suspicious. Registry registration costs are trivial. The attacker's edge is pure information asymmetry.

Seth Larson, the researcher who coined the term "slopsquatting," captured the mechanic precisely: register the hallucinated name before anyone else, then intercept every install that follows. Mapped to MITRE ATT&CK T1195.002 (Compromise Software Supply Chain), this is a defined, repeatable playbook with a documented cost structure and a predictable yield.

The Bar Lanyado experiment: what actually happens when a hallucinated name goes live

In early 2024, Lasso Security researcher Bar Lanyado ran the simplest possible test. He asked AI tools to generate Python code, logged every hallucinated package name, and registered one of them on a public registry as an empty package instrumented with download analytics. No malicious payload, no promotion, no social engineering. Just the name existing somewhere that pip or a CI runner would find it.

Within three months, that single inert package had accumulated over 30,000 downloads. Every one represented a developer or automated pipeline executing AI-generated code without verifying that the dependency was real.

What the experiment cannot tell us is the more uncomfortable thing. Lanyado was measuring reach, not damage. The same 30,000 installs carrying a credential harvester in the postinstall script, or a reverse shell, or a supply-chain backdoor propagating into the developer's next commit, would have been 30,000 compromised environments. The experiment establishes an empirical floor: hallucinated names get installed at scale, passively, with no attacker effort beyond registration. Everything downstream of that is payload selection.

Diagram: One Hallucinated Name, 30,000 Installs — With Zero Effort. Visualizes: Show the stark contrast between attacker effort (near zero: one package registration, no payload, no promotion) and result (30,000 downloads in three months) from Bar…Diagram: One Inert Package, 30,000 Installs in Three Months. Visualizes: Show the Bar Lanyado experiment as a stark magnitude callout: a single hallucinated package name was registered on a public registry with no malicious payload, no promotion…

How autonomous agents remove the last human checkpoint in the install chain

Traditional slopsquatting, if that phrase can apply to something that barely existed two years ago, had one implicit speed bump. A developer reading AI output might pause at an unfamiliar package name. The friction was minimal, but it existed. Some fraction of hallucinated installs would get caught simply because a developer searched for documentation and found nothing.

Autonomous agents eliminate that friction by design. An agent tasked with building and deploying a feature generates code, resolves dependencies, and executes installs without pausing for human review. That is the point of the agent. Endor Labs' study of agent-driven development found that 45% of agent edits introduce entirely new direct dependencies, each one a potential hallucination event with no human in the loop.

The cross-repository propagation problem makes this worse in a way I find underappreciated. Researchers documented the fictitious npm package name react-codeshift spreading across 237 repositories, carried there by agent-driven workflows that inherited a single hallucinated dependency and propagated it without generating a second hallucination event. Once an agent installs and commits a poisoned package, downstream agents and CI pipelines treat it as a known, reviewed dependency. The original hallucination disappears into the commit history.

Veracode's 2025 analysis of AI-generated code across 100 models and 80 coding tasks found that only 55% of AI-generated code was secure overall. Dependency hallucination sits inside a wider pattern: the code generation step introduces flaws that the development pipeline was not designed to catch.

The MCP layer adds another injection point that few teams have fully mapped. Endor Labs' analysis of thousands of GitHub repositories implementing MCP servers found agents importing vulnerable or non-existent dependencies at scale across PyPI, npm, Maven, and NuGet. Researcher Mickey Shmueli demonstrated that malicious documentation merged upstream into an MCP-based pipeline could silently inject fake dependencies into every downstream project the agent subsequently touched, across 240 isolated Docker runs. The attack surface is not the model alone; it is everything the model ingests.

What a poisoned package can do once it lands in the execution environment

The install event is only the entry point. Both npm and PyPI support postinstall scripts that execute automatically with the permissions of whatever process invoked the install. A malicious postinstall can exfiltrate environment variables, establish persistence, or install a backdoor before a single line of the application's actual code runs. A vulnerability disclosed in 2025 in the @anthropic/mcp-server package demonstrated that MCP servers could execute arbitrary code during postinstall; in agent contexts where installation is automated, this happens without any operator awareness.

What an attacker can reach from that entry point depends entirely on what the execution environment shares with the host. Standard containers share the host kernel. A compromised postinstall running inside one can attempt kernel exploits, read mounted secrets, or move laterally to adjacent containers on the same host. CVE-2025-54135, involving a Slack MCP server prompt injection, gave attackers immediate code execution on the developer's local machine, which is not a sandboxed environment but the machine where credentials, keys, and repository access live.

The dependency confusion variant amplifies the same risk through a different mechanism. Alex Birsan's demonstration of publishing a package to public npm that matched an internal package name used by a popular agent framework succeeded because default registry resolution order favored the public registry. Agent plugin registries are often uncurated and carry weak trust signals; the trust developers extend to well-known registries does not transfer automatically to ecosystem-adjacent stores that agents increasingly pull from.

The payload does not have to be dramatic to be damaging. Credential theft from environment variables is the most common documented outcome: API keys, authentication tokens, cloud credentials usable long after the compromised container has been torn down. CVE-2026-32211 in Microsoft's @azure-devops/mcp package, rated CVSS 9.1, exposed Azure DevOps API keys and auth tokens without requiring valid credentials from the attacker. One poisoned install in a well-credentialed environment can reach a great deal.

Why standard containers are the wrong isolation boundary for AI-generated installs

Container security models were designed around a specific assumption: isolate known, reviewed workloads from each other. The code inside is presumed trusted; the isolation prevents one trusted workload from interfering with another. AI-generated code inverts that assumption. The content of a dependency may be unknown before execution. The container boundary was never built to hold that.

The shared-kernel problem is the most direct consequence. A kernel exploit from inside a standard container can escape to the host. A poisoned postinstall running inside a standard Docker container has a plausible path to the host if the attacker brings an appropriate exploit. The blast radius is not, in any meaningful sense, contained.

Ephemeral containers help at the margins. A fresh environment per task limits persistence: a malicious postinstall cannot install a backdoor that survives into the next session. But it still runs, and it can still exfiltrate secrets before teardown. More practically, agents doing real work need persistent state. Discarding the environment after every step is not operationally viable for tasks that span hours or accumulate context.

The range of isolation approaches has grown since I first started thinking about this. MicroVMs, exemplified by Firecracker and Kata Containers, provide hardware-level isolation appropriate for untrusted code execution. gVisor interposes a user-space kernel between the workload and the host kernel, intercepting syscalls before they reach the host and reducing the escape surface for multi-tenant compute workloads. Hardened containers with reduced capabilities and stripped syscall permissions represent incremental improvement over defaults, though they remain in the same isolation class as standard containers.

None of these are the default. A team reaching for a generic container runtime gets the weakest option without being told so. The security posture of the execution environment is determined by deliberate choice, and most engineers are not choosing with slopsquatting anywhere in their threat model.

What execution-layer controls can actually contain a poisoned install

Assume the package is malicious. Design the execution environment to limit what malice can accomplish. That starting point is harder to accept than it sounds for teams accustomed to trusting their dependency graphs, but it is where the productive work happens.

Network egress restriction during install is the highest-leverage single control. Most credential exfiltration requires an outbound connection. Blocking egress from the install environment does not prevent the malicious postinstall from running, but it breaks the exfiltration channel before the attacker receives anything. Combined with an environment that carries no production secrets, no mounted volumes from adjacent workloads, and no persistent storage that survives the task, the damage surface shrinks considerably.

Stateful sandboxes that snapshot before and after dependency installation turn installation from an implicit trust grant into an auditable event. A diff of files written, processes spawned, and network calls attempted during install is not a perfect detector, but it surfaces the behaviors that distinguish a legitimate dependency from a malicious one. Agents that can resume from a pre-install snapshot when a dependency is flagged do not have to restart long-running tasks from scratch, which makes security controls operationally viable rather than theoretically correct and practically abandoned.

Syscall filtering via seccomp profiles or gVisor's intercept layer limits what a compromised postinstall can invoke against the host even if network egress is somehow available. These controls compose; the combination of network restriction, syscall filtering, and secret isolation degrades the attacker's capability at each layer without requiring any single control to be perfect.

Customer-managed compute extends this to the infrastructure boundary. Running agent workloads inside the customer's own cloud environment means a compromised install cannot reach a shared-tenant secret store or an adjacent customer's environment.

Purpose-built sandbox runtimes that treat all of these design choices as defaults, rather than configurations an engineer must consciously select, change the math for teams building on AI-generated code at scale.

Registry-side and model-side controls and where they fall short

Lockfiles and version pinning with integrity hashes address a real but adjacent problem. Pinning prevents an attacker from silently swapping a package version after the initial install. It does not protect against a name that was never legitimate. If a hallucinated package name is registered maliciously before the first install, the lockfile dutifully captures the malicious version and reproduces it faithfully across every subsequent environment. The control assumes the first install was safe.

Registry-side verification efforts, including namespace reservation and automated scanning for suspicious postinstall scripts, raise the cost of attack without closing the window. The period between a hallucinated name becoming predictable and its registration being flagged is exactly where slopsquatting operates. Attacker registration is instantaneous; registry review is not.

Model-side interventions are the most promising long-term control and the least mature. Fine-tuning on current registry state, retrieval-augmented generation against live package indexes, constrained decoding that forces generated package names to resolve against a known-good list before output: all of these reduce hallucination rates, and the data from 2026 re-evaluations suggests they are working incrementally. The frontier model rates of 4.62% to 6.10% are lower than earlier baselines. They are not zero, and given how these models generate text, they may not reach zero.

The 53 registrable names found across five frontier models in April 2026 (names that all models agreed on and that an attacker could register that afternoon) illustrate the gap between "improved" and "solved." Registry-side and model-side controls will keep narrowing the attack surface. What determines the consequences when a narrowed attack surface still lets something through is the execution environment.

The position I keep returning to, after looking at this problem from several angles, is that defense-in-depth here requires accepting that hallucinations will reach the install step and designing the install environment to constrain what happens next. The model controls the name. The registry controls what that name resolves to. The execution environment controls what the resolved package can actually do. Only the last of these is fully within the installing organization's control, and it is the one that most organizations have spent the least time hardening.

Diagram: Three Layers of Control — Only One Is Yours. Visualizes: Illustrate the three-layer defense model described in the article's closing argument: (1) the Model controls the package name generated; (2) the Registry controls what that name…Diagram: The Three Layers of Control — and Who Owns Each. Visualizes: Visualize a three-level stack showing where control over a slopsquatting attack sits: (1) the Model — controls the hallucinated name, owned by the model vendor; (2) the Registry…

Sources

  1. simonroses.com
  2. techbytes.app
  3. mallory.ai
  4. snyk.io
  5. newsroom.bedefended.com
  6. activestate.com

More in Running AI-Generated Code Safely