PRACTITIONER GUIDE | SUPPLY CHAIN SECURITY
Practitioner GuideUpdated 13 min read

Using sigstore and cosign to Verify npm Packages Have Not Been Tampered With

1B+
artifacts signed via sigstore's public Rekor transparency log as of 2025, adoption accelerated sharply after the XZ Utils and TanStack attacks
0
long-lived signing keys required, sigstore uses ephemeral OIDC-backed certificates that expire after 10 minutes
6 min
was all it took to publish 84 malicious @tanstack versions in May 2026, a verified package would have made this detectable at install time

SponsoredRetool

Retool's new app builder is where AI-generated code ships safely

Building apps with AI is easy. Getting them to production safely is another story.

Start building for free today

sigstore solves the core problem of software supply chain verification: proving that a package you are about to install was built from a specific source commit by a specific identity, and has not been modified since. The TanStack attack succeeded in part because nothing at the install step alerted developers that the package they were installing was not the one the TanStack team built. npm provenance, built on sigstore, would have surfaced that discrepancy. This guide covers the practical setup: signing your own packages during CI, verifying packages you consume, and what verification failure means in practice.

How sigstore Works Without Long-Lived Keys

Traditional code signing requires a signing key that must be stored securely, rotated periodically, and revoked if compromised. Stolen signing keys are a major attack vector, a stolen key lets an attacker sign arbitrary packages as a legitimate maintainer.

sigstore eliminates the long-lived key problem with a keyless signing model:

  1. The developer (or CI system) authenticates via OIDC, in a GitHub Actions context, this is the same OIDC token used for cloud provider authentication.
  2. Fulcio (sigstore's certificate authority) issues a short-lived X.509 signing certificate that expires in 10 minutes, bound to the OIDC identity (e.g., the GitHub Actions workflow URL).
  3. The package is signed with this ephemeral certificate.
  4. The signature and a certificate verification bundle are recorded in Rekor, sigstore's append-only transparency log.
  5. At verification time, anyone can retrieve the certificate and signature from Rekor and verify that the package was signed by the claimed identity during the claimed build.

The result: there is no long-lived key for an attacker to steal. A malicious actor who compromises the package registry cannot retroactively create a valid sigstore signature for a tampered package, they would need to also compromise the OIDC identity at the time of publication.

Step 1: Enable npm Provenance When Publishing

npm's provenance feature, released in 2023, is sigstore integration built into the npm CLI. When you publish with --provenance from a GitHub Actions workflow, npm automatically:

  • Requests a sigstore certificate for your workflow's OIDC identity
  • Signs the package tarball
  • Records the signature in Rekor
  • Attaches the provenance statement to your package on the registry

To enable it, add --provenance to your publish command in your GitHub Actions workflow:

jobs:
  publish:
    runs-on: ubuntu-latest
    permissions:
      id-token: write  # Required for sigstore OIDC
      contents: read
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          registry-url: 'https://registry.npmjs.org'
      - run: npm ci
      - run: npm publish --provenance --access public
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

After publishing, your package page on npmjs.com shows a provenance badge linking to the Rekor entry and the specific GitHub Actions run that built the package.

Free daily briefing

Briefings like this, every morning before 9am.

Threat intel, active CVEs, and campaign alerts, distilled for practitioners. 50,000+ subscribers. No noise.

Step 2: Verify Provenance of Packages You Consume

Verifying provenance before installing a package confirms it was built from the claimed repository by an authenticated CI system.

Using npm audit signatures:

npm audit signatures

This command verifies the registry signatures for all packages in your current node_modules. It does not verify provenance (build source), but it confirms the packages match what the registry served, detecting a man-in-the-middle or a registry compromise.

Checking provenance for a specific package:

npm view @tanstack/react-router --json | jq '.dist.attestations'

This returns the attestation metadata including the Rekor log ID and the GitHub Actions workflow that built the package. Cross-reference the workflow URL against the official TanStack repository.

Using the sigstore CLI for deeper verification:

# Install cosign
brew install cosign

# Verify an npm package's provenance
cosign verify-npm-provenance @tanstack/react-router@1.120.0

This verifies the OIDC identity that signed the package matches the expected GitHub repository and workflow path.

Step 3: Add Verification to Your CI Pipeline

Verification in developer workflows is valuable, but verification in CI, before a build uses a dependency, catches tampered packages before they reach any developer machine.

Add this step to your CI workflow after npm ci:

- name: Verify npm package provenance
  run: |
    # Verify signatures for all installed packages
    npm audit signatures
    
    # For critical dependencies, verify provenance explicitly
    PACKAGES="@tanstack/react-router react react-dom"
    for pkg in $PACKAGES; do
      version=$(node -e "console.log(require('./node_modules/$pkg/package.json').version)")
      echo "Verifying $pkg@$version"
      npm view "$pkg@$version" --json | jq -e '.dist.attestations // error("No provenance")' || \
        echo "WARNING: $pkg has no provenance attestation"
    done

This approach fails fast if any critical dependency lacks provenance or has an invalid signature. Over time, as more packages adopt provenance, you can convert warnings to hard failures.

Step 4: Sign and Verify Container Images with cosign

If your Node.js application ships as a Docker image, cosign extends the same verification model to container images.

Signing an image after build in GitHub Actions:

- name: Sign container image
  run: |
    cosign sign --yes \
      ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.build.outputs.digest }}
  env:
    COSIGN_EXPERIMENTAL: '1'

