Commit 9b23566
fix(bridge): distinguish direct vs transitive phantom deps in triage action
Closes #47.
`bridge triage` was telling users to "Remove unused dependency
`<pkg>` from Cargo.toml" for any phantom-classified CVE, but the
2026-05-26 estate sweep showed every sampled phantom (28/28 in a 6-repo
sample, 157 phantoms total) was a *transitive* dep pulled in by some
upstream crate — never declared in any Cargo.toml. The action string was
unactionable.
Fix: parse the project's Cargo.toml dependency tables to distinguish
direct from transitive deps, then emit the right action for each.
Changes:
- `src/bridge/lockfile.rs`: new `collect_direct_cargo_dependencies()`
walks the root Cargo.toml + each `workspace.members` manifest, indexing
`[dependencies]`, `[dev-dependencies]`, `[build-dependencies]`,
`[workspace.dependencies]`, and target-prefixed variants
(`[target.cfg(...).dependencies]`). Crate names normalised to
hyphen+lowercase for CVE-feed matching (serde_json ↔ serde-json).
- `src/bridge/classify.rs`: `classify()` takes a new `is_direct: bool`.
- Phantom + direct → unchanged "Remove unused dependency" action.
- Phantom + transitive → new action: "Transitive — run `cargo update -p
<pkg>` ... Otherwise informational: code unreachable from this
project."
- Reachable arms unchanged.
- `src/bridge/mod.rs`: `triage()` builds the direct-deps set once
(outside the per-CVE loop) and passes the lookup result into classify.
Regression coverage:
- `direct_deps_skips_transitive_only_crates` — repro of the `lru` /
ratatui case from the issue.
- `direct_deps_collects_dev_and_build_sections`,
`direct_deps_handles_target_sections`,
`direct_deps_handles_workspace_members`,
`direct_deps_normalises_underscore_to_hyphen`,
`direct_deps_ignores_commented_lines_and_strings_with_hash`,
`direct_deps_empty_when_no_manifest` — parser edge cases.
- `test_phantom_transitive_recommends_cargo_update` — asserts no "Remove
unused dependency" string for the transitive phantom case.
- `test_phantom_direct_recommends_removal` — direct case unchanged.
- `test_reachable_classification_unaffected_by_is_direct` — sanity check
that the new flag only affects the Phantom arm.
All 236 binary tests pass. cargo clippy clean. cargo fmt clean.
Out of scope (per the issue):
- "For unmitigable + reachable: actionable warning" — the current
rationale already describes the import sites + lack of fix. Whether
the action wording needs further sharpening is a separate concern
from the wrong-action-on-phantom-transitive bug.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>1 parent edf8d6e commit 9b23566
3 files changed
Lines changed: 396 additions & 8 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
19 | 26 | | |
20 | 27 | | |
21 | 28 | | |
22 | 29 | | |
| 30 | + | |
23 | 31 | | |
24 | 32 | | |
25 | 33 | | |
26 | | - | |
| 34 | + | |
27 | 35 | | |
28 | 36 | | |
29 | 37 | | |
| |||
37 | 45 | | |
38 | 46 | | |
39 | 47 | | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
40 | 66 | | |
41 | 67 | | |
42 | 68 | | |
| |||
178 | 204 | | |
179 | 205 | | |
180 | 206 | | |
181 | | - | |
182 | | - | |
| 207 | + | |
| 208 | + | |
183 | 209 | | |
184 | | - | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
185 | 239 | | |
186 | 240 | | |
187 | 241 | | |
188 | 242 | | |
189 | | - | |
| 243 | + | |
190 | 244 | | |
191 | 245 | | |
192 | 246 | | |
193 | 247 | | |
194 | 248 | | |
195 | | - | |
| 249 | + | |
196 | 250 | | |
197 | 251 | | |
198 | 252 | | |
199 | 253 | | |
200 | 254 | | |
201 | 255 | | |
202 | | - | |
| 256 | + | |
203 | 257 | | |
204 | 258 | | |
205 | 259 | | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
206 | 269 | | |
0 commit comments