Fix in-run hosted VEX attesting npm bundled copies (#325) - #669
Mikola Lysenko (mikolalysenko) wants to merge 5 commits into
Conversation
Assisted-by: Claude Code:claude-opus-5-5
When a package-lock.json also lists a bundled copy of the patched package (inBundle, or the legacy v1 "bundled" flag), hosted mode redirects the regular entry but can't reach the bundled one: npm unpacks it from the parent's tarball. The run already warned that copy stays unpatched, yet `scan --vex` still attested the patch as not_affected. The npm rewriter now records the patch as having a skipped bundled copy, the same way the Bun rewriter does, so the in-run VEX verifies it instead of assuming it applied, and leaves it out. Fixes #325 Assisted-by: Claude Code:claude-opus-5-5
Assisted-by: Claude Code:claude-opus-5-5
The seeded npm lock sweep generates bundled entries, and its recorded rewrite result now carries the patch uuids those entries skipped. Input digests are unchanged; only the cases with a bundled match moved. Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
|
[burn-down agent] Labeled Ready for review at head
Generated by Claude Code |
|
Codex follow-up review of The legacy bundled UUID is now recorded only when no object-valued Validation on the exact committed source:
482 successful checks, 7 skipped, and 12 successful workflows (1 additional workflows skipped). Bugbot is clear on this commit; no unresolved review threads or new actionable findings. Ready for review has been restored. |
|
Cursor (@cursor) review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit afd320e. Configure here.
LLM Description written by Claude Code:claude-opus-5-5
Fixes #325
Summary
In hosted mode,
scan --vexno longer attests an npm patch asnot_affectedwhenpackage-lock.jsonalso contains a bundled copy of the samename@version. The bundled copy isinBundle: true, orbundled: truein a v1 lock. npm unpacks that copy from its parent's tarball, so hosted mode can't redirect it and it stays unpatched.Root cause
scan --mode hosted --vexattests the patches this run redirected without checking hashes (assume_applied). It leaves out a patch whose uuid the rewriter put inRewriteResult::bundled_skipped_uuids, meaning some copy couldn't be redirected (#469).The Bun rewriters record that uuid. The npm
package-lock.jsonrewriter skips the bundled entry and warnsredirect_npm_bundled_instance_skipped, but never records the uuid. So the hoisted copy gets redirected, and the in-run VEX attests the purl while the same run warns that the bundled copy stays unpatched.Fix
crates/socket-patch-core/src/patch/redirect/mod.rs: recorddep.patch_uuidinbundled_skipped_uuidsfor the tree npm actually installs from:packagesinBundleentries;dependenciesbundledentries, but only when there is no object-valuedpackagesmap.A stale legacy mirror keeps its skip and warning but no longer blocks an in-run attestation. The existing
assume_appliedfilter inscan/hosted.rsthen sends the purl through normal verification. That matches standalonevexand the vendored in-run path.tests/equivalence/npm_lock_rewrite.golden: re-blessed. Input digests are unchanged; only output digests for cases with a bundled match in the install tree moved.CHANGELOG entry under Unreleased / Fixed.
Review follow-up
Codex found that the first version also recorded the uuid from a stale v2
dependenciesmirror while an object-valuedpackagesmap existed. npm 7+ ignores that mirror, so a correctly redirected package lost its in-run attestation.afd320efixed this: the legacy uuid is now recorded only when there's no object-valuedpackagesmap, the same precedence npm lock discovery uses. It also addsnpm_stale_legacy_bundled_mirror_does_not_contest_packages, covering direct and nested mirrors with and without a real bundle, plus an in-process VEX regression. I reviewed the diff and it's correct.Tests (red → green)
npm_inbundle_entry_is_skipped_with_loud_warning(asserts uuid recorded)npm_inbundle_skip_leaves_sibling_rewrite_intact(redirected sibling + bundled copy)npm_legacy_bundled_dependency_is_skipped(v1bundled)in_process_redirect::scan_redirect_npm_bundled_copy_is_not_attested_in_run(lock v3inBundleand lock v1bundled, real in-run--vex)inBundle: in-run VEX must not attest a purl whose bundled copy stays unpatchednpm_stale_legacy_bundled_mirror_does_not_contest_packages(v2 stale mirror)4b18db6afd320ePer-issue checklist
scan --vexattests a bundled-contested npm purl. Covered byscan_redirect_npm_bundled_copy_is_not_attested_in_runand the unit tests above. The other paths in the issue's table (post-installvex, vendored in-run) were already fixed by Fix npm VEX attesting packages with an unpatched bundled copy (#325) #337.Local validation
cargo clippy --workspace --all-features -- -D warnings: clean.cargo fmt: main itself failscargo fmt --all -- --check, and CI doesn't run it. My hunks are rustfmt-clean.cargo test --workspace --all-features --no-fail-fast: everything passes except tests that rely on chmod-based write failures. This sandbox runs as root, which ignores those modes, and the same tests fail identically onorigin/main.e2e_redirect_npm_build -- --ignored(npm 10.9.4): the only failures were at therollbackstep, which needs registry.npmjs.org from the Rust client and is blocked by the sandbox proxy.Additional focused validation (reviewer, on
afd320e)CI
CI and Bugbot on
afd320eb9819d9f30758f307ef6e641370d4c98fare clear: 482 successful checks, 7 skipped, and 12 successful workflows (1 additional workflows skipped). Bugbot is clear on this commit; no unresolved review threads or new actionable findings.No wrapper changes are needed. The npm, pypi and gem wrappers only dispatch to the binary.
🤖 Generated with Claude Code