Back to glossary

Slopsquatting

Slopsquatting is registering a package name that AI coding tools hallucinate, so developers or agents who follow the AI's advice install attacker code.

Also known as AI package hallucination, package hallucination attack, hallucinated package, hallucinated dependency

Slopsquatting is a software supply chain attack in which someone registers a package name that AI coding tools invent, then waits for a developer or agent to install it. The underlying model error is called package hallucination. Models repeat the same fake names across prompts and vendors, so one registration can reach many victims.

The mechanism is a namespace gap. Code refers to packages by import names, CLI commands, and plugin shorthands, but a registry resolves a separate, flat namespace of distribution names that anyone can claim first. A model trained on code learns the first namespace well and guesses at the second. The most important fact is that the guesses are stable. In the USENIX Security 2025 study of 16 code models, 43% of hallucinated package names came back in all ten repeated runs of the same prompt. In 2026, five frontier models from four vendors produced the same 127 fake names.

Our position: slopsquatting is not mainly a model-accuracy problem, and typosquatting defenses will not catch it. Nearly half of hallucinated names are six or more edits away from any real package, so name-similarity checks have nothing to match. The control has to sit at the moment a developer or agent installs or runs a name for the first time. It has to check what the package contains and who published it, not merely whether the name exists.

What is slopsquatting?

#

Slopsquatting is the registration of a package name that a large language model (LLM) hallucinated, so that code written or run on the model's advice installs the registrant's code. The word joins "AI slop" and typosquatting. Python Software Foundation Developer-in-Residence Seth Larson coined it, and Andrew Nesbitt popularized it in April 2025.

Slopsquatting and typosquatting share a payoff and differ in who makes the mistake. Typosquatting bets that a human will mistype requests as reqeusts. Slopsquatting bets that a model will confidently write a name nobody ever published, and that the human or agent acting on it will not check. A typosquat needs a popular target to imitate. A slopsquat needs only a name the model keeps producing.

A slopsquatted package delivers its payload the way any malicious package does: install hooks, import-time code, or a command-line entry point. The targeting is what is new. The attacker does not guess what humans will mistype. The attacker queries models, collects the names they invent, and registers the ones that recur. Slopsquatting is the clearest supply chain risk specific to AI-generated code, because the vulnerable artifact is a single line the model wrote: the install command.

Two namespaces, one gap

#

Every package ecosystem has two names for the same thing. Source code uses the import name or the command name. The registry uses the distribution name. They often differ, and nothing in the language requires them to match.

Python shows the gap most clearly. import sklearn comes from the scikit-learn distribution, import bs4 from beautifulsoup4, and import attr from attrs. The huggingface-cli command ships inside huggingface_hub. A model that has read millions of import lines learns the left side of each mapping far better than the right.

We queried PyPI's JSON API (https://pypi.org/pypi/<name>/json) and the npm registry on October 27, 2026 to see what each code-side name resolves to when someone types it into an installer.

Name in codeReal distributionWhat the code-side name resolves to (October 2026)
import sklearnscikit-learnsklearn on PyPI: a deprecated placeholder from the scikit-learn project, uploaded December 2023, that fails to install and tells you to use scikit-learn
import bs4beautifulsoup4bs4 on PyPI: a dummy package from Beautiful Soup's maintainer that depends on beautifulsoup4
import attrattrsattr on PyPI: an unrelated decorator library, last released in 2022
huggingface-cli download …huggingface_hubhuggingface-cli on PyPI: 404 today, but registered as an empty research package in 2024 (see below)
import "@ember/service"ember-source@ember/service on npm: 404, and the @ember scope is owned, so no one else can publish it
ESLint plugins: ["unused-imports"]eslint-plugin-unused-importsunused-imports on npm: a security-holding package since October 27, 2025, after npm removed versions that contained malicious code

Three outcomes show up in that table. A maintainer can defend the code-side name, as Beautiful Soup and scikit-learn did. The bs4 description states the intent plainly: "This is a dummy package designed to prevent namesquatting on PyPI." A stranger can already own the name, as with attr, which is harmless here but proves the name was open to anyone. Or the name can sit unclaimed, and then whoever registers it next decides what the install command does.

npm scopes close the gap structurally. The npm documentation states that "only you can add packages in your scope," so @ember/service returns a 404 that no attacker can fill. Unscoped npm names, and every PyPI name, are first come, first served.

How often do AI models hallucinate package names?

#

