deno
702cafbc - feat(npm): publishing-trust ranking and no-downgrade trust policy (#34927)

Commit
135 days ago
feat(npm): publishing-trust ranking and no-downgrade trust policy (#34927) This teaches Deno's npm resolver about how a package version was published and adds an opt-in `no-downgrade` trust policy, following pnpm's implementation (`resolving/npm-resolver/src/trustChecks.ts`) closely. npm's full packument exposes whether a version was published via trusted publishing (OIDC, `_npmUser.trustedPublisher`), carries a provenance attestation (`dist.attestations.provenance`), or went through staged publishing (`_npmUser.approver`, a maintainer approving with a live 2FA challenge). Each version's strongest signal is ranked into a single trust level. The tiers are mutually exclusive and mirror pnpm: a staged publish is strongest, then trusted publishing *backed by* a provenance attestation, then a provenance attestation on its own. A `trustedPublisher` flag without provenance is deliberately not counted, since on its own it is just metadata a future staged-publish flow could mint. The `no-downgrade` policy, enabled with `trust-policy=no-downgrade` in `.npmrc`, refuses to resolve a version whose trust evidence is weaker than the strongest evidence on any earlier-published version of the same package. The comparison is by publish date, not semver, exactly as pnpm does it: if a package has been published through trusted publishing or with provenance and a later version suddenly appears as a plain token publish, that is a strong signal of a stolen maintainer token, and the policy turns it into a hard error instead of a silent install. When a downgrade is the only thing blocking resolution, the resolver returns a dedicated error explaining the rejection and how to override it, rather than silently falling back to an older version. The motivation is supply-chain safety. This is the same defense that would have caught the August 2025 s1ngularity incident, where a package consistently published through CI was suddenly published from a laptop with basic credentials. The trust signals only exist in the full packument. Enabling the policy fetches the full packument, but `min-release-age` already makes Deno do that by default, so in the common case there is no extra fetch. To keep the cached `registry.json` small, the never-read signal objects (`_npmUser.approver`, `_npmUser.trustedPublisher`, `dist.attestations.provenance`) are reduced to a one-byte presence marker before the packument is cached. A `trust-policy-ignore-after` setting (in minutes, mirroring pnpm's `trustPolicyIgnoreAfter`) skips the check for versions published more than that long ago, so genuinely pre-provenance releases still install. It is turned into an absolute cutoff in the resolver factory so the resolver stays free of a wall clock. Coverage: tiered ranking, the publish-date downgrade scan (including prerelease exclusion and the ignore-after cutoff), the targeted downgrade error, and compact cache serialization are covered by resolver unit tests. An end-to-end spec test installs a package whose newer version downgrades from a staged publish to a plain publish and asserts the install is rejected, plus a spec test that the cache keeps compact markers. The test npm registry now preserves `dist.attestations` from fixtures so provenance is exercisable end to end. Unlike pnpm, the trust level is not persisted to the lockfile: the policy recomputes the baseline from the packument's version history on each resolution, so existing lockfiles are unaffected. A `trust-policy-exclude[]` setting (mirroring pnpm's `trustPolicyExclude`) exempts named packages from the policy: repeated `trust-policy-exclude[]=<package>` entries in `.npmrc` list packages that resolve as if the policy were off. Home and project excludes are unioned. The policy is opt-in and stays off by default. Provenance, trusted publishing and staged publishing are still unevenly adopted across the registry, so enabling `no-downgrade` by default would turn legitimate trust-evidence gaps into hard install failures; it can be revisited once adoption is high.
Author
Parents
Loading