Installing a dependency does not tell you whether its maintainer’s account was compromised, whether the tarball matches the source on GitHub, or whether a registry mirror silently served a different file than what was published. Running npm audit signatures after npm install surfaces exactly that gap:

$ npm audit signatures

audited 342 packages

1 package has an invalid signature:
  [email protected] was not signed and could not be verified

284 packages have verified registry signatures
57 packages have no verified provenance attestation

This guide covers reading that output correctly, and turning it into a CI gate that fails a build when a signature is invalid — as opposed to merely absent, which is common and not itself a failure.


Root Cause

Two independent supply-chain checks exist inside npm, and conflating them leads teams to either over-trust or over-block their dependency tree. Provenance attestation is opt-in and proves a specific CI workflow produced the tarball; it is only present on packages whose maintainers published with --provenance. Registry signature verification is far more widespread — nearly every package published through npmjs.org in the last several years carries a signature the registry itself generates and can validate against its own public key. npm audit signatures reports on both simultaneously, and an invalid registry signature (not merely a missing provenance attestation) is the condition that indicates real tampering risk.


Minimal Reproduction

Any project with a package-lock.json and an installed node_modules can run the check — no special configuration is required to trigger a report:

mkdir demo && cd demo
npm init -y
npm install [email protected] [email protected]
npm audit signatures

Because dependency signature and provenance coverage varies package by package based on when and how each was published, the mix of verified, unverified, and attested results will differ across projects — the important distinction is between “no provenance” (common, low severity) and “invalid signature” (rare, high severity).


How the Verification Check Resolves Each Dependency

npm audit signatures Resolution Flow A flow showing each installed package checked first for a valid registry signature, branching into pass, invalid, or unsigned outcomes, and separately checked for an optional Sigstore provenance attestation which is either present or absent without being a hard failure on its own. Each installed package Registry signature check against npmjs.org public key Verified — pass Invalid — hard fail Unsigned — legacy pkg Provenance attestation checked separately — absence alone is not a failure

Step-by-Step Fix

Step 1 — Run the check locally and read the summary lines

npm audit signatures

The three summary counts to distinguish:

284 packages have verified registry signatures    # good — most packages
57 packages have no verified provenance attestation  # informational, not a failure
1 package has an invalid signature                # actionable — investigate immediately

Expected result: you can identify which category [email protected] falls into and confirm it is the invalid-signature case, not merely an unattested one.

Step 2 — Investigate an invalid signature before continuing

npm view [email protected] dist.integrity
npm view [email protected] dist.signatures

Compare the returned integrity hash against what your lockfile records:

node -p "require('./package-lock.json').packages['node_modules/left-pad'].integrity"

Expected result: if the two values differ, the installed tarball does not match what the registry currently serves for that version — treat this as a potential tampering or cache-poisoning event and do not proceed with the install until resolved, typically by clearing the local cache with npm cache clean --force and reinstalling.

HAZARD PREVENTION

Symptom: npm audit signatures reports an invalid signature that disappears after clearing the cache and reinstalling.

Root cause: A corrupted or partially-written tarball in the local npm cache, not an actual registry-side tampering event.

Fix: Run npm cache clean --force followed by a fresh npm ci before escalating. If the invalid signature persists after a clean install from the public registry, treat it as a genuine security incident.

Step 3 — Add the check as a required CI step

- run: npm ci
- run: npm audit signatures

Expected result: npm audit signatures exits non-zero when any package has an invalid signature, failing the job automatically — no additional flag is required for this baseline behavior.

Step 4 — Optionally require provenance for your own scoped packages

To enforce provenance specifically for packages your organization publishes, without blocking on third-party dependencies that have not yet adopted it, script a targeted check:

#!/usr/bin/env bash
set -euo pipefail

for pkg in $(npm ls --json --depth=0 | node -pe "Object.keys(JSON.parse(require('fs').readFileSync(0,'utf8')).dependencies).filter(n => n.startsWith('@acme/'))"); do
  url=$(npm view "$pkg" dist.attestations.url 2>/dev/null || echo "")
  if [ -z "$url" ]; then
    echo "Missing provenance attestation for internal package: $pkg"
    exit 1
  fi
done

Expected result: the build fails only when an @acme/* package lacks an attestation, while third-party dependencies without provenance do not block the pipeline.


Verification

npm ci
npm audit signatures
echo "exit code: $?"

Expected output and exit code on a clean dependency tree:

audited 342 packages

341 packages have verified registry signatures
1 package has no verified provenance attestation
exit code: 0

An exit code of 1 indicates at least one invalid signature was found and should stop a CI pipeline before any build or deploy step runs.


Edge Cases / Gotchas

  • Monorepos with workspaces report signatures per-workspace; run npm audit signatures from the repository root so it covers the full deduplicated node_modules tree, not just one package’s subset.
  • npm audit signatures requires network access to the public registry to fetch current signing keys — air-gapped CI runners need a periodically refreshed local key cache or the check will fail closed.
  • Private/scoped registries (Verdaccio, Artifactory, GitHub Packages) proxy signatures inconsistently; verify your specific registry mirror preserves upstream signature metadata rather than stripping it during proxying.
  • Yarn and pnpm do not currently expose an equivalent single command; teams on those package managers typically shell out to npx npm audit signatures against the resolved node_modules tree, or rely on Sigstore’s own cosign verify-attestation tooling directly.
  • Lockfile drift between package-lock.json and installed node_modules can cause the check to report against stale version numbers — always run npm ci, not npm install, immediately before verification in CI.

Frequently Asked Questions

Does npm audit signatures check for known vulnerabilities too?

No. That is npm audit’s job, which checks package versions against the advisory database. npm audit signatures is a separate, unrelated command that verifies cryptographic registry signatures and Sigstore provenance attestations. Run both in CI; they catch different classes of supply-chain risk.

Why do most of my dependencies show zero provenance attestations?

Provenance adoption is voluntary and comparatively recent. A package only carries an attestation if its maintainer published it with npm 9.5+ and the --provenance flag from a supported CI environment. Registry signature verification, by contrast, applies to nearly all packages published since npm introduced package signing.

Can I require provenance for only my own organization’s packages?

Yes. Filter npm audit signatures output or use a custom script against npm ls --json combined with npm view <pkg> dist.attestations.url for packages matching your scope, then fail CI only when an internal package lacks an attestation, while tolerating third-party dependencies that do not yet provide one.

What does an invalid signature actually indicate, versus a missing one?

A missing signature means the package predates signing or its publisher never opted in — a lower-severity gap. An invalid signature means a signature exists but does not match the package’s content hash, which can indicate tampering, a registry mirror serving altered content, or a corrupted download, and should be treated as a hard failure.

Does this work with private registries or Verdaccio?

npm audit signatures only verifies packages resolved from the public npmjs.org registry keys and Sigstore’s public infrastructure. Private registries and proxies like Verdaccio that mirror packages generally pass signatures through unmodified, but self-hosted packages published internally without --provenance will show as unattested, which is expected.



↑ Back to npm Provenance & Sigstore Attestation