AI models hallucinated package names in 4.6% to 6.1% of code answers in 2026 tests, down from about 20% across older models. The USENIX Security 2025 study by Spracklen et al. generated 576,000 Python and JavaScript samples from 16 models. It found that 19.7% of recommended packages did not exist. Commercial models averaged 5.2% and open source models 21.7%, and the samples contained 205,474 unique fake names.

Repetition is what makes those rates exploitable. The researchers re-ran 500 hallucination-triggering prompts ten times each. 43% of the fake names reappeared every time, and 58% appeared more than once. An attacker does not need anyone's prompt logs. A few hundred queries reveal which names a model will keep producing.

The 2026 frontier models narrowed the spread without closing the gap. Aleksandr Churilov's replication, run from April 22 to 28, 2026, measured five models on the same prompt corpus across 199,845 responses.

ModelOverallPythonJavaScript
Claude Haiku 4.54.62%5.49%2.76%
Claude Sonnet 4.65.41%6.63%2.62%
Gemini 2.5 Pro5.80%6.75%3.61%
DeepSeek V3.25.89%6.69%3.78%
GPT-5.4-mini6.10%7.27%3.14%

GPT-5.4-mini was tested at minimal reasoning effort and refused 32.14% of prompts, so its rate covers only the answers it gave. Python rates exceeded JavaScript rates for every model. Churilov suggests that newer models absorbed npm's naming conventions better than Python's mix of import names and distribution names, which is the namespace gap again.

Cross-model overlap matters more to an attacker than any single rate. All five models produced the same 127 fake names, so one registration reaches users of every vendor at once. Churilov's paper puts it directly: "Single-model rate measurements understate the attack surface."

Names hallucinated by all 5 models127
109 PyPI, 18 npm
After PyPI prohibited-name and normalization rules59
68 PyPI names already unregistrable
After npm review53
6 npm false positives removed: 4 Ember virtual modules, 1 existing package, 1 security hold
PyPInpmremoved

Socket and PyPI Security reviewed the 127 shared names through coordinated disclosure. PyPI's prohibited-name and normalization rules already blocked 68 of the 109 Python names, which left 41. Our review of the 18 npm names removed six false positives. Four were Ember.js virtual modules (@ember/service, @ember/object, @ember/routing, @ember/controller) that resolve through ember-source. One, ssh-keys, was an existing package. One, metro-evaluator, was already a security hold. That left 53 registrable names as of April 2026: 41 on PyPI and 12 on npm. The Ember cases are correct code with no matching registry entry, which is why a registry lookup alone overcounts hallucinations.

The paper's public examples included the npm name css-color-stop. On July 28, 2026, six days after our coverage of the study, someone registered it:

Bash
$ curl -s https://registry.npmjs.org/css-color-stop \
    | jq '{time, fileCount: .versions["0.0.0"].dist.fileCount, unpackedSize: .versions["0.0.0"].dist.unpackedSize}'
{
  "time": {
    "created": "2026-07-28T19:22:40.055Z",
    "0.0.0": "2026-07-28T19:22:40.251Z",
    "modified": "2026-07-28T19:22:40.538Z"
  },
  "fileCount": 1,
  "unpackedSize": 53
}

The tarball holds a 53-byte package.json with a name, a version, and nothing else, so today the package is harmless. It still shows the race. A published hallucination list is a registration list, and whoever reads it first decides what the name installs.

Why typosquatting defenses miss slopsquatting

#

Typosquatting defenses compare a new name against popular names, and most hallucinated names are not close to anything. In the USENIX study, only 13.4% of hallucinated names were within an edit distance of 2 of a real package. Another 37.9% were 3 to 5 edits away, and 48.6% were 6 or more.

npm's registry-side rule shows the limit. Since December 2017, npm has refused a new name that matches an existing package once punctuation is removed. That rule blocks reactnative because react-native exists. A blended name like react-codeshift matches nothing after stripping punctuation, so the rule never fires.

Socket's Possible typosquat attack alert has the same blind spot by design. It ranks candidate names by similarity to much more popular packages, which catches reqeusts and has no reason to flag a plausible name that resembles no single target. Similarity checks still catch the 13.4% of hallucinations that are near-misses. They cannot be the primary control.

The punctuation rule has a second effect: a registry 404 does not prove a name is free. A target list built from 404s overcounts on npm, for researchers and attackers alike.

How does a hallucinated package name reach an install?

#

A hallucinated package name reaches an install through three paths: a person copying a chat answer, a written artifact that preserves the name, and an agent that runs it directly. The second and third paths are where slopsquatting scales.

A README: huggingface-cli

#

