mirror of
https://github.com/anchore/syft.git
synced 2026-10-30 14:02:26 +01:00
Add python wheel egg relationships (#2903)
* add python package relationships Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * nil for empty relationships collections Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * new json schema for optional python requiremenets Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * update format snapshots for python packages Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * decompose python parsers more + add tests around plural fields Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * update JSON schema with python dep refs Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> --------- Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"path"
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/internal/relationship"
|
||||
"github.com/anchore/syft/internal/sbomsync"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
@@ -20,22 +21,22 @@ func NewDependencyRelationships(resolver file.Resolver, accessor sbomsync.Access
|
||||
|
||||
// 3. craft package-to-package relationships for each binary that represent shared library dependencies
|
||||
//note: we only care about package-to-package relationships
|
||||
var relIndex *relationshipIndex
|
||||
var relIndex *relationship.Index
|
||||
accessor.ReadFromSBOM(func(s *sbom.SBOM) {
|
||||
relIndex = newRelationshipIndex(s.Relationships...)
|
||||
relIndex = relationship.NewIndex(s.Relationships...)
|
||||
})
|
||||
|
||||
return generateRelationships(resolver, accessor, index, relIndex)
|
||||
}
|
||||
|
||||
func generateRelationships(resolver file.Resolver, accessor sbomsync.Accessor, index *sharedLibraryIndex, relIndex *relationshipIndex) []artifact.Relationship {
|
||||
func generateRelationships(resolver file.Resolver, accessor sbomsync.Accessor, index *sharedLibraryIndex, relIndex *relationship.Index) []artifact.Relationship {
|
||||
// read all existing dependencyOf relationships
|
||||
accessor.ReadFromSBOM(func(s *sbom.SBOM) {
|
||||
for _, r := range s.Relationships {
|
||||
if r.Type != artifact.DependencyOfRelationship {
|
||||
continue
|
||||
}
|
||||
relIndex.track(r)
|
||||
relIndex.Track(r)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -58,7 +59,7 @@ func generateRelationships(resolver file.Resolver, accessor sbomsync.Accessor, i
|
||||
}
|
||||
})
|
||||
|
||||
return relIndex.newRelationships()
|
||||
return relIndex.NewRelationships()
|
||||
}
|
||||
|
||||
// PackagesToRemove returns a list of binary packages (resolved by the ELF cataloger) that should be removed from the SBOM
|
||||
@@ -146,7 +147,7 @@ func getBinaryPackagesToDelete(resolver file.Resolver, s *sbom.SBOM) []artifact.
|
||||
return pkgsToDelete
|
||||
}
|
||||
|
||||
func populateRelationships(exec file.Executable, parentPkg pkg.Package, resolver file.Resolver, relIndex *relationshipIndex, index *sharedLibraryIndex) {
|
||||
func populateRelationships(exec file.Executable, parentPkg pkg.Package, resolver file.Resolver, relIndex *relationship.Index, index *sharedLibraryIndex) {
|
||||
for _, libReference := range exec.ImportedLibraries {
|
||||
// for each library reference, check s.Artifacts.Packages.Sorted(pkg.BinaryPkg) for a binary package that represents that library
|
||||
// if found, create a relationship between the parent package and the library package
|
||||
@@ -166,7 +167,7 @@ func populateRelationships(exec file.Executable, parentPkg pkg.Package, resolver
|
||||
realBaseName := path.Base(loc.RealPath)
|
||||
pkgCollection := index.owningLibraryPackage(realBaseName)
|
||||
if pkgCollection.PackageCount() < 1 {
|
||||
relIndex.add(
|
||||
relIndex.Add(
|
||||
artifact.Relationship{
|
||||
From: loc.Coordinates,
|
||||
To: parentPkg,
|
||||
@@ -175,7 +176,7 @@ func populateRelationships(exec file.Executable, parentPkg pkg.Package, resolver
|
||||
)
|
||||
}
|
||||
for _, p := range pkgCollection.Sorted() {
|
||||
relIndex.add(
|
||||
relIndex.Add(
|
||||
artifact.Relationship{
|
||||
From: p,
|
||||
To: parentPkg,
|
||||
|
||||
@@ -225,7 +225,6 @@ func TestNewDependencyRelationships(t *testing.T) {
|
||||
resolver: nil,
|
||||
coordinateIndex: map[file.Coordinates]file.Executable{},
|
||||
packages: []pkg.Package{},
|
||||
want: make([]artifact.Relationship, 0),
|
||||
},
|
||||
{
|
||||
name: "given a package that imports glibc, expect a relationship between the two packages when the package is an executable",
|
||||
@@ -297,7 +296,6 @@ func TestNewDependencyRelationships(t *testing.T) {
|
||||
Type: artifact.DependencyOfRelationship,
|
||||
},
|
||||
},
|
||||
want: []artifact.Relationship{},
|
||||
},
|
||||
{
|
||||
name: "given a package that imports a library that is not tracked by the resolver, expect no relationships to be created",
|
||||
@@ -308,7 +306,6 @@ func TestNewDependencyRelationships(t *testing.T) {
|
||||
parallelLibCoordinate: syftTestFixtureExecutable2,
|
||||
},
|
||||
packages: []pkg.Package{glibCPackage, syftTestFixturePackage},
|
||||
want: []artifact.Relationship{},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
package binary
|
||||
|
||||
import (
|
||||
"github.com/scylladb/go-set/strset"
|
||||
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
)
|
||||
|
||||
type relationshipIndex struct {
|
||||
typesByFromTo map[artifact.ID]map[artifact.ID]*strset.Set
|
||||
additional []artifact.Relationship
|
||||
}
|
||||
|
||||
func newRelationshipIndex(existing ...artifact.Relationship) *relationshipIndex {
|
||||
r := &relationshipIndex{
|
||||
typesByFromTo: make(map[artifact.ID]map[artifact.ID]*strset.Set),
|
||||
additional: make([]artifact.Relationship, 0),
|
||||
}
|
||||
for _, rel := range existing {
|
||||
r.track(rel)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// track this relationship as "exists" in the index (this is used to prevent duplicate relationships from being added).
|
||||
// returns true if the relationship is new to the index, false otherwise.
|
||||
func (i *relationshipIndex) track(r artifact.Relationship) bool {
|
||||
fromID := r.From.ID()
|
||||
if _, ok := i.typesByFromTo[fromID]; !ok {
|
||||
i.typesByFromTo[fromID] = make(map[artifact.ID]*strset.Set)
|
||||
}
|
||||
|
||||
toID := r.To.ID()
|
||||
if _, ok := i.typesByFromTo[fromID][toID]; !ok {
|
||||
i.typesByFromTo[fromID][toID] = strset.New()
|
||||
}
|
||||
|
||||
var exists bool
|
||||
if i.typesByFromTo[fromID][toID].Has(string(r.Type)) {
|
||||
exists = true
|
||||
}
|
||||
|
||||
i.typesByFromTo[fromID][toID].Add(string(r.Type))
|
||||
return !exists
|
||||
}
|
||||
|
||||
// add a new relationship to the index, returning true if the relationship is new to the index, false otherwise (thus is a duplicate).
|
||||
// nolint:unparam
|
||||
func (i *relationshipIndex) add(r artifact.Relationship) bool {
|
||||
if i.track(r) {
|
||||
i.additional = append(i.additional, r)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (i *relationshipIndex) newRelationships() []artifact.Relationship {
|
||||
return i.additional
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
package binary
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/scylladb/go-set/strset"
|
||||
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
)
|
||||
|
||||
func Test_newRelationshipIndex(t *testing.T) {
|
||||
from := fakeIdentifiable{id: "from"}
|
||||
to := fakeIdentifiable{id: "to"}
|
||||
tests := []struct {
|
||||
name string
|
||||
given []artifact.Relationship
|
||||
want *relationshipIndex
|
||||
}{
|
||||
{
|
||||
name: "newRelationshipIndex returns an empty index with no existing relationships",
|
||||
want: &relationshipIndex{
|
||||
typesByFromTo: make(map[artifact.ID]map[artifact.ID]*strset.Set),
|
||||
additional: make([]artifact.Relationship, 0),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "newRelationshipIndex returns an index which tracks existing relationships",
|
||||
given: []artifact.Relationship{
|
||||
{
|
||||
From: from,
|
||||
To: to,
|
||||
Type: artifact.EvidentByRelationship,
|
||||
},
|
||||
},
|
||||
want: &relationshipIndex{
|
||||
typesByFromTo: map[artifact.ID]map[artifact.ID]*strset.Set{
|
||||
"from": {
|
||||
"to": strset.New("evident-by"),
|
||||
},
|
||||
},
|
||||
additional: make([]artifact.Relationship, 0),
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := newRelationshipIndex(tt.given...); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("newRelationshipIndex() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_relationshipIndex_track(t *testing.T) {
|
||||
from := fakeIdentifiable{id: "from"}
|
||||
to := fakeIdentifiable{id: "to"}
|
||||
relationship := artifact.Relationship{From: from, To: to, Type: artifact.EvidentByRelationship}
|
||||
tests := []struct {
|
||||
name string
|
||||
existing []artifact.Relationship
|
||||
given artifact.Relationship
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "track returns true for a new relationship",
|
||||
existing: []artifact.Relationship{},
|
||||
given: relationship,
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "track returns false for an existing relationship",
|
||||
existing: []artifact.Relationship{relationship},
|
||||
given: relationship,
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
i := newRelationshipIndex(tt.existing...)
|
||||
if got := i.track(tt.given); got != tt.want {
|
||||
t.Errorf("track() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_relationshipIndex_add(t *testing.T) {
|
||||
from := fakeIdentifiable{id: "from"}
|
||||
to := fakeIdentifiable{id: "to"}
|
||||
relationship := artifact.Relationship{From: from, To: to, Type: artifact.EvidentByRelationship}
|
||||
tests := []struct {
|
||||
name string
|
||||
existing []artifact.Relationship
|
||||
given artifact.Relationship
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "add returns true for a new relationship",
|
||||
existing: []artifact.Relationship{},
|
||||
given: relationship,
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "add returns false for an existing relationship",
|
||||
existing: []artifact.Relationship{relationship},
|
||||
given: relationship,
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
i := newRelationshipIndex(tt.existing...)
|
||||
if got := i.add(tt.given); got != tt.want {
|
||||
t.Errorf("add() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type fakeIdentifiable struct {
|
||||
id string
|
||||
}
|
||||
|
||||
func (f fakeIdentifiable) ID() artifact.ID {
|
||||
return artifact.ID(f.id)
|
||||
}
|
||||
Reference in New Issue
Block a user