A package nobody chose can run with your developer’s permissions the moment someone types npm install. An npm malware package is a hostile release carrying code that usually steals tokens and keys; CISA’s alert of September 23, 2025 described a self-replicating worm that spread by publishing to npm as the compromised developer. The first question is whether a named bad version sits in your lockfile.

What an npm malware package is, and the four ways one reaches you

An npm malware package is a published version of a package, under a trusted or look-alike name, that carries code built to steal, usually the tokens and keys on the machine that installs it. It reaches a project by four routes: a hijacked maintainer account, a self-spreading worm, a confusable name, or a compromised build pipeline.

The npm ecosystem, in npm’s own docs, consists of three components: the website, the command line interface and the registry. When a headline says npm was hacked, or that an npm package was hacked, the useful question is which of those, or which publisher, was hit. In the chalk and debug, Shai-Hulud and TanStack cases below, the registry itself was not broken into: a publisher’s account, credentials or build pipeline was, and the compromised npm packages were real names carrying hostile versions. For the chalk and debug case, the maintainer’s own words were “It was a personal account.” Dependencies are one of the controls that make up web app security.

The four routes, each with a named example and the setting that makes it less likely to land:

RouteHow it worksNamed exampleWhat stops it
Maintainer account takeoverA phished or stolen maintainer login publishes a new patch version of a package people already trustchalk, debug and 16 other packages from one maintainer, September 8, 2025A committed lockfile installed with npm ci, and a minimum release age
Self-spreading wormCode in an installed version reads the developer’s credentials, then publishes compromised versions of more packages as that developerShai-Hulud, in CISA’s alert of September 23, 2025Install scripts off by default, and trusted publishing instead of long-lived npm tokens
Confusable nameA look-alike public name or scope gets installed in place of the intended one (typosquatting, dependency confusion)Dozens of scoped packages across nine organizational scopes, May 2026, reported by MicrosoftScoped names under an organization you own, with the scope mapped to your registry
Compromised build pipeline or CI actionThe attacker publishes through the project’s own trusted-publisher identity, or replaces the tool itselfTanStack, May 11, 2026; Trivy, March 19, 2026A lockfile and a minimum release age for packages; a full commit SHA pin for actions

A fifth route sits outside npm: a third-party script whose CDN changed hands, as with polyfill.io, told with the incidents below. Most of the malicious npm packages below ran on a developer laptop or a CI runner with access to tokens, so the damage is the credentials, not the app. The exceptions are the chalk and debug payload, which the GitHub advisories describe acting in the browser, and the polyfill.io script, which reached the visitors of pages that loaded it.

What a malicious package actually contains

Examples of malicious code in npm packages usually share four parts: a hook that runs on install or import, a payload file or dependency the previous version did not have, a harvest of tokens, keys and cloud credentials, and an exit over the network or through an upload to a public repository.

Malicious code, in this setting, is code shipped inside a package to act against the person who installs or loads it. By type, the npm cases on this page cover credential stealers (Shai-Hulud and TanStack), a browser-side interceptor aimed at cryptocurrency transactions (chalk and debug), a remote access trojan (axios) and, in Shai-Hulud’s case, a worm. This page describes each part and never prints a sample.

PartWhere it sitsWhat to look for
HookA preinstall, install or postinstall script in package.json, or the package’s own code, which runs when the app loads it. TanStack’s malicious versions ran through the prepare script of an added optional git dependencyA new install script. In package-lock.json, hasInstallScript marks a package that has one
PayloadA file or dependency the previous version did not haveTanStack’s large obfuscated file at the package root, left out of its files list; axios’s new dependency that downloads multi-stage payloads
HarvestCredentials the installing machine can read: files, environment variables, cloud metadataTanStack’s list: AWS instance metadata and Secrets Manager, the GCP metadata service, Kubernetes service-account tokens, HashiCorp Vault tokens, npm tokens in ~/.npmrc, GitHub tokens including those in environment variables, SSH private keys. The chalk and debug advisories describe no harvest
ExitA network request or an uploadA request to a host the package has no reason to contact, or credentials uploaded to a public repository, as with Shai-Hulud

In Shai-Hulud and in the May 2026 dependency confusion campaign, the hook was a postinstall script. npm’s lifecycle scripts documentation lists preinstall, install and postinstall among the scripts npm install runs, and says “You should almost never have to explicitly set a preinstall or install script.” So a new install script in a package that never had one is the first thing to question.

