fix(security): harden build-approval artifact identities on v10#12306
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Code Review by Qodo
1. File-based approvals ignored
|
PR Summary by QodoHarden allowBuild/onlyBuiltDependencies by pinning approvals to depPath identity (v10) WalkthroughsDescription• Gate lifecycle-script approval on trusted depPath identity, not manifest-supplied package name. • Reject lockfile entries where name@semver keys resolve to git/tarball/directory artifacts. • Add depPath-based rebuild/approve flows, update tests, and pin shell-quote to 1.8.4. Diagramgraph TD
CLI["Install / Rebuild"] --> Policy["AllowBuild policy"] --> DepPath["DepPath utils"]
CLI --> LockUtils["Lockfile shape check"] --> Lockfile[("pnpm-lock")]
Fetchers["Git/Tarball fetchers"] --> Prepare["preparePackage"] --> Policy
CLI --> Prepare
High-Level AssessmentThe following are alternative approaches to this PR: 1. Centralized lockfile verification phase (single gate)
2. Require depPath approvals for all builds (drop name-based rules)
3. Trust lockfile gitHosted flag / resolution metadata without URL inspection
Recommendation: Keep the PR’s approach: use depPath as the approval identity, infer trust only from registry-shaped depPaths, and enforce the depPath↔resolution shape invariant at materialization time (pkgSnapshotToResolution) and immediately before running scripts (rebuild). This preserves existing name-based allowlists for registry packages while closing the identity-confusion hole for git/tarball/directory artifacts, and it is compatible with v10’s architecture. File ChangesEnhancement (3)
Bug fix (15)
Refactor (3)
Tests (9)
Documentation (1)
Other (6)
|
Port of #12294 (commit bf1b731) to release/10. Package-name entries in onlyBuiltDependencies (and allowBuilds) no longer approve lifecycle scripts for artifacts whose identity a name cannot pin: git, git-hosted tarball, direct tarball, and local directory dependencies. To approve such an artifact explicitly, use its peer-suffix-free lockfile depPath as the key — the GIT_DEP_PREPARE_NOT_ALLOWED hint, pnpm ignored-builds, and pnpm approve-builds print exactly that key. - AllowBuild policy functions identify packages by DepPath instead of caller-supplied name/version. Identity trust is derived from the depPath shape: a registry-style depPath (name@semver) is a trusted identity. - The trust is sound because lockfile entries are structurally checked wherever they are materialized into fetchable resolutions (pkgSnapshotToResolution) and before rebuild runs scripts: a registry-style key backed by a git, directory, or git-hosted tarball resolution is rejected with ERR_PNPM_RESOLUTION_SHAPE_MISMATCH. - preparePackage requires a pkgResolutionId and gates on the synthesized name@<resolution id> depPath; scp-style git URLs are normalized to ssh:// form in resolution ids and the git fetcher reuses createGitHostedPkgId from the resolver. - isGitHostedTarballUrl matches case-insensitively and is shared from @pnpm/lockfile.utils; new removePeersSuffix() in @pnpm/dependency-path and allowBuildKeyFromIgnoredBuild() in @pnpm/builder.policy. - pnpm rebuild and approve-builds accept depPath specs for selecting and approving artifact builds; installs rebuild ignored builds approved by depPath keys. - shell-quote is overridden to 1.8.4 (GHSA-w7jw-789q-3m8p / CVE-2026-9277). Differences from main: v10 has no lockfile resolution verifier, so the structural pass lives in pkgSnapshotToResolution and the rebuild loop; the AllowBuild policy keeps v10's boolean return; the revoked-approval detection and global-virtual-store hash changes have no v10 counterpart.
900b1b1 to
7288c44
Compare
|
Code review by qodo was updated up to the latest commit 7288c44 |
The registry mock binds only localhost, so a hard-coded 127.0.0.1 URL is refused in CI, and a tarball URL on the registry's own origin resolves as a registry package, which defeats the direct-tarball identity the test needs. An in-test HTTP server redirecting to the mock provides a separate origin whose binding the test controls.
|
Code review by qodo was updated up to the latest commit 4c9251c |
A git-hosted artifact has an untrusted package identity, so the ajv-keywords build has to be approved by its depPath. Pin the fixture to the commit the depPath names.
|
Code review by qodo was updated up to the latest commit a010c4d |
Summary
Port of #12294 (commit
bf1b731ee6) torelease/10.Package-name entries in
onlyBuiltDependencies(andallowBuilds) no longer approve lifecycle scripts for artifacts whose identity a name cannot pin: git, git-hosted tarball, direct tarball, and local directory dependencies. A dependency claiming the name of an allow-listed package (e.g. a git dependency whose manifest says"name": "esbuild") could previously get its scripts approved by that name. To approve such an artifact explicitly, use its peer-suffix-free lockfile depPath as the key — theGIT_DEP_PREPARE_NOT_ALLOWEDhint,pnpm ignored-builds, andpnpm approve-buildsprint exactly that key.AllowBuildpolicy functions identify packages byDepPathinstead of caller-supplied name/version. The policy parses name and version out of the depPath itself, so name-keyed rules can never be fed an identity that disagrees with the gated artifact.name@semver) is a trusted identity. This is sound because lockfile entries are structurally checked wherever they are materialized into fetchable resolutions (pkgSnapshotToResolution, which covers the headless isolated/hoisted graphs and the non-headless lockfile-reuse path) and in the rebuild loop before scripts run: a registry-style key backed by a git, directory, or git-hosted tarball resolution is rejected withERR_PNPM_RESOLUTION_SHAPE_MISMATCH. Non-http(s)-schemed and host-escaping tarball URLs under semver keys are rejected too; registry-relative tarball paths written by older pnpm versions stay accepted.preparePackagealways treats the fetched manifest as an untrusted identity: it requires apkgResolutionIdand gates on the synthesizedname@<resolution id>depPath. scp-style git URLs are normalized tossh://form in resolution ids, and the git fetcher reusescreateGitHostedPkgIdfrom the resolver instead of re-deriving ids.pnpm rebuildandpnpm approve-buildsaccept depPath specs for selecting and approving artifact builds; installs rebuild previously ignored builds approved by depPath keys (runUnignoredDependencyBuilds).isGitHostedTarballUrlmatches case-insensitively and is shared from@pnpm/lockfile.utils(thelockfile.fscopy is removed). New shared helpers:removePeersSuffix()in@pnpm/dependency-pathandallowBuildKeyFromIgnoredBuild()in@pnpm/builder.policy.shell-quoteis overridden to 1.8.4 (GHSA-w7jw-789q-3m8p / CVE-2026-9277).Differences from main
v10 has no lockfile resolution verifier (
verifyLockfileResolutions), so the structural shape pass lives inpkgSnapshotToResolutionand the rebuild loop instead of the verification gate, and there is no verification-cache identity to thread. TheAllowBuildpolicy keeps v10's boolean return (no explicit-deny tri-state) and is built fromonlyBuiltDependencies/neverBuiltDependencies, into whichallowBuildsis folded by config. The revoked-approval detection and the global-virtual-store hash/rebuild-projection changes from main have no v10 counterpart.Testing
Ported/adapted the policy, prepare-package, fetcher, git-resolver, dlx, and core lifecycle-script tests from main, and added a v10 test suite for the new structural check (
lockfile/utils/test/assertRegistryShapedResolution.ts). Suites for all touched packages pass locally except failures that reproduce identically on the untouchedrelease/10tip in the same environment (rebuild/build-commands/headless spawn-related local-env issues).Written by an agent (Claude Code, claude-fable-5).