Record a container package under the ecosystem it belongs to - #42
Merged
Conversation
Container persistence hardcoded pkg:apk/{name}@{version} and package type
"apk" for every package in every image. A Rocky image's rpms, a Debian
image's debs and pip inside a python image were all inventoried as Alpine
packages. No finding was lost, so the counts were right and only the identity
was wrong — which is why it survived, and why data-sources.md already
documented the pkg:apk / pkg:deb / pkg:rpm split the code did not honour.
Read the PURL Trivy attaches to each finding, as the SBOM matcher now does,
and derive the package type from it. Qualifiers are dropped: distro=rocky-9.3
would make the same package a new component on every point release and reset
the first-detected clock for the whole image. A finding with no derivable
identity is skipped rather than called apk.
Existing rows are left as they were stored. Re-scanning an rpm or deb image
corrects its inventory but resets that project's triage, since verdicts are
keyed on the component; upgrade.md says so in both languages.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Found while fixing the SBOM-side OS matching in #35, and tracked as parity-review row #39 — this one is our own defect rather than a gap against another tool.
What was wrong
_persist_trivy_reportnamed every package in every image the same way:A Rocky image's rpms, a Debian image's debs, and
pipinside a python image were all inventoried as Alpine packages. No finding was lost — this path creates its own components — so the counts were right and only the identity was wrong. That is why it survived: nothing failed, and the screens looked correct.docs/reference/data-sources.mdalready promisedpkg:apk/*,pkg:deb/*andpkg:rpm/*for the container pipeline. The documentation was right and the code was not, so no doc change was needed for the fix itself.The fix
_component_identityreads the PURL Trivy attaches to each finding — every Result it emits carriesPkgIdentifier.PURL, verified against real scans ofrockylinux:9-minimal(pkg:rpm/rocky/...),python:3.12-slim(pkg:deb/debian/...pluspkg:pypi/pip) and the existingalpine:3.19recording. The package type is derived from the PURL rather than assumed.Qualifiers are dropped from what is stored.
distro=rocky-9.3would make the same package version a new component on every point release of the base image, and both the first-detected clock and carried triage are keyed on the component version — so a 9.3 → 9.4 rebuild would report the whole image as newly vulnerable.A finding with no derivable identity is skipped and logged. Calling it apk is the thing this change exists to stop.
The tests were confirming the bug
Two integration tests hand-built
pkg:apk/{name}@{version}as their expectation, so a wrong implementation and a wrong expectation agreed with each other. Their fixtures now carry the shape a realtrivy imagewrites, and the SLA test derives the PURL from the report instead of rebuilding it.New recordings:
rocky-9-image-report.jsonanddebian-python-image-report.json, trimmed to the six most-affected packages and three CVEs each (per-package density preserved), withPackagesnarrowed to the same six to keep the files reviewable. Every entry is otherwise the tool's own output.Migration
Existing rows are left as stored — a past scan keeps the identity it recorded, which is what that scan saw. Re-scanning an rpm or deb image corrects its inventory but resets that project's triage: verdicts and the first-detected clock are keyed on the component, and the corrected package is a different component. Deployments that only scanned Alpine images are unaffected, since those values were already right.
installation/upgrade.mdstates this in EN and KO.A data migration was considered and rejected. Correcting the old rows is not a rename:
pkg:apk/opensslis one Component row that an Alpine openssl and a Rocky openssl both hang off, so untangling them means readingscan_components.raw_dataper version, moving rows between components, merging where a correct component already exists, and cleaning up what empties. That is a data reconstruction whose failure mode is silent mis-attribution — the same class of defect being fixed here.Verification
tests/unit tests/integrationon a fresh database: 6658 passed. The twotest_reset_demo_scope_dbfailures reproduce onmainunchanged.ko-style --all --fail-on S2reports 0.