In 2024, security researcher Bar Lanyado found that several models kept telling users to run pip install huggingface-cli. The real command ships with huggingface_hub. Lanyado uploaded an empty package under the hallucinated name, and it received more than 30,000 authentic downloads in three months. The name had already escaped the chat window. Alibaba's GraphTranslator README told readers to install it:

Bash
pip install -U huggingface_hub
pip install huggingface-cli

The first line installs the real package. The second line installs whatever owns the hallucinated name. On PyPI, installing a source distribution runs the package's build backend, so the owner's code executes during pip install itself. pip's --only-binary :all: option refuses source distributions, at the cost of failing for any package that ships no wheel.

A skill file: react-codeshift

#

On October 17, 2025, a single commit to the public wshobson/agents repository added 47 agent skills across 14 plugins. The react-modernization skill told agents to run a package that did not exist:

Bash
# Install jscodeshift
npm install -g jscodeshift

# React 16.9 codemod (rename unsafe lifecycle methods)
npx react-codeshift <transform> <path>

The name blends jscodeshift, the codemod runner, with react-codemod, the React transform collection. Forks copied the file unchanged, so one model error became a standing instruction in many repositories. A security researcher registered react-codeshift on January 14, 2026, as a placeholder whose description reads "Placeholder to prevent dependency confusion." The upstream skill has since been fixed.

The agent loop: npx with no TTY

#

Agent execution removes the last human gate. When a requested package is missing locally, npm exec prints a confirmation prompt. The npm exec documentation adds: "When standard input is not a TTY or a CI environment is detected, --yes is assumed." Most coding agents run commands in subprocess shells without a TTY. An npx command in a skill therefore fetches and runs whatever owns the name, without asking.

  1. Model, writing a skillEmits npx react-codeshift --parser=tsx … into SKILL.md.
    Checks: none. The name blends jscodeshift and react-codemod.
  2. GitCommit lands in a public repo. Forks copy the file unchanged.
    Checks: none. The command is now a durable instruction.
  3. AgentActivates the skill and runs the command in a subprocess shell.
    stdin is not a TTY.
  4. npm execPackage is not in local dependencies, so npm would prompt. With no TTY, --yes is assumed.
    Checks: none. The prompt was the only human gate.
  5. RegistryGET /react-codeshift → 404. The command fails and the agent moves on or retries another name.
    RegistryGET /react-codeshift → 200. The tarball belongs to whoever registered the name first.
  6. OutcomeNothing executes. The skill keeps shipping a broken command to every fork.
    Outcomenpm unpacks it into the npm cache, puts its bin on PATH, and runs it with the agent's credentials. Blocking install scripts does not stop this. Running the bin is the requested action.
Steps 1 to 4 are identical in both states. Only the registry's answer differs.

The figure traces the react-codeshift skill through six steps, in two states. Steps 1 to 4 are identical in both. (1) A model writes npx react-codeshift … into SKILL.md, with no check that the package exists. (2) The commit lands in a public repository, and forks copy the file. (3) An agent activates the skill and runs the command in a shell whose standard input is not a TTY. (4) npm exec finds the package missing locally and assumes --yes. In the unclaimed state, before January 14, 2026, (5) the registry returns 404 and (6) nothing runs, though every fork keeps shipping the broken command. In the claimed state, (5) the registry returns 200 with a tarball owned by whoever registered the name first, and (6) npm unpacks it into the npm cache, puts its bin on PATH, and runs it with the agent's credentials.

Install-script defaults do not help on this path. npm v12 turned dependency install scripts off by default in July 2026, and pnpm 10 blocked lifecycle scripts by default in January 2025. Both changes close the install script path that most recent npm worms used. With npx, the package's own entry point is the thing the user asked to run, so there is nothing for a script policy to block. uvx, pipx run, and bunx work the same way.

A timeline of slopsquatting evidence

#

The record so far is short, and most of it comes from registries and Git history rather than incident reports.

DateEventEvidence
March 28, 2024A researcher reports that an empty huggingface-cli package he uploaded to PyPI drew more than 30,000 downloads in three months. An Alibaba README tells users to install it.Lasso Security write-up; GitHub commit 4394d72
April 2025Seth Larson coins "slopsquatting." Andrew Nesbitt popularizes it.Socket coverage
2025USENIX Security study: 19.7% of 2.23 million recommended packages do not exist.Spracklen et al.
October 17, 2025A commit adding 47 agent skills includes npx react-codeshift.GitHub commit 65e5cb0
October 27, 2025npm replaces unused-imports, the ESLint shorthand for eslint-plugin-unused-imports, with a security-holding package. The removed versions were published March 17 and September 15, 2025.npm registry metadata
January 14, 2026react-codeshift is registered as an empty placeholder.npm registry metadata
March 28 to April 9, 2026huggingface-cli appears on npm as two dev versions and is replaced by a security-holding package 12 days later.npm registry metadata
April 22 to 28, 2026Five frontier models share 127 hallucinated names. 53 are registrable.Churilov; Socket and PyPI review
July 28, 2026css-color-stop, one of the published names, is registered as a 53-byte placeholder.npm registry metadata

