[agent] Found by the scheduled vlt bug-hunt routine (ledger #307).
Summary
In a vlt project, the patched name@version is often also shipped as a bundled copy inside another package's tarball (bundleDependencies). vlt unpacks that copy into the parent's store entry, node_modules/.vlt/~npm~<parent>@<ver>/node_modules/<parent>/node_modules/<name>. vlt-lock.json has no node for it. The hosted and vendored rewiring only reaches the lock node, so after vlt ci the bundled copy is still the registry bytes. Even so, scan reports success with no warning, and vex attests the package not_affected / inline_mitigations_already_exist.
For npm this exact case was fixed in #337 (#325). The hosted rewriter warns redirect_npm_bundled_instance_skipped, and VEX drops the ref with patched_ref_unattributable. Neither guard exists for vlt, because vlt's lock doesn't record bundled copies, and vex/discover/vlt.rs and patch/redirect/vlt.rs only look at the lock.
Impact
The VEX document certifies a vulnerability as mitigated while the build ships and runs unpatched bytes of the vulnerable version (whatever code requires it through the bundling parent). This is the same false attestation #325 fixed for npm, and it reproduces on every OS and every vlt version tested.
Repro
Mock registry and patch server: the mock from the ledger's probe workflow (left-pad@1.3.0 pristine/patched, plus a bundler@1.0.0 whose tarball has bundleDependencies: ["left-pad"] and package/node_modules/left-pad/ with pristine bytes). The registry runs on :18555 and the patch API on :18556.
export SOCKET_NPM_REGISTRY=http://127.0.0.1:18555 SOCKET_API_URL=http://127.0.0.1:18556 \
SOCKET_PATCH_SERVER_URL=http://127.0.0.1:18556 SOCKET_ORG_SLUG=test-org SOCKET_API_TOKEN=fake
mkdir proj && cd proj
echo '{"name":"app","version":"1.0.0","dependencies":{"left-pad":"1.3.0","bundler":"1.0.0"}}' > package.json
echo '{"config":{"registries":{"npm":"http://127.0.0.1:18555/"}}}' > vlt.json
vlt install
socket-patch scan --yes --json # or --mode vendored
rm -rf node_modules && vlt ci
node -p "require('left-pad')" # patched
node -p "require('bundler')" # pristine <- bundled copy, unpatched
cat node_modules/.vlt/~npm~bundler@1.0.0/node_modules/bundler/node_modules/left-pad/index.js # 'pristine'
socket-patch vex --output vex.json # rc 0, no warning
# statement: not_affected / inline_mitigations_already_exist, subcomponent pkg:npm/left-pad@1.3.0
The lock after vlt install has only ~npm~bundler@1.0.0 and ~npm~left-pad@1.3.0, with no trace of the bundled copy.
Same project with npm (npm install → scan → vex): the scan warns redirect_npm_bundled_instance_skipped ("that copy stays UNPATCHED"), and vex prints "…also installs a bundled copy of it at node_modules/bundler/node_modules/left-pad … the patch is not attested". No statement is emitted.
Related symptom: if the patched package is present only as a bundled copy (dependencies: {"bundler":"1.0.0"}), hosted scan exits 0 with redirect_vlt_entry_not_found "vlt-lock.json has no default-registry entry for left-pad@1.3.0; run vlt install first". That's misleading: vlt install can't help, because the copy is bundled. npm's equivalent says the bundled copy cannot be redirected.
Expected vs actual
- Expected (CLI_CONTRACT.md, "Contested locks"): "A bundled npm copy … of the same
name@version contests the reference too, … in any other lock. npm unpacks it from the parent package's tarball, so no rewire reaches it and it stays unpatched." vlt unpacks bundled deps the same way. So for vlt, the ref should be dropped with patched_ref_unattributable, and scan should warn that the bundled copy stays unpatched, the way it does for npm.
- Actual:
scan rc 0 success with no bundled warning, and vex rc 0 with a not_affected statement.
OS × vlt version (main 5678b76)
| OS |
vlt 1.0.10 |
vlt 1.2.0 |
vlt 1.3.3 |
| Linux (ubuntu-latest + sandbox) |
repro (hosted + vendored) |
repro (hosted + vendored) |
repro (hosted + vendored; sandbox 3/3 runs) |
| macOS (macos-latest) |
repro (hosted + vendored) |
repro (hosted + vendored) |
repro (hosted + vendored) |
| Windows (windows-latest) |
repro (hosted + vendored) |
repro (hosted + vendored) |
repro (hosted + vendored) |
All 18 cells print RESULT top=patched bundled=pristine and STATEMENT not_affected inline_mitigations_already_exist pkg:npm/left-pad@1.3.0. Probe run: https://github.com/SocketDev/socket-patch/actions/runs/36871535059
First bad: not a regression. It has been present since vlt hosted/vendored + VEX support; release 4.0.0 predates vlt support. #337 fixed npm only.
Suspect code
crates/socket-patch-core/src/vex/discover/vlt.rs:93 (extract): attests from lock nodes only, with no bundled-copy contest like vex/discover/npm.rs:95. Since the vlt lock omits bundled copies, the contest has to come from the installed store (any node_modules/.vlt/*/node_modules/<parent>/node_modules/<name> whose package.json is the ref's name@version) or from each lock node's manifest bundleDependencies.
crates/socket-patch-core/src/patch/redirect/vlt.rs and vendor/vlt_lock.rs:735: vendored only refuses when the target itself declares bundleDependencies (vendor_bundled_deps_unsupported). It doesn't check whether some other package bundles the target. Hosted has no counterpart of redirect_npm_bundled_instance_skipped (patch/redirect/mod.rs:904).
[agent] Found by the scheduled vlt bug-hunt routine (ledger #307).
Summary
In a vlt project, the patched
name@versionis often also shipped as a bundled copy inside another package's tarball (bundleDependencies). vlt unpacks that copy into the parent's store entry,node_modules/.vlt/~npm~<parent>@<ver>/node_modules/<parent>/node_modules/<name>.vlt-lock.jsonhas no node for it. The hosted and vendored rewiring only reaches the lock node, so aftervlt cithe bundled copy is still the registry bytes. Even so,scanreportssuccesswith no warning, andvexattests the packagenot_affected/inline_mitigations_already_exist.For npm this exact case was fixed in #337 (#325). The hosted rewriter warns
redirect_npm_bundled_instance_skipped, and VEX drops the ref withpatched_ref_unattributable. Neither guard exists for vlt, because vlt's lock doesn't record bundled copies, andvex/discover/vlt.rsandpatch/redirect/vlt.rsonly look at the lock.Impact
The VEX document certifies a vulnerability as mitigated while the build ships and runs unpatched bytes of the vulnerable version (whatever code
requires it through the bundling parent). This is the same false attestation #325 fixed for npm, and it reproduces on every OS and every vlt version tested.Repro
Mock registry and patch server: the mock from the ledger's probe workflow (
left-pad@1.3.0pristine/patched, plus abundler@1.0.0whose tarball hasbundleDependencies: ["left-pad"]andpackage/node_modules/left-pad/with pristine bytes). The registry runs on :18555 and the patch API on :18556.The lock after
vlt installhas only~npm~bundler@1.0.0and~npm~left-pad@1.3.0, with no trace of the bundled copy.Same project with npm (
npm install→scan→vex): the scan warnsredirect_npm_bundled_instance_skipped("that copy stays UNPATCHED"), andvexprints "…also installs a bundled copy of it at node_modules/bundler/node_modules/left-pad … the patch is not attested". No statement is emitted.Related symptom: if the patched package is present only as a bundled copy (
dependencies: {"bundler":"1.0.0"}), hostedscanexits 0 withredirect_vlt_entry_not_found"vlt-lock.json has no default-registry entry for left-pad@1.3.0; runvlt installfirst". That's misleading:vlt installcan't help, because the copy is bundled. npm's equivalent says the bundled copy cannot be redirected.Expected vs actual
name@versioncontests the reference too, … in any other lock. npm unpacks it from the parent package's tarball, so no rewire reaches it and it stays unpatched." vlt unpacks bundled deps the same way. So for vlt, the ref should be dropped withpatched_ref_unattributable, and scan should warn that the bundled copy stays unpatched, the way it does for npm.scanrc 0successwith no bundled warning, andvexrc 0 with anot_affectedstatement.OS × vlt version (main
5678b76)All 18 cells print
RESULT top=patched bundled=pristineandSTATEMENT not_affected inline_mitigations_already_exist pkg:npm/left-pad@1.3.0. Probe run: https://github.com/SocketDev/socket-patch/actions/runs/36871535059First bad: not a regression. It has been present since vlt hosted/vendored + VEX support; release 4.0.0 predates vlt support. #337 fixed npm only.
Suspect code
crates/socket-patch-core/src/vex/discover/vlt.rs:93(extract): attests from lock nodes only, with no bundled-copy contest likevex/discover/npm.rs:95. Since the vlt lock omits bundled copies, the contest has to come from the installed store (anynode_modules/.vlt/*/node_modules/<parent>/node_modules/<name>whosepackage.jsonis the ref'sname@version) or from each lock node's manifestbundleDependencies.crates/socket-patch-core/src/patch/redirect/vlt.rsandvendor/vlt_lock.rs:735: vendored only refuses when the target itself declaresbundleDependencies(vendor_bundled_deps_unsupported). It doesn't check whether some other package bundles the target. Hosted has no counterpart ofredirect_npm_bundled_instance_skipped(patch/redirect/mod.rs:904).