Skip to content

fix(scan): scope takeover cleanup per package, warn on hosted --prune - #191

Open
Mikola Lysenko (mikolalysenko) wants to merge 1 commit into
mainfrom
fix/takeover-remediation-safety
Open

fix(scan): scope takeover cleanup per package, warn on hosted --prune#191
Mikola Lysenko (mikolalysenko) wants to merge 1 commit into
mainfrom
fix/takeover-remediation-safety

Conversation

@mikolalysenko

@mikolalysenko Mikola Lysenko (mikolalysenko) commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

Problem

Four audit findings (2026-08-13 adversarial audit of origin/main 4e5288e) around the mode-takeover warning and hosted-mode flag handling:

  1. Destructive takeover remediation (medium) — the takeover warning fires per package, but its cleanup advice was file/tree scoped. Vendored direction told users to delete the whole .socket/vendor/redirect-state.json, destroying other packages' live VEX records and the only recorded pre-redirect lockfile originals. Hosted direction told users to delete the whole .socket/vendor/<eco>/ tree (which holds every vendored uuid dir, including still-live packages) or run a blanket vendor --revert (mass-reverting unrelated packages). Concrete failure: redirect packages A and B, vendor A, follow the warning literally → vex stops attesting B and B's pre-redirect lock fragment is unrecoverable.

  2. scan --mode hosted --prune silently drops --prune (low) — both hosted terminals return before the GC blocks, so a bot migrating from --mode agent --prune stops pruning forever with exit 0, no warning, and a JSON envelope that simply omits the gc key.

  3. Takeover detection blind to degraded ledgers (low) — a hosted run where every record fetch failed persists a ledger with edits but an empty records map; overlapping_ledger_purls derived the overlap exclusively from record keys, so a later vendored takeover of those same packages emitted no warning at all.

  4. Hosted direction proof hardcoded patch.socket.dev and could not see berry/bun wiring (low) — liveness was proved only by the inventory resolved URL containing the default hostname, so non-default patch hosts (staging, --patch-server-url deployments) were unprovable, and yarn-berry (inventory resolved always None) plus bun (URL 3-tuples skipped by the inventory) were structurally unprovable.

No findings were skipped. Note: the in-flight branch work/mode-takeover-fix is content-identical to what is already merged as PR #164 and covers neither blind spot, so nothing here duplicates it.

Fix

  1. mode_takeover_detail now gives per-package, non-destructive cleanup. Hosted direction: run socket-patch remove <purl> per named package — its revert path is drift-guarded (vendor_lock_entry_drifted), so when hosted has won the lockfile it drops only that ledger entry and its own .socket/vendor/<eco>/<uuid>/ dir without touching the live wiring — with an explicit warning against tree deletion and blanket reverts. Vendored direction: delete only the named packages' entries under records in redirect-state.json, with an explicit warning against deleting the file (it holds other packages' live records plus the pre-redirect originals in edits).

  2. --prune with --mode hosted stays accepted (CLI_CONTRACT documents it as an orthogonal knob that never conflicts, so a hard error would break existing pipelines) but now emits an explicit redirect_prune_ignored warning on stderr (human path, once in run) and in the JSON redirect.warnings[], including the zero-discovery envelope. CLI_CONTRACT.md and the arg docs name the code.

  3. overlapping_ledger_purls falls back to matching vendored purls against the recorded FileEdit keys (name, name@version, /name suffix) when records is empty but edits exist. The fallback only activates on the degraded shape, so the well-formed-ledger path is unchanged, and the direction gate in classify_overlap_takeover still requires live-lock proof before anything is reported.

  4. Hosted liveness is now proved host-agnostically by the record's patch uuid: in the inventory resolved URL on any host (patch.socket.dev kept only as a no-record fallback), or in the redirect-edited lockfile text outside a .socket/vendor/<eco>/ path — making berry (::__archiveUrl= bindings) and bun (URL 3-tuples) provable. Vendored wiring embeds the same uuid in its path, so only non-vendored occurrences count (guard test included); ledger-named files pass through the same traversal guard as vendored_wiring_live (extracted as is_safe_project_rel_file). Unprovable direction still stays silent rather than guessing.

Testing

  • cargo test -p socket-patch-cli --lib — 373 passed
  • cargo test -p socket-patch-cli --test cli_parse_scan — 48 passed
  • cargo test -p socket-patch-cli --test in_process_scan — 23 passed
  • cargo test -p socket-patch-cli --test in_process_redirect — 24 passed

