#1678

fix(report): compare manifests by one spelling, not several

Openlxcxjxhxwants to merge intofrom

Fixes #1677

_distinct_manifest_paths compares the raw manifest_path strings, and that path is authored by the model, so one manifest arrives in several spellings - ./package.json, package.json//, a//package.json, .\\package.json - and each spelling made the pair look like two manifests. The duplicate was then kept as a second finding.

_manifest_path now normalizes the spelling with posixpath.normpath (separators unified, // and . segments collapsed, .. resolved lexically). The comparisons that matter stay as they were: a leading / is preserved so an absolute path never equals a relative one, and two different manifests remain distinct. normpath never touches the filesystem, so a symlinked parent still compares by the spelling it was written with.

Validation: pytest tests/test_dedupe_decisions.py -q -> 25 passed, including a parametrised case per spelling above, a case that keeps a/package.json vs b/package.json distinct, and one that keeps /a/package.json distinct from a/package.json.

leftrightbeforeafter
package.json./package.jsondistinctsame manifest
package.jsonpackage.json//distinctsame manifest
a/package.jsona//package.jsondistinctsame manifest
.\\package.jsonpackage.jsondistinctsame manifest
/a/../a/package.json/a/package.jsondistinctsame manifest
a/package.jsonb/package.jsondistinctdistinct
/a/package.jsona/package.jsondistinctdistinct

ruff check + format clean.

Activity

4
greptile-apps[bot]1d ago

Confidence Score: 5/5

[Medium impact] The PR appears safe to merge; no actionable issues were found.

Summary

The PR normalizes manifest_path strings before comparing dependency reports.

  • Dependency reports match when their manifest paths use different spellings.

Reviews (1) · Last reviewed commit: "fix(report): compare manifests by one sp..." · Reviewed by Greptile

lxcxjxhx1d ago

End-to-end case for the chain this branch fixes: check_duplicate on two dependency reports that differ only in case and manifest spelling (./package.json vs package.json) returns the incumbent's id, and the test makes that branch unreachable if the model is consulted (resolve_dedupe_model raises). Two guard cases sit beside it: the same package in a different manifest stays a second finding, and a report without a CVE is left to the model rather than guessed.

pytest tests/test_dedupe_chain.py -q -> 3 passed; ruff clean. New file, so it does not touch the matrix in test_dedupe_decisions.py.