Skip to content

[Feature Request] Support trustPolicy to reject packages with provenance downgrade at install time #9242

Open

Description

@rgomezcasas

Summary

Add a configuration option (e.g. trust-policy=no-downgrade in .npmrc) that prevents npm from installing a package version whose trust level (trusted publisher → provenance → none) is lower than what previous versions of that package established. This is the single most impactful client-side mitigation against account-takeover supply chain attacks.

Motivation

On March 31, 2026, axios — a package with 60M+ weekly downloads — was compromised when an attacker gained access to the lead maintainer's machine via social engineering and published two malicious versions (1.14.1 and 0.30.4) directly from the compromised account. The malicious versions injected a dependency containing a cross-platform remote access trojan. They were live for ~3 hours before removal. Full post-mortem: axios/axios#10636.

The critical detail: axios had been publishing with provenance attestations prior to the attack, but the malicious versions were published manually from the attacker's machine — without provenance. This is a clear, detectable trust downgrade. If npm had a mechanism to reject installs where provenance disappears after previously being present, every consumer with that setting enabled would have been protected automatically, without needing to wait for community detection or registry intervention.

This is not a hypothetical scenario. The same pattern appeared in the s1ngularity attack (August 2025) and is the expected fingerprint of any credential-theft attack: the attacker has the maintainer's npm token but not access to the CI/CD pipeline, so the malicious publish lacks provenance.

Prior art

pnpm (trustPolicy: no-downgrade)

pnpm shipped this in v10.21 (November 2025). Configuration:

# pnpm-workspace.yaml
trustPolicy: no-downgrade
trustPolicyExclude:
  - 'chokidar@4.0.3'
  - 'webpack@4.47.0 || 5.102.1'
trustPolicyIgnoreAfter: 525600  # ignore for packages published >1 year ago

Trust levels are ordered: trusted publisher > provenance > none. If any previously published version of a package had a higher trust level than the version being resolved, installation fails with a clear error:

ERR_PNPM_TRUST_DOWNGRADE  High-risk trust downgrade for "axios@1.14.1" (possible package takeover)
Earlier versions had provenance attestation, but this version has no trust evidence.
A trust downgrade may indicate a supply chain incident.

The trustPolicyExclude setting allows allowlisting specific package@version pairs for legitimate downgrades (new maintainer, CI migration, etc.), and trustPolicyIgnoreAfter prevents false positives from old packages that predate provenance.

npm RFC 0049

The accepted RFC 0049 already anticipated this direction:

"The long-term solution to bypassing verification is enforcing or requiring that all packages have provenance information set during install time in the npm CLI. [...] We could make it harder to remove provenance information once it's been set by a maintainer. For example, blocking a npm publish that doesn't include it if the current latest version does include it."

This feature request is asking for the consumer-side half of that vision.

Proposal

Configuration

Add a trust-policy setting to .npmrc:

trust-policy=no-downgrade

Behavior

During npm install / npm ci, before writing to node_modules, check each resolved package:

  1. Query the registry for attestation metadata across published versions of that package.
  2. Determine the highest trust level any previously published version achieved.
  3. If the version being installed has a lower trust level, fail the install with a clear, actionable error message.

Escape hatches

Following pnpm's proven model:

  • trust-policy-exclude: allowlist specific package@version pairs that are known-safe despite a downgrade.
  • trust-policy-ignore-after: skip the check for packages whose most recent provenance-bearing version was published more than N minutes ago (handles legacy packages that never had provenance).

Example .npmrc

trust-policy=no-downgrade
trust-policy-exclude[]=chokidar@4.0.3
trust-policy-exclude[]=webpack@4.47.0
trust-policy-ignore-after=525600

Impact

  • axios incident: would have been blocked. The attacker published without provenance after prior versions had it → trust downgrade → install refused.
  • s1ngularity incident: same pattern, would have been blocked.
  • Any credential-theft attack where the attacker doesn't control CI/CD: blocked by default.

This doesn't prevent all supply chain attacks (a compromised CI/CD pipeline would still produce valid provenance), but it eliminates the most common and easiest attack vector at near-zero cost to developers.

Considerations

  • Performance: attestation metadata is already fetched by npm audit signatures. The registry could include trust-level info in the packument to avoid extra round-trips.
  • Adoption: this should be opt-in initially (off by default), similar to how pnpm rolled it out. Organizations and security-conscious teams can enable it immediately.
  • False positives: legitimate trust downgrades do happen (maintainer migrating CI, publishing a hotfix manually). The exclude list and ignore-after settings handle this gracefully. pnpm's real-world experience shows these are manageable.
  • Registry-side complement: ideally the registry could also enforce this (block a publish without provenance if previous latest had it, as RFC 0049 suggested). But the client-side check is independently valuable and doesn't require registry changes.

References

Activity

  1. ljharb commented on Sep 8, 2026

    @ljharb
    Contributor

    repeating my comment from #9902 (comment):

    This is a terrible idea. First of all, provenance is NOT a security indicator. Second, it's perfectly legitimate for a maintainer to decide to start using provenance, and then decide to stop. Third, pnpm's implementation of it has (or at least, had, I'm not sure) a bug (semver v7 is published with provenance, v6 is not, but pnpm was keying on dates, not release lines, to mark a "downgrade"), and that highlights some of the flaws in even attempting to use provenance in this manner.

    Provenance's only value whatsoever is post-incident forensics, and it should never be used as part of an install gate of any kind. This should simply be closed.

    That people make bad choices on which signals to rely on does not mean those signals should be made easier to incorrectly rely on.

  2. Jwrede commented on Nov 4, 2026

    @Jwrede

    For npm users who want this check today, I wrote a small tool that runs it against the lockfile as a separate step: https://github.com/Jwrede/npm-trust-downgrade

    It reads package-lock.json, including transitive dependencies, and reports a version when a release that is both published earlier and lower in semver order has stronger evidence (trusted publisher, then provenance, then none). It also reports versions that a lockfile still points at but that were removed from the registry.

    The semver condition is a response to the release line point raised in #9902: a backport to an old line is not compared with a newer line. On a lockfile with 2815 package versions it reported 2, and the publish date rule reported 5. The other three (@octokit/endpoint@9.0.6, ua-parser-js@1.0.41, css-declaration-sorter@6.4.1) are releases on an older line, and pnpm 10.34.9 did reject each of them. CI compares the verdicts with real npm lockfiles and real pnpm.

    Limits: this does not settle the objection that provenance is not a security signal. A finding asks for a review and says nothing about whether the release is malicious, and that sample contains no known compromised version, so it says nothing about detection rates. A maintainer who stopped using provenance is reported too. It is a separate step and does not gate npm install. Attestations are not verified cryptographically (npm audit signatures does that). An attacker with access to the CI pipeline is not detected. The semver condition also has a cost: a new release below the first attested version passes, where pnpm would reject it. Only npm lockfiles are read.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Enhancementnew feature or improvementPriority 2secondary priority issue

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions