Slopsquatting: how to stop hallucinated packages in AI-generated code from reaching production
Code assistants invent package names, and attackers can register them. Practical controls to keep hallucinated dependencies out of your builds.
- Published
- Reading time
- 7 min read

Key takeaways
- Slopsquatting is registering a package name that code-generating models hallucinate, so that developers who trust the suggestion install the attacker's code.
- A USENIX Security 2025 study of 576,000 generated code samples found hallucinated packages in at least 5.2% of commercial-model output and 21.7% of open-source-model output.
- Hallucinated names are repeatable: 43% recurred in all ten re-runs of the same prompt, which is what makes them worth registering.
- A package existing on the registry does not prove it is the one you meant; check age, maintainer, and source repository for every new dependency.
- Lockfiles, frozen installs, disabled install scripts, and a CI check on newly added dependencies stop most hallucinated packages before they run.
On this page
- What is slopsquatting?
- How often do code assistants hallucinate packages?
- Why "the package exists" is not a sufficient check
- Which controls actually stop it?
- 1. Make new dependencies visible in review
- 2. Install only from the lockfile
- 3. Do not run install scripts by default
- 4. Gate newly added dependencies in CI
- 5. Constrain coding agents
- Can model settings reduce the problem?
- A short checklist
Slopsquatting is a supply-chain attack in which someone registers a package name that AI coding assistants tend to invent, then waits for developers (or coding agents) to install it on the model's say-so. The defence is not a better prompt. It is treating every AI-suggested dependency as untrusted input and gating new dependencies at review, install, and CI time.
The risk is measurable. Code-generating models suggest non-existent packages often, and they suggest the same non-existent packages repeatedly, which gives an attacker a predictable list of names to claim.
What is slopsquatting?
The term was coined by Seth Larson, security developer-in-residence at the Python Software Foundation, as reported by The Register. It borrows from typosquatting, where attackers register misspellings of popular packages. The difference is whose mistake is being exploited: in typosquatting it is a human typo, in slopsquatting it is a model's hallucination.
The attack chain is short:
- A developer asks an assistant for code. The output imports, or tells them to install, a package that does not exist.
- An attacker who has probed the same models has already registered that name on npm or PyPI with a malicious install script or payload.
- The developer, or an agent with shell access, runs the install command. The attacker's code executes on a laptop or in CI.
Nothing in that chain requires the developer to make a typo. The command they ran was exactly what the assistant suggested.
How often do code assistants hallucinate packages?
The most thorough measurement to date is We Have a Package for You! by Spracklen et al., accepted at USENIX Security 2025. The authors generated 576,000 code samples in two programming languages with 16 popular code-generating models and checked every referenced package against the real registries.
- Commercial models hallucinated packages at an average rate of at least 5.2%.
- Open-source models averaged 21.7%.
- Across all samples they found 205,474 unique hallucinated package names.
Two further findings matter for defenders. First, hallucinations are persistent: when the same prompt was re-run ten times, 43% of hallucinated packages appeared in all ten runs and 58% appeared more than once. An attacker does not need luck; they need to run popular prompts a few times and register what keeps coming back.
Second, most hallucinated names are not near-misspellings of real packages. Only 13.4% were within an edit distance of one or two from a valid name. That means typosquatting defences that look for names close to popular packages will miss most of them.
OWASP's 2025 entry on misinformation (LLM09) lists the model suggesting "insecure or non-existent code libraries" as a risk, and its example attack scenario describes attackers looking for commonly hallucinated package names.
Why "the package exists" is not a sufficient check
The instinctive fix is to verify that a suggested package exists before installing it. That catches hallucinations nobody has claimed yet. It fails against the actual attack, because a slopsquatted package does exist. The attacker made sure of it.
The useful questions are about provenance, not existence:
- How old is it? A package registered last week that an assistant confidently recommends is suspicious.
- Does it link to a real source repository, and does that repository's code match what is published?
- Who maintains it, and do they maintain anything else you recognise?
- Is it the package the documentation of the tool you are using actually tells you to install?
None of these checks needs AI. They are the same review you would apply to any unfamiliar dependency. The change is recognising that AI-suggested code produces unfamiliar dependencies at a much higher rate than a human copying from official docs.
Which controls actually stop it?
Layer the controls so that a hallucinated package has to get past a person, the install tooling, and CI.
1. Make new dependencies visible in review
Most slopsquatting risk enters through a single changed line in package.json, requirements.txt, or pyproject.toml. Require that any pull request adding a dependency says why, and have reviewers check the provenance questions above. Do not let lockfile churn hide a new top-level package.
2. Install only from the lockfile
Ad-hoc npm install <name> and pip install <name> are where a hallucinated name turns into running code. In CI, install strictly from a reviewed lockfile.
For npm, npm ci requires an existing lockfile, exits with an error if it does not match package.json, and never rewrites either file. For pip, hash-checking mode requires every requirement to be pinned with a locally stored hash; pip's documentation recommends pairing it with --only-binary :all: so that no source distribution is built during install.
# Node: fail if the lockfile and package.json disagree; skip lifecycle scripts
npm ci --ignore-scripts
# Python: every requirement pinned and hashed, wheels only
python -m pip install --require-hashes --only-binary :all: -r requirements.txt
3. Do not run install scripts by default
Install-time lifecycle scripts run code on the machine before your application imports anything, which makes them an attractive place for a payload. npm's --ignore-scripts option stops lifecycle scripts defined in package.json files from running. Turn it on by default and allow scripts only for the packages that need them. Expect some packages with native builds to need an exception; that list should be short and reviewed.
4. Gate newly added dependencies in CI
A small check on every pull request can flag the two signals that matter most: a name that is not on the registry at all (an unclaimed hallucination), and a name that is very new or has no source repository (a possible claimed one). This script uses the public npm registry metadata endpoint:
// check-new-deps.mjs
// Usage: node check-new-deps.mjs <package> [<package> ...]
const MIN_AGE_DAYS = 90;
for (const name of process.argv.slice(2)) {
const res = await fetch(`https://registry.npmjs.org/${encodeURIComponent(name)}`);
if (res.status === 404) {
console.log(`FAIL ${name}: not on the registry (likely hallucinated)`);
process.exitCode = 1;
continue;
}
const meta = await res.json();
const ageDays = (Date.now() - Date.parse(meta.time.created)) / 86_400_000;
const repo = meta.repository?.url ?? "none";
const flag = ageDays < MIN_AGE_DAYS || repo === "none" ? "REVIEW" : "ok";
if (flag === "REVIEW") process.exitCode = 1;
console.log(`${flag} ${name}: created ${Math.round(ageDays)} days ago, repo ${repo}`);
}
Feed it only the dependency names a pull request adds, not the whole tree. A REVIEW result is a prompt for a human, not an automatic block; the 90-day threshold is a starting point you should tune. The same idea works for PyPI using https://pypi.org/pypi/<name>/json, which returns 404 for unknown projects.
5. Constrain coding agents
An assistant that only suggests code leaves a human between the hallucination and the install. A coding agent with shell access removes that human. Treat package installation as a privileged tool call: require explicit approval for any command that adds a dependency, show the exact package name and registry, and run agents in environments without production credentials. This is the same principle we apply to prompt injection: the model can propose an action, but something outside the model decides whether it happens.
Can model settings reduce the problem?
Somewhat. Spracklen et al. found that higher sampling temperatures sharply increased hallucinated packages, and that retrieval-augmented generation, self-refinement, and fine-tuning each reduced the rate in the models they tested. Fine-tuning gave the largest single reduction but also lowered code-generation accuracy on their benchmark.
These are worthwhile for teams that run their own models, but they lower the rate rather than remove it. Most teams use hosted assistants whose settings they do not control. The dependency controls above work regardless of which model wrote the code, which is why they should come first.
A short checklist
- New dependencies in a pull request require a stated reason and a provenance check.
- CI installs only from lockfiles:
npm ci, or pip with--require-hashes. - Install-time scripts are off by default, with a reviewed allowlist of exceptions.
- A CI step flags added packages that are missing from the registry, very new, or have no source repository.
- Coding agents need approval to install packages and run without production secrets.
- Your AI inventory lists which code assistants and agents are in use.
AI assistants are useful for writing code. They are not a trustworthy source of dependency names, and your build pipeline should not treat them as one.