New tests:

  • takeover_detail_remediation_is_per_package_and_non_destructive (verified red against the pre-fix remediation text)
  • overlap_detected_when_redirect_ledger_has_edits_but_no_records
  • hosted_direction_provable_on_non_default_patch_host
  • hosted_direction_provable_for_bun_url_tuple
  • hosted_direction_provable_for_berry_archive_url
  • vendored_path_uuid_does_not_prove_hosted (guard: a vendored-path uuid must not prove hosted)
  • hosted_prune_emits_explicit_ignored_warning (subprocess --json contract: warning present, no gc key, redirect unaffected)

🤖 Generated with Claude Code


Note

Medium Risk
Changes warning text and overlap/takeover logic that drives CI bots and cleanup actions; behavior is safer but mis-detection could still mis-route users; no auth or network contract changes.

Overview
Fixes scan mode-takeover warnings and hosted-mode flag handling so automation and operators get accurate signals and safe cleanup guidance.

Per-package takeover remediationmode_takeover_detail no longer tells users to delete whole redirect-state.json or entire .socket/vendor/<eco>/ trees. Hosted wins: use socket-patch remove <purl> per listed package; vendored wins: remove only those packages’ records entries, keeping other live redirects and edits revert data.

Hosted + --prunescan --mode hosted --prune (and --sync) still succeeds but emits redirect_prune_ignored on stderr and in JSON redirect.warnings[], including the zero-discovery hosted envelope, so migrations from agent sync jobs don’t silently stop GC.

Overlap / direction detectionoverlapping_ledger_purls can match vendored packages when the redirect ledger has edits but empty records (failed record fetch). Hosted liveness is proved by patch UUID in inventory resolved URLs (any host, not only patch.socket.dev) or in redirect-edited lockfile text outside vendored paths, covering bun URL tuples and yarn berry ::__archiveUrl= bindings; vendored-path UUID matches are excluded. Ledger lockfile reads use a shared path traversal guard.

Reviewed by Cursor Bugbot for commit a9f2e45. Configure here.

The mode-takeover warning told users to delete the whole redirect
ledger (vendored direction) or the whole .socket/vendor/<eco>/ tree
(hosted direction). Following it destroyed live data for packages the
takeover never touched: other packages' VEX records, the only recorded
pre-redirect lockfile originals, and still-live vendored artifacts.
The remediation is now per-package and non-destructive — run
`socket-patch remove <purl>` per named package (hosted direction) or
delete only the named records from the redirect ledger (vendored
direction) — and explicitly warns against whole-file/tree deletion
and blanket reverts.

`scan --mode hosted --prune` silently dropped --prune: both hosted
terminals return before the GC blocks, so a bot migrating from
`--mode agent --prune` stopped pruning forever with exit 0 and no
signal. The flag stays accepted (an orthogonal knob per the CLI
contract), but the run now emits an explicit `redirect_prune_ignored`
warning on stderr and in the JSON `redirect.warnings[]`, including
the zero-discovery envelope.

Two takeover blind spots are also closed. A redirect ledger whose
every record fetch failed (edits present, records empty) was
invisible to overlap detection; the vendored purls are now matched
against the recorded edit keys so the stale ledger is still flagged.
And hosted liveness was proved only by a hardcoded patch.socket.dev
inventory URL — structurally unprovable for yarn-berry (inventory
resolved is always None), bun (URL 3-tuples are skipped), and any
non-default patch host. It is now proved by the record's patch uuid:
in the inventory URL on any host, or in the redirect-edited lockfile
text outside a .socket/vendor/<eco>/ path (vendored wiring embeds the
same uuid, so only non-vendored occurrences count).

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.

Fix All in Cursor

Bugbot Autofix is ON. A cloud agent has been kicked off to fix the reported issues.

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit a9f2e45. Configure here.

// The scan inventory keeps only http(s) `resolved` URLs and DROPS our own
// `file:.socket/vendor/…` specs (see `lock_inventory`), so a
// `patch.socket.dev` resolved for a purl is a purl-scoped proof the lock now
// points at hosted.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cleanup leaves sticky takeover warning

Medium Severity

Vendored-direction cleanup tells operators to drop only records and keep edits. After those records are gone, overlapping_ledger_purls treats the leftover edits as a degraded ledger and still reports the same packages, so the takeover warning can keep firing—especially when the takeover cleared the last redirect records.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit a9f2e45. Configure here.

VEX do not read superseded wiring. Do not delete the whole \
`.socket/vendor/<eco>/` tree and do not run `vendor --revert`: \
other vendored package(s) may still be live in the lockfile and \
would break or be mass-reverted."

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove advice drops live hosted patch

Medium Severity

Hosted-direction takeover text says socket-patch remove only drops the stale vendored ledger entry and its uuid directory. remove also rolls back applied files and deletes the manifest patch, so following the warning can strip the still-live hosted package from .socket/manifest.json.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit a9f2e45. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants