mirror of
https://github.com/anchore/syft.git
synced 2025-11-18 00:43:20 +01:00
add relationships for go binary packages (#2912)
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
parent
ac34808b9c
commit
f4a69e6d35
@ -4,16 +4,9 @@ Package golang provides a concrete Cataloger implementation relating to packages
|
||||
package golang
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/syft/internal"
|
||||
"github.com/anchore/syft/internal/mimetype"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/cpe"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||
)
|
||||
@ -30,102 +23,17 @@ func NewGoModuleFileCataloger(opts CatalogerConfig) pkg.Cataloger {
|
||||
c := goModCataloger{
|
||||
licenses: newGoLicenses(modFileCatalogerName, opts),
|
||||
}
|
||||
return &progressingCataloger{
|
||||
cataloger: generic.NewCataloger(modFileCatalogerName).
|
||||
WithParserByGlobs(c.parseGoModFile, "**/go.mod"),
|
||||
}
|
||||
|
||||
return generic.NewCataloger(modFileCatalogerName).
|
||||
WithParserByGlobs(c.parseGoModFile, "**/go.mod")
|
||||
}
|
||||
|
||||
// NewGoModuleBinaryCataloger returns a new cataloger object that searches within binaries built by the go compiler.
|
||||
func NewGoModuleBinaryCataloger(opts CatalogerConfig) pkg.Cataloger {
|
||||
return &progressingCataloger{
|
||||
cataloger: generic.NewCataloger(binaryCatalogerName).
|
||||
return generic.NewCataloger(binaryCatalogerName).
|
||||
WithParserByMimeTypes(
|
||||
newGoBinaryCataloger(opts).parseGoBinary,
|
||||
mimetype.ExecutableMIMETypeSet.List()...,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
type progressingCataloger struct {
|
||||
cataloger *generic.Cataloger
|
||||
}
|
||||
|
||||
func (p *progressingCataloger) Name() string {
|
||||
return p.cataloger.Name()
|
||||
}
|
||||
|
||||
func (p *progressingCataloger) Catalog(ctx context.Context, resolver file.Resolver) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
pkgs, relationships, err := p.cataloger.Catalog(ctx, resolver)
|
||||
goCompilerPkgs := []pkg.Package{}
|
||||
totalLocations := file.NewLocationSet()
|
||||
for _, goPkg := range pkgs {
|
||||
mValue, ok := goPkg.Metadata.(pkg.GolangBinaryBuildinfoEntry)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
// go binary packages should only contain a single location
|
||||
for _, location := range goPkg.Locations.ToSlice() {
|
||||
if !totalLocations.Contains(location) {
|
||||
stdLibPkg := newGoStdLib(mValue.GoCompiledVersion, goPkg.Locations)
|
||||
if stdLibPkg != nil {
|
||||
goCompilerPkgs = append(goCompilerPkgs, *stdLibPkg)
|
||||
totalLocations.Add(location)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pkgs = append(pkgs, goCompilerPkgs...)
|
||||
return pkgs, relationships, err
|
||||
}
|
||||
|
||||
func newGoStdLib(version string, location file.LocationSet) *pkg.Package {
|
||||
stdlibCpe, err := generateStdlibCpe(version)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
goCompilerPkg := &pkg.Package{
|
||||
Name: "stdlib",
|
||||
Version: version,
|
||||
PURL: packageURL("stdlib", strings.TrimPrefix(version, "go")),
|
||||
CPEs: []cpe.CPE{stdlibCpe},
|
||||
Locations: location,
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicense("BSD-3-Clause")),
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
Metadata: pkg.GolangBinaryBuildinfoEntry{
|
||||
GoCompiledVersion: version,
|
||||
},
|
||||
}
|
||||
goCompilerPkg.SetID()
|
||||
|
||||
return goCompilerPkg
|
||||
}
|
||||
|
||||
func generateStdlibCpe(version string) (stdlibCpe cpe.CPE, err error) {
|
||||
// GoCompiledVersion when pulled from a binary is prefixed by go
|
||||
version = strings.TrimPrefix(version, "go")
|
||||
|
||||
// we also need to trim starting from the first +<metadata> to
|
||||
// correctly extract potential rc candidate information for cpe generation
|
||||
// ex: 2.0.0-rc.1+build.123 -> 2.0.0-rc.1; if no + is found then + is returned
|
||||
after, _, found := strings.Cut("+", version)
|
||||
if found {
|
||||
version = after
|
||||
}
|
||||
|
||||
// extracting <version> and <candidate>
|
||||
// https://regex101.com/r/985GsI/1
|
||||
captureGroups := internal.MatchNamedCaptureGroups(versionCandidateGroups, version)
|
||||
vr, ok := captureGroups["version"]
|
||||
if !ok || vr == "" {
|
||||
return stdlibCpe, fmt.Errorf("could not match candidate version for: %s", version)
|
||||
}
|
||||
|
||||
cpeString := fmt.Sprintf("cpe:2.3:a:golang:go:%s:-:*:*:*:*:*:*", captureGroups["version"])
|
||||
if candidate, ok := captureGroups["candidate"]; ok && candidate != "" {
|
||||
cpeString = fmt.Sprintf("cpe:2.3:a:golang:go:%s:%s:*:*:*:*:*:*", vr, candidate)
|
||||
}
|
||||
|
||||
return cpe.New(cpeString, cpe.GeneratedSource)
|
||||
).
|
||||
WithProcessors(stdlibProcessor)
|
||||
}
|
||||
|
||||
@ -8,6 +8,92 @@ import (
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
|
||||
)
|
||||
|
||||
func Test_PackageCataloger_Binary(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
fixture string
|
||||
expectedPkgs []string
|
||||
expectedRels []string
|
||||
}{
|
||||
{
|
||||
name: "simple module with dependencies",
|
||||
fixture: "image-small",
|
||||
expectedPkgs: []string{
|
||||
"anchore.io/not/real @ (devel) (/run-me)",
|
||||
"github.com/andybalholm/brotli @ v1.0.1 (/run-me)",
|
||||
"github.com/dsnet/compress @ v0.0.2-0.20210315054119-f66993602bf5 (/run-me)",
|
||||
"github.com/golang/snappy @ v0.0.2 (/run-me)",
|
||||
"github.com/klauspost/compress @ v1.11.4 (/run-me)",
|
||||
"github.com/klauspost/pgzip @ v1.2.5 (/run-me)",
|
||||
"github.com/mholt/archiver/v3 @ v3.5.1 (/run-me)",
|
||||
"github.com/nwaples/rardecode @ v1.1.0 (/run-me)",
|
||||
"github.com/pierrec/lz4/v4 @ v4.1.2 (/run-me)",
|
||||
"github.com/ulikunitz/xz @ v0.5.9 (/run-me)",
|
||||
"github.com/xi2/xz @ v0.0.0-20171230120015-48954b6210f8 (/run-me)",
|
||||
"stdlib @ go1.22.3 (/run-me)",
|
||||
},
|
||||
expectedRels: []string{
|
||||
"github.com/andybalholm/brotli @ v1.0.1 (/run-me) [dependency-of] anchore.io/not/real @ (devel) (/run-me)",
|
||||
"github.com/dsnet/compress @ v0.0.2-0.20210315054119-f66993602bf5 (/run-me) [dependency-of] anchore.io/not/real @ (devel) (/run-me)",
|
||||
"github.com/golang/snappy @ v0.0.2 (/run-me) [dependency-of] anchore.io/not/real @ (devel) (/run-me)",
|
||||
"github.com/klauspost/compress @ v1.11.4 (/run-me) [dependency-of] anchore.io/not/real @ (devel) (/run-me)",
|
||||
"github.com/klauspost/pgzip @ v1.2.5 (/run-me) [dependency-of] anchore.io/not/real @ (devel) (/run-me)",
|
||||
"github.com/mholt/archiver/v3 @ v3.5.1 (/run-me) [dependency-of] anchore.io/not/real @ (devel) (/run-me)",
|
||||
"github.com/nwaples/rardecode @ v1.1.0 (/run-me) [dependency-of] anchore.io/not/real @ (devel) (/run-me)",
|
||||
"github.com/pierrec/lz4/v4 @ v4.1.2 (/run-me) [dependency-of] anchore.io/not/real @ (devel) (/run-me)",
|
||||
"github.com/ulikunitz/xz @ v0.5.9 (/run-me) [dependency-of] anchore.io/not/real @ (devel) (/run-me)",
|
||||
"github.com/xi2/xz @ v0.0.0-20171230120015-48954b6210f8 (/run-me) [dependency-of] anchore.io/not/real @ (devel) (/run-me)",
|
||||
"stdlib @ go1.22.3 (/run-me) [dependency-of] anchore.io/not/real @ (devel) (/run-me)",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "partially built binary",
|
||||
// the difference is the build flags used to build the binary... they will not reference the module directly
|
||||
// see the dockerfile for details
|
||||
fixture: "image-not-a-module",
|
||||
expectedPkgs: []string{
|
||||
"command-line-arguments @ (devel) (/run-me)", // this is the difference!
|
||||
"github.com/andybalholm/brotli @ v1.0.1 (/run-me)",
|
||||
"github.com/dsnet/compress @ v0.0.2-0.20210315054119-f66993602bf5 (/run-me)",
|
||||
"github.com/golang/snappy @ v0.0.2 (/run-me)",
|
||||
"github.com/klauspost/compress @ v1.11.4 (/run-me)",
|
||||
"github.com/klauspost/pgzip @ v1.2.5 (/run-me)",
|
||||
"github.com/mholt/archiver/v3 @ v3.5.1 (/run-me)",
|
||||
"github.com/nwaples/rardecode @ v1.1.0 (/run-me)",
|
||||
"github.com/pierrec/lz4/v4 @ v4.1.2 (/run-me)",
|
||||
"github.com/ulikunitz/xz @ v0.5.9 (/run-me)",
|
||||
"github.com/xi2/xz @ v0.0.0-20171230120015-48954b6210f8 (/run-me)",
|
||||
"stdlib @ go1.22.3 (/run-me)",
|
||||
},
|
||||
expectedRels: []string{
|
||||
"github.com/andybalholm/brotli @ v1.0.1 (/run-me) [dependency-of] command-line-arguments @ (devel) (/run-me)",
|
||||
"github.com/dsnet/compress @ v0.0.2-0.20210315054119-f66993602bf5 (/run-me) [dependency-of] command-line-arguments @ (devel) (/run-me)",
|
||||
"github.com/golang/snappy @ v0.0.2 (/run-me) [dependency-of] command-line-arguments @ (devel) (/run-me)",
|
||||
"github.com/klauspost/compress @ v1.11.4 (/run-me) [dependency-of] command-line-arguments @ (devel) (/run-me)",
|
||||
"github.com/klauspost/pgzip @ v1.2.5 (/run-me) [dependency-of] command-line-arguments @ (devel) (/run-me)",
|
||||
"github.com/mholt/archiver/v3 @ v3.5.1 (/run-me) [dependency-of] command-line-arguments @ (devel) (/run-me)",
|
||||
"github.com/nwaples/rardecode @ v1.1.0 (/run-me) [dependency-of] command-line-arguments @ (devel) (/run-me)",
|
||||
"github.com/pierrec/lz4/v4 @ v4.1.2 (/run-me) [dependency-of] command-line-arguments @ (devel) (/run-me)",
|
||||
"github.com/ulikunitz/xz @ v0.5.9 (/run-me) [dependency-of] command-line-arguments @ (devel) (/run-me)",
|
||||
"github.com/xi2/xz @ v0.0.0-20171230120015-48954b6210f8 (/run-me) [dependency-of] command-line-arguments @ (devel) (/run-me)",
|
||||
"stdlib @ go1.22.3 (/run-me) [dependency-of] command-line-arguments @ (devel) (/run-me)",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
pkgtest.NewCatalogTester().
|
||||
WithImageResolver(t, test.fixture).
|
||||
ExpectsPackageStrings(test.expectedPkgs).
|
||||
ExpectsRelationshipStrings(test.expectedRels).
|
||||
TestCataloger(t, NewGoModuleBinaryCataloger(DefaultCatalogerConfig()))
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Test_Mod_Cataloger_Globs(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
||||
@ -69,17 +69,38 @@ func (c *goBinaryCataloger) parseGoBinary(_ context.Context, resolver file.Resol
|
||||
mods := scanFile(unionReader, reader.RealPath)
|
||||
internal.CloseAndLogError(reader.ReadCloser, reader.RealPath)
|
||||
|
||||
var rels []artifact.Relationship
|
||||
for _, mod := range mods {
|
||||
pkgs = append(pkgs, c.buildGoPkgInfo(resolver, reader.Location, mod, mod.arch, unionReader)...)
|
||||
var depPkgs []pkg.Package
|
||||
mainPkg, depPkgs := c.buildGoPkgInfo(resolver, reader.Location, mod, mod.arch, unionReader)
|
||||
if mainPkg != nil {
|
||||
rels = createModuleRelationships(*mainPkg, depPkgs)
|
||||
pkgs = append(pkgs, *mainPkg)
|
||||
}
|
||||
pkgs = append(pkgs, depPkgs...)
|
||||
}
|
||||
|
||||
return pkgs, nil, nil
|
||||
return pkgs, rels, nil
|
||||
}
|
||||
|
||||
func (c *goBinaryCataloger) buildGoPkgInfo(resolver file.Resolver, location file.Location, mod *extendedBuildInfo, arch string, reader io.ReadSeekCloser) []pkg.Package {
|
||||
func createModuleRelationships(main pkg.Package, deps []pkg.Package) []artifact.Relationship {
|
||||
var relationships []artifact.Relationship
|
||||
|
||||
for _, dep := range deps {
|
||||
relationships = append(relationships, artifact.Relationship{
|
||||
From: dep,
|
||||
To: main,
|
||||
Type: artifact.DependencyOfRelationship,
|
||||
})
|
||||
}
|
||||
|
||||
return relationships
|
||||
}
|
||||
|
||||
func (c *goBinaryCataloger) buildGoPkgInfo(resolver file.Resolver, location file.Location, mod *extendedBuildInfo, arch string, reader io.ReadSeekCloser) (*pkg.Package, []pkg.Package) {
|
||||
var pkgs []pkg.Package
|
||||
if mod == nil {
|
||||
return pkgs
|
||||
return nil, pkgs
|
||||
}
|
||||
|
||||
var empty debug.Module
|
||||
@ -110,13 +131,12 @@ func (c *goBinaryCataloger) buildGoPkgInfo(resolver file.Resolver, location file
|
||||
}
|
||||
|
||||
if mod.Main == empty {
|
||||
return pkgs
|
||||
return nil, pkgs
|
||||
}
|
||||
|
||||
main := c.makeGoMainPackage(resolver, mod, arch, location, reader)
|
||||
pkgs = append(pkgs, main)
|
||||
|
||||
return pkgs
|
||||
return &main, pkgs
|
||||
}
|
||||
|
||||
func (c *goBinaryCataloger) makeGoMainPackage(resolver file.Resolver, mod *extendedBuildInfo, arch string, location file.Location, reader io.ReadSeekCloser) pkg.Package {
|
||||
|
||||
@ -964,7 +964,10 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
||||
c := newGoBinaryCataloger(DefaultCatalogerConfig())
|
||||
reader, err := unionreader.GetUnionReader(io.NopCloser(strings.NewReader(test.binaryContent)))
|
||||
require.NoError(t, err)
|
||||
pkgs := c.buildGoPkgInfo(fileresolver.Empty{}, location, test.mod, test.mod.arch, reader)
|
||||
mainPkg, pkgs := c.buildGoPkgInfo(fileresolver.Empty{}, location, test.mod, test.mod.arch, reader)
|
||||
if mainPkg != nil {
|
||||
pkgs = append(pkgs, *mainPkg)
|
||||
}
|
||||
require.Len(t, pkgs, len(test.expected))
|
||||
for i, p := range pkgs {
|
||||
pkgtest.AssertPackagesEqual(t, test.expected[i], p)
|
||||
|
||||
100
syft/pkg/cataloger/golang/stdlib_package.go
Normal file
100
syft/pkg/cataloger/golang/stdlib_package.go
Normal file
@ -0,0 +1,100 @@
|
||||
package golang
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/syft/internal"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/cpe"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
func stdlibProcessor(pkgs []pkg.Package, relationships []artifact.Relationship, err error) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
compilerPkgs, newRelationships := stdlibPackageAndRelationships(pkgs)
|
||||
return append(pkgs, compilerPkgs...), append(relationships, newRelationships...), err
|
||||
}
|
||||
|
||||
func stdlibPackageAndRelationships(pkgs []pkg.Package) ([]pkg.Package, []artifact.Relationship) {
|
||||
var goCompilerPkgs []pkg.Package
|
||||
var relationships []artifact.Relationship
|
||||
totalLocations := file.NewLocationSet()
|
||||
for _, goPkg := range pkgs {
|
||||
mValue, ok := goPkg.Metadata.(pkg.GolangBinaryBuildinfoEntry)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// go binary packages should only contain a single location
|
||||
for _, location := range goPkg.Locations.ToSlice() {
|
||||
if totalLocations.Contains(location) {
|
||||
continue
|
||||
}
|
||||
|
||||
stdLibPkg := newGoStdLib(mValue.GoCompiledVersion, goPkg.Locations)
|
||||
if stdLibPkg != nil {
|
||||
goCompilerPkgs = append(goCompilerPkgs, *stdLibPkg)
|
||||
totalLocations.Add(location)
|
||||
}
|
||||
|
||||
relationships = append(relationships, artifact.Relationship{
|
||||
From: *stdLibPkg,
|
||||
To: goPkg,
|
||||
Type: artifact.DependencyOfRelationship,
|
||||
})
|
||||
}
|
||||
}
|
||||
return goCompilerPkgs, relationships
|
||||
}
|
||||
|
||||
func newGoStdLib(version string, location file.LocationSet) *pkg.Package {
|
||||
stdlibCpe, err := generateStdlibCpe(version)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
goCompilerPkg := &pkg.Package{
|
||||
Name: "stdlib",
|
||||
Version: version,
|
||||
PURL: packageURL("stdlib", strings.TrimPrefix(version, "go")),
|
||||
CPEs: []cpe.CPE{stdlibCpe},
|
||||
Locations: location,
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicense("BSD-3-Clause")),
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
Metadata: pkg.GolangBinaryBuildinfoEntry{
|
||||
GoCompiledVersion: version,
|
||||
},
|
||||
}
|
||||
goCompilerPkg.SetID()
|
||||
|
||||
return goCompilerPkg
|
||||
}
|
||||
|
||||
func generateStdlibCpe(version string) (stdlibCpe cpe.CPE, err error) {
|
||||
// GoCompiledVersion when pulled from a binary is prefixed by go
|
||||
version = strings.TrimPrefix(version, "go")
|
||||
|
||||
// we also need to trim starting from the first +<metadata> to
|
||||
// correctly extract potential rc candidate information for cpe generation
|
||||
// ex: 2.0.0-rc.1+build.123 -> 2.0.0-rc.1; if no + is found then + is returned
|
||||
after, _, found := strings.Cut("+", version)
|
||||
if found {
|
||||
version = after
|
||||
}
|
||||
|
||||
// extracting <version> and <candidate>
|
||||
// https://regex101.com/r/985GsI/1
|
||||
captureGroups := internal.MatchNamedCaptureGroups(versionCandidateGroups, version)
|
||||
vr, ok := captureGroups["version"]
|
||||
if !ok || vr == "" {
|
||||
return stdlibCpe, fmt.Errorf("could not match candidate version for: %s", version)
|
||||
}
|
||||
|
||||
cpeString := fmt.Sprintf("cpe:2.3:a:golang:go:%s:-:*:*:*:*:*:*", captureGroups["version"])
|
||||
if candidate, ok := captureGroups["candidate"]; ok && candidate != "" {
|
||||
cpeString = fmt.Sprintf("cpe:2.3:a:golang:go:%s:%s:*:*:*:*:*:*", vr, candidate)
|
||||
}
|
||||
|
||||
return cpe.New(cpeString, cpe.GeneratedSource)
|
||||
}
|
||||
137
syft/pkg/cataloger/golang/stdlib_package_test.go
Normal file
137
syft/pkg/cataloger/golang/stdlib_package_test.go
Normal file
@ -0,0 +1,137 @@
|
||||
package golang
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/internal/cmptest"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/cpe"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
func Test_stdlibPackageAndRelationships(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
pkgs []pkg.Package
|
||||
wantPkgs int
|
||||
wantRels int
|
||||
}{
|
||||
{
|
||||
name: "no packages",
|
||||
},
|
||||
{
|
||||
name: "ignore non-go-binary packages",
|
||||
pkgs: []pkg.Package{
|
||||
{
|
||||
Name: "not-go",
|
||||
Version: "1.0.0",
|
||||
Metadata: pkg.GolangModuleEntry{},
|
||||
},
|
||||
},
|
||||
wantPkgs: 0,
|
||||
wantRels: 0,
|
||||
},
|
||||
{
|
||||
name: "with go-binary packages -- missing location",
|
||||
pkgs: []pkg.Package{
|
||||
{
|
||||
Name: "github.com/something/go",
|
||||
Version: "1.0.0",
|
||||
Metadata: pkg.GolangBinaryBuildinfoEntry{
|
||||
GoCompiledVersion: "go1.22.2",
|
||||
MainModule: "github.com/something/go",
|
||||
},
|
||||
},
|
||||
},
|
||||
wantPkgs: 0,
|
||||
wantRels: 0,
|
||||
},
|
||||
{
|
||||
name: "with go-binary packages",
|
||||
pkgs: []pkg.Package{
|
||||
{
|
||||
Name: "github.com/something/go",
|
||||
Version: "1.0.0",
|
||||
Locations: file.NewLocationSet(file.NewLocation("/bin/my-app")),
|
||||
Metadata: pkg.GolangBinaryBuildinfoEntry{
|
||||
GoCompiledVersion: "go1.22.2",
|
||||
MainModule: "github.com/something/go",
|
||||
},
|
||||
},
|
||||
},
|
||||
wantPkgs: 1,
|
||||
wantRels: 1,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
gotPkgs, gotRels := stdlibPackageAndRelationships(tt.pkgs)
|
||||
assert.Len(t, gotPkgs, tt.wantPkgs)
|
||||
assert.Len(t, gotRels, tt.wantRels)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_stdlibPackageAndRelationships_values(t *testing.T) {
|
||||
loc := file.NewLocation("/bin/my-app")
|
||||
locSet := file.NewLocationSet(loc)
|
||||
p := pkg.Package{
|
||||
Name: "github.com/something/go",
|
||||
Version: "1.0.0",
|
||||
Locations: locSet,
|
||||
Metadata: pkg.GolangBinaryBuildinfoEntry{
|
||||
GoCompiledVersion: "go1.22.2",
|
||||
MainModule: "github.com/something/go",
|
||||
},
|
||||
}
|
||||
p.SetID()
|
||||
|
||||
expectedPkg := pkg.Package{
|
||||
Name: "stdlib",
|
||||
Version: "go1.22.2",
|
||||
PURL: packageURL("stdlib", "1.22.2"),
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicense("BSD-3-Clause")),
|
||||
CPEs: []cpe.CPE{
|
||||
{
|
||||
Attributes: cpe.MustAttributes("cpe:2.3:a:golang:go:1.22.2:-:*:*:*:*:*:*"),
|
||||
Source: "syft-generated",
|
||||
},
|
||||
},
|
||||
Locations: locSet,
|
||||
Metadata: pkg.GolangBinaryBuildinfoEntry{
|
||||
GoCompiledVersion: "go1.22.2",
|
||||
},
|
||||
}
|
||||
|
||||
expectedPkg.SetID()
|
||||
|
||||
expectedRel := artifact.Relationship{
|
||||
From: expectedPkg,
|
||||
To: p,
|
||||
Type: artifact.DependencyOfRelationship,
|
||||
}
|
||||
|
||||
gotPkgs, gotRels := stdlibPackageAndRelationships([]pkg.Package{p})
|
||||
require.Len(t, gotPkgs, 1)
|
||||
|
||||
gotPkg := gotPkgs[0]
|
||||
if d := cmp.Diff(expectedPkg, gotPkg, cmptest.DefaultCommonOptions()...); d != "" {
|
||||
t.Errorf("unexpected package (-want +got): %s", d)
|
||||
}
|
||||
|
||||
require.Len(t, gotRels, 1)
|
||||
gotRel := gotRels[0]
|
||||
|
||||
if d := cmp.Diff(expectedRel, gotRel, cmptest.DefaultCommonOptions()...); d != "" {
|
||||
t.Errorf("unexpected relationship (-want +got): %s", d)
|
||||
}
|
||||
|
||||
}
|
||||
1
syft/pkg/cataloger/golang/test-fixtures/image-not-a-module/.gitignore
vendored
Normal file
1
syft/pkg/cataloger/golang/test-fixtures/image-not-a-module/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/run-me
|
||||
@ -0,0 +1,25 @@
|
||||
FROM --platform=linux/amd64 golang:1.22 AS builder
|
||||
|
||||
RUN mkdir /app
|
||||
WORKDIR /app
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
COPY main.go main.go
|
||||
|
||||
# building with "." vs "main.go" is a difference in the buildinfo section
|
||||
# specifically with main.go the buildinfo section will contain the following:
|
||||
#
|
||||
# path command-line-arguments
|
||||
#
|
||||
# instead of
|
||||
#
|
||||
# mod anchore.io/not/real
|
||||
#
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -o run-me main.go
|
||||
|
||||
|
||||
FROM scratch
|
||||
|
||||
COPY --from=builder /app/run-me /run-me
|
||||
ENTRYPOINT ["/run-me"]
|
||||
@ -0,0 +1,17 @@
|
||||
module anchore.io/not/real
|
||||
|
||||
go 1.22.1
|
||||
|
||||
require github.com/mholt/archiver/v3 v3.5.1
|
||||
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.0.1 // indirect
|
||||
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
|
||||
github.com/golang/snappy v0.0.2 // indirect
|
||||
github.com/klauspost/compress v1.11.4 // indirect
|
||||
github.com/klauspost/pgzip v1.2.5 // indirect
|
||||
github.com/nwaples/rardecode v1.1.0 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.2 // indirect
|
||||
github.com/ulikunitz/xz v0.5.9 // indirect
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
|
||||
)
|
||||
@ -0,0 +1,26 @@
|
||||
github.com/andybalholm/brotli v1.0.1 h1:KqhlKozYbRtJvsPrrEeXcO+N2l6NYT5A2QAFmSULpEc=
|
||||
github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
|
||||
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY=
|
||||
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s=
|
||||
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
|
||||
github.com/golang/snappy v0.0.2 h1:aeE13tS0IiQgFjYdoL8qN3K1N2bXXtI6Vi51/y7BpMw=
|
||||
github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.11.4 h1:kz40R/YWls3iqT9zX9AHN3WoVsrAWVyui5sxuLqiXqU=
|
||||
github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE=
|
||||
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo=
|
||||
github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4=
|
||||
github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7MQ=
|
||||
github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
|
||||
github.com/pierrec/lz4/v4 v4.1.2 h1:qvY3YFXRQE/XB8MlLzJH7mSzBs74eA2gg52YTk6jUPM=
|
||||
github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/ulikunitz/xz v0.5.9 h1:RsKRIA2MO8x56wkkcd3LbtcE/uMszhb6DpRf+3uwa3I=
|
||||
github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import "github.com/mholt/archiver/v3"
|
||||
|
||||
func main() {
|
||||
|
||||
z := archiver.Zip{
|
||||
MkdirAll: true,
|
||||
SelectiveCompression: true,
|
||||
ContinueOnError: false,
|
||||
OverwriteExisting: false,
|
||||
ImplicitTopLevelFolder: false,
|
||||
}
|
||||
|
||||
err := z.Archive([]string{"main.go"}, "test.zip")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
1
syft/pkg/cataloger/golang/test-fixtures/image-small/.gitignore
vendored
Normal file
1
syft/pkg/cataloger/golang/test-fixtures/image-small/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/run-me
|
||||
@ -0,0 +1,16 @@
|
||||
FROM --platform=linux/amd64 golang:1.22 AS builder
|
||||
|
||||
RUN mkdir /app
|
||||
WORKDIR /app
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
COPY main.go main.go
|
||||
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -o run-me .
|
||||
|
||||
|
||||
FROM scratch
|
||||
|
||||
COPY --from=builder /app/run-me /run-me
|
||||
ENTRYPOINT ["/run-me"]
|
||||
17
syft/pkg/cataloger/golang/test-fixtures/image-small/go.mod
Normal file
17
syft/pkg/cataloger/golang/test-fixtures/image-small/go.mod
Normal file
@ -0,0 +1,17 @@
|
||||
module anchore.io/not/real
|
||||
|
||||
go 1.22.1
|
||||
|
||||
require github.com/mholt/archiver/v3 v3.5.1
|
||||
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.0.1 // indirect
|
||||
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
|
||||
github.com/golang/snappy v0.0.2 // indirect
|
||||
github.com/klauspost/compress v1.11.4 // indirect
|
||||
github.com/klauspost/pgzip v1.2.5 // indirect
|
||||
github.com/nwaples/rardecode v1.1.0 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.2 // indirect
|
||||
github.com/ulikunitz/xz v0.5.9 // indirect
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
|
||||
)
|
||||
26
syft/pkg/cataloger/golang/test-fixtures/image-small/go.sum
Normal file
26
syft/pkg/cataloger/golang/test-fixtures/image-small/go.sum
Normal file
@ -0,0 +1,26 @@
|
||||
github.com/andybalholm/brotli v1.0.1 h1:KqhlKozYbRtJvsPrrEeXcO+N2l6NYT5A2QAFmSULpEc=
|
||||
github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
|
||||
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY=
|
||||
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s=
|
||||
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
|
||||
github.com/golang/snappy v0.0.2 h1:aeE13tS0IiQgFjYdoL8qN3K1N2bXXtI6Vi51/y7BpMw=
|
||||
github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.11.4 h1:kz40R/YWls3iqT9zX9AHN3WoVsrAWVyui5sxuLqiXqU=
|
||||
github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE=
|
||||
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo=
|
||||
github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4=
|
||||
github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7MQ=
|
||||
github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
|
||||
github.com/pierrec/lz4/v4 v4.1.2 h1:qvY3YFXRQE/XB8MlLzJH7mSzBs74eA2gg52YTk6jUPM=
|
||||
github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/ulikunitz/xz v0.5.9 h1:RsKRIA2MO8x56wkkcd3LbtcE/uMszhb6DpRf+3uwa3I=
|
||||
github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
19
syft/pkg/cataloger/golang/test-fixtures/image-small/main.go
Normal file
19
syft/pkg/cataloger/golang/test-fixtures/image-small/main.go
Normal file
@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import "github.com/mholt/archiver/v3"
|
||||
|
||||
func main() {
|
||||
|
||||
z := archiver.Zip{
|
||||
MkdirAll: true,
|
||||
SelectiveCompression: true,
|
||||
ContinueOnError: false,
|
||||
OverwriteExisting: false,
|
||||
ImplicitTopLevelFolder: false,
|
||||
}
|
||||
|
||||
err := z.Archive([]string{"main.go"}, "test.zip")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
@ -2,8 +2,10 @@ package pkgtest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@ -40,6 +42,7 @@ type CatalogTester struct {
|
||||
compareOptions []cmp.Option
|
||||
locationComparer cmptest.LocationComparer
|
||||
licenseComparer cmptest.LicenseComparer
|
||||
packageStringer func(pkg.Package) string
|
||||
customAssertions []func(t *testing.T, pkgs []pkg.Package, relationships []artifact.Relationship)
|
||||
}
|
||||
|
||||
@ -48,6 +51,7 @@ func NewCatalogTester() *CatalogTester {
|
||||
wantErr: require.NoError,
|
||||
locationComparer: cmptest.DefaultLocationComparer,
|
||||
licenseComparer: cmptest.DefaultLicenseComparer,
|
||||
packageStringer: stringPackage,
|
||||
ignoreUnfulfilledPathResponses: map[string][]string{
|
||||
"FilesByPath": {
|
||||
// most catalogers search for a linux release, which will not be fulfilled in testing
|
||||
@ -187,6 +191,23 @@ func (p *CatalogTester) Expects(pkgs []pkg.Package, relationships []artifact.Rel
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *CatalogTester) WithPackageStringer(fn func(pkg.Package) string) *CatalogTester {
|
||||
p.packageStringer = fn
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *CatalogTester) ExpectsPackageStrings(expected []string) *CatalogTester {
|
||||
return p.ExpectsAssertion(func(t *testing.T, pkgs []pkg.Package, _ []artifact.Relationship) {
|
||||
diffPackages(t, expected, pkgs, p.packageStringer)
|
||||
})
|
||||
}
|
||||
|
||||
func (p *CatalogTester) ExpectsRelationshipStrings(expected []string) *CatalogTester {
|
||||
return p.ExpectsAssertion(func(t *testing.T, pkgs []pkg.Package, relationships []artifact.Relationship) {
|
||||
diffRelationships(t, expected, relationships, pkgs, p.packageStringer)
|
||||
})
|
||||
}
|
||||
|
||||
func (p *CatalogTester) ExpectsResolverPathResponses(locations []string) *CatalogTester {
|
||||
p.expectedPathResponses = locations
|
||||
return p
|
||||
@ -347,3 +368,70 @@ func AssertPackagesEqual(t *testing.T, a, b pkg.Package) {
|
||||
t.Errorf("unexpected packages from parsing (-expected +actual)\n%s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
func diffPackages(t *testing.T, expected []string, actual []pkg.Package, pkgStringer func(pkg.Package) string) {
|
||||
t.Helper()
|
||||
sort.Strings(expected)
|
||||
if d := cmp.Diff(expected, stringPackages(actual, pkgStringer)); d != "" {
|
||||
t.Errorf("unexpected package strings (-want, +got): %s", d)
|
||||
}
|
||||
}
|
||||
|
||||
func diffRelationships(t *testing.T, expected []string, actual []artifact.Relationship, pkgs []pkg.Package, pkgStringer func(pkg.Package) string) {
|
||||
t.Helper()
|
||||
pkgsByID := make(map[artifact.ID]pkg.Package)
|
||||
for _, p := range pkgs {
|
||||
pkgsByID[p.ID()] = p
|
||||
}
|
||||
sort.Strings(expected)
|
||||
if d := cmp.Diff(expected, stringRelationships(actual, pkgsByID, pkgStringer)); d != "" {
|
||||
t.Errorf("unexpected relationship strings (-want, +got): %s", d)
|
||||
}
|
||||
}
|
||||
|
||||
func stringRelationships(relationships []artifact.Relationship, nameLookup map[artifact.ID]pkg.Package, pkgStringer func(pkg.Package) string) []string {
|
||||
var result []string
|
||||
for _, r := range relationships {
|
||||
var fromName, toName string
|
||||
{
|
||||
fromPkg, ok := nameLookup[r.From.ID()]
|
||||
if !ok {
|
||||
fromName = string(r.From.ID())
|
||||
} else {
|
||||
fromName = pkgStringer(fromPkg)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
toPkg, ok := nameLookup[r.To.ID()]
|
||||
if !ok {
|
||||
toName = string(r.To.ID())
|
||||
} else {
|
||||
toName = pkgStringer(toPkg)
|
||||
}
|
||||
}
|
||||
|
||||
result = append(result, fromName+" ["+string(r.Type)+"] "+toName)
|
||||
}
|
||||
sort.Strings(result)
|
||||
return result
|
||||
}
|
||||
|
||||
func stringPackages(pkgs []pkg.Package, pkgStringer func(pkg.Package) string) []string {
|
||||
var result []string
|
||||
for _, p := range pkgs {
|
||||
result = append(result, pkgStringer(p))
|
||||
}
|
||||
sort.Strings(result)
|
||||
return result
|
||||
}
|
||||
|
||||
func stringPackage(p pkg.Package) string {
|
||||
locs := p.Locations.ToSlice()
|
||||
var loc string
|
||||
if len(locs) > 0 {
|
||||
loc = p.Locations.ToSlice()[0].RealPath
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s @ %s (%s)", p.Name, p.Version, loc)
|
||||
}
|
||||
|
||||
@ -4,13 +4,10 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
|
||||
@ -482,52 +479,20 @@ func Test_PackageCataloger_SitePackageRelationships(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
pkgtest.NewCatalogTester().
|
||||
WithImageResolver(t, test.fixture).
|
||||
ExpectsAssertion(func(t *testing.T, pkgs []pkg.Package, relationships []artifact.Relationship) {
|
||||
diffRelationships(t, test.expectedRelationships, relationships, pkgs)
|
||||
}).
|
||||
WithPackageStringer(stringPackage).
|
||||
ExpectsRelationshipStrings(test.expectedRelationships).
|
||||
TestCataloger(t, NewInstalledPackageCataloger())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func diffRelationships(t *testing.T, expected []string, actual []artifact.Relationship, pkgs []pkg.Package) {
|
||||
pkgsByID := make(map[artifact.ID]pkg.Package)
|
||||
for _, p := range pkgs {
|
||||
pkgsByID[p.ID()] = p
|
||||
}
|
||||
sort.Strings(expected)
|
||||
if d := cmp.Diff(expected, stringRelationships(actual, pkgsByID)); d != "" {
|
||||
t.Errorf("unexpected relationships (-want, +got): %s", d)
|
||||
func stringPackage(p pkg.Package) string {
|
||||
locs := p.Locations.ToSlice()
|
||||
var loc string
|
||||
if len(locs) > 0 {
|
||||
// we want the location of the site-packages, not the metadata file
|
||||
loc = path.Dir(path.Dir(p.Locations.ToSlice()[0].RealPath))
|
||||
}
|
||||
}
|
||||
|
||||
func stringRelationships(relationships []artifact.Relationship, nameLookup map[artifact.ID]pkg.Package) []string {
|
||||
var result []string
|
||||
for _, r := range relationships {
|
||||
var fromName, toName string
|
||||
{
|
||||
fromPkg, ok := nameLookup[r.From.ID()]
|
||||
if !ok {
|
||||
fromName = string(r.From.ID())
|
||||
} else {
|
||||
loc := path.Dir(path.Dir(fromPkg.Locations.ToSlice()[0].RealPath))
|
||||
fromName = fmt.Sprintf("%s @ %s (%s)", fromPkg.Name, fromPkg.Version, loc)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
toPkg, ok := nameLookup[r.To.ID()]
|
||||
if !ok {
|
||||
toName = string(r.To.ID())
|
||||
} else {
|
||||
loc := path.Dir(path.Dir(toPkg.Locations.ToSlice()[0].RealPath))
|
||||
toName = fmt.Sprintf("%s @ %s (%s)", toPkg.Name, toPkg.Version, loc)
|
||||
}
|
||||
}
|
||||
|
||||
result = append(result, fromName+" ["+string(r.Type)+"] "+toName)
|
||||
}
|
||||
sort.Strings(result)
|
||||
return result
|
||||
|
||||
|
||||
return fmt.Sprintf("%s @ %s (%s)", p.Name, p.Version, loc)
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user