As of October 2026, we know of no publicly confirmed malware campaign that was shown to be built by harvesting model hallucinations. The registrations on record are research packages, defensive placeholders, and removed packages whose names sit in the namespace gap but whose origin is unproven. unused-imports is the closest case. The name is the one an ESLint config uses, not the name of the package that provides the plugin, and npm removed it for malicious code. Nobody has shown that a model put it in front of its victims. We expect that evidence gap to close, because the economics favor the attacker: one registration, no popular target to imitate, and agents that execute without asking.

How do you prevent slopsquatting?

#

You prevent slopsquatting by vetting every new package name at the moment it first enters a project or a shell, and by checking its publisher, age, and contents rather than only its existence. An existence check is the most common advice and the weakest control. Once an attacker registers the name, the check passes.

ControlWhat it stopsWhat it misses
Check that the name existsUnclaimed hallucinations, which fail anywayEvery registered slopsquat. Existence is the attacker's goal.
Typosquat detection by name similarityNear-miss names, about 13% of hallucinationsFabricated and blended names with no close target
Install scripts off by default (npm v12, pnpm 10+)Payloads in preinstall and postinstallnpx and uvx entry points, import-time code, Python source builds
Dependency cooldown (pnpm 11 defaults to 24 hours)Names registered in reaction to a disclosure, inside the windowNames registered before anyone needs them. css-color-stop cleared a 24-hour window on July 29, 2026.
Lockfiles and pinningDrift after a dependency was reviewedThe first introduction of a name. npx never touches the lockfile.
Scopes and defensive registrationNames maintainers anticipate, such as bs4, sklearn, @ember/*Names nobody anticipated. PyPI has no scopes.
Registry reservation lists68 of 109 shared PyPI names in 2026Names outside the list. npm has no shared hallucination blocklist.
npx --no or NPM_CONFIG_YES=false in agent environmentsAny npx fetch of a package that is not already installedDirect npm install and pip install commands
Package firewallKnown malware at install time, at any depthBrand-new or empty packages with no verdict yet

Our verdict: no single row is sufficient, and the right mix depends on who runs the command. For people, a dependency cooldown plus a package firewall covers the most ground. pnpm 11 ships a 24-hour minimum release age by default, and npm added minimumReleaseAge in early 2026. For agents, add a hard rule. An agent may not install or npx a package that is not already in the lockfile until a check returns the publisher, the age, and what the code does. In our analysis of the axios compromise, we recommend npx --no --offline in CI and NPM_CONFIG_YES=false for agent tooling, so npx errors instead of fetching. Treat an AI-suggested dependency like a pull request from a stranger.

Socket covers part of this, with a trade-off we chose on purpose. Socket Firewall Free wraps npm, yarn, pnpm, pip, uv, and cargo (sfw npm install <pkg>) and blocks confirmed malware anywhere in the dependency tree. It only warns on AI-detected potential malware, so an unconfirmed verdict never breaks a build. The cost is that a freshly registered slopsquat with no confirmed verdict gets through. Earlier in the loop, Socket MCP gives an AI assistant a depscore tool, so the assistant can look up a package's supply chain, quality, and maintenance scores before it suggests the name.

The fix belongs before the name is emitted

#

The cheapest place to stop slopsquatting is the model and the agent harness, not the registry. A coding agent that resolves every package name against the registry before it writes an install command would turn a silent hallucination into a visible error. Churilov's paper notes that agents with retrieval could in principle avoid these errors, but it did not test them. A registry lookup alone is not enough, as the Ember false positives show, so the check has to understand each ecosystem's module system. The harness also has to treat a brand-new package that matches a hallucinated name as suspect, not as confirmation.

Registries can help too. PyPI's existing prohibited-name and normalization rules blocked 68 of the 109 shared names in 2026. A coordinated list of cross-model hallucinations, reserved the way npm reserves names with security holds, would turn each published study from a target list into a blocklist. Until that list exists, every disclosure starts a race, and css-color-stop shows how short the race is.

Slopsquatting does not need models to be wrong often. It needs them to be wrong the same way, and they are.