diff --git a/syft/pkg/cataloger/golang/cataloger_test.go b/syft/pkg/cataloger/golang/cataloger_test.go index e268e41db..e63e856bd 100644 --- a/syft/pkg/cataloger/golang/cataloger_test.go +++ b/syft/pkg/cataloger/golang/cataloger_test.go @@ -53,7 +53,7 @@ func Test_PackageCataloger_Binary(t *testing.T) { // see the dockerfile for details fixture: "image-not-a-module", expectedPkgs: []string{ - "command-line-arguments @ (devel) (/run-me)", // this is the difference! + "command-line-arguments @ (/run-me)", // this is the difference! "github.com/andybalholm/brotli @ v1.1.1 (/run-me)", "github.com/dsnet/compress @ v0.0.2-0.20210315054119-f66993602bf5 (/run-me)", "github.com/golang/snappy @ v0.0.4 (/run-me)", @@ -67,17 +67,17 @@ func Test_PackageCataloger_Binary(t *testing.T) { "stdlib @ go1.23.2 (/run-me)", }, expectedRels: []string{ - "github.com/anchore/archiver/v3 @ v3.5.3-0.20241210171143-5b1d8d1c7c51 (/run-me) [dependency-of] command-line-arguments @ (devel) (/run-me)", - "github.com/andybalholm/brotli @ v1.1.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.4 (/run-me) [dependency-of] command-line-arguments @ (devel) (/run-me)", - "github.com/klauspost/compress @ v1.17.11 (/run-me) [dependency-of] command-line-arguments @ (devel) (/run-me)", - "github.com/klauspost/pgzip @ v1.2.6 (/run-me) [dependency-of] command-line-arguments @ (devel) (/run-me)", - "github.com/nwaples/rardecode @ v1.1.3 (/run-me) [dependency-of] command-line-arguments @ (devel) (/run-me)", - "github.com/pierrec/lz4/v4 @ v4.1.21 (/run-me) [dependency-of] command-line-arguments @ (devel) (/run-me)", - "github.com/ulikunitz/xz @ v0.5.12 (/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.23.2 (/run-me) [dependency-of] command-line-arguments @ (devel) (/run-me)", + "github.com/anchore/archiver/v3 @ v3.5.3-0.20241210171143-5b1d8d1c7c51 (/run-me) [dependency-of] command-line-arguments @ (/run-me)", + "github.com/andybalholm/brotli @ v1.1.1 (/run-me) [dependency-of] command-line-arguments @ (/run-me)", + "github.com/dsnet/compress @ v0.0.2-0.20210315054119-f66993602bf5 (/run-me) [dependency-of] command-line-arguments @ (/run-me)", + "github.com/golang/snappy @ v0.0.4 (/run-me) [dependency-of] command-line-arguments @ (/run-me)", + "github.com/klauspost/compress @ v1.17.11 (/run-me) [dependency-of] command-line-arguments @ (/run-me)", + "github.com/klauspost/pgzip @ v1.2.6 (/run-me) [dependency-of] command-line-arguments @ (/run-me)", + "github.com/nwaples/rardecode @ v1.1.3 (/run-me) [dependency-of] command-line-arguments @ (/run-me)", + "github.com/pierrec/lz4/v4 @ v4.1.21 (/run-me) [dependency-of] command-line-arguments @ (/run-me)", + "github.com/ulikunitz/xz @ v0.5.12 (/run-me) [dependency-of] command-line-arguments @ (/run-me)", + "github.com/xi2/xz @ v0.0.0-20171230120015-48954b6210f8 (/run-me) [dependency-of] command-line-arguments @ (/run-me)", + "stdlib @ go1.23.2 (/run-me) [dependency-of] command-line-arguments @ (/run-me)", }, }, } diff --git a/syft/pkg/cataloger/golang/package.go b/syft/pkg/cataloger/golang/package.go index c3071420c..bc3ac8e27 100644 --- a/syft/pkg/cataloger/golang/package.go +++ b/syft/pkg/cataloger/golang/package.go @@ -9,28 +9,28 @@ import ( "github.com/anchore/syft/syft/pkg" ) -func (c *goBinaryCataloger) newGoBinaryPackage(dep *debug.Module, mainModule, goVersion, architecture string, buildSettings pkg.KeyValues, cryptoSettings, experiments []string, licenses []pkg.License, locations ...file.Location) pkg.Package { +func (c *goBinaryCataloger) newGoBinaryPackage(dep *debug.Module, m pkg.GolangBinaryBuildinfoEntry, licenses []pkg.License, locations ...file.Location) pkg.Package { if dep.Replace != nil { dep = dep.Replace } + version := dep.Version + if version == devel { + // this is a special case for the "devel" version, which is used when the module is built from source + // and there is no vcs tag info available. In this case, we remove the placeholder to indicate + // we don't know the version. + version = "" + } + p := pkg.Package{ Name: dep.Path, - Version: dep.Version, + Version: version, Licenses: pkg.NewLicenseSet(licenses...), - PURL: packageURL(dep.Path, dep.Version), + PURL: packageURL(dep.Path, version), Language: pkg.Go, Type: pkg.GoModulePkg, Locations: file.NewLocationSet(locations...), - Metadata: pkg.GolangBinaryBuildinfoEntry{ - GoCompiledVersion: goVersion, - H1Digest: dep.Sum, - Architecture: architecture, - BuildSettings: buildSettings, - MainModule: mainModule, - GoCryptoSettings: cryptoSettings, - GoExperiments: experiments, - }, + Metadata: m, } p.SetID() @@ -38,6 +38,22 @@ func (c *goBinaryCataloger) newGoBinaryPackage(dep *debug.Module, mainModule, go return p } +func newBinaryMetadata(dep *debug.Module, mainModule, goVersion, architecture string, buildSettings pkg.KeyValues, cryptoSettings, experiments []string) pkg.GolangBinaryBuildinfoEntry { + if dep.Replace != nil { + dep = dep.Replace + } + + return pkg.GolangBinaryBuildinfoEntry{ + GoCompiledVersion: goVersion, + H1Digest: dep.Sum, + Architecture: architecture, + BuildSettings: buildSettings, + MainModule: mainModule, + GoCryptoSettings: cryptoSettings, + GoExperiments: experiments, + } +} + func packageURL(moduleName, moduleVersion string) string { // source: https://github.com/package-url/purl-spec/blob/master/PURL-TYPES.rst#golang // note: "The version is often empty when a commit is not specified and should be the commit in most cases when available." diff --git a/syft/pkg/cataloger/golang/parse_go_binary.go b/syft/pkg/cataloger/golang/parse_go_binary.go index 79fdf006e..28be593d3 100644 --- a/syft/pkg/cataloger/golang/parse_go_binary.go +++ b/syft/pkg/cataloger/golang/parse_go_binary.go @@ -124,7 +124,8 @@ func (c *goBinaryCataloger) buildGoPkgInfo(ctx context.Context, licenseScanner l lics := c.licenseResolver.getLicenses(ctx, licenseScanner, resolver, dep.Path, dep.Version) gover, experiments := getExperimentsFromVersion(mod.GoVersion) - p := c.newGoBinaryPackage( + + m := newBinaryMetadata( dep, mod.Main.Path, gover, @@ -132,6 +133,11 @@ func (c *goBinaryCataloger) buildGoPkgInfo(ctx context.Context, licenseScanner l nil, mod.cryptoSettings, experiments, + ) + + p := c.newGoBinaryPackage( + dep, + m, lics, location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), ) @@ -163,7 +169,8 @@ func (c *goBinaryCataloger) makeGoMainPackage(ctx context.Context, licenseScanne gbs := getBuildSettings(mod.Settings) lics := c.licenseResolver.getLicenses(ctx, licenseScanner, resolver, mod.Main.Path, mod.Main.Version) gover, experiments := getExperimentsFromVersion(mod.GoVersion) - main := c.newGoBinaryPackage( + + m := newBinaryMetadata( &mod.Main, mod.Main.Path, gover, @@ -171,33 +178,27 @@ func (c *goBinaryCataloger) makeGoMainPackage(ctx context.Context, licenseScanne gbs, mod.cryptoSettings, experiments, + ) + + if mod.Main.Version == devel { + version := c.findMainModuleVersion(&m, gbs, reader) + + if version != "" { + // make sure version is prefixed with v as some build systems parsed + // during `findMainModuleVersion` can include incomplete semver + // vx.x.x is correct + version = ensurePrefix(version, "v") + } + mod.Main.Version = version + } + + main := c.newGoBinaryPackage( + &mod.Main, + m, lics, location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), ) - if main.Version != devel { - // found a full package with a non-development version... return it as is... - return main - } - - // we have a package, but the version is "devel"... let's try and find a better answer - var metadata *pkg.GolangBinaryBuildinfoEntry - if v, ok := main.Metadata.(pkg.GolangBinaryBuildinfoEntry); ok { - metadata = &v - } - version := c.findMainModuleVersion(metadata, gbs, reader) - - if version != "" { - // make sure version is prefixed with v as some build systems parsed - // during `findMainModuleVersion` can include incomplete semver - // vx.x.x is correct - version = ensurePrefix(version, "v") - main.Version = version - main.PURL = packageURL(main.Name, main.Version) - - main.SetID() - } - return main } diff --git a/syft/pkg/cataloger/golang/parse_go_binary_test.go b/syft/pkg/cataloger/golang/parse_go_binary_test.go index 12d12cd8c..36158507a 100644 --- a/syft/pkg/cataloger/golang/parse_go_binary_test.go +++ b/syft/pkg/cataloger/golang/parse_go_binary_test.go @@ -152,8 +152,8 @@ func TestBuildGoPkgInfo(t *testing.T) { Name: "github.com/anchore/syft", Language: pkg.Go, Type: pkg.GoModulePkg, - Version: "(devel)", - PURL: "pkg:golang/github.com/anchore/syft@%28devel%29", + Version: "", // this was (devel) but we cleared it explicitly + PURL: "pkg:golang/github.com/anchore/syft", Locations: file.NewLocationSet( file.NewLocationFromCoordinates( file.Coordinates{ @@ -178,6 +178,7 @@ func TestBuildGoPkgInfo(t *testing.T) { name string mod *extendedBuildInfo expected []pkg.Package + cfg *CatalogerConfig binaryContent string }{ { @@ -282,8 +283,8 @@ func TestBuildGoPkgInfo(t *testing.T) { expected: []pkg.Package{ { Name: "github.com/a/b/c", - Version: "(devel)", - PURL: "pkg:golang/github.com/a/b@%28devel%29#c", + Version: "", // this was (devel) but we cleared it explicitly + PURL: "pkg:golang/github.com/a/b#c", Language: pkg.Go, Type: pkg.GoModulePkg, Locations: file.NewLocationSet( @@ -934,8 +935,8 @@ func TestBuildGoPkgInfo(t *testing.T) { Name: "github.com/anchore/syft", Language: pkg.Go, Type: pkg.GoModulePkg, - Version: "(devel)", - PURL: "pkg:golang/github.com/anchore/syft@%28devel%29", + Version: "", // this was (devel) but we cleared it explicitly + PURL: "pkg:golang/github.com/anchore/syft", Locations: file.NewLocationSet( file.NewLocationFromCoordinates( file.Coordinates{ @@ -1057,7 +1058,12 @@ func TestBuildGoPkgInfo(t *testing.T) { }, ) - c := newGoBinaryCataloger(DefaultCatalogerConfig()) + if test.cfg == nil { + c := DefaultCatalogerConfig() + test.cfg = &c + } + + c := newGoBinaryCataloger(*test.cfg) reader, err := unionreader.GetUnionReader(io.NopCloser(strings.NewReader(test.binaryContent))) require.NoError(t, err) mainPkg, pkgs := c.buildGoPkgInfo(context.Background(), licenseScanner, fileresolver.Empty{}, location, test.mod, test.mod.arch, reader)