the scanner changed its package-version string format (epoch added) and the agent's dedup hash stopped matching history
Fixes dedup history mismatches after a scanner adds epochs to package version strings by normalizing versions with an epoch-aware parser before hashing. Use when a scanner format change makes identical packages hash differently and history stops matching. Key trigger: scanner added epoch to version strings and the dedup hash stopped matching history.
Scanner added epochs to version strings and the dedup hash stopped matching
TL;DR
Normalize version strings with an epoch-aware parser before hashing: split the epoch, upstream version, and revision explicitly so "2:1.2.3-1" and "1.2.3-1" compare deliberately instead of as raw strings. The scanner started emitting Debian-style epochs, so byte-identical packages hashed differently and every historical finding looked new. Version your dedup-key format too, so a normalization change migrates old keys instead of orphaning them.
The failure
dedup hash stopped matching history after the scanner added epoch prefixes to versions
("2:1.2.3-1" versus "1.2.3-1" hashed differently, identical packages re-filed as new)Steps
- Add a normalize-version step before key hashing that splits each version into epoch, upstream version, and revision using the ecosystem's own comparison rules. Expected: "2:1.2.3-1" and "1.2.3-1" normalize per your stated policy, consistently, every time.
- Decide the policy explicitly: either treat a missing epoch as epoch 0 (so the two forms match) or treat them as different (so they do not), and document which you chose. Expected: no ambiguity for the next person who reads the code.
- Bump the dedup-key format version and migrate stored keys through the normalizer. Expected: history matches again and old raw-string keys are retired, not left to mismatch.
- Add fixture tests with epoch and non-epoch forms of the same version, plus a scanner-output sample containing epochs. Expected: the next scanner format change fails in CI before it reaches production.
Use this when
- dedup stops matching history right after a scanner upgrade
- version strings gain epochs, revisions, or other new segments
- identical packages re-file as new with no inventory change
- the dedup key hashes raw scanner output instead of normalized values
Not for this skill when
- versions are genuinely different and findings are real (do not normalize those away)
- the mismatch comes from feed description edits (exclude mutable text from the key)
- the key includes a mutable image tag (key on the digest instead)
- the scanner reports different identifiers per CVE (normalize identifiers, not versions)
Variant phrasings
- debian epoch in version string broke vulnerability dedup
- scanner version format change duplicate findings
- dedup hash mismatch after trivy upgrade version strings
Why it happens
Dedup keys hashed the scanner's raw version string, which made the scanner's formatting choices part of your identity scheme. When the scanner started emitting Debian-style epochs (the "2:" prefix), the same installed package produced a different string and therefore a different hash. Nothing about the package changed, only the serialization. Normalizing before hashing separates identity (what version is installed, compared with the ecosystem's own rules) from serialization (how the scanner happened to print it). The key-format version exists because normalization itself will evolve, and you want migrations, not orphaned history.
Edge cases
- Epoch 0 versus missing epoch: Debian treats a missing epoch as 0, but your policy should be explicit because other ecosystems differ.
- Some scanners strip epochs while others add them. If you run both, normalize at ingest per scanner so the canonical form is identical.
- Version comparison and version identity are different jobs. Use the ecosystem comparator for "is this vulnerable" and the normalizer for "is this the same finding".
- During migration, run the old and new key formats in parallel for one cycle so in-flight findings do not double-file.
Provenance
Resolved from the public thread: https://vectle.com/posts/pstyecdlgNN-9TvDvhBMs24g