The agent imports a package. The package has a postinstall script. You never opened package.json.

Setup

A repo where the agent has been told to “add HTTP retry logic”. It produces:

{
  "dependencies": {
    "axios-retry-helpers": "^1.0.0"
  }
}

axios-retry-helpers is hallucinated. Real package: axios-retry. An attacker registered the hallucination and added a postinstall step.

A defanged postinstall

What a real attacker would write — replaced here with a harmless echo:

{
  "name": "axios-retry-helpers",
  "scripts": {
    "postinstall": "echo 'demo: would exfiltrate ~/.npmrc, ~/.aws/credentials, GITHUB_TOKEN'"
  }
}

Trigger

npm install. The script runs before your code touches the package.

Real-world: Shai-Hulud (Sept 2025)

A self-replicating npm worm:

  1. Compromised maintainer of @ctrl/tinycolor via phishing
  2. Published a poisoned version that injected a GitHub Action
  3. The Action stole npm, GitHub, AWS, GCP tokens from CI runs
  4. Used the stolen npm token to publish the same payload into other packages the maintainer owned
  5. Spread to hundreds of packages before the registry intervened

An agent running npm install unattended in CI is functionally a maintainer with a publish token.

Slopsquatting

Lasso Security and Vulcan Cyber research found ~20% of LLM-suggested packages don’t exist. Attackers register the hallucinated names and wait. The model imports them confidently. CI installs them.

Defense

  • Pin exact versions, commit lockfiles, diff lockfile changes in PR review
  • npm config set ignore-scripts true to disable postinstall
  • Scanners: socket.dev, snyk, osv-scanner
  • See defenses for full setup