Skip to content

Commit 88c609d

Browse files
committed
fix(contrib/trivy): dedup package names, almost for dpkg
1 parent b9ac2ff commit 88c609d

File tree

2 files changed

+370
-19
lines changed

2 files changed

+370
-19
lines changed

contrib/trivy/pkg/converter.go

Lines changed: 55 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
trivydbTypes "github.com/aquasecurity/trivy-db/pkg/types"
1313
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
1414
"github.com/aquasecurity/trivy/pkg/types"
15+
debver "github.com/knqyf263/go-deb-version"
1516

1617
"github.com/future-architect/vuls/models"
1718
)
@@ -171,9 +172,21 @@ func Convert(results types.Results, artifactType ftypes.ArtifactType, artifactNa
171172
vulnInfos[vuln.VulnerabilityID] = vulnInfo
172173
}
173174

174-
// --list-all-pkgs flg of trivy will output all installed packages, so collect them.
175175
switch trivyResult.Class {
176176
case types.ClassOSPkg:
177+
// Collect all installed packages (requires --list-all-pkgs flag in Trivy).
178+
//
179+
// On Debian/Ubuntu, Trivy's dpkg analyzer reads both /var/lib/dpkg/status
180+
// and /var/lib/dpkg/status.d/*, producing duplicate entries for the
181+
// same package. The applier's dedup key includes FilePath, so entries
182+
// from different paths survive and appear as duplicates in the result.
183+
// (Other analyzers — RPM, APK — are not affected.)
184+
//
185+
// This is not a complete fix; ideally Trivy itself should deduplicate.
186+
// As a workaround we keep the newer version: for Debian/Ubuntu we
187+
// compare using dpkg version semantics; for other OS types we fall
188+
// back to lexicographic string comparison.
189+
177190
for _, p := range trivyResult.Packages {
178191
pv := p.Version
179192
if p.Release != "" {
@@ -182,28 +195,34 @@ func Convert(results types.Results, artifactType ftypes.ArtifactType, artifactNa
182195
if p.Epoch > 0 {
183196
pv = fmt.Sprintf("%d:%s", p.Epoch, pv)
184197
}
185-
pkgs[p.Name] = models.Package{
186-
Name: p.Name,
187-
Version: pv,
188-
Arch: p.Arch,
189-
}
190198

191-
v, ok := srcPkgs[p.SrcName]
192-
if !ok {
193-
sv := p.SrcVersion
194-
if p.SrcRelease != "" {
195-
sv = fmt.Sprintf("%s-%s", sv, p.SrcRelease)
199+
if existing, ok := pkgs[p.Name]; !ok || versionGreaterThan(trivyResult.Type, pv, existing.Version) {
200+
pkgs[p.Name] = models.Package{
201+
Name: p.Name,
202+
Version: pv,
203+
Arch: p.Arch,
196204
}
197-
if p.SrcEpoch > 0 {
198-
sv = fmt.Sprintf("%d:%s", p.SrcEpoch, sv)
199-
}
200-
v = models.SrcPackage{
201-
Name: p.SrcName,
202-
Version: sv,
205+
}
206+
207+
sv := p.SrcVersion
208+
if p.SrcRelease != "" {
209+
sv = fmt.Sprintf("%s-%s", sv, p.SrcRelease)
210+
}
211+
if p.SrcEpoch > 0 {
212+
sv = fmt.Sprintf("%d:%s", p.SrcEpoch, sv)
213+
}
214+
215+
existing := srcPkgs[p.SrcName]
216+
existing.AddBinaryName(p.Name)
217+
if existing.Name == "" || versionGreaterThan(trivyResult.Type, sv, existing.Version) {
218+
srcPkgs[p.SrcName] = models.SrcPackage{
219+
Name: p.SrcName,
220+
Version: sv,
221+
BinaryNames: existing.BinaryNames,
203222
}
223+
} else {
224+
srcPkgs[p.SrcName] = existing
204225
}
205-
v.AddBinaryName(p.Name)
206-
srcPkgs[p.SrcName] = v
207226
}
208227
case types.ClassLangPkg:
209228
for _, p := range trivyResult.Packages {
@@ -289,6 +308,23 @@ func isTrivySupportedOS(family ftypes.TargetType) bool {
289308
return ok
290309
}
291310

311+
// versionGreaterThan reports whether a is strictly greater than b.
312+
// For Debian/Ubuntu, dpkg version semantics are used.
313+
// For other OS types, lexicographic string comparison is used as a fallback.
314+
func versionGreaterThan(osType ftypes.TargetType, a, b string) bool {
315+
switch osType {
316+
case ftypes.Debian, ftypes.Ubuntu:
317+
va, erra := debver.NewVersion(a)
318+
vb, errb := debver.NewVersion(b)
319+
if erra != nil || errb != nil {
320+
return a > b
321+
}
322+
return va.GreaterThan(vb)
323+
default:
324+
return a > b
325+
}
326+
}
327+
292328
func getPURL(p ftypes.Package) string {
293329
if p.Identifier.PURL == nil {
294330
return ""

0 commit comments

Comments
 (0)