The warning signs to check when a new version of a package looks wrong follow from those four parts:

  • a new install script, or a new hasInstallScript entry in the lockfile
  • a new large or minified file in a package that ships readable source
  • a new dependency nobody asked for, a git dependency included
  • a new network or child-process call
  • a version published minutes before your build installed it

Malware code in the consumer sense, what phone and PC antivirus looks for, is a different topic from this one.

Why it matters for a small app: is npm safe now?

npm is as safe as the install habits around it. The exposed teams in the incidents below installed or built inside the window with floating versions and, for the install-time payloads, with install scripts on. A committed lockfile, install scripts off by default and a delay before new releases are installed close most of that window for a small app.

A small app’s exposure is mostly the part of the tree nobody picked: packages that arrive as dependencies of dependencies, under a direct list an AI builder may have chosen. In my June and July 2026 audits, the dependencies and supply chain pillar averages 34.5 out of 100, scored on 20 of the 21 third-party apps I audited. In the same audits, 9 of the 26 apps ran a framework version with a publicly known, reachable RCE or auth bypass, and the fix was often a one-line version bump. Those apps are a selected set of apps I audited, not a random sample and not a rate for AI-built apps in general. Neither number counts malware. The 9 of 26 are apps on outdated framework versions with known flaws. The 34.5 is a pillar score across the 20 apps scored on that pillar, not a count of hostile packages.

Whether npm is safe now, for a given app, depends on timing. CISA’s Shai-Hulud recommendations start with a dependency review and a lockfile check and go on to pinning to known safe releases produced before September 16, 2025. The TanStack advisory pins to versions published before 19:00 UTC on May 11, 2026.

Three things explain why software supply chain attacks are trending. One popular package reaches many projects: CISA’s alert credits vendor research with a count of over 500 compromised packages for a single worm. Publish tokens and CI secrets unlock more than any one app: CISA’s alert names GitHub personal access tokens and cloud API keys, and the TanStack advisory’s harvest list adds npm tokens. And install scripts run code before anyone reads a line: up to npm 11, npm install runs preinstall, install and postinstall scripts unless npm is told not to; npm 12 blocks dependency install scripts by default unless package.json allows them.

Supply chain attacks in 2025 and 2026 fill five of the six rows in the incident table below, from the chalk and debug takeover in September 2025 to TanStack in May 2026.

A known vulnerability and a hostile version are different software supply chain vulnerabilities, with different checks: reading the output of the npm audit command deals with the first, and the lockfile check further down deals with the second. Open source software supply chain security, for a small team, starts with knowing what the lockfile pins.

How it works: what the named incidents actually did

The six incidents in the table share one shape: a trusted name delivered hostile code. Three went after credentials such as publish tokens, cloud keys and CI secrets, one pulled in a remote access trojan, and two acted in the browser. The table gives each one’s route in, what it took, its exposure window and its primary source.

