fix(vex): honest machine diagnostics, stale-doc removal, out-of-sync disclosure; offline proxy-notice suppression - #208
Merged
Mikola Lysenko (mikolalysenko) merged 2 commits intoAug 19, 2026
Conversation
…disclosure; suppress the proxy notice under --offline Findings from the 2026-08-18 pnpm e2e campaign's vex probe (real-pnpm projects; the core attestation invariant — verified:true only on matching bytes — was healthy throughout and is untouched): - Property-7 setup-filter drops were invisible in --json: a byte-verified applied patch omitted only because its ecosystem has no install hook (and no setup.manual) produced events:[] and the factually wrong "No applied patches with vulnerability metadata to attest." Drops now emit per-purl skipped events (errorCode ecosystem_not_setup) and the all-drops error message names the filter and both remediations. - A failed vex run left a stale prior attestation byte-untouched at --output. Failed runs now remove a recognizably-OpenVEX file at the path (JSON @context naming openvex.dev — unrelated files never touched). - write_failed errors now name the path; a non-IRI --product override warns (product_not_iri) instead of silently landing in the OpenVEX product @id; the vendored attestation gains a vendored_tree_out_of_sync warning when the PRESENT installed tree hash-mismatches the patched bytes (attestation basis — the committed artifact — unchanged). - apply/vendor/rollback --offline runs no longer print the "using the public patch API proxy" notice (network-implying under strict airgap); the SOCKET_OFFLINE parse is hoisted to a shared is_offline_env() used by the client and telemetry. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.
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 dfdc94b. Configure here.
Mikola Lysenko (mikolalysenko)
added a commit
that referenced
this pull request
Aug 19, 2026
…ouch trustLockfile, takeover reconciliation, revert guards (#213) * feat(pnpm): full pnpm 7-12 vendor+hosted support — legacy lock grammars, zero-touch trustLockfile, takeover reconciliation, revert guards Built and verified against real corepack-pinned pnpm 7.33.5 / 8.15.9 / 9.15.9 / 10.34.5 / 11.22.0 / 12.0.0-rc.7 (plus legacy 1-6 probes) in the 2026-08-18 e2e campaign. HOSTED, pnpm 7/8: the v5.4/v6.0 refusal is replaced by a real rewrite — every instance key of a dep is spliced (v5 /name/ver and _peer-suffixed, v6 (peer)-parenthesized; each owns its resolution), one ledger edit per instance; a post-splice residual detector refuses the dep set-wide if any instance shape the splice regex cannot claim remains (no partial rewrites). Frozen installs from empty stores land patched bytes on both majors; tamper fails ERR_PNPM_TARBALL_INTEGRITY. HOSTED, pnpm 11/12 zero-touch: rewriting a v9 root lock now auto-writes trustLockfile: true into pnpm-workspace.yaml (create with scaffold or byte-preserving append; ledger-recorded as redirect_pnpm_workspace_trust; --no-trust-lockfile-config opt-out; only ErrorKind::NotFound creates — an unreadable existing file falls back to guidance, never overwrite; re-scans heal a missing config on already-redirected locks). pnpm 11.22 and 12-rc frozen installs succeed with no flags and no CI changes; 9/10 ignore the key (verified); the sha512 pin still fails closed under trust. Warnings name the actual spliced host (userinfo stripped) and both per-major error codes, and pre-empt pnpm 12's own rebuild-the-lock advice that silently unpatches. VENDOR, pnpm 7/8: new pnpm-legacy backend (flavor-stamped so older binaries fail closed) — package.json pnpm.overrides + legacy lock surgery emitting exactly what those majors serialize (byte-stable under pnpm's own re-lock). pnpm <= 8 absolutizes file: specifiers, so frozen installs are path-bound: surfaced as vendor_pnpm_legacy_absolute_specifier, with plain `pnpm install --offline` as the moved-checkout path (marker bytes verified). Windows-shaped canonical paths are normalized (verbatim prefix stripped, forward slashes). CONVERSIONS + SAFETY: vendoring over a hosted-redirected npm-family purl now reconciles the redirect ledger (artifact-uuid-anchored matching — version-exact, v5 underscore keys claimed; degraded ledgers keep edits fail-closed), firing vendor_supersedes_redirect exactly once; vendor --revert byte-restores the hosted lock from the wiring originals. All six npm-family vendor backends refuse to delete an artifact the live lock still references when a repair-reconstructed entry has no wiring (vendor_wiring_unknown_revert_blocked — the revert-brick fix); repair stamps detected flavors and preserves corrupt artifacts when no rebuild source exists. Legacy-era diagnostics: shrinkwrap.yaml projects get pnpm-flavored no-lockfile guidance and join the lockfile-only supplement; vendored lock entries get redirect_pnpm_entry_vendored instead of entry-not-found. Tests: e2e_redirect_pnpm_build.rs (new hosted capstone: pnpm 7-11 real corepack legs incl. the zero-touch pnpm 11 proof, tamper negative, hermetic v5/v6 legs), e2e_vendor_pnpm_build.rs ladder (@9/@10/@11 + real pnpm 7/8 lifecycle legs), takeover/reconciliation/guard/heal unit + e2e suites — all RED-verified where behavior changed. CLI_CONTRACT.md and docs/ecosystems.md updated. Stacked on #203 (lock-inventory legacy grammars); trivially overlapping test hunks with #204/#208 carry identical content. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(pnpm-legacy): moved-checkout recovery needs --no-frozen-lockfile (pnpm defaults frozen on under CI) CI caught what local runs could not: pnpm turns --frozen-lockfile ON when CI=true, and the pnpm <= 8 moved-checkout recovery works precisely by re-resolving the path-bound absolute specifier — frozen semantics skip that re-resolution (pnpm 8: ERR_PNPM_OUTDATED_LOCKFILE; pnpm 7: stale-path install). The lifecycle legs' recovery step now passes --no-frozen-lockfile explicitly, and the vendor_pnpm_legacy_absolute_ specifier remedy (warning text, module doc, CLI_CONTRACT.md, docs/ecosystems.md) recommends `pnpm install --offline --no-frozen-lockfile` so real CI users get working advice. Verified: the full capstone (10 legs incl. real pnpm 7/8 lifecycles) passes under CI=true locally. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(pnpm-legacy): lock oracle uses the real path normalizer (Windows byte-exactness) Windows CI proved the production normalizer right and the test oracle wrong: the hermetic splice legs built their expected absolute specifier with raw canonicalize().display() — the \\?\C:\ verbatim form the normalizer exists to strip. normalize_canonical_root is now pub and the oracle consumes it at both assertion sites, so the expected string is built by the same transformation the backend writes and cannot drift. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(pnpm-legacy): in-file oracles also use the shared root normalizer Windows CI surfaced the same oracle-drift bug in the module's own unit tests: the fixture helper handed raw canonicalize().display() (verbatim \\?\C:\ form) to the ROOT_TOKEN substitution, the no-leak contains probe, and the moved-checkout fixture builder. All three now go through a canon_root_str() helper built on normalize_canonical_root, so every oracle spells the root exactly as the splice writes it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Mikola Lysenko (mikolalysenko)
enabled auto-merge (squash)
August 19, 2026 14:58
… expect-messages in new tests
Review feedback, two fixes:
1. note_warning silences stderr under --json and stores advisories in
VexWriteSummary.warnings, but only the standalone vex envelope copied
them out — embedded apply/scan/vendor --json --vex runs swallowed
product_not_iri and vendored_tree_out_of_sync entirely on the machine
channel this change targets. VexSummary gains an additive warnings
field (skip_serializing_if empty, same RunWarning shape as the
standalone envelope's warnings[]), populated by the apply host here;
the scan/vendor hosts ship with the feature branch that owns those
files. Pinned by apply_json_vex_warnings_ride_in_envelope
(RED-verified: failed with the population removed) plus a
clean-product control pinning the key absent.
2. Newly-added tests' bare .unwrap() fixture steps converted to
.expect("step description") in cli_apply_silent.rs, e2e_vex_vendor.rs,
and e2e_embedded_vex.rs (new tests only; long-standing tests
untouched).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Mikola Lysenko (mikolalysenko)
force-pushed
the
fix/vex-diagnostics-offline-notice
branch
from
August 19, 2026 15:07
cccb8ec to
a787a5e
Compare
Wenxin Jiang (Wenxin-Jiang)
approved these changes
Aug 19, 2026
Mikola Lysenko (mikolalysenko)
deleted the
fix/vex-diagnostics-offline-notice
branch
August 19, 2026 15:14
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.

Problems (found by the 2026-08-18 pnpm e2e campaign's vex/offline probes; all ecosystem-independent)
setup.manual) madevex --jsonexit 1 withevents: []and the factually wrong error "No applied patches with vulnerability metadata to attest." — indistinguishable from "not patched". The explanatory note was human-mode-only.--output— a pipeline reusing one path could ship yesterday's attestation for a now-unpatched tree.--outputwrite failures reported a bare OS error with no path;--productaccepted arbitrary non-IRI strings verbatim into the OpenVEX product@id; a healthy vendored attestation said nothing when the live installed tree was out of sync with the patched bytes.apply/vendor/rollback--offlineruns printed the "using the public patch API proxy" notice — network-implying output under strict airgap — and theSOCKET_OFFLINEparse was duplicated between the client and telemetry.Fixes
skippedevents with errorCodeecosystem_not_setup; when all omissions are filter drops, the error message names the filter and both remediations.@contextnaming openvex.dev) — unrelated files are never touched; the contract is pinned by tests both ways.write_failednames the path; a non-purl/non-IRI product override warns (product_not_iri, honored verbatim); vendored attestations gain avendored_tree_out_of_syncwarning when the present installed tree hash-mismatches (attestation basis unchanged — the committed artifact is the product; the warning says to reinstall).is_offline_env()is hoisted toutils/env_compatand shared by the client (both gates) and telemetry.The core attestation invariant is untouched and was verified healthy throughout the probes:
verified: trueonly ever appears when on-disk bytes hash toafterHash; every tamper flipped the attestation. The stable(vendored)/(redirected)impact-marker strings are unchanged (contract-pinned).Verification
New tests: stale-doc removal + non-OpenVEX preservation, write-failure path naming, setup-filter skipped events + specific message,
product_not_iriwarn + purl control, out-of-sync warn with absent-tree and pristine controls (core), offline proxy-notice suppression. Suites: cli_parse_vex 7, e2e_embedded_vex 12, e2e_vex_vendor 13, cli_apply_silent 5, core vex 241 — all green; verified against main in an isolated worktree before opening.🤖 Generated with Claude Code
Note
Medium Risk
Changes VEX JSON contracts, failure cleanup at output paths, and offline messaging—high impact for CI pipelines consuming envelopes, but core attestation rules (hash-to-afterHash, vendored precedence) are preserved and heavily tested.
Overview
VEX
--jsonand envelopes now expose ecosystem-setup (Property 7) drops as per-purlskippedevents witherrorCodeecosystem_not_setup, with clearer top-level errors when every omission is that filter—not the generic “nothing to attest.” Run-levelwarnings[]covers non-IRI--productoverrides (product_not_iri, still emitted verbatim) and vendored patches whose livenode_modulestree hash-mismatches the committed artifact (vendored_tree_out_of_sync; attestation still based on.socket/vendor).Failed runs delete a recognizably OpenVEX file at
--output/--vexso CI cannot ship a priornot_affecteddoc; unrelated paths are untouched.write_failednames the path and operation.Core verification adds
VerifyOutcome.vendored_out_of_syncwhen a healthy vendored attestation sees a present installed tree with wrong bytes.Offline / airgap: tokenless “public patch API proxy” stderr is suppressed when
SOCKET_OFFLINEis set;is_offline_env()is shared across API client and telemetry instead of duplicated parses.Reviewed by Cursor Bugbot for commit dfdc94b. Configure here.