Repository navigation
Stop fetching patch views during hosted scans - #2021
Conversation
A hosted scan of a vlt project downloaded every granted patch archive the way vlt fetches it, and withheld the dependency (`redirect_vlt_artifact_unverifiable`) when the server re-gzipped it. That guarded against patch.socket.dev's CDN re-encoding archives, which vlt rejects with EINTEGRITY. The patch server has sent archives with `Cache-Control: no-transform` since depscan #26856 and #26867, and production serves them unencoded today. On any mirror that still re-encodes, `vlt ci` fails closed with EINTEGRITY, so nothing installs unpatched bytes. So the scan no longer fetches the archives. It pins vlt dependencies directly, without a full download of every patched package per run. The in-memory hosted engine (the Node addon and `hosted-bundle`) has no network, so it used to withhold every vlt dependency as "offline"; it now pins them the way the disk flow does. The warm-tree heal now checks the Socket-owned lock instances this run confirmed in `vlt-lock.json`. With no patch record and no hidden lock it reports the copy as undeterminable (asking for `vlt ci`) instead of comparing it against downloaded archive bytes. The vlt backtest still probes the live artifact itself: a must-patch hosted cell records `blocked-by-server-encoding` when its probe sees a content-encoded response, without running the CLI. Assisted-by: Claude Code:claude-opus-5-5
A vendored run decides before any download which patches the engine would refuse, so it never fetches a view, asks for a grant or strips a hosted pin for a package it cannot vendor. That decision was assembled in the CLI from five sources (the Bun and vlt preflights, the package-lock project refusal, the lock-text gates and the pnpm takeover), and each entry point merged its own subset by hand. They had drifted: the download phase never asked the package-lock project refusal or the pnpm takeover gates, `get <uuid>` raised only the Bun and vlt refusals before the engine, and the vendor loop judged only Bun and vlt before its takeover. All of it now lives in one core function, `socket_patch_core::vendor::refusals::vendor_refusals`, which runs the checks in the engine's order and scopes the lock-text gates (hosted pins, the ledger's idempotency skip, packages that never reach their backend). The scan preview and rollout plan, the download phase, both `get` paths and the vendor loop call it. The CLI's bun_preflight.rs and vlt_preflight.rs are gone, and their tests moved to core. What users see: a v1 package-lock project, or a hosted pnpm pin whose takeover the pnpm gates refuse, is now refused before its patch view is fetched; `get <uuid> --mode vendored` raises every such refusal with its pre-record error envelope and writes nothing; and `vendor` refuses them before restoring a hosted pin instead of restoring and rolling back. Codes and details are unchanged. Assisted-by: Claude Code:claude-opus-5-5
A hosted scan fetched the full patch view (every patched file's blob content) for each confirmed redirect, only to judge installed copies and feed the same run's VEX. It now makes no view call at all: - In-run `--vex` fetches the records it attests from itself, through the same record path the standalone `vex` uses, so the scan pipeline never asks for them. - The gem stale-install probe judges an installed gem by the archive RubyGems installed it from (`<home>/cache/<leaf>.gem`) against the patched artifact's sha256. - The Python stale-install probe downloads the patched wheel the lock now pins, through its grant URL and verified against the pinned sha256, and compares the installed copy with the wheel's own RECORD when the installed WHEEL tags match. It downloads only when a copy is installed. - Both probes still treat an agent-mode manifest record of the same patch as proof of an in-place patch. - The vlt warm-tree heal judges a store entry by vlt's hidden lock alone: the pinned integrity there is healthy, another is stale, and without a readable hidden lock the copy is kept and reported as unchecked. - The in-memory hosted engine no longer calls the provider's `fetchPatch` or warns `record_fetch_failed`. The Node binding still accepts a `fetchPatch` provider method and ignores it, so existing providers keep working. Assisted-by: Claude Code:claude-opus-5-5
…c) e2e judge_installed_wheel read the installed WHEEL and every RECORD path with bare std::fs reads. Site-packages is user-writable, so a FIFO there would block the hosted scan's blocking pool. Read through read_regular_*_sync so a FIFO or device counts as unreadable, and add a regression test for both the WHEEL and a RECORD path. The real-vlt e2e for heal rule (c) without a hidden lock still expected the old record-based heal (copies invalidated). Hosted scans no longer fetch patch records, so that case is now undeterminable: the copies are kept and the advisory says to run `vlt ci`. Match the in-process test and check that `vlt ci` then installs the patched package. Co-Authored-By: Claude <noreply@anthropic.com>
|
bugbot run Generated by Claude Code |
|
Labeled Ready for review by the burn-down agent.
Generated by Claude Code |
Final review brief (for the human approver)What it does: hosted scans stop calling Risk: medium. I found no security problem and nothing that makes the probes warn wrongly. The risk is in the Python probe: it now judges an installed copy only when its Look here
Verified: I read the PR's own diff ( Changes I made: none. Open questions / nits (none block merging)
Auto-merge is not armed. This PR targets #2015's branch, which has no review rule, so arming it would merge it immediately. Approve and merge it after #2015, or retarget it to Generated by Claude Code |
The record-fetch-failure test that mounted a 404 mock is gone, so Mock, method and path were left unused after merging main. Co-Authored-By: Claude <noreply@anthropic.com>
|
bugbot run Generated by Claude Code |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 4298af6. Configure here.
|
Re-labeled Ready for review by the burn-down agent after resolving merge conflicts.
Generated by Claude Code |
|
Final reviewer: blocked on an open review thread, needs an owner decision ( The review thread on Question for the owner: when the archive or wheel evidence is missing or can't be fetched, which should the scan do?
CI is green (31/31 non-skipped) and Bugbot is clean on this head, so this thread is the only thing blocking it. I haven't posted a review brief. Generated by Claude Code |
Resolve vlt_heal.rs imports: main's #2002 moved hosted_patch_uuid to crate::origin; this PR dropped the patch-view helpers, so keep only the origin import. Main's new go-workspace vex_sources tests build Sources without the hosted_uuids field this PR adds; give them the empty list every other test uses. Co-Authored-By: Claude <noreply@anthropic.com>
|
Burn-down agent: merged Generated by Claude Code |
Wenxin Jiang (Wenxin-Jiang)
left a comment
There was a problem hiding this comment.
[agent] Review findings (Claude Opus + codex, independent passes) — same defect as the open thread at hosted.rs:405:
- Unjudgeable gem is treated as not stale (
crates/socket-patch-cli/src/commands/scan/hosted.rs:387-405, both reviewers). With no agent-mode record and no readable<home>/cache/<leaf>.gem, the probe returnsInstalledRecordJudgment::default(), the purl stays inassume_applied, and VEX (which now fetches the record itself) attests it. Input: image built withbundle install && rm -rf $BUNDLE_PATH/cache→scan --mode hosted --vexattests the unpatched installed gem. Main compared against the view record'safterHashand flagged it stale. - Same gap for PyPI (
scan/hosted/python.rs:46,90-108,crates/socket-patch-core/src/hosted/wheel_record.rs:141-144). An sdist pin, a missingintegrity.sha256, a failed wheel download/sha mismatch, aWHEEL-tag mismatch, or (codex) a legacy*.egg-infoinstall without<dist-info>/WHEELall fall to default → noredirect_pypi_stale_install, not inknown_stale, attested.
Suggested fix: judge with the record VEX already fetches, or put any purl the probe can't judge into known_stale / drop it from assume_applied.
Minor: deleting rule_c_bytes_mismatch_whatever_the_hidden_lock_says (vlt_heal) may open a similar path if vlt can rewrite its hidden lock without re-extracting; the probe uses only the first .whl override per uuid.
Wenxin Jiang (Wenxin-Jiang)
left a comment
There was a problem hiding this comment.
[agent] Approving per maintainer decision; see the findings in the review above.
|
Final reviewer (head The owner question from the Nov 7 comment is still unanswered. When gem or wheel evidence is missing or can't be fetched, should the scan (1) fetch that purl's patch-view record, or (2) treat the purl as unjudgeable ( Generated by Claude Code |
Keeps main's user-patch warnings in the hosted scan's warning list and drops the record warnings this PR removes. Main's two new cargo VEX gate tests pass hosted_uuids: Vec::new(). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Owner decision on the hosted.rs:405 review thread. The gem and Python stale-install probes no longer turn "can't judge" into "not stale": - Gem: when there is no readable cache/<leaf>.gem (pruned cache, gem cleanup) or no patched sha to compare it with, and no agent-mode record, the probe fetches that patch's /patches/view record and judges the installed dir by its afterHash map. - Python: when the patched wheel cannot judge a copy (sdist pin, failed download or sha256 check, installed copy of another build), the probe falls back to the view record the same way, unless another variant already proved the copy patched. - A copy the view cannot judge either (fetch failure, or --offline) is reported as unjudged: it leaves assume_applied and goes to a new VexBuildParams::unjudged list, which in-run --vex withholds as stale_install_unverified, even under --vex-no-verify. The fallback fetches at most once per uuid and only for copies nothing else settled, so the normal path still makes no view request. vlt is unchanged: an undeterminable heal target already lands in stale_purls. The in-memory engine runs no stale probe, so the Node binding's fetchPatch stays ignored. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
/code-review high finding: a stale gem dir put only the variant named in its warning in stale_purls. The other platform variants stayed in assume_applied, and in-run VEX matches exemptions qualifier-insensitively, so they re-exempted the stale dir. Also drops a leftover blank line in the Node smoke test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Owner decision implemented (head
Generated by Claude Code |
LLM Description written by Claude Code:claude-opus-5-5
Summary
Hosted scans no longer fetch
/patches/viewon the normal path. A view carries every patched file's blob, and v5 keeps no hosted ledger, so the scan fetched full patch bodies only to feed checks within the same run. Each check now uses data the scan already has, or fetches narrowly:--vexvex_sources::fetch_records, like standalonevex<home>/cache/<leaf>.gem(and committedvendor/cache) vs the grant's artifact sha256; fallback: that patch's view recordRECORD(downloaded, sha256-checked, only for an installed copy with matchingWHEELtags); fallback: that patch's view recordfetchPatchis accepted and ignoredAn agent-mode manifest record of the same patch takes precedence in both probes.
Owner decision (review thread on
hosted.rs:405)When the gem archive or wheel evidence is missing, can't be fetched, or can't judge the copy (pruned
cache/,gem cleanup, no patched sha, sdist pin, failed download or sha check, another build's install), the probe fetches that purl's view record and judges the copy from itsafterHashmap, at most once per uuid and only if no other variant already proved the copy patched. If the view fetch also fails, or under--offline, the copy is unjudged: it leavesassume_appliedand goes toVexBuildParams::unjudged, which in-run--vexwithholds asstale_install_unverified, even with--vex-no-verify. It is never treated as not stale.Behavior changes
record_fetch_failedis gone; an in-run VEX view failure isvex_record_fetch_failed, and an all-failed run ends with arecord_unavailableskip.stale_install_unverified(see above).vlt ci.Where to look
scan/hosted.rs:ViewFallback,gem_stale_install_warnings, theassume_applied/unjudgedwiringscan/hosted/python.rs:Evidence::judgeand the pending → view passvex.rs: theunjudgedblock afterknown_stalecore/hosted/wheel_record.rs:judge_installed_wheelreturnsNonewhen it can't judgeCLI_CONTRACT.md(gem/Python stale-install guards, omission table),docs/testing/poetry-compatibility.mdTesting
--offline→ unjudged (no requests offline); archive or wheel that judges → zero view requests; unjudged purls withheld even when assumed applied and under--no-verify.--vex; patched gem install makes zero view requests; pdm wheel 500 → view fallback.cargo fmt --check,cargo clippy --workspace --all-features -- -D warnings, CLI lib, corevlt_heal/wheel_record/hosted::,e2e_redirect_gem_stale_install,in_process_redirect, pdm/pipenv/poetry redirect suites,hosted_memory_engine,hosted_memory_parity,scan,e2e_redirect_vlt_build.Review history
4298af6.hosted.rs:405: dropping the view turned "can't judge" into "not stale", and in-run VEX then attested the purl viaassume_applied.🤖 Generated with Claude Code