GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Harden vendored-mode trust in patch-service artifacts by mikolalysenko · Pull Request #249 · SocketDev/socket-patch

Conversation

@mikolalysenko

@mikolalysenko Mikola Lysenko (mikolalysenko) commented Sep 23, 2026 •
edited by cursor Bot
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Collaborator

Summary

These are pre-existing gaps in how vendored mode trusts and records artifacts from the patch service (patch.socket.dev). They were found while root-causing #248's CI flake, and each was reproduced with a failing test before it was fixed. The companion PR for outage idempotence (vendored re-runs that churn locks after a 503) is separate.

  1. Stale ledger fingerprint after a rebuild (gem / maven / nuget).
    • When a wired artifact was missing and got rebuilt, the vendor ledger kept the old fingerprint: the gem file inventory, the maven/nuget artifact.sha256, and the nuget lock record. As a result:
      • vex/verify reported tamper;
      • repair could fail;
      • vendor --revert left packages.lock.json pinned to the patched contentHash;
      • the run was labelled already_vendored.
    • The rebuild branches now return a refreshed entry built from the new bytes, and the persist step carries the wiring forward. It saves the entry only over a predecessor with the same uuid.
  2. Service archives accepted without checking the patch (maven / nuget / pypi / npm). Served bytes that passed the SRI were trusted even if they didn't contain the patched files. Every patched member is now checked against the record's afterHashes before anything is written:
    • auto falls back and warns vendor_prebuilt_layout_mismatch;
    • service refuses.
  3. An integrity mismatch fell back under auto. This affected golang, composer, pypi, npm and gem (the .gem and the stub gemspec). It broke the documented rule that tampered bytes are always a hard failure. They now fail closed with vendor_prebuilt_integrity_mismatch, as cargo already did.
  4. --vendor-source service failed open. With no API client it quietly built locally. It now refuses with vendor_prebuilt_required. The cargo wired-copy rebuild also follows --vendor-source now: offline or no client refuses, and online uses the prebuilt crate.

CLI_CONTRACT.md and CHANGELOG.md ([Unreleased], Fixed) are updated.

Test plan

  • A regression test per issue and per backend. Each one fails when its fix is temporarily reverted:
    • rebuild refreshes the ledger, then --revert restores the lock (nuget), and the artifact is Healthy afterwards;
    • served archive missing the patched files → refused under service, falls back under auto;
    • IntegrityMismatch under auto hard-fails, per backend;
    • service with no client, and service with --offline, both refuse;
    • a rebuild over a ledger entry with another uuid leaves the ledger untouched.
  • Existing tests that pinned the old behaviour are updated. Service fixtures now serve real archives.
  • cargo test -p socket-patch-cli -p socket-patch-core --no-fail-fast: 7760 passed, 0 failed.
  • cargo clippy -p socket-patch-cli -p socket-patch-core --all-targets -- -D warnings: clean.

Not in this PR

  • When a maven/nuget rebuild recreates a whole uuid directory, it doesn't rewrite the uuid marker file. This predates these changes.
  • cargo/composer/golang rebuilds still return no ledger entry. That's harmless, because those entries record no fingerprint.
  • npm_common.rs also changes in the outage-idempotence PR. The hunk here is small and separate, so expect at most a trivial merge.

🤖 Generated with Claude Code


Note

High Risk
Changes fail-closed behavior for tampered or unpatched service artifacts and mutates vendor ledger persistence on rebuilds across many ecosystems, affecting verify, repair, and revert flows.

Overview
Hardens vendored-mode trust in patch-service artifacts and fixes ledger drift when missing committed copies are rebuilt.

Artifact rebuilds now refresh ledger fingerprints (gem inventory, maven/nuget sha256, nuget lock pin) while preserving wiring via carry_forward_wiring; the CLI only persists a rebuilt entry when a same-uuid predecessor exists, so revert cannot be left with an orphan artifact.

Prebuilt downloads must carry patched files: members are checked against afterHash before use (vendor_prebuilt_layout_mismatch under auto, refuse under service). Integrity mismatches no longer fall back to local builds under auto for npm, pypi, composer, gem, and golang (vendor_prebuilt_integrity_mismatch). --vendor-source=service without an API client now refuses instead of building locally; cargo wired-copy rebuilds follow the same service/offline rules as a fresh vendor.

CHANGELOG and CLI_CONTRACT.md document the new refusal codes and fallback table; repair/vendor paths and broad regression tests cover revert, uuid-mismatch, and service fixtures with real archives.

Reviewed by Cursor Bugbot for commit 032ab2b. Configure here.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Mikola Lysenko (mikolalysenko) and others added 7 commits September 23, 2026 17:35
@mikolalysenko @claude
fix(vendor): refresh the ledger fingerprint when a wired gem/maven/nu…
d3e7ef0
…get artifact is rebuilt

When the hot path found the committed artifact missing or stale, it rebuilt
it but returned no entry, so the ledger kept the previous fingerprint: the
gem file inventory, the maven/nuget sha256, and the nuget lock pin. A
rebuild from the other source (service vs local) then produced bytes the
ledger did not describe. VEX and verify reported tamper, repair of a
service-vendored maven/nuget entry could fail, --revert left
packages.lock.json pinned to the patched contentHash, and a service-sourced
rebuild was labelled already_vendored.

The rebuild branches now return a refreshed entry built from the new bytes
or tree, with no wiring of their own. For nuget the entry carries only the
re-pinned lock record, with original: None. carry_forward_wiring (same
uuid) re-attaches the first run's records and fills in the true
pre-vendor hash.

