Required Reading
~5 min

Incident Playbooks

Step‑by‑step actions for common npm threat scenarios.

Postinstall downloads a binary

CRITICAL
  1. Block PR/update. Capture the tarball and diff recent versions.
    Stops propagation while you verify what changed and whether payload is legitimate.
  2. Verify host: must be trusted GitHub Releases/CDN. If unknown, treat as malicious.
    Attackers commonly host payloads on throwaway domains to avoid scrutiny.
  3. Look for obfuscation and child_process launches. Remove the dependency if non‑essential.
    Hidden behavior plus shell execution is a strong indicator of malware.
  4. Quarantine build containers; rotate tokens if any env secrets were accessible.
    If the payload ran, credentials may be compromised; rotate to be safe.

Prevent this from recurring

  • Default CI to npm ci --ignore-scripts; allowlist only packages with a documented, legitimate reason to run lifecycle scripts.
  • Require checksum or signature verification for any binary a lifecycle script downloads.
  • Restrict install-time egress to the registry and trusted GitHub Releases/CDN hosts.

Suspected typosquat

HIGH
  1. Check maintainers and repo; confirm legitimate lineage.
    Typosquats often have unknown maintainers or no real upstream project.
  2. Inspect README and code size; tiny or empty repos are red flags.
    Malicious packages minimize content and hide behavior in postinstall.
  3. Replace with the intended package; add allow‑lists to prevent recurrence.
    Prevents future accidents in CI and developer machines.

Prevent this from recurring

  • Pin dependencies by exact version in lockfiles and review new dependency additions during PR review, not just at install time.
  • Run new/unfamiliar package names through search_packages' possibleTyposquatOf check before adding them.
  • Have engineers copy package names from npmjs.com search results rather than typing from memory or a chat message.

Supply‑chain compromise

CRITICAL
  1. Pin to last known‑good version; block auto‑upgrades.
    Freezes to a safe state while investigation proceeds.
  2. Audit changes in the compromised release: look for obfuscation, network I/O, or env access.
    These are common attacker behaviors in compromised releases.
  3. Run builds with --network=none and scrubbed HOME until confidence restored.
    Limits blast radius and stops exfil while you assess.
  4. If the compromised version was installed anywhere (a laptop, a CI runner), rotate every credential that machine could read: npm and GitHub tokens, cloud keys, CI secrets.
    Install-time malware like the Shai-Hulud worm harvests tokens on the first install; pinning afterwards does not undo a theft that already happened.
  5. Monitor maintainer activity; require two‑person review for re‑adoption.
    Adds friction and oversight for future releases.

Prevent this from recurring

  • Require --provenance and two-person review before adopting the first re-published version after any ownership or maintainer change.
  • Run first-adoption builds in a container with --network=none and a scrubbed HOME until confidence is restored.
  • Subscribe to GitHub Security Advisories (or npmscan's get_latest_advisories) for every direct and transitive dependency, not just at upgrade time.

Our own npm account or tokens published malware

CRITICAL
  1. Revoke every npm access token on the affected accounts and reset their passwords; revoke GitHub personal access tokens and any CI secrets that could publish.
    Worms like Shai-Hulud reuse stolen npm and GitHub tokens to publish again; until they are revoked, the attacker can keep releasing.
  2. Rotate every other credential reachable from the machines and CI runners where the malicious version was installed (cloud keys, deploy keys, .npmrc, .env files).
    The payload usually harvests whatever secrets it can read on install, not just npm tokens.
  3. List what was published from the affected accounts during the incident window (npm view <pkg> time for each package you maintain) and compare each release against your source.
    A stolen token is used against every package the account can publish to, often within hours.
  4. Deprecate the malicious versions with a clear message, unpublish them where npm's policy allows, and publish a clean version above them.
    Deprecation warns anyone who already depends on the bad version; a higher clean version stops ranges like ^x.y from resolving to it.
  5. Check your GitHub org for repositories, workflows or branches you did not create, and remove them.
    Shai-Hulud created public repositories and pushed workflow files to exfiltrate secrets and persist.
  6. Tell your users: publish an advisory naming the affected versions and the safe version, and report it to npm security.
    Downstream projects need the exact versions to check their lockfiles and rotate their own credentials.

Prevent this from recurring

  • Publish from CI with npm trusted publishing (OIDC) instead of long-lived tokens, so there is no token on a laptop to steal.
  • Require two-factor authentication for publishing on every maintainer account.
  • Publish with --provenance so a release that did not come from your CI stands out.

child_process in install scripts

HIGH
  1. Assume high risk. Identify exact command and purpose.
    Shell commands during install can touch filesystem, network, and secrets.
  2. If purely build‑related and host chain is trusted, isolate and whitelist.
    Some native builds need compilers; whitelist only after verification.
  3. Otherwise, remove or replace the dependency; report to registry/security teams.
    Prevents propagation and helps the ecosystem respond quickly.

Prevent this from recurring

  • Default npm_config_ignore_scripts=true in CI; allowlist specific packages with a documented native-build reason.
  • Block outbound network access during install (--network=none) so a shell-piped download can't complete even if a script runs.
  • Alert on any new child_process/exec/spawn usage a dependency bump introduces, not just at first install.

Unexpected network activity during install

MODERATE
  1. Capture logs; identify which package initiates outbound connections.
    Pinpoints the source for targeted mitigation.
  2. Run with --network=none; confirm build still succeeds.
    Proves whether network access is truly required.
  3. Allowlist domains if needed and verify checksums/signatures.
    Minimizes exposure when network dependency is legitimate.

Prevent this from recurring

  • Run installs with --network=none in CI to prove which dependencies actually require network access.
  • Allowlist only the registry and known trusted hosts for install-time egress; alert on anything else.
  • Treat a newly introduced network call in a lifecycle script (see diff_dependencies' installScriptIntroduced) as a blocking review item, not routine.

Maintainer change flagged

HIGH
  1. Freeze to last known‑good version; audit diffs of latest release.
    Stabilizes while you verify new ownership.
  2. Check repo activity and communication; look for transparency.
    Legitimate handovers are usually documented.
  3. Require two‑person review for first re‑adopted versions.
    Adds oversight during the riskiest period.

Prevent this from recurring

  • Alert on any maintainer-list change or repository transfer/archival for production dependencies, not just at upgrade time.
  • Require two-person review for the first version published under a changed maintainer list.
  • Prefer packages published via npm trusted publishing (OIDC) over long-lived personal publish tokens where the option exists.

Publish‑provenance mismatch

HIGH
  1. Freeze to the last version whose provenance/source‑diff came back clean.
    A mismatched build attestation or an install script/dependency absent from source is the signature of a stolen‑token publish that bypassed CI.
  2. Diff the flagged version's install scripts and dependencies against the git source at the attested commit yourself.
    Confirms exactly what was added outside the normal release process before deciding how far the compromise reaches.
  3. Rotate the npm publish token and any CI secrets with access to it for this package.
    A bypassed‑CI publish means the legitimate publish credential itself may be compromised, not just the install-time environment.
  4. Require --provenance and a second maintainer's review before trusting a new release from this package again.
    Adds a verifiable build trail and oversight so the same bypass can't recur silently.

Prevent this from recurring

  • Prefer dependencies published with --provenance, and treat a package missing it while its scope/maintainer peers consistently have it as worth checking.
  • Verify the attested source commit/repository matches package.json's declared repository before trusting a new release.
  • Rotate npm publish tokens on a schedule and scope them to trusted CI environments only, not long-lived personal tokens.