Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion syft/pkg/cataloger/common/generic_cataloger.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,18 +56,46 @@ func (c *GenericCataloger) Catalog(resolver source.FileResolver) ([]pkg.Package,
continue
}

pkgsForRemoval := make(map[artifact.ID]struct{})
var cleanedRelationships []artifact.Relationship
for _, p := range discoveredPackages {
p.FoundBy = c.upstreamCataloger
p.Locations.Add(location)
p.SetID()
// doing it here so all packages have an ID,
// IDs are later used to remove relationships
if !pkg.IsValid(p) {
pkgsForRemoval[p.ID()] = struct{}{}
continue
}

packages = append(packages, *p)
}

relationships = append(relationships, discoveredRelationships...)
cleanedRelationships = removeRelationshipsWithArtifactIDs(pkgsForRemoval, discoveredRelationships)
relationships = append(relationships, cleanedRelationships...)
}
return packages, relationships, nil
}

func removeRelationshipsWithArtifactIDs(artifactsToExclude map[artifact.ID]struct{}, relationships []artifact.Relationship) []artifact.Relationship {
if len(artifactsToExclude) == 0 || len(relationships) == 0 {
// no removal to do
return relationships
}

var cleanedRelationships []artifact.Relationship
for _, r := range relationships {
_, removeTo := artifactsToExclude[r.To.ID()]
_, removaFrom := artifactsToExclude[r.From.ID()]
if !removeTo && !removaFrom {
cleanedRelationships = append(cleanedRelationships, r)
}
}

return cleanedRelationships
}

// SelectFiles takes a set of file trees and resolves and file references of interest for future cataloging
func (c *GenericCataloger) selectFiles(resolver source.FilePathResolver) map[source.Location]ParserFn {
var parserByLocation = make(map[source.Location]ParserFn)
Expand Down
137 changes: 121 additions & 16 deletions syft/pkg/cataloger/common/generic_cataloger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,50 +7,59 @@ import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/source"
)

func parser(_ string, reader io.Reader) ([]*pkg.Package, []artifact.Relationship, error) {
contents, err := ioutil.ReadAll(reader)
if err != nil {
panic(err)
}
return []*pkg.Package{
{
Name: string(contents),
},
}, nil, nil
}

func TestGenericCataloger(t *testing.T) {
allParsedPathes := make(map[string]bool)
parser := func(path string, reader io.Reader) ([]*pkg.Package, []artifact.Relationship, error) {
allParsedPathes[path] = true
contents, err := ioutil.ReadAll(reader)
require.NoError(t, err)

p := &pkg.Package{Name: string(contents)}
r := artifact.Relationship{From: p, To: p,
Type: artifact.ContainsRelationship,
}

return []*pkg.Package{p}, []artifact.Relationship{r}, nil
}

globParsers := map[string]ParserFn{
"**/a-path.txt": parser,
"**/empty.txt": parser,
}
pathParsers := map[string]ParserFn{
"test-fixtures/another-path.txt": parser,
"test-fixtures/last/path.txt": parser,
}
upstream := "some-other-cataloger"

expectedSelection := []string{"test-fixtures/last/path.txt", "test-fixtures/another-path.txt", "test-fixtures/a-path.txt"}
expectedSelection := []string{"test-fixtures/last/path.txt", "test-fixtures/another-path.txt", "test-fixtures/a-path.txt", "test-fixtures/empty.txt"}
resolver := source.NewMockResolverForPaths(expectedSelection...)
cataloger := NewGenericCataloger(pathParsers, globParsers, upstream)

actualPkgs, relationships, err := cataloger.Catalog(resolver)
assert.NoError(t, err)

expectedPkgs := make(map[string]pkg.Package)
for _, path := range expectedSelection {
require.True(t, allParsedPathes[path])
expectedPkgs[path] = pkg.Package{
FoundBy: upstream,
Name: fmt.Sprintf("%s file contents!", path),
}
}

actualPkgs, _, err := cataloger.Catalog(resolver)
assert.NoError(t, err)
assert.Len(t, actualPkgs, len(expectedPkgs))
assert.Len(t, allParsedPathes, len(expectedSelection))
// empty.txt won't become a package
assert.Len(t, actualPkgs, len(expectedPkgs)-1)
// right now, a relationship is created for each package, but if the relationship includes an invalid package it should be dropped.
assert.Len(t, relationships, len(actualPkgs))

for _, p := range actualPkgs {
ref := p.Locations.ToSlice()[0]
Expand All @@ -69,3 +78,99 @@ func TestGenericCataloger(t *testing.T) {
}
}
}

