Skip to content

fixed false positives for ubuntu kernel packages and backfixes#3129

Open
rezmoss wants to merge 8 commits intoanchore:mainfrom
rezmoss:false-post-ubuntu-ubuntu-backfixes
Open

fixed false positives for ubuntu kernel packages and backfixes#3129
rezmoss wants to merge 8 commits intoanchore:mainfrom
rezmoss:false-post-ubuntu-ubuntu-backfixes

Conversation

@rezmoss
Copy link
Copy Markdown

@rezmoss rezmoss commented Dec 19, 2025

skip cpe matching for linux-kernel packages on major distros like ubuntu to avoid false positives from nvd's version ranges not covering backported fixes

kernel vulns are still found accurately using dpkg/rpm matchers with distro data that includes backported fixes

e.g CVE-2023-2163 for 5.15.0-164 ,fixed in 5.15.0-70.77~20.04.1

…VE-2023-2163

Signed-off-by: Rez Moss <hi@rezmoss.com>
…VE-2023-2163

Signed-off-by: Rez Moss <hi@rezmoss.com>
@rezmoss rezmoss force-pushed the false-post-ubuntu-ubuntu-backfixes branch from 21c89c3 to 40349fd Compare December 19, 2025 21:48
Comment thread grype/matcher/stock/matcher.go Outdated
func (m *Matcher) Match(store vulnerability.Provider, p pkg.Package) ([]match.Match, []match.IgnoreFilter, error) {
// skip cpe matching for linux-kernel packages on major distros like ubuntu to avoid false positives from nvd's version ranges not covering backported fixes
// kernel vulns are still found accurately using dpkg/rpm matchers with distro data that includes backported fixes
if p.Type == syftPkg.LinuxKernelPkg && isMainDistro(p.Distro) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is minor, however, since the first branch of this is by package type (kernel) we should make a separate matcher for this. This also keeps the stock matcher as simple as possible (and matchers are tailor made to handle by package type).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added this as a matcher. Now we just need to talk about the matching it's doing and make sure this is behaving the way we expect.

@spiffcs spiffcs self-assigned this Jan 29, 2026
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
@spiffcs
Copy link
Copy Markdown
Contributor

spiffcs commented Jan 29, 2026

Working on getting this up to date with a new matcher:

this is minor, however, since the first branch of this is by package type (kernel) we should make a separate matcher for this. This also keeps the stock matcher as simple as possible (and matchers are tailor made to handle by package type).

@rezmoss
Copy link
Copy Markdown
Author

rezmoss commented Jan 30, 2026

@spiffcs dropped this here i worked on this earlier & since i'm tied up with other stuff, didnt get to do a final review but tested it pretty thoroughly with the all distro listed, it's all yours to edit, delete, or revert

Ubuntu
cat grype-test-results.json | jq '[.matches[] | select(.artifact.type == "linux-kernel")] | length'
0
cat grype-original-results.json | jq '[.matches[] | select(.artifact.type == "linux-kernel")] | length'
5194

grype result before/after and dockerfile to reprod
ubuntu.zip

Alpine
cat grype-test-results.json | jq '[.matches[] | select(.artifact.type == "linux-kernel")] | length'
0
cat grype-original-results.json | jq '[.matches[] | select(.artifact.type == "linux-kernel")] | length'
537

alpine.zip

Amazon
cat grype-test-results.json | jq '[.matches[] | select(.artifact.type == "linux-kernel")] | length'
0
cat grype-original-results.json | jq '[.matches[] | select(.artifact.type == "linux-kernel")] | length'
934

amazon.zip

Debian
cat grype-test-results.json | jq '[.matches[] | select(.artifact.type == "linux-kernel")] | length'
0
cat grype-original-results.json | jq '[.matches[] | select(.artifact.type == "linux-kernel")] | length'
4070

debian.zip

Fedoea
cat grype-test-results.json | jq '[.matches[] | select(.artifact.type == "linux-kernel")] | length'
0
cat grype-original-results.json | jq '[.matches[] | select(.artifact.type == "linux-kernel")] | length'
993

fedora.zip

Signed-off-by: Rez Moss <hi@rezmoss.com>
@spiffcs
Copy link
Copy Markdown
Contributor

spiffcs commented Jan 30, 2026

Thanks so much @rezmoss - I finally got out from under some work and am trying to grind through PRs that can get over the line. Your name came up a couple times so I wanted to start with the ones you had from Christmas.

Thank you for all the effort and thought put into these PRs. I really appreciate the work and will be sure to get these landed 😄

Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
@rezmoss
Copy link
Copy Markdown
Author

rezmoss commented Feb 2, 2026

@spiffcs all good on timing, happy to see it moving forward,just ping me if anything comes up

// distrosWithReliableKernelData are distros where kernel vulnerabilities are handled by their
// native package matchers (dpkg/rpm) with distro-specific backport data.
// For these distros, the kernel matcher skips CPE matching to avoid false positives.
var distrosWithReliableKernelData = []distro.Type{
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to infer this from our database or something? I don't love adding yet another hard coded list of distros. Maybe we can query the distro for kernel RPM / dpkg matches and then assume it's a reliable kernel?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think in an ideal world, we would distinguish between:

  1. The distro package owns the kernel artifact and has fixed vulns for the kernel - no fallback, vulns are fixed (or not, but the distro feed is right)
  2. The distro package owns the kernel artifact and there's no kernel vulns in this distro - search miss, CPE fallback
  3. The distro package does not own the kernel - who knows where it came from - CPE fallback.

But I'd love to see the SBOMs that made these scans.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@willmurphyscode still new to the grype codebase so finding the answer wasnt easy, but i did my best, hope its not too off ;)

to reliably infer distros from the db, we’d need to:

  • check if the distro has kernel vuln data
  • see if the kernel binary came from a package manager
  • make sure the version formats match

about the hardcoded approach, i think it because since sbom doesnt show ownership, the kernel binary and deb package are separate with no link

@willmurphyscode
Copy link
Copy Markdown
Contributor

Can we get Syft SBOMs for these images? I'd like to understand why

func excludePackage(comprehensiveDistroFeed bool, p syftPkg.Package, parent syftPkg.Package) bool {
isn't filtering the Kernel package out before matching in the case where it's owned by an rpm or dpkg.

@spiffcs
Copy link
Copy Markdown
Contributor

spiffcs commented Feb 3, 2026

@rezmoss do you have any syft sboms for your test images? We're looking on trying to identify how these kernel packages could link to the distro (is the kernel artifact owned by an RPM or pick your specific package manager).

A Dockerfile or link to a public image you're scanning would help us answer some of the questions we have about how grype should behave.

grype/grype/pkg/package.go

Lines 151 to 175 in 411d3c6

func excludePackage(comprehensiveDistroFeed bool, p syftPkg.Package, parent syftPkg.Package) bool {
// NOTE: we are not checking the name because we have mismatches like:
// python 3.9.2 binary
// python3.9 3.9.2-1 deb
// If the version is not approximately the same, keep both
if !strings.HasPrefix(parent.Version, p.Version) && !strings.HasPrefix(p.Version, parent.Version) {
return false
}
// If the parent is an OS package and the child is not, exclude the child
// for distros that have a comprehensive feed. That is, distros that list
// vulnerabilities that aren't fixed. Otherwise, the child package might
// be needed for matching.
if comprehensiveDistroFeed && isOSPackage(parent) && !isOSPackage(p) {
return true
}
// filter out binary packages, even for non-comprehensive distros
if p.Type != syftPkg.BinaryPkg {
return false
}
return true
}

We want to see why the above would not be filtering out the kernel packages before they even get to the new matcher.

We also want to take a look at the SBOM to see what the case would be when this matcher would emit a true positive (vulnerable) for when a kernel package is sent through this matcher.

This would be the case where we could not associate the found package to having been installed as a part of the upstream distros package management.

If it's installed that way then 👍 we take the comprehensiveDistroFeed as authoritative. If it was brought over or made it's way into the image outside of that package ecosystem we still want to emit a true positive given that we can't determine if those upstream fixes have been applied.

Adding blocked tag to this PR for now until we have an image we can test as authoritative that demonstrates both TP/FP behavior for the new matcher 😄

@spiffcs spiffcs added the blocked Progress is being stopped by something label Feb 3, 2026
@rezmoss
Copy link
Copy Markdown
Author

rezmoss commented Feb 5, 2026

Can we get Syft SBOMs for these images? I'd like to understand why

func excludePackage(comprehensiveDistroFeed bool, p syftPkg.Package, parent syftPkg.Package) bool {

isn't filtering the Kernel package out before matching in the case where it's owned by an rpm or dpkg.

@willmurphyscode already shared the grype output & dockerfile before, generating an sbom from those should be simple => #3129 (comment)

@rezmoss
Copy link
Copy Markdown
Author

rezmoss commented Feb 5, 2026

@spiffcs you can find the dockerfile in t zip files above, but just in case, here it is too

ubuntu

FROM --platform=linux/amd64 ubuntu:22.04

RUN apt-get update && \
    apt-get install -y linux-image-generic && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

alpine

FROM --platform=linux/amd64 alpine:3.19

RUN apk update && \
    apk add linux-lts && \
    rm -rf /var/cache/apk/*

linux kernel bin, picked up by the linux-kernel-cataloger

Screenshot 2026-02-05 at 12 51 51

and linux image deb pkg, picked up by dpkg-db-cataloger

Screenshot 2026-02-05 at 12 56 38

also after i replied to @willmurphyscode , i realized syft might already link them somehow, but no idea why that didnt work

look this

Screenshot 2026-02-05 at 13 10 26

and parent

Screenshot 2026-02-05 at 13 11 25

and child

Screenshot 2026-02-05 at 13 11 44

hope this helps

@witchcraze
Copy link
Copy Markdown

Before, I reported similar topic for Vuls.
future-architect/vuls#1559

From Vuls team, OVAL does not include kernel CVEs informaiton.
Vuls team coordinated with Ubuntu team, Vuls witched to use gost (Ubuntu CVE Tracker) data.
To detect CVE of ubuntu kernel, maybe using Ubuntu CVE Tracker) data is required.

@westonsteimel
Copy link
Copy Markdown
Contributor

Before, I reported similar topic for Vuls. future-architect/vuls#1559

From Vuls team, OVAL does not include kernel CVEs informaiton. Vuls team coordinated with Ubuntu team, Vuls witched to use gost (Ubuntu CVE Tracker) data. To detect CVE of ubuntu kernel, maybe using Ubuntu CVE Tracker) data is required.

Thanks, we're already using the Ubuntu CVE tracker data to populate the grype database: https://github.com/anchore/vunnel/blob/10be0cb42e12a7ba0cf8f21e2ecb156d03583b04/src/vunnel/providers/ubuntu/parser.py#L28

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

blocked Progress is being stopped by something

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants