diff --git a/pkg/sbom/io/decode.go b/pkg/sbom/io/decode.go index 917684962d20..4f5ac4cd19bb 100644 --- a/pkg/sbom/io/decode.go +++ b/pkg/sbom/io/decode.go @@ -10,6 +10,7 @@ import ( debver "github.com/knqyf263/go-deb-version" rpmver "github.com/knqyf263/go-rpm-version" "github.com/package-url/packageurl-go" + "github.com/samber/lo" "golang.org/x/exp/maps" "golang.org/x/xerrors" @@ -375,8 +376,12 @@ func (m *Decoder) addOrphanPkgs(sbom *types.SBOM) error { // TODO: mismatch between the OS and the packages should be rejected. // e.g. OS: debian, Packages: rpm - sort.Sort(pkgs) - sbom.Packages = append(sbom.Packages, ftypes.PackageInfo{Packages: pkgs}) + + // `applier` package merges `PackageInfo` elements along the filepath, + // but sbom files may not contain filepath for OS packages. + // In these cases we will overwrite `PackageInfo` and miss some `Packages`. + // This is why we need to merge all found `Packages` into one `PackageInfo`. + sbom.Packages = appendOrphanPkgsToPackageInfo(sbom.Packages, pkgs) break // Just take the first element } @@ -391,3 +396,25 @@ func (m *Decoder) addOrphanPkgs(sbom *types.SBOM) error { } return nil } + +// appendOrphanPkgsToPackageInfo merges `orphanPkgs` into `packages[0]` (the comparison is done by PURL). +// Merged `Packages` are sorted. +func appendOrphanPkgsToPackageInfo(packages []ftypes.PackageInfo, orphanPkgs ftypes.Packages) []ftypes.PackageInfo { + // Length of `packages` can be only 0 or 1 + // packages[].FilePath is always empty + // cf. https://github.com/aquasecurity/trivy/blob/09e50ce6a82073ba62f1732d5aa0cd2701578693/pkg/sbom/io/decode.go#L331 + var pkgs ftypes.Packages + if len(packages) == 1 { // + pkgs = packages[0].Packages + } + pkgs = append(pkgs, orphanPkgs...) + pkgs = lo.UniqBy(pkgs, func(pkg ftypes.Package) string { + return pkg.Identifier.PURL.String() + }) + sort.Sort(pkgs) + return []ftypes.PackageInfo{ + { + Packages: pkgs, + }, + } +}