func Test_removeRelationshipsWithArtifactIDs(t *testing.T) {
one := &pkg.Package{Name: "one", Version: "1.0"}
two := &pkg.Package{Name: "two", Version: "1.0"}
three := &pkg.Package{Name: "three", Version: "1.0"}
four := &pkg.Package{Name: "four", Version: "bla"}
five := &pkg.Package{Name: "five", Version: "1.0"}

pkgs := make([]artifact.Identifiable, 0)
for _, p := range []*pkg.Package{one, two, three, four, five} {
// IDs are necessary for comparison
p.SetID()
pkgs = append(pkgs, p)
}

type args struct {
remove map[artifact.ID]struct{}
relationships []artifact.Relationship
}
tests := []struct {
name string
args args
want []artifact.Relationship
}{
{
name: "nothing-to-remove",
args: args{
relationships: []artifact.Relationship{
{From: one, To: two},
},
},
want: []artifact.Relationship{
{From: one, To: two},
},
},
{
name: "remove-all-relationships",
args: args{
remove: map[artifact.ID]struct{}{
one.ID(): {},
three.ID(): {},
},
relationships: []artifact.Relationship{
{From: one, To: two},
{From: two, To: three},
{From: three, To: four},
},
},
want: []artifact.Relationship(nil),
},
{
name: "remove-half-of-relationships",
args: args{
remove: map[artifact.ID]struct{}{
one.ID(): {},
},
relationships: []artifact.Relationship{
{From: one, To: two},
{From: one, To: three},
{From: two, To: three},
{From: three, To: four},
},
},
want: []artifact.Relationship{
{From: two, To: three},
{From: three, To: four},
},
},
{
name: "remove-repeated-relationships",
args: args{
remove: map[artifact.ID]struct{}{
one.ID(): {},
two.ID(): {},
},
relationships: []artifact.Relationship{
{From: one, To: two},
{From: one, To: three},
{From: two, To: three},
{From: two, To: three},
{From: three, To: four},
{From: four, To: five},
},
},
want: []artifact.Relationship{
{From: three, To: four},
{From: four, To: five},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equalf(t, tt.want, removeRelationshipsWithArtifactIDs(tt.args.remove, tt.args.relationships), "removeRelationshipsWithArtifactIDs(%v, %v)", tt.args.remove, tt.args.relationships)
})
}
}
Empty file.
9 changes: 5 additions & 4 deletions syft/pkg/cataloger/deb/parse_dpkg_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ var (
sourceRegexp = regexp.MustCompile(`(?P<name>\S+)( \((?P<version>.*)\))?`)
)

