[agent] Found by the scheduled vlt bug-hunt routine (ledger #307).
Summary
vlt 1.3.0 added --brotli-tarballs (default on). When a registry's packument advertises dist.alternates: [{ "kind": "tar.br", ... }] for a version, vlt resolves the .tar.br artifact and records a new node flag bit, LockfileNodeFlagBrotli = 4, in slot [0] (so a node's flags can now be 4, 5, 6 or 7). vlt 1.3's lockfile code is otherwise unchanged from 1.2.0: the DepID grammar and the one-node-per-line layout are the same, and lockfileVersion stays 1.
socket-patch's strict node-line grammar only accepts slot [0] ∈ {0,1,2,3} (crates/socket-patch-core/src/vendor/vlt_lock_text.rs:673), so a node with a brotli flag doesn't parse:
- Hosted (
scan --mode hosted): when every node is brotli (the normal case for a project on such a registry), the lock is refused with redirect_vlt_lock_unsupported: "nodes section is not in vlt's canonical layout; re-save it with a current vlt (vlt install) or update socket-patch". The command exits 0, nothing is redirected, and vlt ci installs the unpatched bytes. The remedy the message gives is wrong: the lock was just written by the current vlt. In a mixed lock, the lock-level check passes (it uses .any(), patch/redirect/vlt.rs:93-98), but a brotli target still can't be located by instance_line, so that dependency is refused.
- Vendored (
scan --mode vendored / vendor): fails with vendor_lockfile_version_unsupported, "vlt-lock.json is not in vlt's canonical layout; re-save it with vlt install". The code and message are both misleading: the lockfileVersion is 1.
- Agent mode is unaffected (apply and rollback pass: it doesn't read the lock).
Impact
A vlt ≥ 1.3.0 user on a registry that serves Brotli tarballs can't use hosted or vendored mode at all. The refusal is loud in --json, but scan --mode hosted still exits 0. vlt 1.3.0–1.3.2 are published (1.3.2 is latest). The nightly canary already flags them as unlisted (vlt-compatibility run https://github.com/SocketDev/socket-patch/actions/runs/36669570554, canary (ubuntu-latest)), but the canary's capstones run against npmjs bytes, which don't advertise alternates. So they pass on 1.3.1 and this doesn't show.
Repro (Linux, vlt 1.3.2, Node 22.22.2, main f6b7fb9)
A ~60-line Node mock (mock.mjs, serving a registry plus the patch API) serves [email protected] with dist.alternates → left-pad-1.3.0.tar.br, plus a free hosted patch. NO_ALT=1 turns the alternates off for the control. The mock is in the ledger entry.
node mock.mjs & # PORT=18555
mkdir p && cd p
echo '{"name":"app","version":"1.0.0","dependencies":{"left-pad":"1.3.0"}}' > package.json
echo '{"config":{"registries":{"npm":"http://127.0.0.1:18555/"}}}' > vlt.json
LANG=C vlt install
grep '~npm~left-pad' vlt-lock.json
# "[email protected]": [4,"left-pad","sha512-p3Lt…","http://127.0.0.1:18555/left-pad/-/left-pad-1.3.0.tar.br"]
socket-patch scan --mode hosted --yes --api-url http://127.0.0.1:18555 --org test-org --api-token fake --json
# rc=0, "redirected": 0, warnings: [redirect_vlt_lock_unsupported "nodes section is not in vlt's canonical layout …"]
rm -rf node_modules && vlt ci && node -e "console.log(require('left-pad'))" # pristine
socket-patch scan --mode vendored --yes --api-url http://127.0.0.1:18555 --org test-org --api-token fake --json
# rc=1, errorCode vendor_lockfile_version_unsupported "vlt-lock.json is not in vlt's canonical layout; re-save it with `vlt install`"
Control (same mock with NO_ALT=1, so the node flag is 0): hosted redirects 1 dependency, and vlt install then gives patched.
Expected vs actual
- Expected: docs/ecosystems.md "npm: vlt notes" says a lock is refused only when socket-patch "cannot read [it] exactly as vlt does (a UTF-8 BOM, another
lockfileVersion, a nodes section outside vlt's one-node-per-line layout)". This lock has none of those. It's canonical vlt output with lockfileVersion: 1.
- Actual: refused as non-canonical, with a wrong remedy (hosted) and a wrong error code (vendored).
What a fix must also handle (verified with real vlt 1.3.2)
I hand-applied the pin a hosted rewrite would write to the brotli node (patched sha512 in slot [2], the hosted .tgz URL in slot [3]):
| slot [0] kept as |
vlt ci installs |
lock after vlt ci |
4 |
patched |
rewritten: vlt recomputes brotli from the URL and saves [0,…] |
0 (bit 4 cleared) |
patched |
byte-identical; vlt install --frozen-lockfile is byte-stable too |
So the hosted rewrite should clear bit 4 when it points a node at the .tgz artifact, and the slot revert should restore it. Also, vlt_heal::reinstalls_after_removal (patch/redirect/vlt_heal.rs:199, matches!(flags, Some(0 | 2))) treats a prod or dev brotli node (4, 6) like an optional one and keeps the stale copy. It probably wants flags & 1 == 0 once bit 4 is known. VEX discovery goes through the same line parser (vex/discover), so it likely skips these nodes as well.
Matrix
| OS |
vlt |
brotli alternates advertised |
hosted |
vendored |
agent |
| Linux |
1.2.0 |
yes (ignored, flag 0) |
pass |
untested |
untested |
| Linux |
1.3.1 |
yes |
fail (refused) |
fail |
untested |
| Linux |
1.3.2 |
yes |
fail (refused) |
fail |
pass |
| Linux |
1.3.2 |
no (control) |
pass |
untested |
untested |
| macOS / Windows |
1.3.x |
yes |
untested (pure text-parser path, expected identical) |
untested |
untested |
vlt 1.3.0 itself fails vlt install with "Integrity check failure" against a registry advertising alternates (a vlt bug, fixed in 1.3.1), so 1.3.0 locks with bit 4 are unlikely in practice.
First bad: vlt 1.3.0 (first release with LockfileNodeFlagBrotli). Release 4.0.0 of socket-patch predates vlt hosted support (it reports redirect_npm_no_lockfile), so this isn't a socket-patch regression.
Suspect code: crates/socket-patch-core/src/vendor/vlt_lock_text.rs:673 (the "0" | "1" | "2" | "3" whitelist), crates/socket-patch-core/src/patch/redirect/vlt.rs:93-103, crates/socket-patch-core/src/patch/redirect/vlt_heal.rs:199.
[agent] Found by the scheduled vlt bug-hunt routine (ledger #307).
Summary
vlt 1.3.0 added
--brotli-tarballs(default on). When a registry's packument advertisesdist.alternates: [{ "kind": "tar.br", ... }]for a version, vlt resolves the.tar.brartifact and records a new node flag bit,LockfileNodeFlagBrotli = 4, in slot [0] (so a node's flags can now be 4, 5, 6 or 7). vlt 1.3's lockfile code is otherwise unchanged from 1.2.0: the DepID grammar and the one-node-per-line layout are the same, andlockfileVersionstays1.socket-patch's strict node-line grammar only accepts slot [0] ∈ {0,1,2,3} (
crates/socket-patch-core/src/vendor/vlt_lock_text.rs:673), so a node with a brotli flag doesn't parse:scan --mode hosted): when every node is brotli (the normal case for a project on such a registry), the lock is refused withredirect_vlt_lock_unsupported: "nodes section is not in vlt's canonical layout; re-save it with a current vlt (vlt install) or update socket-patch". The command exits 0, nothing is redirected, andvlt ciinstalls the unpatched bytes. The remedy the message gives is wrong: the lock was just written by the current vlt. In a mixed lock, the lock-level check passes (it uses.any(),patch/redirect/vlt.rs:93-98), but a brotli target still can't be located byinstance_line, so that dependency is refused.scan --mode vendored/vendor): fails withvendor_lockfile_version_unsupported, "vlt-lock.json is not in vlt's canonical layout; re-save it withvlt install". The code and message are both misleading: thelockfileVersionis 1.Impact
A vlt ≥ 1.3.0 user on a registry that serves Brotli tarballs can't use hosted or vendored mode at all. The refusal is loud in
--json, butscan --mode hostedstill exits 0. vlt 1.3.0–1.3.2 are published (1.3.2 islatest). The nightly canary already flags them as unlisted (vlt-compatibility run https://github.com/SocketDev/socket-patch/actions/runs/36669570554,canary (ubuntu-latest)), but the canary's capstones run against npmjs bytes, which don't advertise alternates. So they pass on 1.3.1 and this doesn't show.Repro (Linux, vlt 1.3.2, Node 22.22.2, main
f6b7fb9)A ~60-line Node mock (
mock.mjs, serving a registry plus the patch API) serves[email protected]withdist.alternates→left-pad-1.3.0.tar.br, plus a free hosted patch.NO_ALT=1turns the alternates off for the control. The mock is in the ledger entry.Control (same mock with
NO_ALT=1, so the node flag is 0): hosted redirects 1 dependency, andvlt installthen givespatched.Expected vs actual
lockfileVersion, anodessection outside vlt's one-node-per-line layout)". This lock has none of those. It's canonical vlt output withlockfileVersion: 1.What a fix must also handle (verified with real vlt 1.3.2)
I hand-applied the pin a hosted rewrite would write to the brotli node (patched sha512 in slot [2], the hosted
.tgzURL in slot [3]):vlt ciinstallsvlt ci4brotlifrom the URL and saves[0,…]0(bit 4 cleared)vlt install --frozen-lockfileis byte-stable tooSo the hosted rewrite should clear bit 4 when it points a node at the
.tgzartifact, and the slot revert should restore it. Also,vlt_heal::reinstalls_after_removal(patch/redirect/vlt_heal.rs:199,matches!(flags, Some(0 | 2))) treats a prod or dev brotli node (4, 6) like an optional one and keeps the stale copy. It probably wantsflags & 1 == 0once bit 4 is known. VEX discovery goes through the same line parser (vex/discover), so it likely skips these nodes as well.Matrix
vlt 1.3.0 itself fails
vlt installwith "Integrity check failure" against a registry advertising alternates (a vlt bug, fixed in 1.3.1), so 1.3.0 locks with bit 4 are unlikely in practice.First bad: vlt 1.3.0 (first release with
LockfileNodeFlagBrotli). Release 4.0.0 of socket-patch predates vlt hosted support (it reportsredirect_npm_no_lockfile), so this isn't a socket-patch regression.Suspect code:
crates/socket-patch-core/src/vendor/vlt_lock_text.rs:673(the"0" | "1" | "2" | "3"whitelist),crates/socket-patch-core/src/patch/redirect/vlt.rs:93-103,crates/socket-patch-core/src/patch/redirect/vlt_heal.rs:199.