@@ -19,8 +19,9 @@ into the new version's section — see docs/releasing.md.
1919
2020> ** Semver note:** this entry changes ` rollback ` 's default behavior, narrows
2121> the meaning of its existing ` vendored: [] ` JSON key, makes vendored mode
22- > manifest-free, and turns a plain non-TTY ` scan ` report-only — all MAJOR
23- > per CLI_CONTRACT.md's semver policy — so it ships as the next major
22+ > manifest-free, turns a plain non-TTY ` scan ` report-only, and makes ` vex `
23+ > refuse to attest stale ledger records and corrupt vendor ledgers — all
24+ > MAJOR per CLI_CONTRACT.md's semver policy — so it ships as the next major
2425> release (v5.0).
2526
2627### Changed (BREAKING)
@@ -129,9 +130,165 @@ into the new version's section — see docs/releasing.md.
129130 ` [patch.crates-io] ` entries now refuse as ` user_authored_patch_entry ` ) and
130131 the ` pypi_pipenv_invalid_wheel ` refusal code (the Pipenv backend takes the
131132 resolved version instead of parsing the wheel filename).
133+ - ** ` vex ` attests a ledger record only while a lockfile still wires it —
134+ even under ` --no-verify ` .** A vendor-ledger entry whose artifact no
135+ lockfile/config references any more is omitted as ` vendor_unwired ` , and a
136+ redirect-ledger record whose hosted patch no lockfile references as
137+ ` redirect_unwired ` (a manifest-owned purl falls back to agent-mode
138+ verification instead). Previously a reverted lockfile plus a leftover
139+ ` .socket/vendor/state.json ` / ` redirect-state.json ` kept attesting, and
140+ ` --no-verify ` / ` --vex-no-verify ` attested every ledger record outright;
141+ those flags now skip only the hashing, never the wiring, record-match and
142+ conflict gates. A malformed or unreadable ` .socket/vendor/state.json ` is
143+ now the hard error ` vendor_ledger_corrupt ` (exit 2 standalone, the host
144+ command fails under ` --vex ` ), mirroring ` redirect_ledger_corrupt ` , instead
145+ of a warning after which vendored patches silently lost their
146+ committed-artifact verification and detached records.
147+ Lockfiles that wire one package to different patches attest none of them
148+ (` wiring_conflict ` ), and when the lockfile wires a package to patch U, a
149+ manifest or ledger record for it under another uuid is superseded.
132150
133151### Added
134152
153+ - ** Hosted npm redirects configure npm 12's ` allow-remote ` for you.** npm 12
154+ defaults to ` allow-remote=none ` and refuses (EALLOWREMOTE) a lock that
155+ resolves patched packages from the Socket patch host. When `scan --mode
156+ hosted` / ` get --mode hosted` leaves a root ` package-lock.json` /
157+ ` npm-shrinkwrap.json ` redirected, it now writes ` allow-remote=all ` to the
158+ project ` .npmrc ` — creating the file, or appending one line with the BOM,
159+ CRLF and every other byte preserved — so a plain ` npm ci ` installs the
160+ patched bytes on npm 12 (verified on npm 10.9.9, 11.20.0 and 12.1.0). The
161+ edit is ledger-recorded (` redirect_npmrc_allow_remote ` , ` created ` / ` added ` )
162+ and ` rollback ` , ` remove ` , scoped unwinds and the hosted → vendored takeover
163+ remove exactly what was added once no package-lock entry needs it (a
164+ modified created file keeps its other lines:
165+ ` redirect_npmrc_allow_remote_modified ` , reported by ` rollback ` , ` remove ` ,
166+ ` vendor ` and the vendored reconcile). An explicit user
167+ ` allow-remote=none ` / ` root ` is respected — in the project ` .npmrc ` , in the
168+ user / global / builtin npm config (a committed project line would
169+ silently override that machine policy), or in an ` npm_config_allow_remote `
170+ environment variable (which beats every ` .npmrc ` ) — and the warning names
171+ where it was found. A symlinked, unreadable or bare-CR ` .npmrc ` is left
172+ alone (and a symlinked one refuses an unwind before anything is written),
173+ ` --dry-run ` writes nothing but previews the write — also for a vendored →
174+ hosted takeover — and
175+ ` --no-npm-allow-remote-config ` / ` SOCKET_NO_NPM_ALLOW_REMOTE_CONFIG ` opts
176+ out. ` redirect_npm_allow_remote ` now fires on EVERY hosted npm run —
177+ including when ` .npmrc ` already allows it — and always states the
178+ tradeoff: ` allow-remote=all ` admits any url-resolved dependency, not just
179+ Socket's, while the sha512 integrity pins stay enforced. The ` .npmrc `
180+ sniff now follows npm 12's measured grammar: only the exact key
181+ ` allow-remote ` counts (an ` allow_remote ` / ` ALLOW-REMOTE ` line, which npm
182+ ignores, previously silenced the warning), lines split on a bare ` \r ` as
183+ well as ` \n ` , a ` [section] ` header only counts on the untrimmed line (as
184+ in npm's ` ini ` ), section bodies are not top-level (a section-scoped copy of
185+ the line never makes the unwind ambiguous), and the value is
186+ case-sensitive. Mode-preserving atomic writes now create their stage file
187+ with the destination's permission bits, so a 0600 token-bearing ` .npmrc `
188+ is never staged world-readable. Vendored mode is unaffected
189+ (npm gates ` file: ` tarballs by ` allow-file ` , default ` all ` ).
190+
191+ - ** Manifest-less VEX: ` vex ` attests hosted and vendored patches straight
192+ from the lockfiles.** ` socket-patch vex ` , and ` apply ` / ` scan ` / `vendor
193+ --vex` , no longer need ` .socket/manifest.json` , or the ` .socket/vendor`
194+ ledgers, to attest hosted and vendored patches. That covers a
195+ depscan-opened PR, a clone that never committed its ledgers, and a lock-only
196+ CI checkout. New read-only discovery (` socket-patch-core ` ` vex::discover ` )
197+ reads every supported root lockfile and config:
198+ - npm: package-lock / shrinkwrap;
199+ - pnpm: every lock generation, plus Rush locks;
200+ - yarn classic and berry;
201+ - ` bun.lock ` / ` bun.lockb ` ;
202+ - cargo: ` Cargo.lock ` + ` Cargo.toml ` + cargo config;
203+ - Go: ` go.mod ` / ` go.work ` + sums;
204+ - Python: uv, PEP 723 script locks, ` pylock.toml ` , poetry, pdm,
205+ ` Pipfile.lock ` , requirements (+ ` -r ` includes), Hatch / PEP 621 direct
206+ references;
207+ - ` Gemfile.lock ` / ` gems.locked ` ;
208+ - ` composer.lock ` ;
209+ - maven: ` pom.xml ` ;
210+ - nuget: ` nuget.config ` + ` packages.lock.json ` .
211+
212+ Each patch uuid is recovered from a Socket patch-host URL (or the
213+ ` --patch-server-url ` origin) or a ` .socket/vendor/<eco>/<uuid>/… ` path,
214+ validated fail-closed. A lock that resolves the same package elsewhere
215+ contests the wiring and blocks it. Records come from the manifest, then
216+ the ledgers, then (online only) the patch API by uuid; nothing is written
217+ to the manifest. ` --offline ` or a failed fetch omits the patch as
218+ ` record_unavailable ` , and a record naming another patch or package as
219+ ` record_mismatch ` . Evidence: vendored patches hash the committed artifact.
220+ Hosted patches hash the installed copy the build consumes (the Go
221+ replacement module, never the pristine cache copy), or, before any
222+ install, attest from the lockfile's integrity pin. Unreadable or
223+ unparseable lockfiles and rejected references surface as run warnings
224+ (` lockfile_unreadable ` , ` lockfile_unparseable ` , ` patched_ref_invalid ` ,
225+ ` patched_ref_unattributable ` ) and never abort the run. Why a patch was
226+ gated rides ` warnings[] ` too, so ` --json ` keeps it: a failed record fetch
227+ (` vex_record_fetch_failed ` , ` vex_record_not_found ` ,
228+ ` vex_record_offline ` ), a stale-credential fallback to the public proxy
229+ (` api_auth_fallback ` , ` get ` / ` scan ` 's warning), a wiring conflict
230+ (` vex_wiring_conflict ` ), a superseded record (` vex_record_superseded ` )
231+ and a dead ledger claim (` vex_claim_unwired ` ); a failed embedded ` --vex `
232+ adds one ` vex_omitted ` per omitted patch to the host command's
233+ ` warnings[] ` . ` apply --vex ` and
234+ ` vendor --vex ` with no manifest now write the document instead of exiting
235+ 0 with none. A project with nothing wired anywhere keeps that calm exit,
236+ and ` apply --check ` never generates. Manifest-less runs honor `vex
237+ --dry-run` and ` -O -` like every ` vex` run, and show a transient
238+ ` Fetching patch records... ` status line on a terminal. Per-PM support and
239+ limitations: the README's "No manifest needed for hosted and vendored
240+ patches" and CLI_CONTRACT.md's "Manifest-less VEX".
241+ - ** One lockfile reader per format, and one ledger-liveness rule.**
242+ Discovery and the lock inventory read each lockfile through one reader
243+ (the package-lock, composer.lock and Pipfile.lock entry walks, the hosted
244+ rewriter's ` pnpm-lock.yaml ` grammar with one key grammar for every lock
245+ generation, the backends' fail-closed ` bun.lock ` line grammar — so a hand
246+ re-indented ` bun.lock ` is diagnosed ` lockfile_unparseable ` instead of
247+ attested — one berry key splitter, which the vendored berry backend now
248+ uses too, a shared ` Gemfile.lock ` model, the vendor backend's
249+ ` Cargo.lock ` model, the Python lock and requirements-file readers the
250+ rewriters use, with one exact-pin rule and one hosted pypi url
251+ grammar).
252+ ` scan ` 's cross-mode takeover warnings (` redirect_supersedes_vendored ` ,
253+ ` vendor_supersedes_redirect ` ), ` hosted_wiring_retained ` and
254+ ` redirectState.wiringLive ` now prove the live lock with the same
255+ discovery and liveness rules ` vex ` gates attestations on, instead of a
256+ looser text scan: a ledger record counts as live only while a lockfile
257+ wires that package to the record's patch (a hosted url carrying its
258+ uuid, or the exact vendored artifact the ledger names). That rule reads
259+ EVERY lock's entry for the package (a PEP 723 script lock resolving it
260+ from PyPI no longer vetoes the project lock's hosted entry) and a
261+ recorded Gemfile with discovery's source-block grammar (a commented-out
262+ ` source … do ` block no longer keeps a reverted record alive). The
263+ shared readers
264+ also change the lock inventory at the edges: a v1 ` Cargo.lock ` 's
265+ ` [metadata] ` checksums now verify a crates.io fetch, ` requirements.txt ` is
266+ read as pip's logical lines (continuations joined, comments cut, a BOM
267+ dropped), a wildcard ` requirements.txt ` pin (` six==1.* ` ) is no exact
268+ version, a Pipfile.lock hosted reference to an sdist, an ` http:// ` origin
269+ or a path-prefixed origin stays inventoried, a CRLF ` pnpm-lock.yaml ` is
270+ inventoried (it used to read as having no packages), and a ` poetry.lock ` /
271+ ` pdm.lock ` / ` Cargo.lock ` that is not valid TOML contributes nothing
272+ instead of a best-effort line scan. The vendored berry backend splits a
273+ multi-descriptor lock key the way yarn writes it, so a key mixing the
274+ patched package with another descriptor (`"lp@npm: left-pad @1.3.0,
275+ left-pad@npm:1.3.0"` ) is refused ` vendor_override_conflict` instead of
276+ being read as one descriptor.
277+ - ** Standalone ` vendor ` embeds the patch record in its ledger entries too.**
278+ Vendored mode (` scan ` / ` get --mode vendored ` ) already writes every entry
279+ ` detached: true ` with its embedded ` record ` ; the manifest-driven ` vendor `
280+ command now also writes ` record ` into ` .socket/vendor/state.json ` (never
281+ ` detached ` — the manifest record stays authoritative while the manifest
282+ covers the package), so a project it wired whose manifest is gone still
283+ verifies, lists and attests offline: ` vex ` , ` list ` and ` setup --check ` read
284+ the embedded copy whenever no manifest entry covers the package, and
285+ ` repair ` recovers the record without the API when there is no manifest at
286+ all. Entries written by older releases keep working.
287+ - ** VEX product auto-detection covers Go, Composer, Maven, NuGet and
288+ RubyGems projects** , after the existing git / ` package.json ` /
289+ ` pyproject.toml ` / ` Cargo.toml ` probes: ` go.mod ` ` module ` , ` composer.json `
290+ ` name ` , ` pom.xml ` coordinates, the root's single ` *.csproj ` , the root's
291+ single ` *.gemspec ` . Ambiguous roots yield no product rather than a guess.
135292- ** Pipenv projects can use hosted patches, and vendored patches keep every
136293 category.** ` scan --mode hosted ` rewrites every ` Pipfile.lock ` category
137294 (` default ` , ` develop ` , Pipenv 2022+ named categories) that pins the patched
@@ -452,8 +609,8 @@ into the new version's section — see docs/releasing.md.
452609 of being swallowed. ` setup --check ` reads the vendor ledger even without
453610 a manifest and, on a corrupt one, warns ` unreadable vendor state ` and
454611 reports a ` vendor_ledger ` error entry (verdict ` error ` , exit 1) — never
455- ` configured ` ; ` vex ` discloses the same unreadable ledger before its
456- ` manifest_not_found ` / ` no_patches ` exit on a manifest-free project ; ` list `
612+ ` configured ` ; ` vex ` refuses the same unreadable ledger outright
613+ ( ` vendor_ledger_corrupt ` , see Changed) ; ` list `
457614 degrades a corrupt vendor ledger to a ` Warning: unreadable vendor ledger … `
458615 line (muted by ` --silent ` ) rather than an error; ` patch_setup ` telemetry
459616 fires only for a successful, non-dry-run setup.
@@ -532,6 +689,30 @@ into the new version's section — see docs/releasing.md.
532689 "malformed"; a blob-cleanup pass keeps sweeping after one unremovable file
533690 and reports the first error afterwards; the ledgers skip byte-identical
534691 rewrites.
692+ - ** Hosted composer redirects no longer fall back to the pristine git
693+ source.** Composer 1 and 2.2 LTS silently install the upstream commit from
694+ ` source ` when the hosted dist fails; the rewriter now drops the entry's
695+ adjacent ` source ` block (one fragment edit, reverted byte-for-byte) and
696+ warns ` redirect_composer_source_kept ` when a hand-ordered source cannot be
697+ dropped.
698+ - ** Hosted gem locks keep bundler's source order.** The patch-registry
699+ ` GEM ` section is inserted where bundler sorts it, so `BUNDLE_FROZEN=true
700+ bundle install` on bundler ≥ 4.0.19 no longer refuses the converged lock.
701+ - ** v1 ` Cargo.lock ` files redirect and vendor correctly.** Hosted and
702+ vendored rewrites now follow the ` [metadata] ` checksum table and rewrite
703+ dependents' full-id references, so ` cargo --locked ` accepts the lock (and
704+ the revert stays byte-identical).
705+ - ** yarn 4.0.x checksums keep the lock's own spelling.** Vendored and hosted
706+ berry rewrites write bare-hex ` cacheKey: 10c0 ` checksums when the lock
707+ does, so ` yarn install --immutable ` no longer fails with YN0028.
708+ - ** npm 12 dual-lock projects vendor both locks.** ` vendor ` rewires a
709+ ` package-lock.json ` npm 12 keeps beside a committed shrinkwrap (else warns
710+ ` vendor_npm_sibling_lock_unwired ` ), and hosted runs warn
711+ ` redirect_npm_allow_remote ` (npm 12's ` allow-remote=none ` refuses
712+ redirected tarballs unless ` .npmrc ` sets ` allow-remote=all ` — which hosted
713+ mode now writes itself, see Added) and ` redirect_npm_legacy_client ` (npm 6
714+ ignores a v1 lock's ` resolved ` ).
715+
535716- ** Bun refusal safety:** hosted compatibility is checked before removing
536717 an existing vendored patch, including during dry-run. Vendored preflight
537718 exemptions require live local lock tuples; a ledger retained by
0 commit comments