IncidentDateRoute inWhat it tookExposure windowPrimary source
chalk, debug and 16 more packagesSeptember 8, 2025A phishing email, “a 2FA reset email that looked shockingly authentic”, took over the maintainer’s npm accountA browser-side interceptor: debug and simple-swizzle redirect cryptocurrency transactions in the browser; chalk’s hijacks network traffic and application APIsPublished September 8, 2025; npm removed the versions over the course of that dayThe debug incident thread; the GitHub Advisory Database
Shai-HuludAlert September 23, 2025; pin to releases before September 16, 2025A self-replicating worm, publishing as each compromised developerGitHub personal access tokens and cloud API keys (AWS, GCP, Azure), uploaded to a public repositoryNot stated in CISA’s alertCISA’s alert
axiosMarch 31, 2026 (alert April 20, 2026)axios@1.14.1 and axios@0.30.4 injected a malicious dependency; how they were published is not stated in CISA’s alertMulti-stage payloads, including a remote access trojanNot stated in CISA’s alertCISA’s alert
TanStackMay 11, 202684 malicious versions across 42 @tanstack/* packages, published through the project’s GitHub Actions trusted-publisher bindingCloud, Kubernetes, Vault, npm, GitHub and SSH credentials; republishes the victim’s own packagesPublished between about 19:20 and 19:26 UTCGitHub Advisory Database, CVE-2026-45321
TrivyMarch 19, 2026; Docker Hub images March 22, 2026; part of an attack that began in late February 2026Compromised credentials: a malicious v0.69.4 release and hijacked GitHub Action tagsCI runner secrets from process memory, plus SSH keys, cloud credentials and other files on 50+ pathsAbout 3 hours (v0.69.4), 12 hours (trivy-action), 4 hours (setup-trivy), 10 hours (Docker Hub images)The Trivy project’s advisory, CVE-2026-33634
polyfill.ioAdvisory June 25, 2024The polyfill.io CDN was soldNot stated in the pdoc advisory, which says the CDN “now serves malicious code”Not stated in the pdoc advisoryGitHub Advisory Database (pdoc), CVE-2024-38526

Incident facts checked on September 30, 2026 against the primary sources in the last column.

npm debug and chalk packages compromised: the qix account and simple-swizzle

On September 8, 2025, the npm maintainer known as Qix lost the publishing account to “a 2FA reset email that looked shockingly authentic”, and hostile versions of the account’s packages were published under the trusted names. The maintainer’s list in the debug incident thread runs to eighteen packages, from debug@4.4.2 and chalk@5.6.1 to simple-swizzle@0.2.3, followed by “There might be others; these are just the ones I got email notifications for.”

The GitHub Advisory Database has the exact versions. On npm, debug 4.4.2 was the compromised version and 4.4.3 is patched; simple-swizzle 0.2.3 was bad and 0.2.4 is patched; the chalk package on npm has 5.6.1 as its bad version and no patched version listed. The debug and simple-swizzle advisories describe a payload “attempting to redirect cryptocurrency transactions to the attacker’s own addresses from within browser environments” and add that “Local environments, server environments, command line applications, etc. are not affected.” The chalk advisory is broader: any computer with it installed or running “should be considered affected by a browser-based interceptor that hijacks network traffic and application APIs.”

According to the debug and simple-swizzle advisories, npm removed the versions over the course of September 8, and users should remove node_modules, clean the package manager’s global cache and “rebuild any browser bundles from scratch”; the chalk advisory gives none of those steps. So for the debug package, a front end bundled while 4.4.2 was on the registry is checked in its build output, not only in its lockfile. The lesson: a patch-level bump of a trusted name was the delivery, so floating version ranges in a build that runs on publish day are the exposure.

Shai-Hulud: the npm worm behind the headlines

Shai-Hulud is a self-replicating npm worm that CISA described in an alert on September 23, 2025. It scanned for credentials such as GitHub tokens and cloud API keys, uploaded them to a public repository, and spread by authenticating to npm as the compromised developer and injecting code into other packages.

The name also belongs to the sandworm of Dune and to a metalcore band; this is the npm worm. CISA’s alert on the npm ecosystem compromise says the worm had compromised over 500 packages, a figure it footnotes to vendor research. The alert’s own wording for the spread is that the malware moved “by authenticating to the npm registry as the compromised developer, injecting code into other packages, and publishing compromised versions to the registry.”

CISA’s first five recommendations, in its order: review dependencies across all software that uses npm; check package-lock.json or yarn.lock for affected packages, nested ones included; search artifact repositories and dependency tools for cached copies; pin to known safe releases produced before September 16, 2025; and rotate all developer credentials. My reading, not CISA’s words: a publish token sitting on a developer’s machine turns one bad install into many poisoned packages, so the first control I would add is a publish path with no long-lived token on a laptop.

GitHub’s own account of the response, on its blog on September 22, 2025, lists the removal of 500+ compromised packages from the registry and npm blocking uploads that carried the malware’s indicators, and says publishing options would change to only include local publishing with required 2FA, granular tokens with a seven-day lifetime, and trusted publishing. Vendor research, Unit 42’s npm threat landscape report of July 15, 2026 among it, covers later waves. Of the primary sources, the TanStack advisory describes a payload that republishes the victim’s packages “with the same injection, propagating the compromise across npm.”

Axios and TanStack: a compromised version and a CVE are different things

A CVE and a compromised version need different responses. A CVE usually names a flaw in the package’s own code, fixed by upgrading. A compromised version is a hostile release under the real name, fixed by removing it and rotating what the installing machine could read. The advisory’s text, not the id, tells you which one you have.

An axios CVE such as CVE-2026-40175 is an ordinary vulnerability in the library’s own code. The axios security advisories list it as GHSA-fvcv-3m26-pcqx, published April 9, 2026 and rated moderate, and the GitHub Advisory Database’s copy describes a header injection chain in which “prototype pollution in a third-party dependency may be leveraged to inject unsanitized header values into outbound requests.” It affects versions from 1.0.0 up to but not including 1.15.0, and versions before 0.31.0; 1.15.0 and 0.31.0 are the patched releases.

The axios compromised versions are a different event. CISA’s alert on the axios compromise, released April 20, 2026, treats it as a supply chain compromise of axios on npm: on March 31, 2026, versions axios@1.14.1 and axios@0.30.4 injected the malicious dependency plain-crypto-js@4.2.1, which downloads multi-stage payloads, a remote access trojan among them. Its recommended actions include downgrading to axios@1.14.0 or axios@0.30.3, deleting that dependency’s folder, and rotating or revoking credentials that may have been exposed on affected systems or pipelines. CISA does not say how the two versions came to be published. So an axios vulnerability can sit in the library’s code or in its supply chain, and the fix differs: upgrade for the first, remove and rotate for the second.

TanStack is the twist: its CVE is itself a malware advisory, and the TanStack advisory, CVE-2026-45321, says that on May 11, 2026, 84 malicious versions across 42 @tanstack/* packages were published, authenticated via the project’s legitimate GitHub Actions trusted-publisher binding.

To see which axios you have, npm ls axios limits npm’s installed tree to the paths that lead to axios, nested copies included. If moving versions breaks the build, the recovery steps are the ones for when a dependency update breaks your app.

The Trivy supply chain attack: when the scanner was the package

Trivy is a security scanner, not an npm package, and its security incident shows the same pattern one layer up. The Trivy project’s advisory, GHSA-69fq-xp46-6x23 (CVE-2026-33634), opens: “On March 19, 2026, a threat actor used compromised credentials to publish a malicious Trivy v0.69.4 release, force-push 76 of 77 version tags in aquasecurity/trivy-action to credential-stealing malware, and replace all 7 tags in aquasecurity/setup-trivy with malicious commits.”

The Aqua Security Trivy action, aquasecurity/trivy-action, is the GitHub Action that runs a Trivy scan inside a workflow, and the advisory lists it among the affected components. In the advisory’s words, the injected exploit code in that Trivy action “executes before the legitimate Trivy scan”, dumps the runner’s process memory to extract secrets, and sweeps 50+ filesystem paths for SSH keys, cloud credentials, Kubernetes tokens, Docker configs, .env files, database credentials and cryptocurrency wallets.

A workflow was exposed to the compromised Trivy action if it referenced any trivy-action tag from 0.0.1 to 0.34.2, set version: latest explicitly (not the default) during the binary’s exposure window, or pinned a commit from before 2025-04-09; the trivy-action window ran from about 17:43 UTC on March 19 to about 05:40 UTC on March 20, 2026. The advisory also dates a second publication, malicious Docker Hub images v0.69.5 and v0.69.6 on March 22, 2026, and calls the Trivy breach a continuation of an attack that began in late February 2026.

The lesson is a GitHub supply chain attack in the plain sense: third-party actions are dependencies too, and the advisory itself says to “Pin GitHub Actions to full, immutable commit SHA hashes, don’t use mutable version tags.” Giving the job only the token it needs is part of CI CD security best practices. One caution from the advisory: a trivy-action commit pinned by SHA from before its pull request 456 (merged 2025-04-09) would get a safe trivy-action but a malicious setup-trivy if it ran inside the setup-trivy exposure window. My reading: a pin covers what the pinned code fetches only if that code pins too.

What to rotate is every secret that workflow could read. The advisory’s root cause is itself a rotation lesson: after an earlier disclosure, “credential rotation was performed but was not atomic (not all credentials were revoked simultaneously)”. Revoking the old credentials at the moment the new ones go live is the core of how to rotate API keys safely. Whether a Trivy install is safe to use comes down to which version and which action reference the workflow pins, which the advisory lists; the known-safe versions are in the questions at the end of this page. What Trivy reports as a vulnerability scanner is a separate question from this incident.

The polyfill.io supply chain attack: a CDN script that changed owners

The polyfill.io supply chain attack came through a script tag, not a package. The pdoc advisory on polyfill.io, CVE-2024-38526, published June 25, 2024, says documentation generated with math mode (pdoc --math) linked to JavaScript files from polyfill.io, that “The polyfill.io CDN has been sold and now serves malicious code.”, and that “All other users are unaffected.” pdoc is a Python package, so the route did not depend on the ecosystem.

Any site that loaded the CDN polyfill from that host served whatever the new owner sent. The check: search the built HTML and the code for polyfill.io and for every other third-party script host. The control: self-host what you can, add Subresource Integrity to what you cannot, and restrict script sources with Content Security Policy without unsafe-inline.

Dependency confusion and the rest of the supply-chain playbook

Dependency confusion is an attack on private package names: an outsider publishes a look-alike of an internal name or scope on the public registry, and a misconfigured installer fetches the public one. An app with no private packages has nothing to imitate; one with them needs scoped names under an organization it owns and a registry mapping for that scope.

The dependency confusion attack is still in use. Microsoft’s security blog reported on May 29, 2026 a campaign of malicious npm packages “registered under organizational scopes that mirror real internal corporate namespaces”, dozens of scoped packages across nine organizational scopes, some at “version 100.100.100, an absurdly high version number designed to win npm’s server resolution against any real internal package version.”

The defense is configuration. npm’s documentation on scopes says “only you can add packages in your scope”, and that once a scope is associated with a registry, “any npm install for a package with that scope will request packages from that registry instead.” So own the scope on the public registry too, map it to your registry in .npmrc, as Microsoft’s mitigation advice also does, and commit the lockfile.

The rest of the playbook, one line each:

  • Look-alike names: hallucinated and typosquatted package names bet on a guess or a typo instead of a private name.
  • Abandoned packages: a package whose maintainer has moved on can end up with a new publisher.
  • Hostile pull requests to a build pipeline: TanStack’s chain included a pull_request_target “Pwn Request” misconfiguration.
  • Editor extensions: the VS Code extension host “has the same permissions as VS Code itself”.

Whether VSCode extensions are safe comes down to the publisher, in my reading. Since release 1.97, VS Code asks you to confirm trust in a third-party publisher on first install, and the signals VS Code’s extension runtime security page lists before installing include ratings and reviews, the repository and license, and the blue verified-publisher check mark. Tampering in transit, one of the man-in-the-middle attacks, is a different problem, stopped by TLS and the lockfile’s integrity hashes.

How to check your own app for a named bad version

Checking for a compromised npm package takes seven steps: get the bad versions from the primary advisory, search every lockfile and CI workflow for them, record a clean result, or else treat what that machine could read as exposed and follow the advisory’s order to remove the version, rotate secrets, reinstall and search for exfiltration artifacts.

Before any terminal, GitHub can do a first pass from the browser. Dependabot can raise malware alerts as well as vulnerability alerts, and npm is on the malware alerts’ list of ecosystems. To turn both on, open the repository’s Settings, click Advanced Security in the “Security and quality” section of the sidebar, click Enable next to Dependabot alerts, then Enable next to Dependabot malware alerts. Vulnerability alerts appear on the repository’s Security and quality tab and dependency graph, malware alerts on the Security and quality tab, and the graph is enabled from the same Advanced Security page. GitHub’s Dependabot alerts documentation adds two limits: Dependabot scans the default branch, and “Only advisories reviewed by GitHub trigger alerts.” An alert-free tab is a starting point, not the check.

The check below works for any incident that comes with a list of bad versions, in this order:

  1. 01 Get the list of bad versions from the primary source: the project's advisory, the GitHub Advisory Database or CISA, not a screenshot or a vendor's summary.
  2. 02 Search the lockfile in every repository and every branch that deploys: npm ls <package> for the tree, and a text search of package-lock.json, pnpm-lock.yaml or yarn.lock for the exact version. For axios, search for the malicious dependency CISA names as well.
  3. 03 Search CI: workflow files for the affected action references, and build logs from the exposure window for an install that resolved a bad version.
  4. 04 If nothing matches, write down what you checked and when, and stop.
  5. 05 If something matches, treat every secret that machine or runner could read as exposed, and follow the advisory's own remediation order.
  6. 06 After the advisory's steps, confirm the lockfile now pins the known-safe version, commit it, and rebuild with npm ci.
  7. 07 Search for the exfiltration artifacts the advisories describe, such as unexpected new repositories in your GitHub account or packages you maintain republished without you, and report a malicious package to npm with every affected version listed.

The four commands behind steps 2, 3 and 6, with placeholders to replace:

# Step 2: where the package sits in the installed tree
npm ls <package>

# Step 2: every lockfile entry for the package, with the lines after it
grep -n -A3 "<package>" package-lock.json pnpm-lock.yaml yarn.lock 2>/dev/null

# Step 3: workflows that reference an affected action
grep -rn "<action-name>" .github/workflows

# Step 6: clean reinstall from the committed lockfile
rm -rf node_modules && npm ci

The lockfile search reads the lines around each hit because each format records a version differently. package-lock.json keys each installed copy by its path from the project root and gives it a version field; Yarn’s yarn.lock puts a version line under each entry; pnpm-lock.yaml gives each dependency a version under importers and keys each packages entry by name and version, such as lodash@4.17.21. CISA’s Shai-Hulud alert asks for this same lockfile check, nested dependencies included, with its list in the Shai-Hulud section above.

For step 3, GitHub keeps “the artifacts and log files generated by workflows” for 90 days by default, so for an older window the lockfile’s history in Git is what is left to read.

Step 5 follows the advisory because the orders differ. The TanStack advisory pins to a known-good version, deletes node_modules and the lockfile and reinstalls, turns off lifecycle scripts, then, for CI, treats any runner that ran an install against @tanstack/* between 19:20 and 19:30 UTC on May 11, 2026 as compromised and rotates its secrets. CISA’s axios alert, after its review, cache-search and pinning steps, says to revert the environment to a known safe state, downgrade and delete the malicious dependency, then rotate. For each secret, what to do when an API key leaks applies: rotate in order of blast radius and check each provider’s logs for use of the old credentials.

For step 6, npm ci “will never write to package.json or any of the package-locks”, and “The project must have an existing package-lock.json or npm-shrinkwrap.json”, so a pnpm or Yarn project rebuilds with its own package manager’s install from its lockfile instead. The rm -rf in the last command is redundant but harmless, since npm ci removes an existing node_modules itself before it installs. The TanStack advisory’s own step also deletes the lockfile before reinstalling, and when an advisory says so, the advisory wins.

For step 7, the advisories name the traces: CISA’s Shai-Hulud alert describes credentials uploaded to a public repository, the Trivy advisory, as a fallback, a public repository created on the victim’s GitHub account, and the TanStack advisory the victim’s own packages republished. npm’s page on reporting malware asks you to click Report malware on the package page and to include all affected versions. Secrets that were ever committed to the repository are a separate check, secret scanning across the Git history.

Supply chain security tools, and the four settings that matter more

Four free settings do more for npm supply chain security than any tool: a committed lockfile with npm ci in CI, install scripts off by default, a minimum release age so nothing published today is installed today, and short-lived publish tokens or trusted publishing with 2FA on the npm account.

npm supply chain attack mitigation starts with the lockfile. Commit it and install with npm ci in CI, which needs an existing lockfile and exits with an error, instead of updating the lockfile, when it and package.json disagree. In one app I audited, a voice-AI SDK’s server Dockerfile installed whatever npm resolved on build day; the committed lockfile never reached the image. A lockfile protects only the builds that install from it.

Install scripts come next. npm’s ignore-scripts setting is “Default: false”, and when it is true, “npm does not run scripts specified in package.json files.” The allowScripts field of package.json, which npm’s config docs name for team-wide policy, is the short allowlist of packages that may run their scripts, but --ignore-scripts overrides it. On npm 12, dependency install scripts are blocked by default unless listed there; on npm 11 the field is advisory, and scripts still run by default. pnpm already stops by default: since pnpm 11, strictDepBuilds defaults to true and fails the install when a dependency has unreviewed build scripts, and approvals live in allowBuilds.

A minimum release age means nothing published today gets installed today. npm’s min-release-age counts in days. pnpm’s minimumReleaseAge counts in minutes, with a default of 1440 since v11, and pnpm’s settings reference gives the reason: “In most cases, malicious releases are discovered and removed from the registry within an hour.” CISA’s axios alert itself recommends ignore-scripts=true and min-release-age=7 in .npmrc, the second so only packages published at least seven days ago install. npm’s min-release-age, allow-git and the allowScripts field appear in npm’s v11 config docs and not in its v10 docs, so a project on npm 10 updates npm first or uses pnpm’s settings. If an update bot such as Renovate proposes your upgrades, give it the same waiting period in its own settings, since what Renovate bot is, in practice, is a second installer that opens pull requests on its own schedule.

Publish and CI tokens are the fourth setting. Trusted publishing lets CI publish through OpenID Connect, “eliminating the need for long-lived npm tokens”, and npm’s trusted publishing documentation says it “requires npm CLI version 11.5.1 or later and Node version 22.14.0 or higher” and that “Self-hosted runners are not currently supported”. For a package still published from a laptop, its publishing access can be set to “Require two-factor authentication and disallow tokens”.

npm’s registry signatures documentation describes npm audit signatures, which checks the registry signatures of installed packages; “The CLI will error if packages don’t have signatures and if the package registry supports signatures.” Provenance proves where and how a version was built, not that its code is safe: TanStack’s malicious versions went out through the project’s own trusted-publisher binding. npm’s allow-git setting, set to root, “only allows git dependencies defined in your project’s package.json to be fetched and installed” ; my reading is that it narrows the route TanStack’s payload used, an optional git dependency added to the published package.

Supply chain security tools add a layer on top. Aikido Safe Chain, as one example, wraps the install commands of npm, pnpm, yarn and other package managers, runs “a lightweight proxy server that intercepts package downloads from the npm registry and PyPI”, checks each download against Aikido’s own threat intelligence, blocks a flagged download, and by default refuses packages less than 48 hours old. A hosted scanner can work at pull-request time instead, reviewing the dependency changes each pull request brings in. A tool is worth adding after the four settings, not before.

Where the sprint fits

In the Production Hardening Sprint, the dependency work is deliverable 3.5: we audit dependencies, upgrade or remove known-vulnerable packages, and remove unused packages, and we record the dependency scan, remediation, and regression checks after changes. Deliverable 2.2 scans Git history for committed secrets and rotates every exposed credential found, verified by retaining scan results and confirming exposed credentials have been invalidated and replacements work. Deliverable 7.9 configures Renovate, Dependabot, or an equivalent to propose dependency updates with checks, verified by checking that a proposed update triggers the appropriate CI checks and review requirements. Each result is recorded in the production readiness report, deliverable 13.1, which accounts for all 123 IDs, keeps failures visible until resolved and explains genuine non-applicable items. The sprint covers one codebase, and support after handover is 14 calendar days of fixes for defects in the delivered sprint work. The settings in the section above are what make the next hostile version less likely to land after that. The full list is in the dependency and secrets deliverables in the published scope.

Common questions about npm and hostile packages

What does npm stand for?

Officially, nothing. The npm CLI’s README calls the name “a recursive backronymic abbreviation” for “npm is not an acronym”, and npm’s logo and usage policy says the name “should never be used or explained as an acronym.”

Is npm owned by Microsoft?

Yes, through GitHub. GitHub announced on March 16, 2020 that it had signed an agreement to acquire npm, and Microsoft completed its acquisition of GitHub on October 26, 2018. npm’s privacy policy lists GitHub’s Data Protection Officer as the contact at its United States HQ.

How many packages are in npm?

The last count on a page npm or GitHub owns is over 1.3 million packages, from GitHub’s March 16, 2020 post on acquiring npm, so treat it as a 2020 figure, not today’s. npm’s own docs give no current count; its about page calls npm “the world’s largest software registry.”

How to clean all npm packages?

In a project with a committed lockfile, run npm ci: it removes an existing node_modules before it installs, and it never writes to package.json or the lockfile. After a malicious version, the debug and simple-swizzle advisories add two more steps: clear the package manager’s global cache, and build every browser bundle again from scratch.

Is aikido safe chain free?

Free to use, its repository says, with a license condition for commercial use in closed-source apps. The repository’s description reads “Free to use, no tokens required.” Its LICENSE file offers Safe Chain “under a commercial and under the AGPL license” and says “Buying such a license is mandatory as soon as you develop commercial activities involving Safe Chain software without disclosing the source code of your own applications”, activities that “include but are not limited to: offering paid services to customers in a web application”. A paid app with closed code should read that license before relying on “free”.

Is Trivy still compromised?

Not at the source, according to the Trivy project’s advisory: all malicious components, artifacts and commits “have been removed from all sources and destinations (yet they may linger in intermediary caches)”. The known-safe versions it lists are Trivy v0.69.2 and v0.69.3, trivy-action v0.35.0 and setup-trivy v0.2.6. The original trivy-action tags 0.0.1 to 0.34.2 were deleted and re-published with a v prefix, except v0.0.10, v0.34.1 and v0.34.2, which have not yet been restored, so an older reference reads like aquasecurity/trivy-action@v0.34.0. Check which version and action reference your own workflows pin.