Skip to content

Integration cleanup: coherent fix hints, honest refusal blame, audit plumbing#108

Merged
juangaitanv merged 4 commits into
install-vuln-gatefrom
ivg/integration-cleanup
Jun 11, 2026
Merged

Integration cleanup: coherent fix hints, honest refusal blame, audit plumbing#108
juangaitanv merged 4 commits into
install-vuln-gatefrom
ivg/integration-cleanup

Conversation

@juangaitanv

Copy link
Copy Markdown
Contributor

Follow-up to #100#107, addressing the cross-unit review findings:

Fix-version helpers consolidated. safe_version (certification: every match must carry a parseable fix) and advertised_fix (best-effort max) were parallel near-duplicates; both now delegate to one highest_fix core with a strict/lenient parse flag. Output coherence: the fix with: hint on pre-existing findings follows the steer re-check — a Verified steer drops the (advertised fix) hedge, a Rejected steer suppresses the hint (the rejection line already printed), only an unverified proposal keeps the hedge.

Refusal blame is now origin-aware. Refusing to run install: your existing dependency tree… previously fired whenever no named target was flagged — lying for pip install -r reqs.txt (finding labeled (from requirements) right above) and for fresh-project installs whose named target pulls a vulnerable transitive. New rule: blame the existing tree only when every vulnerable finding predates the command (bare installs, or manifest-declared PreExisting deps). The summary parenthetical (N from existing tree) is reworded to (N from resolved tree) for the same reason.

Audit kill switch plumbed like every other knob. CORGEA_NO_NPM_AUDIT was read via std::env deep in tree.rs; it's now PrecheckOptions::npm_audit, read once in main alongside the registry overrides. Env semantics unchanged (e2e kill-switch test passes untouched).

Audit collect window 2s → 1s. The warn-only second opinion never changes the outcome; a finished gate shouldn't stall 2s for it.

Verified: ./harness check green (306 tests), and live staging re-runs — npm install mkdirp@0.5.1 (vulnerable transitive minimist) now gets the generic refusal; bare install with poisoned manifest keeps the existing-tree refusal and prints the unhedged fix with: corgea npm install axios@0.21.2 after steer verification.

…plumbing

Post-merge cleanup across the eight install-gate units:

- Consolidate safe_version/advertised_fix into one highest_fix core
  (strict vs lenient parsing); the pre-existing fix hint's hedge now
  follows the steer re-check: Verified drops '(advertised fix)',
  Rejected suppresses the hint, Unverified keeps the hedge.
- The existing-tree refusal only fires when every vulnerable finding
  predates the command: Requested (pip -r) findings and transitives
  pulled in by named targets get the generic refusal. Summary
  parenthetical reworded to 'from resolved tree'.
- CORGEA_NO_NPM_AUDIT moves out of tree.rs into PrecheckOptions.npm_audit,
  read in main like the registry overrides; env semantics unchanged.
- Audit collect window tightened to 1s: the warn-only signal never
  changes the outcome, so a finished gate shouldn't stall for it.
cursor[bot]
cursor Bot previously requested changes Jun 11, 2026
Comment thread src/precheck/mod.rs Outdated
Comment thread src/precheck/mod.rs Outdated
Two fixes from the PR #108 review:

- refusal_blames_existing_tree filtered tree findings to Vulnerable
  before the origin test, but should_block_install refuses on
  Unverifiable too — a command-added unverifiable transitive alongside
  a pre-existing vulnerable dep printed 'none were added by this
  command'. Every blocking tree finding now passes the origin test.

- The audit thread was detached with the Child trapped inside it; a
  fast gate exit orphaned a slow 'npm audit' past the CLI's lifetime.
  spawn_audit now returns an AuditHandle whose collect() does the 1s
  recv, then kills whatever is left in the shared child slot and joins
  the thread — no orphan, TempDir drops deterministically, happy-path
  latency unchanged. The hang test's fake now execs its sleep (a
  grandchild would dodge the kill) and asserts the child is dead after
  the CLI exits.
A requirements-only install (pip install -r reqs.txt) has no named
outcomes — exactly like a bare install — so outcomes.is_empty()
wrongly let a vulnerable transitive of a clean requirements entry
blame the existing tree. PrecheckReport now carries bare_install
(no CLI targets AND no requirements files), set from the parsed
command, and the Transitive arm tests that instead of inferring
from outcomes. Regressions: requirements-only e2e + full
origin/named/bare unit matrix.
@juangaitanv

Copy link
Copy Markdown
Contributor Author

Re: outcomes.is_empty() conflating bare installs with requirements-only installs (src/precheck/mod.rs:539) — yes on both counts, fixed in c708958.

PrecheckReport now carries explicit bare_install context (no CLI targets and no requirements files, computed from the parsed command before resolution), and the Transitive arm of refusal_blames_existing_tree tests that instead of inferring from outcomes. The inference was indeed wrong for pip install -r reqs.txt: no named outcomes, but every resolved package — including transitives of clean requirements entries — is added by the command, so the existing-tree refusal lied one level deeper than the Requested-origin fix caught. (Inferring from Requested entries in the tree would also have been fragile: parse_pip_report defaults requested to false when pip omits the flag.)

Regressions added:

  • e2e requirements_only_install_with_vulnerable_transitive_keeps_generic_refusal (cli_refusal_context.rs): -r reqs.txt, clean requested entry, vulnerable transitive → generic refusal, exit 1, pip not run.
  • Unit matrix refusal_blame_respects_finding_origin expanded to origin × named-outcomes × bare_install (9 cases), with the requirements-only shape (named=false, bare=false) as its own row.

The bin existed for live e2e dogfooding, now done. Every consumer in the
repo (7 integration test files + precheck unit tests) uses the in-process
vuln_api_stub::spawn_with_statuses, so the bin-only surface goes with it:
the fixtures.rs file loader, spawn_from_file, VulnApiStub::block, and the
StubFixtures/spawn_on_port indirection (the ephemeral-port bind now lives
directly in spawn_with_statuses).
@juangaitanv juangaitanv merged commit b6c2e83 into install-vuln-gate Jun 11, 2026
15 checks passed
@juangaitanv juangaitanv deleted the ivg/integration-cleanup branch June 11, 2026 07:28
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.

1 participant