The CLI does not record such an entry when the ledger has no previous one,
because it would carry no wiring and --revert would delete the artifact
while the project still points at it. repair carries the repaired entry's
wiring forward before persisting, and emits vendor_inventory_refreshed when
the backend's refreshed inventory differs from the recorded one.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
@mikolalysenko @claude
fix(vendor): verify a served maven/nuget archive carries the patch be…
32b73d5
…fore accepting it

service_archive_copy (the Tier-A maven .jar / nuget .nupkg path) accepted any
archive that matched its SRI. The bytes were written verbatim and every
patched file was reported AlreadyPatched, so a served archive without the
patch was committed as patched. The hot path then saw a stale artifact on
every later run and rebuilt it.

The helper now takes the PatchRecord and requires every patched member to
hash to its afterHash (zip_bytes_match_after_hashes, the check the hot path
already uses) before returning Used. A mismatch is a service miss: auto
falls back to the local build with vendor_prebuilt_layout_mismatch, and
service refuses with vendor_prebuilt_required. The vendor_prebuilt_downloaded
advisory is only pushed for accepted bytes.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
@mikolalysenko @claude
fix(vendor): refuse a prebuilt artifact that fails integrity under au…
8c37f5a
…to too

ServiceArtifact documents IntegrityMismatch as always a hard error, but
golang, composer, pypi, npm and gem (the .gem and the stub gemspec) mapped
it through their miss policy. Under auto they warned and built the package
locally, so a sign of tampering became a quiet fallback. Under service
they refused with the generic vendor_prebuilt_required code.

These arms now refuse in every mode, as cargo already did: golang,
composer, pypi and gem return vendor_prebuilt_integrity_mismatch, and npm
fails the package with the integrity detail. The npm test that pinned the
old fallback is replaced by one that expects the refusal. The service-mode
tests for these ecosystems now expect the specific code.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
@mikolalysenko @claude
fix(vendor): refuse --vendor-source=service when no API client is con…
611c8bb
…figured

Every backend's service helper treats !service_enabled() as "build
locally", and service_enabled() is false without a client. The
service-only policy was enforced by common::service_offline_conflict, which
checked only --offline. So a VendorServiceConfig with source: Service and
client: None built the artifact locally in every backend, contradicting
service's fail-closed promise. The CLI always passes a client, so only
library callers could reach this.

The gate every backend already calls at its entry point now also refuses
that combination (vendor_prebuilt_required), before any service
consultation or write. Regression tests cover all eight backends.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
@mikolalysenko @claude
fix(vendor): route the cargo wired-copy rebuild through --vendor-source
c0581ac
The in-sync hot path rebuilt a missing/stale crate copy with
copy_and_patch directly, so `--vendor-source=service` with --offline or
no API client rebuilt locally and reported success, and online service
mode never consulted the patch service. Refuse via
service_offline_conflict first and prefer cargo_service_copy, falling
back to the local build only where the policy allows, as composer and
gem already do.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
@mikolalysenko @claude
fix(vendor): verify a served pypi wheel / npm tarball carries the patch
276c8a9
The service SRI proves only that the download is intact. A wheel or
tarball whose patched members still held the original bytes was written
as-is, reported as already patched, and pinned in the lockfile. Check
each patched member against its afterHash before using the artifact:
auto builds locally with vendor_prebuilt_layout_mismatch, service
refuses (pypi: vendor_prebuilt_required; npm fails the package). Adds
read_archive_bytes_to_map for the in-memory tarball check.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
@mikolalysenko @claude
fix(vendor): only record a rebuilt entry over a same-uuid predecessor
032ab2b
A wiring-less refreshed entry relies on carry_forward_wiring, which
re-attaches wiring only from a same-uuid ledger entry. Over an entry
from another patch uuid (the run that wired this uuid never saved), the
guard let it through, saving an entry --revert could not unwire and
sweeping the other uuid's dir. Leave the ledger as it is instead.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
@mikolalysenko Mikola Lysenko (mikolalysenko) mentioned this pull request Sep 24, 2026
Keep vendored re-runs idempotent across patch-service outages #250
Merged
7 of 8 tasks
@mikolalysenko
Mikola Lysenko (mikolalysenko) enabled auto-merge (squash) September 24, 2026 13:02
Wenxin-Jiang
Wenxin Jiang (Wenxin-Jiang) approved these changes Sep 24, 2026
View reviewed changes
@mikolalysenko
Mikola Lysenko (mikolalysenko) merged commit fce6fe6 into main Sep 24, 2026
200 checks passed

Uh oh!

There was an error while loading. Please reload this page.

@mikolalysenko
Mikola Lysenko (mikolalysenko) deleted the fix/vendor-service-integrity-hardening branch September 24, 2026 13:14
Mikola Lysenko (mikolalysenko) added a commit that referenced this pull request Sep 24, 2026
@mikolalysenko @claude
Merge origin/main (#249 integrity hardening) into fix/vendor-service-…
fdc2527
…outage-idempotence

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
@mikolalysenko Mikola Lysenko (mikolalysenko) mentioned this pull request Sep 24, 2026
feat(vex): manifest-less VEX from hosted/vendored lockfiles + npm 12 allow-remote auto-config (v5) #251
Merged
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@Wenxin-Jiang Wenxin Jiang (Wenxin-Jiang) Wenxin-Jiang approved these changes

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

2 participants

@mikolalysenko @Wenxin-Jiang
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.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.