---
title: "Is the npm package your AI agent added safe?"
fixMetaTitle: "Is your AI-added npm package safe?"
slug: hallucinated-and-malicious-npm-packages
severity: high
category: Supply chain
summary: "An agent can add an npm name that does not exist yet, which an attacker can register later, or a real package whose install script runs with your tokens."
platforms:
  - Next.js
  - Vercel
  - GitHub
updated: "2026-10-19"
published: "2026-10-19"
related:
  - outdated-dependencies
  - secrets-committed-to-git
  - secret-keys-in-the-frontend
  - prompt-injection
securityClaim: true
---

An AI coding agent can hand you a package name that is not on npm. Anyone can register that name afterward. People call that slopsquatting. Andrew Nesbitt writes that Seth Larson suggested the name in April 2025. The USENIX Security 2025 paper by Spracklen et al., "We Have a Package for You!", calls the same behavior package hallucination. The paper's adversary model is a model that recommends a missing name, and an attacker who then publishes that name so a later install gets the attacker's code.

The other failure is a real package. It can be malicious from the first publish, or a compromised version of a package you already trust. [CERT-In advisory CIAD-2025-0034](https://www.cert-in.org.in/s2cMainServlet?VLCODE=CIAD-2025-0034&pageid=PUBVLNOTES02) says the Shai-Hulud campaign's malicious versions ran a `postinstall` script. That script runs on the machine or build server doing the install, in an environment that can hold GitHub tokens and cloud API keys. A known-vulnerability audit is a different check. See [Outdated dependencies](/issues/outdated-dependencies) and [npm audit](/tools/npm-audit).

Treat a new name from an agent as untrusted until you have looked it up. "Is this npm package safe" is a question about the registry record, the repository, and the install scripts, not a question the model can answer by sounding sure.

## What the research found

Spracklen, Wijewickrama, Sakib, Maiti, Viswanath, and Jadliwala published "We Have a Package for You! A Comprehensive Analysis of Package Hallucinations by Code Generating LLMs" at the 34th USENIX Security Symposium in 2025. The results section says they ran 30 tests, using 16 models for Python and 14 models for JavaScript, and produced 576,000 code samples. Those tests recommended 2.23 million packages. Of those recommendations, 440,445 (19.7%) were hallucinations, covering 205,474 unique names that were not in PyPI or npm.

The abstract says the average percentage of hallucinated packages is at least 5.2% for commercial models and 21.7% for open-source models. The results section attributes that 5.2% to the GPT series, against 21.7% for the open-source models. Python code hallucinated less than JavaScript: 15.8% on average, against 21.3% for JavaScript. These are the paper's numbers for the models and prompts it tested. They are not a rate for any coding agent today.

The paper also measured how close hallucinated Python names were to a real package. In that test, 13.4% (10,263 of 76,489) were a Levenshtein distance of 1 or 2 from the nearest valid name, which the paper defines as an off-by-one error of one or two characters. The paper's conclusion from that distribution is that most of those names are not simple typos.

## What a malicious install does

CISA's alert of September 23, 2025, "Widespread Supply Chain Compromise Impacting npm Ecosystem," describes a self-replicating worm publicly known as Shai-Hulud. CISA says it compromised over 500 packages. After initial access, the actor's malware scanned the environment for credentials. It targeted GitHub personal access tokens and API keys for Amazon Web Services, Google Cloud Platform, and Microsoft Azure. CISA says the malware then exfiltrated those credentials to an endpoint the actor controlled, uploaded them to a public repository named `Shai-Hulud` through the `GitHub/user/repos` API, and spread by authenticating to the npm registry as the compromised developer, injecting code into other packages, and publishing compromised versions.

CISA's recommendations in that alert are to review dependencies across software that uses npm, check `package-lock.json` or `yarn.lock` for affected packages including nested ones, and search cached copies in artifact repositories and dependency tools. Pin npm dependencies to known safe releases produced prior to September 16, 2025. Rotate all developer credentials. Mandate phishing-resistant multifactor authentication on developer accounts, especially GitHub and npm. Monitor for unusual network traffic, block outbound connections to `webhook.site` domains, and watch firewall logs for suspicious domains. On GitHub, remove unnecessary GitHub Apps and OAuth applications, audit repository webhooks and secrets, enable branch protection, and enable secret scanning alerts and Dependabot security updates.

CISA's alert does not describe how the package code started. CERT-In advisory CIAD-2025-0034, issued September 25, 2025, says malicious versions in this campaign contain a worm that executes a post-installation (`postinstall`) script. That advisory says the script scans for `.npmrc` npm tokens, environment variables, GitHub personal access tokens, and cloud API keys for AWS, GCP, and Azure, then publishes further tainted versions with the compromised developer's npm credentials. CERT-In also says to look for unauthorized repositories named "Shai-Hulud," a workflow at `.github/workflows/shai-hulud-workflow.yml`, and unexpected branches named `shai-hulud`.

## How to check in 5 minutes

1. After every agent session, run `git diff` on `package.json` and the lockfile (`package-lock.json`, `pnpm-lock.yaml`, or `yarn.lock`). A name you did not ask for is the stop.
2. For each new name, run `npm view NAME time.created repository.url`, with NAME replaced by that package. The npm view docs say child fields are separated with a period, and `repository.url` is the documented field for the git URL. A package whose `time.created` is a few days ago and whose repository is empty, or a name one letter off a package you already use, is a stop.
3. Check new packages for install scripts. On npm 12, `npm install-scripts ls` lists dependency install scripts that `allowScripts` does not cover yet. On older npm, `npm view NAME scripts` prints the scripts field. Read any `preinstall`, `install`, or `postinstall`.
4. Turn on [Dependabot malware alerts](/tools/github-dependabot). In the repository, open Settings, then Advanced Security, enable Dependabot alerts, then enable Dependabot malware alerts. GitHub lists malware alerts with the security features available on all plans, without buying GitHub Code Security.

## How to fix

### npm

npm 12.0.0, released July 8, 2026, says dependency lifecycle scripts are blocked by default unless the root package's `allowScripts` policy allows them. The v12 `npm install-scripts` docs say the same thing: install commands skip lifecycle scripts for a dependency with no matching `allowScripts` entry, and they list what they skipped. `npm install-scripts approve PKG` records the approval in `allowScripts` in `package.json`. PKG is the package name. The `allow-scripts-pin` config defaults to true, so the command writes a version-pinned entry (`pkg@1.2.3`). The 12.0.0 notes say to run `npm rebuild` afterward if you want the newly approved scripts to execute.

GitHub's June 9, 2026 changelog says npm 11.16.0 and newer still run those scripts and print a warning, and that the block is the v12 change. The v11 `npm install-scripts` page, fetched on October 19, 2026, uses the sentence "Dependency install scripts are blocked by default." That page and the June changelog do not agree. Run `npm --version`. If it is not 12, do not assume a script was skipped. `ignore-scripts=true` is the blunt option on the versions that still run scripts: the config docs say npm then does not run scripts specified in `package.json` files. Commands such as `npm test` and `npm run` still run the script you named, and they skip pre- and post-scripts.

`--dangerously-allow-all-scripts` is a real flag. The v12 config docs say it bypasses `allowScripts` and runs every dependency install script, and that it is a migration escape hatch whose use is strongly discouraged. Do not set it to make an install pass.

npm 11.10.0, released February 11, 2026, added `min-release-age`. The v11 and v12 config docs say it is a number of days: only versions published more than that many days ago are installed. Set it in `.npmrc` as `min-release-age=7`. `min-release-age-exclude` is a list of names or glob patterns that skip the window. If `before` and `min-release-age` are both set in the same config source, `before` wins. The documented trade-off is the same in those docs: when the window stops `npm audit fix` from installing a patched version because the fix is too new, npm keeps the vulnerable version, warns that the fix was blocked, and exits with a non-zero code.

In CI, commit the lockfile and install with `npm ci`. The v12 `npm ci` docs say the command is for automated environments, requires `package-lock.json`, exits with an error if the lockfile does not match `package.json`, and does not write to either file.

### pnpm

pnpm's v10 settings say that since v10, lifecycle scripts of dependencies do not run unless the package is listed in `onlyBuiltDependencies`. The v11.0 release notes say `allowBuilds` replaces `onlyBuiltDependencies` and the other legacy build-dependency settings. The current supply-chain page says v10 disables automatic execution of `postinstall` scripts in dependencies and recommends listing trusted dependencies in `allowBuilds`.

`minimumReleaseAge` was added in v10.16.0. The v10 settings page types it as a number of minutes and defaults it to 0. The same page says: "In most cases, malicious releases are discovered and removed from the registry within an hour." That is pnpm's claim. The v11.0 release notes change the default to `1440` (1 day) and say to set `minimumReleaseAge: 0` in `pnpm-workspace.yaml` to opt out. The v10 settings page is titled for `pnpm-workspace.yaml`.

### Vercel

Vercel runs an install command as part of the build. The builds docs say the install command is set with the build command, and the fundamentals guide says the build installs dependencies by detecting the lockfile and respecting it. Commit the lockfile so that install has one to respect.

The builds docs say Vercel injects project environment variables, including API keys, during the build, and that those variables stay inside the build environment. Vercel's Node.js docs say to put an npm token in a project environment variable named `NPM_TOKEN` (or the contents of an `.npmrc` in `NPM_RC`) so private modules can install. A token you place there is available to that install. Prefer to keep other long-lived secrets out of build environment variables. If a malicious version was installed in a build, rotate the credentials that build could see. CISA's alert says to rotate all developer credentials. A token that reached git is the separate problem in [Keys committed to GitHub](/issues/secrets-committed-to-git).

### GitHub

Dependabot malware alerts fire when a package on the repository's default branch is flagged as malicious. Alerts for dependencies you already have are generated once the package is flagged in the GitHub Advisory Database, and also when you push a commit that adds a known malicious package or moves one to a known malicious version. The limitations on that page say only advisories reviewed by GitHub trigger alerts, and that new malware can take time to appear in the database and trigger an alert. Alerts do not catch every issue. Keep the manifest and the lockfile current so the scan sees what you installed.

The GitHub changelog of July 28, 2026 says the GitHub Advisory Database now ingests malware advisories from the OpenSSF malicious-packages repository, and that you can view them with the `type:malware` filter. The advisory database docs say the same filter. Browse `type:malware` there when you want the advisory itself rather than the alert on your repo.

[Socket](/tools/socket) looks at dependency behavior. [OSV-Scanner](/tools/osv-scanner) matches lockfiles to known vulnerabilities in the OSV database. Neither one replaces reading the diff of a name an agent just added.

## Agent prompt

```text
You are adding or reviewing dependencies. Do not add features.

1. Never add a dependency without listing the name and saying why it is needed.
2. Prefer a language built-in or a package already in package.json.
3. Before adding a package, confirm it exists on npm. Show me the repository URL and the first publish date from `npm view NAME time.created repository.url`.
4. Never run npm install with --dangerously-allow-all-scripts. Never approve an install script unless you tell me the package name, the version, and the script you read.
5. Do not edit .npmrc or allowScripts to make an install pass.
6. If a name does not resolve on npm, stop and ask me. Do not invent a replacement and install it.

End with the packages you added, the packages you refused, and any install script you want me to approve.
```

## FAQ

### Does npm audit catch this?

`npm audit` sends the dependencies in your project to the registry and asks for a report of known vulnerabilities. A brand-new package, or a compromised version that has no advisory yet, is absent from that report. Read the report for the vulnerabilities it does list, and still check new names and install scripts. [OSV-Scanner](/tools/osv-scanner) is the same kind of check against the OSV database.

### Is a typo-squat the same as a hallucinated package?

A typo-squat is a published name a character or two away from a package people already install. A hallucinated package is a name a model recommended that was not in the registry. The USENIX paper calls that package hallucination. In its test of hallucinated Python names, 13.4% (10,263 of 76,489) were one or two characters from the nearest valid name, and the paper says most of those names are not simple off-by-one errors. Either kind is a reason to stop: look up `time.created` and `repository.url` before you install.

### My agent installed something weird. What now?

Remove the name from `package.json`, delete `node_modules`, and reinstall from a lockfile you trust with `npm ci` or the pnpm equivalent. If an install script ran on your machine or in a Vercel build, rotate the tokens that environment had. CISA's Shai-Hulud alert says to rotate all developer credentials, mandate phishing-resistant MFA on GitHub and npm, and audit GitHub Apps, OAuth applications, repository webhooks, and secrets. CERT-In's advisory for that campaign also says to look for a public repository named Shai-Hulud, `.github/workflows/shai-hulud-workflow.yml`, and a branch named `shai-hulud`. For that alert specifically, CISA says to pin npm dependencies to known safe releases produced prior to September 16, 2025.

## Sources

- [Spracklen et al., USENIX Security 2025, "We Have a Package for You!"](https://www.usenix.org/conference/usenixsecurity25/presentation/spracklen)
- [Spracklen et al. paper (PDF)](https://www.usenix.org/system/files/usenixsecurity25-spracklen.pdf)
- [CISA alert, September 23, 2025: Widespread Supply Chain Compromise Impacting npm Ecosystem](https://www.cisa.gov/news-events/alerts/2025/09/23/widespread-supply-chain-compromise-impacting-npm-ecosystem)
- [CERT-In advisory CIAD-2025-0034](https://www.cert-in.org.in/s2cMainServlet?VLCODE=CIAD-2025-0034&pageid=PUBVLNOTES02)
- [Andrew Nesbitt, "Slopsquatting meets Dependency Confusion"](https://nesbitt.io/2025/12/10/slopsquatting-meets-dependency-confusion.html)
- [npm config (v12)](https://docs.npmjs.com/cli/v12/using-npm/config/)
- [npm config (v11)](https://docs.npmjs.com/cli/v11/using-npm/config/)
- [npm install-scripts (v12)](https://docs.npmjs.com/cli/v12/commands/npm-install-scripts/)
- [npm install-scripts (v11)](https://docs.npmjs.com/cli/v11/commands/npm-install-scripts/)
- [npm ci (v12)](https://docs.npmjs.com/cli/v12/commands/npm-ci/)
- [npm view (v11)](https://docs.npmjs.com/cli/v11/commands/npm-view/)
- [npm audit (v11)](https://docs.npmjs.com/cli/v11/commands/npm-audit)
- [npm CLI v11.10.0](https://github.com/npm/cli/releases/tag/v11.10.0)
- [npm CLI v12.0.0](https://github.com/npm/cli/releases/tag/v12.0.0)
- [GitHub changelog, June 9, 2026: upcoming npm v12 breaking changes](https://github.blog/changelog/2026-06-09-upcoming-breaking-changes-for-npm-v12/)
- [pnpm: mitigating supply chain attacks](https://pnpm.io/supply-chain-security)
- [pnpm v10 settings](https://pnpm.io/10.x/settings)
- [pnpm 11.0 release notes](https://pnpm.io/blog/releases/11.0)
- [Dependabot malware alerts](https://docs.github.com/en/code-security/concepts/supply-chain-security/malware-alerts)
- [Configuring Dependabot malware alerts](https://docs.github.com/en/code-security/how-tos/secure-your-supply-chain/secure-your-dependencies/configure-malware-alerts)
- [GitHub security features](https://docs.github.com/en/code-security/getting-started/github-security-features)
- [GitHub Advisory Database](https://docs.github.com/en/code-security/concepts/vulnerability-reporting-and-management/github-advisory-database)
- [GitHub changelog, July 28, 2026: Dependabot alerts on malicious packages across more ecosystems](https://github.blog/changelog/2026-07-28-dependabot-alerts-on-malicious-packages-across-more-ecosystems/)
- [How Vercel builds your application](https://vercel.com/docs/fundamentals/builds)
- [Vercel builds](https://vercel.com/docs/builds)
- [Configuring a Vercel build](https://vercel.com/docs/builds/configure-a-build)
- [Vercel advanced Node.js usage, including private npm modules](https://vercel.com/docs/functions/runtimes/node-js/advanced-node-configuration)
