npm Supply-Chain Hardening

Harden npm projects against install-time malware and publish-path abuse

npm Supply-Chain Hardening Guide

Use this guide when you maintain an npm package or a JavaScript project that could be affected by install-time malware, compromised package maintainers, or abused CI publishing workflows.

AIWG added these controls after the May 2026 Mini Shai-Hulud npm worm wave demonstrated three high-risk patterns: lifecycle-script execution during install, Git dependency `prepare` hooks, and stolen CI/release credentials being used to publish follow-on malicious versions.

Minimum baseline

ControlRecommended defaultWhy
Runtime versionNode 20+ for users; Node 24 for release workflowsNode 24 carries a current npm 11.x and matches npm's trusted-publishing examples
npm CLInpm 11.5+ before dependency updatesRequired for `min-release-age`
Release-age gate`min-release-age=7` in repo `.npmrc`Blocks versions published less than 7 days ago from entering the lockfile
High-sensitivity gate`--min-release-age=10` for major bumps or release-prep lockfile churnAdds review time before sensitive publishes
Lifecycle scriptsRemove package-level `postinstall`/`preinstall` unless truly requiredInstall hooks are a direct code-execution surface
Exotic dep sourcesReject `git+`, `github:`, non-registry tarballs, `file:`, and `link:` by defaultGit deps run `prepare`; non-registry tarballs bypass normal registry review
Known affected package feedScan exact package/version hits from an operator-maintained CSV before release and after feed refreshesCatches packages already known-bad even when they are too new or too obscure for advisory feeds
Release authPrefer npm trusted publishing over long-lived npm tokensOIDC publish tokens are short-lived and workflow-bound
Release evidenceSigned tags, provenance, signed tarball, SBOMGives users independent checks for source, builder, bytes, and contents

Project setup

Create a repo-root `.npmrc`:

# Refuse dependency versions published less than 7 days ago.
min-release-age=7

Require npm 11.5+ wherever lockfiles can change:

npm install -g npm@^11.5
npm --version

For release workflows, use Node 24 unless you have a hard compatibility reason not to. npm trusted publishing requires npm 11.5.1+ and Node 22.14.0+; Node 24 satisfies both and reduces manual npm upgrades in CI.

Dependency-source review

Audit `package.json` and lockfiles for dependency sources outside the registry path:

rg -n '"(git\\+|git://|github:|file:|link:)|https?://[^"]+\\.(tgz|tar\\.gz)' package.json package-lock.json

Treat findings as security-relevant. If an exotic source is truly required, put it behind an allowlist entry with a reviewer, rationale, and review date. Avoid personal forks and branch-tracking Git specs in production dependency graphs.

Run the affected-package feed scan before releases, after dependency updates, and whenever the operator refreshes the feed:

npm run lint:affected-packages
AIWG_AFFECTED_PACKAGES_CSV=https://gist.githubusercontent.com/<user>/<gist-id>/raw/22-packages.csv npm run lint:affected-packages

Treat exact hits as incident-response evidence, not generic vulnerability noise. Preserve the package name, version, published timestamp, detected timestamp range, and source path/URL in the finding.

Release workflow review

Check release workflows for these properties:

  • `permissions: id-token: write` only on the publish job that needs OIDC.
  • `npm publish --provenance` or a trusted-publishing equivalent.
  • No long-lived npm publish token for npmjs.org once trusted publishing

is active.

  • Signed tag verification before build, pack, publish, or release-asset

upload.

  • Pinned action SHAs and digest-pinned containers for release jobs.
  • `npm audit signatures` against the installed dependency tree before

publish.

  • A tarball allowlist check before publish, so new root-level payload

files fail the release.

  • Signed release assets: tarball signature, manifest signature, and SBOM.

Incident response trigger

If a compromised package version ran on a workstation or CI runner, treat every secret reachable from that environment as exposed. Rotate npm tokens, GitHub/Gitea tokens, cloud credentials, Kubernetes service account tokens, Vault tokens, deployment secrets, and any package publishing permissions. Then audit recent package publishes and workflow runs before resuming releases. If the affected-package feed identifies a hit, quarantine any CI runner cache or workstation npm cache that may still contain the tarball.

AIWG skills

After deploying the security-engineering framework, use:

aiwg use security-engineering
aiwg discover "npm supply-chain audit"
aiwg discover "npm release-age gate"

The focused skills help an AI assistant perform this same audit against your repository without needing to know AIWG's internal planning issue numbers.

References