fix: More fixes to vuln matching
This commit is contained in:
@@ -47,7 +47,7 @@ func Match(src AdvisorySource, os models.OSRelease, pkgs []models.InstalledPacka
|
||||
cache := make(map[string][]Advisory, len(pkgs))
|
||||
|
||||
var out []Result
|
||||
for _, p := range pkgs {
|
||||
for _, p := range newestPerSource(os.Family, pkgs) {
|
||||
// Debian and Ubuntu advisories are keyed on the source package: one
|
||||
// advisory against "openssl" covers libssl3, openssl and libssl-dev.
|
||||
srcName := p.SourceName
|
||||
@@ -111,6 +111,61 @@ func Match(src AdvisorySource, os models.OSRelease, pkgs []models.InstalledPacka
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// newestPerSource collapses the installed set to one binary package per source
|
||||
// package: the one carrying the highest version.
|
||||
//
|
||||
// Advisories are keyed on the source, so every binary package of a source asks
|
||||
// the same question. Normally they all carry the same version and the answer is
|
||||
// the same, so collapsing is free. The kernel is the exception that makes it
|
||||
// necessary: Ubuntu encodes the ABI in the binary name, so an upgrade INSTALLS
|
||||
// linux-headers-6.8.0-137 beside linux-headers-6.8.0-124 rather than replacing
|
||||
// it, and the old one lingers until an autoremove. Matched per binary package,
|
||||
// a fully patched host reports every superseded ABI package as vulnerable —
|
||||
// which is the noise this exists to stop — and reports it twice over, once for
|
||||
// linux-headers-6.8.0-124 and again for its -generic sibling.
|
||||
//
|
||||
// The version, not the name, decides. There is no kernel special case here: a
|
||||
// source's newest installed version is what the fix landed as, whatever the
|
||||
// source is.
|
||||
//
|
||||
// A comparison that cannot be made keeps the incumbent rather than guessing;
|
||||
// the loser is dropped either way, and dropping the parseable one would be the
|
||||
// false-negative direction.
|
||||
func newestPerSource(family string, pkgs []models.InstalledPackage) []models.InstalledPackage {
|
||||
best := make(map[string]models.InstalledPackage, len(pkgs))
|
||||
order := make([]string, 0, len(pkgs))
|
||||
|
||||
for _, p := range pkgs {
|
||||
src := p.SourceName
|
||||
if src == "" {
|
||||
src = p.Name
|
||||
}
|
||||
cur, seen := best[src]
|
||||
if !seen {
|
||||
best[src] = p
|
||||
order = append(order, src)
|
||||
continue
|
||||
}
|
||||
older, err := LessThan(family, cur.Version, p.Version)
|
||||
if err != nil {
|
||||
log.Printf("vulndb: newest for source %s: compare %s vs %s: %v",
|
||||
src, cur.Version, p.Version, err)
|
||||
continue
|
||||
}
|
||||
if older {
|
||||
Debugf("source %s: %s %s supersedes %s %s",
|
||||
src, p.Name, p.Version, cur.Name, cur.Version)
|
||||
best[src] = p
|
||||
}
|
||||
}
|
||||
|
||||
out := make([]models.InstalledPackage, 0, len(order))
|
||||
for _, src := range order {
|
||||
out = append(out, best[src])
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// actionable decides whether an advisory with no fixed version is a finding.
|
||||
//
|
||||
// trivy-db fills Status only when FixedVersion is empty, and Ubuntu publishes a
|
||||
|
||||
Reference in New Issue
Block a user