fix(vendor): refuse catalog-consumed deps in pnpm vendoring - #184
Merged
Mikola Lysenko (mikolalysenko) merged 1 commit intoAug 14, 2026
Merged
Conversation
A dep consumed through a pnpm catalog (pnpm >= 9.5) carries
"specifier: catalog:" in the lock's importers while the real range
lives in pnpm-workspace.yaml, mirrored by a top-level "catalogs:"
snapshot section. The pair surgery matched importer entries on the
version alone, so it rewrote both importer fields to the file: spec
while package.json kept its "catalog:" specifier and the catalogs:
snapshot stayed stale. Vendoring reported success, but the very next
"pnpm install --frozen-lockfile" rejected the lock with
ERR_PNPM_OUTDATED_LOCKFILE (reproduced with real pnpm) — the user was
handed a broken project with no warning.
The catalog definition lives outside the package.json + pnpm-lock.yaml
pair this surgery owns, so a consistent rewrite is out of reach.
check_rewritable_refs now refuses fail-closed
(vendor_lock_entry_unsupported) before anything is staged or written,
whenever the target version is consumed through a catalog: specifier
in any importer or resolved by any catalogs: snapshot entry. The
refusal names the catalog wiring and pnpm-workspace.yaml so the user
knows why. Sibling versions resolved by a catalog do not block
vendoring a plainly-consumed target.
Regression tests pin the default-catalog ('catalog:'), named-catalog
(catalog:tools), and importer-only drifted shapes (lock oracles
generated by real pnpm), plus the sibling-version non-refusal.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Wenxin Jiang (Wenxin-Jiang)
approved these changes
Aug 14, 2026
Mikola Lysenko (mikolalysenko)
merged commit Aug 14, 2026
b107a3c
into
main
231 of 234 checks passed
Mikola Lysenko (mikolalysenko)
deleted the
fix/pnpm-vendor-catalog-guard
branch
August 14, 2026 20:24
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Audit finding (2026-08-13, vendored/pnpm, high): pnpm catalogs unguarded — a catalog-consumed dep's
specifier: catalog:is rewritten tofile:, breaking frozen installs.For a dep consumed through a pnpm catalog (pnpm >= 9.5: pnpm-workspace.yaml
catalog:), the lock importer entry isspecifier: 'catalog:'/version: 1.3.0, mirrored by a top-levelcatalogs:snapshot section.edit_importerstargeted on the version alone, so vendoring rewrote both importer fields to thefile:spec while package.json kept its"left-pad": "catalog:"declaration and thecatalogs:snapshot stayed stale. Vendoring reported success, but the very nextpnpm install --frozen-lockfilerejected the lock withERR_PNPM_OUTDATED_LOCKFILE(reproduced with real pnpm v11.21.0) — the user was handed a broken project with no warning.check_rewritable_refsalready refused the sibling aliased and peer-suffixed spellings, but never looked at catalog wiring.Fix
Refuse fail-closed rather than attempt a rewrite: the catalog range lives in pnpm-workspace.yaml, outside the package.json + pnpm-lock.yaml pair the surgery owns, so a consistent rewrite is out of reach at this boundary.
check_rewritable_refsgains two guards, both firing pre-flight (vendor_lock_entry_unsupported) before anything is staged or written:catalogs:snapshot section, refusing when any catalog resolves the target name to the target version (peer-suffixed versions included).catalog:specifier — covering both the quoted default ('catalog:') and bare named (catalog:tools) spellings via a newunquote_valuehelper — so importer-only drifted locks refuse too.The refusal detail names the catalog wiring and pnpm-workspace.yaml so the user knows why. Sibling versions resolved by a catalog do not block vendoring a plainly-consumed target.
No skipped findings — this lane had one finding and it is fully addressed.
Testing
catalog_consumed_target_refuses_fail_closed: default-catalog, named-catalog, and importer-only drifted shapes (lock oracles generated by real pnpm v11.21.0); asserts the refusal code and detail, both files byte-untouched, and nothing staged under.socket/vendor. Red-verified against pre-fix code: without the guards the fixture vendors withDone (success=true)— the exact fail-broken success the audit described (independently re-verified during review).sibling_version_catalog_entry_does_not_block_vendoring: a catalog pinning 1.2.0 does not refuse a plainly-consumed 1.3.0 target, while the catalog-consumed 1.2.0 itself refuses.cargo test -p socket-patch-core --lib(2077 passed),cargo test -p socket-patch-cli --test e2e_vendor_pnpm_build(6 passed),cargo test -p socket-patch-cli --test in_process_vendor(27 passed).cargo fmt --checkclean.🤖 Generated with Claude Code
Note
Medium Risk
Changes pre-flight lock validation for pnpm vendoring; incorrect guards could block valid vendoring or miss unsupported shapes, but the change is fail-closed and scoped to unsupported catalog wiring.
Overview
pnpm pair surgery now refuses vendoring when the target dep is wired through a pnpm catalog (
catalogs:section or importerspecifier: catalog:…), returningvendor_lock_entry_unsupportedbefore staging or writing anything.Previously, rewriting importer fields to
file:whilepackage.jsonstill declaredcatalog:andpnpm-workspace.yamlheld the real range could report success and then failpnpm install --frozen-lockfilewithERR_PNPM_OUTDATED_LOCKFILE.check_rewritable_refsadds scans of the top-levelcatalogs:block (matching the target name/version, including peer-suffixed versions) and importer deps at the target version whose specifier starts withcatalog:after a newunquote_valuehelper strips YAML quotes. Refusal messages mention catalog wiring andpnpm-workspace.yaml. A catalog on a sibling version does not block vendoring a plainly-consumed target at another version.Tests cover default, named, and importer-only catalog shapes (real pnpm lock oracles), untouched files on refusal, and sibling-version isolation.
Reviewed by Cursor Bugbot for commit 03e2bd4. Configure here.