func newDpkgPackage(d pkg.DpkgMetadata) pkg.Package {
return pkg.Package{
func newDpkgPackage(d pkg.DpkgMetadata) *pkg.Package {
return &pkg.Package{
Name: d.Package,
Version: d.Version,
Type: pkg.DebPkg,
Expand All @@ -46,8 +46,9 @@ func parseDpkgStatus(reader io.Reader) ([]pkg.Package, error) {
}
}

if entry.Package != "" {
packages = append(packages, newDpkgPackage(entry))
p := newDpkgPackage(entry)
if pkg.IsValid(p) {
packages = append(packages, *p)
}
}

Expand Down
6 changes: 5 additions & 1 deletion syft/pkg/cataloger/deb/parse_dpkg_status_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@ func TestMultiplePackages(t *testing.T) {
{
name: "Test Multiple Package",
expected: []pkg.DpkgMetadata{
{
Package: "no-version",
Files: []pkg.DpkgFileRecord{},
},
{
Package: "tzdata",
Version: "2020a-0+deb10u1",
Expand Down Expand Up @@ -209,7 +213,7 @@ func TestMultiplePackages(t *testing.T) {
t.Fatal("Unable to read file contents: ", err)
}

if len(pkgs) != 2 {
if len(pkgs) != 3 {
t.Fatalf("unexpected number of entries: %d", len(pkgs))
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
Package:

Package: libpam-runtime
Status: install ok installed
Priority: required
Expand Down
1 change: 1 addition & 0 deletions syft/pkg/cataloger/deb/test-fixtures/status/empty
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Package:
4 changes: 4 additions & 0 deletions syft/pkg/cataloger/deb/test-fixtures/status/multiple
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
Package:

Package: no-version

Package: tzdata
Status: install ok installed
Priority: required
Expand Down
6 changes: 4 additions & 2 deletions syft/pkg/cataloger/golang/parse_go_bin.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,10 @@ func buildGoPkgInfo(location source.Location, mod *debug.BuildInfo, arch string)
if dep == nil {
continue
}

pkgs = append(pkgs, newGoBinaryPackage(dep, mod.GoVersion, arch, location, nil))
p := newGoBinaryPackage(dep, mod.GoVersion, arch, location, nil)
if pkg.IsValid(&p) {
pkgs = append(pkgs, p)
}
}

// NOTE(jonasagx): this use happened originally while creating unit tests. It might never
Expand Down
32 changes: 32 additions & 0 deletions syft/pkg/cataloger/golang/parse_go_bin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,38 @@ func TestBuildGoPkgInfo(t *testing.T) {
mod: nil,
expected: []pkg.Package(nil),
},
{
name: "package without name",
mod: &debug.BuildInfo{
Deps: []*debug.Module{
{
Path: "github.com/adrg/xdg",
},
{
Path: "",
Version: "v0.2.1",
},
},
},
expected: []pkg.Package{
{
Name: "github.com/adrg/xdg",
FoundBy: catalogerName,
Language: pkg.Go,
Type: pkg.GoModulePkg,
Locations: source.NewLocationSet(
source.Location{
Coordinates: source.Coordinates{
RealPath: "/a-path",
FileSystemID: "layer-id",
},
},
),
MetadataType: pkg.GolangBinMetadataType,
Metadata: pkg.GolangBinMetadata{},
},
},
},
{
name: "buildGoPkgInfo parses a blank mod and returns no packages",
mod: &debug.BuildInfo{},
Expand Down
2 changes: 1 addition & 1 deletion syft/pkg/cataloger/python/package_cataloger.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func (c *PackageCataloger) Catalog(resolver source.FileResolver) ([]pkg.Package,
if err != nil {
return nil, nil, fmt.Errorf("unable to catalog python package=%+v: %w", location.RealPath, err)
}
if p != nil {
if pkg.IsValid(p) {
pkgs = append(pkgs, *p)
}
}
Expand Down
18 changes: 18 additions & 0 deletions syft/pkg/cataloger/python/package_cataloger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,21 @@ func TestPythonPackageWheelCataloger(t *testing.T) {
fixtures []string
expectedPackage pkg.Package
}{
{
name: "egg-file-no-version",
fixtures: []string{"test-fixtures/no-version-py3.8.egg-info"},
expectedPackage: pkg.Package{
Name: "no-version",
Type: pkg.PythonPkg,
Language: pkg.Python,
FoundBy: "python-package-cataloger",
MetadataType: pkg.PythonPackageMetadataType,
Metadata: pkg.PythonPackageMetadata{
Name: "no-version",
SitePackagesRootPath: "test-fixtures",
},
},
},
{
name: "egg-info directory",
fixtures: []string{
Expand Down Expand Up @@ -169,6 +184,9 @@ func TestIgnorePackage(t *testing.T) {
{
MetadataFixture: "test-fixtures/Python-2.7.egg-info",
},
{
MetadataFixture: "test-fixtures/empty-1.0.0-py3.8.egg-info",
},
}

for _, test := range tests {
Expand Down
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Name: no-version
8 changes: 7 additions & 1 deletion syft/pkg/cataloger/rpmdb/parse_rpmdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,19 @@ func parseRpmDB(resolver source.FilePathResolver, dbLocation source.Location, re
return nil, err
}

allPkgs := make([]pkg.Package, 0)
var allPkgs []pkg.Package

for _, entry := range pkgList {
p, err := newPkg(resolver, dbLocation, entry)
if err != nil {
return nil, err
}

if !pkg.IsValid(p) {
continue
}

p.SetID()
allPkgs = append(allPkgs, *p)
}

Expand Down
Loading