Skip to content

Label install-gate tree findings by provenance (requested / pre-existing / transitive)#103

Merged
juangaitanv merged 1 commit into
install-vuln-gatefrom
ivg/u3-provenance-labels
Jun 11, 2026
Merged

Label install-gate tree findings by provenance (requested / pre-existing / transitive)#103
juangaitanv merged 1 commit into
install-vuln-gatefrom
ivg/u3-provenance-labels

Conversation

@juangaitanv

Copy link
Copy Markdown
Contributor

Unit 3: provenance labels for tree findings

Fixes the misleading blanket (transitive) label on tree-pass findings in the corgea <pm> install vulnerability gate.

What changed

  • TreePackage now carries pip's per-item "requested" flag from the --report JSON (previously discarded; always false for npm — its lockfile has no equivalent).
  • TreeOutcome gains a TreeOrigin: Requested (pip-requested leftovers, i.e. -r requirements files), PreExisting (npm leftovers named in the project package.json's direct deps — all four dep groups, read from cwd), Transitive (everything else).
  • Text labels: (from requirements), (already in package.json), (transitive). --json tree entries gain an "origin" field (requested / pre-existing / transitive).
  • PreExisting vulnerable findings with an advertised fix print a remediation hint:
    fix with: corgea npm install <name>@<fix> (advertised fix).
    advertised_fix() is the max parseable fixed_version across matches, ignoring fixless matches — deliberately weaker than safe_version's all-fixes certification and independent of it (degrades gracefully if the verified-steer line is absent).
  • should_block_install semantics untouched.

Tests

  • Unit: requested parsing (incl. missing-field default), manifest direct-dep extraction (all groups + degrade-to-empty), origin assignment precedence, advertised_fix (ignores fixless, max-by-semver).
  • New hermetic e2e tests/cli_provenance.rs (fake PM script + inline registry stub + vuln-api stub): pip -r label, npm pre-existing label + fix hint, no hint without a fix, JSON origins for both ecosystems.
  • ./harness check green (263 tests, clippy -D warnings, fmt).

Live staging smoke (cve-worker-staging)

  • Fresh project, corgea npm install mkdirp@0.5.1✗ minimist@0.0.8 (transitive), blocked.
  • Project with minimist: 0.0.8 in package.json, installing mkdirp@1.0.4✗ minimist@0.0.8 (already in package.json) + fix with: corgea npm install minimist@1.2.2 (advertised fix); --json shows "origin": "pre-existing".
  • venv pip, corgea pip install -r reqs.txt (mezzanine==6.0.0) → ✗ Mezzanine@6.0.0 (from requirements), blocked (no fixed version advertised → no hint, as intended).

Notes for the coherence merge

  • advertised_fix intentionally duplicates part of safe_version's max-by-lenient-semver scaffolding rather than depending on the parallel verified-steer unit; worth unifying once both land.
  • An npm leftover whose name matches a direct dep gets PreExisting even if that particular instance is a nested copy at another version — name+version is all the leftover carries. The label stays truthful and the hint (bump the manifest dep) is the right remediation either way.

Tree-pass findings were all labeled (transitive), which misled when the
flagged package came from the user's own requirements file or was already
a direct dep of the project.

- TreePackage carries pip's per-item "requested" report flag (npm: false).
- New TreeOrigin on TreeOutcome: Requested / PreExisting / Transitive.
  Requested = pip-requested leftovers (-r files); PreExisting = npm
  leftovers named in the project package.json's direct deps (all four
  dep groups), read from cwd; Transitive otherwise.
- Text labels: (from requirements), (already in package.json),
  (transitive). JSON tree entries gain an "origin" field.
- PreExisting findings with an advertised fix get a hint:
  fix with: corgea npm install <name>@<fix> (advertised fix).
  advertised_fix() takes the max parseable fixed_version across matches,
  ignoring fixless matches — deliberately weaker than safe_version's
  certification and independent of it.

should_block_install semantics unchanged. New hermetic e2e coverage in
tests/cli_provenance.rs (fake PM + registry stub + vuln-api stub).

@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.

Found actionable issues that should be fixed before merging.

Open in Web View Automation 

Sent by Cursor Automation: pr-flow

Comment thread src/precheck/mod.rs
fn advertised_fix(matches: &[crate::vuln_api::VulnMatch]) -> Option<String> {
let mut fixes: Vec<&str> = matches
.iter()
.filter_map(|m| m.fixed_version.as_deref())

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

advertised_fix() intentionally drops any advisory that does not provide fixed_version, but the caller renders the result as fix with: corgea npm install .... Evidence: the existing safe_version() path returns None when any match has no fix, while the new test at src/precheck/mod.rs explicitly asserts advertised_fix(&[fixed, none]) == Some(...). Impact: a package with multiple advisories can get a concrete "fix with" command even though one advisory is still known to have no fixed version, so users may apply the suggested update believing the finding is remediated when it is not. Fix: use safe_version(matches) for command-style remediation, or change this output to non-command informational text that clearly says only some advisories advertise that version and the gate may still fail.

Comment thread src/precheck/tree.rs
let Ok(manifest) = serde_json::from_str::<serde_json::Value>(json) else {
return Default::default();
};
let groups = [

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This collapses dependencies, devDependencies, optionalDependencies, and peerDependencies into a name-only set, but src/precheck/mod.rs later prints one generic corgea npm install <pkg>@<fix> command for every PreExisting finding. Impact: for a vulnerable dev/optional/peer dependency, the suggested command can rewrite the dependency into the wrong manifest section or omit the required npm flag (--save-dev, --save-optional, peer handling), which is unsafe remediation guidance for production users. Fix: preserve the dependency group in the provenance data and tailor/omit the fix hint unless the command can preserve the original dependency type; add tests for dev/optional/peer dependencies, not just dependencies.

@juangaitanv juangaitanv merged commit 665c610 into install-vuln-gate Jun 11, 2026
17 checks passed
@juangaitanv juangaitanv deleted the ivg/u3-provenance-labels branch June 11, 2026 07:40
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