mirror of
https://github.com/anchore/syft
synced 2024-11-10 06:14:16 +00:00
fix pruning binary packages when considering ELF packages (#2862)
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
parent
4194a2cd34
commit
c200896a96
4 changed files with 96 additions and 59 deletions
|
@ -1,10 +1,12 @@
|
|||
package integration
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBinaryElfRelationships(t *testing.T) {
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"path"
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
|
||||
"github.com/anchore/syft/internal/sbomsync"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
|
@ -67,34 +66,24 @@ func generateRelationships(resolver file.Resolver, accessor sbomsync.Accessor, i
|
|||
func PackagesToRemove(resolver file.Resolver, accessor sbomsync.Accessor) []artifact.ID {
|
||||
pkgsToDelete := make([]artifact.ID, 0)
|
||||
accessor.ReadFromSBOM(func(s *sbom.SBOM) {
|
||||
// OTHER > ELF > Binary
|
||||
// OTHER package type > ELF package type > Binary package type
|
||||
pkgsToDelete = append(pkgsToDelete, getBinaryPackagesToDelete(resolver, s)...)
|
||||
pkgsToDelete = append(pkgsToDelete, compareElfBinaryPackages(resolver, s)...)
|
||||
pkgsToDelete = append(pkgsToDelete, compareElfBinaryPackages(s)...)
|
||||
})
|
||||
return pkgsToDelete
|
||||
}
|
||||
|
||||
func compareElfBinaryPackages(resolver file.Resolver, s *sbom.SBOM) []artifact.ID {
|
||||
func compareElfBinaryPackages(s *sbom.SBOM) []artifact.ID {
|
||||
pkgsToDelete := make([]artifact.ID, 0)
|
||||
for _, p := range s.Artifacts.Packages.Sorted(pkg.BinaryPkg) {
|
||||
for _, loc := range p.Locations.ToSlice() {
|
||||
if loc.Annotations[pkg.EvidenceAnnotationKey] != pkg.PrimaryEvidenceAnnotation {
|
||||
continue
|
||||
}
|
||||
locations, err := resolver.FilesByPath(loc.RealPath)
|
||||
if err != nil {
|
||||
log.WithFields("error", err).Trace("unable to find path for owned file")
|
||||
continue
|
||||
}
|
||||
for _, ownedL := range locations {
|
||||
for _, pathPkg := range s.Artifacts.Packages.PackagesByPath(ownedL.RealPath) {
|
||||
// we only care about comparing binary packages to each other (not other types)
|
||||
if pathPkg.Type != pkg.BinaryPkg {
|
||||
continue
|
||||
}
|
||||
if _, ok := pathPkg.Metadata.(pkg.ELFBinaryPackageNoteJSONPayload); !ok {
|
||||
pkgsToDelete = append(pkgsToDelete, pathPkg.ID())
|
||||
}
|
||||
for _, elfPkg := range allElfPackages(s) {
|
||||
for _, loc := range onlyPrimaryEvidenceLocations(elfPkg) {
|
||||
for _, otherPkg := range s.Artifacts.Packages.PackagesByPath(loc.RealPath) {
|
||||
// we only care about comparing binary packages to each other (not other types)
|
||||
if otherPkg.Type != pkg.BinaryPkg {
|
||||
continue
|
||||
}
|
||||
if !isElfPackage(otherPkg) {
|
||||
pkgsToDelete = append(pkgsToDelete, otherPkg.ID())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -102,6 +91,34 @@ func compareElfBinaryPackages(resolver file.Resolver, s *sbom.SBOM) []artifact.I
|
|||
return pkgsToDelete
|
||||
}
|
||||
|
||||
func onlyPrimaryEvidenceLocations(p pkg.Package) []file.Location {
|
||||
var locs []file.Location
|
||||
for _, loc := range p.Locations.ToSlice() {
|
||||
if loc.Annotations[pkg.EvidenceAnnotationKey] != pkg.PrimaryEvidenceAnnotation {
|
||||
continue
|
||||
}
|
||||
locs = append(locs, loc)
|
||||
}
|
||||
|
||||
return locs
|
||||
}
|
||||
|
||||
func allElfPackages(s *sbom.SBOM) []pkg.Package {
|
||||
var elfPkgs []pkg.Package
|
||||
for _, p := range s.Artifacts.Packages.Sorted(pkg.BinaryPkg) {
|
||||
if !isElfPackage(p) {
|
||||
continue
|
||||
}
|
||||
elfPkgs = append(elfPkgs, p)
|
||||
}
|
||||
return elfPkgs
|
||||
}
|
||||
|
||||
func isElfPackage(p pkg.Package) bool {
|
||||
_, ok := p.Metadata.(pkg.ELFBinaryPackageNoteJSONPayload)
|
||||
return ok
|
||||
}
|
||||
|
||||
func getBinaryPackagesToDelete(resolver file.Resolver, s *sbom.SBOM) []artifact.ID {
|
||||
pkgsToDelete := make([]artifact.ID, 0)
|
||||
for p := range s.Artifacts.Packages.Enumerate() {
|
||||
|
|
|
@ -34,13 +34,11 @@ func TestPackagesToRemove(t *testing.T) {
|
|||
glibCPackage.SetID()
|
||||
|
||||
glibCBinaryELFPackage := pkg.Package{
|
||||
Name: "glibc",
|
||||
Version: "",
|
||||
Name: "glibc",
|
||||
Locations: file.NewLocationSet(
|
||||
file.NewLocation(glibcCoordinate.RealPath).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
),
|
||||
Language: "",
|
||||
Type: pkg.BinaryPkg,
|
||||
Type: pkg.BinaryPkg,
|
||||
Metadata: pkg.ELFBinaryPackageNoteJSONPayload{
|
||||
Type: "testfixture",
|
||||
Vendor: "syft",
|
||||
|
@ -52,17 +50,25 @@ func TestPackagesToRemove(t *testing.T) {
|
|||
glibCBinaryELFPackage.SetID()
|
||||
|
||||
glibCBinaryClassifierPackage := pkg.Package{
|
||||
Name: "glibc",
|
||||
Version: "",
|
||||
Name: "glibc",
|
||||
Locations: file.NewLocationSet(
|
||||
file.NewLocation(glibcCoordinate.RealPath).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.SupportingEvidenceAnnotation),
|
||||
),
|
||||
Language: "",
|
||||
Type: pkg.BinaryPkg,
|
||||
Metadata: pkg.BinarySignature{},
|
||||
}
|
||||
glibCBinaryClassifierPackage.SetID()
|
||||
|
||||
libCBinaryClassifierPackage := pkg.Package{
|
||||
Name: "libc",
|
||||
Locations: file.NewLocationSet(
|
||||
file.NewLocation(glibcCoordinate.RealPath).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
),
|
||||
Type: pkg.BinaryPkg,
|
||||
Metadata: pkg.BinarySignature{},
|
||||
}
|
||||
libCBinaryClassifierPackage.SetID()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
resolver file.Resolver
|
||||
|
@ -72,21 +78,33 @@ func TestPackagesToRemove(t *testing.T) {
|
|||
{
|
||||
name: "remove packages that are overlapping rpm --> binary",
|
||||
resolver: file.NewMockResolverForPaths(glibcCoordinate.RealPath),
|
||||
accessor: newAccesor([]pkg.Package{glibCPackage, glibCBinaryELFPackage}, map[file.Coordinates]file.Executable{}, nil),
|
||||
accessor: newAccessor([]pkg.Package{glibCPackage, glibCBinaryELFPackage}, map[file.Coordinates]file.Executable{}, nil),
|
||||
want: []artifact.ID{glibCBinaryELFPackage.ID()},
|
||||
},
|
||||
{
|
||||
name: "remove no packages when there is a single binary package",
|
||||
resolver: file.NewMockResolverForPaths(glibcCoordinate.RealPath),
|
||||
accessor: newAccesor([]pkg.Package{glibCBinaryELFPackage}, map[file.Coordinates]file.Executable{}, nil),
|
||||
accessor: newAccessor([]pkg.Package{glibCBinaryELFPackage}, map[file.Coordinates]file.Executable{}, nil),
|
||||
want: []artifact.ID{},
|
||||
},
|
||||
{
|
||||
name: "remove packages when there is a single binary package and a classifier package",
|
||||
resolver: file.NewMockResolverForPaths(glibcCoordinate.RealPath),
|
||||
accessor: newAccesor([]pkg.Package{glibCBinaryELFPackage, glibCBinaryClassifierPackage}, map[file.Coordinates]file.Executable{}, nil),
|
||||
accessor: newAccessor([]pkg.Package{glibCBinaryELFPackage, glibCBinaryClassifierPackage}, map[file.Coordinates]file.Executable{}, nil),
|
||||
want: []artifact.ID{glibCBinaryClassifierPackage.ID()},
|
||||
},
|
||||
{
|
||||
name: "ensure we're considering ELF packages, not just binary packages (supporting evidence)",
|
||||
resolver: file.NewMockResolverForPaths(glibcCoordinate.RealPath),
|
||||
accessor: newAccessor([]pkg.Package{glibCBinaryClassifierPackage}, map[file.Coordinates]file.Executable{}, nil),
|
||||
want: []artifact.ID{},
|
||||
},
|
||||
{
|
||||
name: "ensure we're considering ELF packages, not just binary packages (primary evidence)",
|
||||
resolver: file.NewMockResolverForPaths(glibcCoordinate.RealPath),
|
||||
accessor: newAccessor([]pkg.Package{libCBinaryClassifierPackage}, map[file.Coordinates]file.Executable{}, nil),
|
||||
want: []artifact.ID{},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
@ -103,7 +121,7 @@ func TestNewDependencyRelationships(t *testing.T) {
|
|||
glibcCoordinate := file.NewCoordinates("/usr/lib64/libc.so.6", "")
|
||||
secondGlibcCoordinate := file.NewCoordinates("/usr/local/lib64/libc.so.6", "")
|
||||
nestedLibCoordinate := file.NewCoordinates("/usr/local/bin/elftests/elfbinwithnestedlib/bin/elfbinwithnestedlib", "")
|
||||
parrallelLibCoordinate := file.NewCoordinates("/usr/local/bin/elftests/elfbinwithsisterlib/bin/elfwithparallellibbin1", "")
|
||||
parallelLibCoordinate := file.NewCoordinates("/usr/local/bin/elftests/elfbinwithsisterlib/bin/elfwithparallellibbin1", "")
|
||||
|
||||
// rpm package that was discovered in linked section of the ELF binary package
|
||||
glibCPackage := pkg.Package{
|
||||
|
@ -151,7 +169,7 @@ func TestNewDependencyRelationships(t *testing.T) {
|
|||
FoundBy: "",
|
||||
Locations: file.NewLocationSet(
|
||||
file.NewLocation(nestedLibCoordinate.RealPath).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
file.NewLocation(parrallelLibCoordinate.RealPath).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.SupportingEvidenceAnnotation),
|
||||
file.NewLocation(parallelLibCoordinate.RealPath).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.SupportingEvidenceAnnotation),
|
||||
),
|
||||
Language: "",
|
||||
Type: pkg.BinaryPkg,
|
||||
|
@ -214,13 +232,13 @@ func TestNewDependencyRelationships(t *testing.T) {
|
|||
resolver: file.NewMockResolverForPaths(
|
||||
glibcCoordinate.RealPath,
|
||||
nestedLibCoordinate.RealPath,
|
||||
parrallelLibCoordinate.RealPath,
|
||||
parallelLibCoordinate.RealPath,
|
||||
),
|
||||
// path -> executable (above mock resolver needs to be able to resolve to paths in this map)
|
||||
coordinateIndex: map[file.Coordinates]file.Executable{
|
||||
glibcCoordinate: glibcExecutable,
|
||||
nestedLibCoordinate: syftTestFixtureExecutable,
|
||||
parrallelLibCoordinate: syftTestFixtureExecutable2,
|
||||
glibcCoordinate: glibcExecutable,
|
||||
nestedLibCoordinate: syftTestFixtureExecutable,
|
||||
parallelLibCoordinate: syftTestFixtureExecutable2,
|
||||
},
|
||||
packages: []pkg.Package{glibCPackage, syftTestFixturePackage},
|
||||
want: []artifact.Relationship{
|
||||
|
@ -237,13 +255,13 @@ func TestNewDependencyRelationships(t *testing.T) {
|
|||
glibcCoordinate.RealPath,
|
||||
secondGlibcCoordinate.RealPath,
|
||||
nestedLibCoordinate.RealPath,
|
||||
parrallelLibCoordinate.RealPath,
|
||||
parallelLibCoordinate.RealPath,
|
||||
),
|
||||
coordinateIndex: map[file.Coordinates]file.Executable{
|
||||
glibcCoordinate: glibcExecutable,
|
||||
secondGlibcCoordinate: glibcExecutable,
|
||||
nestedLibCoordinate: syftTestFixtureExecutable,
|
||||
parrallelLibCoordinate: syftTestFixtureExecutable2,
|
||||
glibcCoordinate: glibcExecutable,
|
||||
secondGlibcCoordinate: glibcExecutable,
|
||||
nestedLibCoordinate: syftTestFixtureExecutable,
|
||||
parallelLibCoordinate: syftTestFixtureExecutable2,
|
||||
},
|
||||
packages: []pkg.Package{glibCPackage, glibCustomPackage, syftTestFixturePackage},
|
||||
want: []artifact.Relationship{
|
||||
|
@ -264,12 +282,12 @@ func TestNewDependencyRelationships(t *testing.T) {
|
|||
resolver: file.NewMockResolverForPaths(
|
||||
glibcCoordinate.RealPath,
|
||||
nestedLibCoordinate.RealPath,
|
||||
parrallelLibCoordinate.RealPath,
|
||||
parallelLibCoordinate.RealPath,
|
||||
),
|
||||
coordinateIndex: map[file.Coordinates]file.Executable{
|
||||
glibcCoordinate: glibcExecutable,
|
||||
nestedLibCoordinate: syftTestFixtureExecutable,
|
||||
parrallelLibCoordinate: syftTestFixtureExecutable2,
|
||||
glibcCoordinate: glibcExecutable,
|
||||
nestedLibCoordinate: syftTestFixtureExecutable,
|
||||
parallelLibCoordinate: syftTestFixtureExecutable2,
|
||||
},
|
||||
packages: []pkg.Package{glibCPackage, glibCustomPackage, syftTestFixturePackage},
|
||||
prexistingRelationships: []artifact.Relationship{
|
||||
|
@ -285,9 +303,9 @@ func TestNewDependencyRelationships(t *testing.T) {
|
|||
name: "given a package that imports a library that is not tracked by the resolver, expect no relationships to be created",
|
||||
resolver: file.NewMockResolverForPaths(),
|
||||
coordinateIndex: map[file.Coordinates]file.Executable{
|
||||
glibcCoordinate: glibcExecutable,
|
||||
nestedLibCoordinate: syftTestFixtureExecutable,
|
||||
parrallelLibCoordinate: syftTestFixtureExecutable2,
|
||||
glibcCoordinate: glibcExecutable,
|
||||
nestedLibCoordinate: syftTestFixtureExecutable,
|
||||
parallelLibCoordinate: syftTestFixtureExecutable2,
|
||||
},
|
||||
packages: []pkg.Package{glibCPackage, syftTestFixturePackage},
|
||||
want: []artifact.Relationship{},
|
||||
|
@ -295,7 +313,7 @@ func TestNewDependencyRelationships(t *testing.T) {
|
|||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
accessor := newAccesor(tt.packages, tt.coordinateIndex, tt.prexistingRelationships)
|
||||
accessor := newAccessor(tt.packages, tt.coordinateIndex, tt.prexistingRelationships)
|
||||
// given a resolver that knows about the paths of the packages and executables,
|
||||
// and given an SBOM accessor that knows about the packages and executables,
|
||||
// we should be able to create a set of relationships between the packages and executables
|
||||
|
@ -316,7 +334,7 @@ func relationshipComparer(x, y []artifact.Relationship) string {
|
|||
))
|
||||
}
|
||||
|
||||
func newAccesor(pkgs []pkg.Package, coordinateIndex map[file.Coordinates]file.Executable, prexistingRelationships []artifact.Relationship) sbomsync.Accessor {
|
||||
func newAccessor(pkgs []pkg.Package, coordinateIndex map[file.Coordinates]file.Executable, preexistingRelationships []artifact.Relationship) sbomsync.Accessor {
|
||||
sb := sbom.SBOM{
|
||||
Artifacts: sbom.Artifacts{
|
||||
Packages: pkg.NewCollection(),
|
||||
|
@ -329,8 +347,8 @@ func newAccesor(pkgs []pkg.Package, coordinateIndex map[file.Coordinates]file.Ex
|
|||
accessor := builder.(sbomsync.Accessor)
|
||||
accessor.WriteToSBOM(func(s *sbom.SBOM) {
|
||||
s.Artifacts.Executables = coordinateIndex
|
||||
if prexistingRelationships != nil {
|
||||
s.Relationships = prexistingRelationships
|
||||
if preexistingRelationships != nil {
|
||||
s.Relationships = preexistingRelationships
|
||||
}
|
||||
})
|
||||
return accessor
|
||||
|
|
|
@ -27,7 +27,7 @@ func Test_newShareLibIndex(t *testing.T) {
|
|||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
accessor := newAccesor(tt.packages, tt.coordinateIndex, tt.prexistingRelationships)
|
||||
accessor := newAccessor(tt.packages, tt.coordinateIndex, tt.prexistingRelationships)
|
||||
sharedLibraryIndex := newShareLibIndex(tt.resolver, accessor)
|
||||
if sharedLibraryIndex == nil {
|
||||
t.Errorf("newShareLibIndex() = %v, want non-nil", sharedLibraryIndex)
|
||||
|
@ -93,7 +93,7 @@ func Test_sharedLibraryIndex_build(t *testing.T) {
|
|||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
accessor := newAccesor(tt.packages, tt.coordinateIndex, tt.prexistingRelationships)
|
||||
accessor := newAccessor(tt.packages, tt.coordinateIndex, tt.prexistingRelationships)
|
||||
sharedLibraryIndex := newShareLibIndex(tt.resolver, accessor)
|
||||
sharedLibraryIndex.build(tt.resolver, accessor)
|
||||
pkgs := sharedLibraryIndex.owningLibraryPackage(path.Base(glibcCoordinate.RealPath))
|
||||
|
|
Loading…
Reference in a new issue