## TL;DR
Two scanners seeing the same CVE is one finding, not two. Normalize every finding to a canonical key (CVE plus package plus installed version) at ingestion, dedupe there while keeping the source list, then recompute the report. The dashboard should count findings, not scanner outputs.

```text
the agent's weekly vuln report double-counted findings - trivy and grype both fed the same dashboard with no dedup
```

## Use this when
- Finding counts jump when a second scanner is added, with no change in actual exposure.
- The same CVE appears twice in the report under different scanner names.
- Leadership is asking why the count does not match any single scanner's output.

## Not for this skill when
- One scanner is wrong about a finding (false positive). That is accuracy, not dedup.
- The two scanners genuinely cover different asset sets. Then the union is correct and the per-source breakdown is the fix.

## Steps
1. Define the canonical finding key: CVE ID plus package name plus installed version plus asset. Verify: the same CVE from trivy and grype produces the identical key.
2. Dedupe at ingestion: the first source creates the finding, later sources attach as "also seen by". Verify: ingest both scanner outputs and confirm one finding with two sources.
3. Handle scanner disagreements on package naming (e.g. `python-dateutil` vs `dateutil`) with a normalization map before keying. Verify: the known alias pairs collapse to one finding.
4. Recompute the weekly report from the deduped store. Verify: the total matches a hand-checked sample, and the per-scanner breakdown still adds context.
5. Track scanner agreement as a metric: findings seen by both vs one. Verify: the dashboard shows the overlap rate, which also catches scanner drift.

## Variant phrasings
### trivy grype double count
The short phrasing.
### vuln report dedup scanners
The pipeline phrasing.
### same CVE counted twice dashboard
The symptom search.

Compatibility: any multi-scanner setup; the canonical-key idea works whether the store is a DB, DefectDojo, or a spreadsheet.

## Why it happens
Each scanner writes to the dashboard independently, and the dashboard sums rows. Nobody owned the "same finding" concept across sources, so the union of scanner outputs was treated as the finding set. Counting scanner outputs instead of findings inflates the numbers exactly by the overlap.

## Edge cases / pitfalls
- Alias resolution is never complete; keep an "unmatched" bucket and review it, or the dedup silently drops findings it could not match.
- Do not dedupe away the source list; when the scanners disagree on severity, you need to know who said what.
- Version-string formats differ between scanners (epoch, distro suffix); normalize before keying or the same package will not match.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_DiynWADiy0AsF-UeE30ovQ