Verifying before deployment:

cosign verify \
  --certificate-identity-regexp 'https://github.com/yourorg/yourrepo/.github/workflows/.*' \
  --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
  your-registry/your-image:tag

This confirms the image was built by your GitHub Actions workflow and not modified after push. In a Kubernetes environment, integrate this with Kyverno or OPA policies that reject pods using images without a valid cosign signature.

What Verification Failure Means and What to Do

npm audit signatures failure means one of:

  1. The package was modified after being published to the registry (tampering)
  2. The package was published to a mirror registry and the signature does not match
  3. The npm registry served a different version than the one you requested

For a package with no provenance attestation, it means the package was published without provenance, which describes most packages today. Treat the absence of provenance as a risk factor, not an automatic failure, until your critical dependencies all have provenance.

For a provenance mismatch, where the provenance exists but points to a different repository or workflow than expected, treat it as a security incident. Remove the package from your node_modules immediately, audit your lockfile for the commit that introduced it, and check whether your CI ran with the tampered package already installed.

The bottom line

sigstore provenance does not prevent supply chain attacks, it makes them detectable. A tampered package published without a valid provenance signature fails verification at install time. The investment is adding two lines to your publish workflow and one command to your CI pipeline. Given that the TanStack attack would have been caught at install time with provenance enabled, the risk-reduction-to-effort ratio is as high as any security control available to a Node.js team.

Frequently asked questions

Does sigstore provenance work for private npm packages?

Yes. npm provenance works with private registries and scoped packages. The provenance statement links to the specific GitHub Actions run regardless of whether the package is public or private. The Rekor transparency log entry is public by default, which means the build metadata is public even for private packages, consider this if you need build pipeline confidentiality.

What happens if I publish a package without --provenance?

The package is published normally without a provenance statement. Consumers cannot verify the build source. You can add provenance to future versions without breaking existing consumers, it is additive. npm does not prevent publishing without provenance.

Is npm audit signatures the same as npm audit?

No. npm audit checks your dependencies against a database of known vulnerabilities. npm audit signatures verifies the cryptographic signatures of installed packages against the registry's signing keys. They are complementary, run both.

Can sigstore verification be bypassed if an attacker controls the GitHub Actions OIDC provider?

In theory yes, but that would require compromising GitHub's OIDC infrastructure, which is a different threat model than a compromised package maintainer account. The practical attacks sigstore prevents are: stolen npm tokens used to publish tampered packages, and compromised package registry serving different content than what the maintainer published.

Which npm packages currently have provenance attestations?

As of 2025, all packages published by major projects using GitHub Actions with --provenance include provenance. You can check any package with: npm view package-name --json | jq '.dist.attestations'. Coverage is growing rapidly following the XZ Utils and TanStack incidents.

How do I integrate sigstore provenance verification into a pull request gate so that dependency changes without valid provenance are blocked before merge?

Add a required GitHub Actions status check that runs npm audit signatures after every npm ci in your CI workflow. If the command exits non-zero, the check fails and the PR cannot merge. For stricter enforcement, write a custom script that calls npm view for each newly added dependency in the diff, checks for a .dist.attestations field in the returned JSON, and fails the check if any new dependency lacks provenance. You can extract the list of changed packages by comparing package-lock.json between the PR branch and main using jq. Over time, convert the warning-level absence of provenance to a hard failure as your dependency tree gains coverage, starting with the highest-risk dependency categories like authentication libraries and cloud SDK clients.

Sources & references

  1. sigstore project documentation
  2. npm provenance documentation
  3. cosign GitHub repository

Free resources

25
Free download

Critical CVE Reference Card 2025–2026

25 actively exploited vulnerabilities with CVSS scores, exploit status, and patch availability. Print it, pin it, share it with your SOC team.

No spam. Unsubscribe anytime.

Free download

Ransomware Incident Response Playbook

Step-by-step 24-hour IR checklist covering detection, containment, eradication, and recovery. Built for SOC teams, IR leads, and CISOs.

No spam. Unsubscribe anytime.

Free newsletter

Get threat intel before your inbox does.

50,000+ security professionals read Decryption Digest for early warnings on zero-days, ransomware, and nation-state campaigns. Free, daily, no spam.

Unsubscribe anytime. We never sell your data.

Eric Bang
Author

Founder & Cybersecurity Evangelist, Decryption Digest

Cybersecurity professional with expertise in threat intelligence, vulnerability research, and enterprise security. Covers zero-days, ransomware, and nation-state operations for 50,000+ security professionals every morning.

Black Hat Giveaway

Win a $2,495 Black Hat pass.

Full-access to Black Hat USA 2026 in Las Vegas. Subscribe free to enter.

Joins Decryption Digest daily briefing. Unsubscribe anytime.

Related Questions: Answer Hub

Giveaway: Black Hat USA 2026 Full-Access Pass ($2,495 value)

Details →
Daily Briefing

Subscribe to enter the giveaway

Every subscriber is automatically entered. You also get daily threat intel every morning: zero-days, ransomware, and nation-state campaigns. Free. No spam.

Already subscribed? You're already entered.

Giveaway

Win a $2,495 Black Hat USA 2026 pass.