diff --git a/syft/pkg/cataloger/golang/parse_go_binary.go b/syft/pkg/cataloger/golang/parse_go_binary.go index e2e1c53eba6..89ed4ba0742 100644 --- a/syft/pkg/cataloger/golang/parse_go_binary.go +++ b/syft/pkg/cataloger/golang/parse_go_binary.go @@ -85,35 +85,32 @@ func (c *goBinaryCataloger) makeGoMainPackage(resolver file.Resolver, mod *debug version, hasVersion := gbs["vcs.revision"] timestamp, hasTimestamp := gbs["vcs.time"] - if hasVersion { - if hasTimestamp { - //NOTE: err is ignored, because if parsing fails - // we still use the empty Time{} struct to generate an empty date, like 00010101000000 - // for consistency with the pseudo-version format: https://go.dev/ref/mod#pseudo-versions - ts, _ := time.Parse(time.RFC3339, timestamp) - if len(version) >= 12 { - version = version[:12] - } - - var ldflags string - if metadata, ok := main.Metadata.(pkg.GolangBinMetadata); ok { - ldflags = metadata.BuildSettings["-ldflags"] - } - - majorVersion, fullVersion := extractVersionFromLDFlags(ldflags) - if fullVersion != "" { - // we've found a specific version from the ldflags! use it as the version. - // why not combine that with the pseudo version (e.g. v1.2.3-0.20210101000000-abcdef123456)? - // short answer: we're assuming that if a specific semver was provided in the ldflags that - // there is a matching vcs tag to match that could be referenced. This assumption could - // be incorrect in terms of the go.mod contents, but is not incorrect in terms of the logical - // version of the package. - version = fullVersion - } else { - version = module.PseudoVersion(majorVersion, fullVersion, ts, version) - } + var ldflags string + if metadata, ok := main.Metadata.(pkg.GolangBinMetadata); ok { + // we've found a specific version from the ldflags! use it as the version. + // why not combine that with the pseudo version (e.g. v1.2.3-0.20210101000000-abcdef123456)? + // short answer: we're assuming that if a specific semver was provided in the ldflags that + // there is a matching vcs tag to match that could be referenced. This assumption could + // be incorrect in terms of the go.mod contents, but is not incorrect in terms of the logical + // version of the package. + ldflags = metadata.BuildSettings["-ldflags"] + } + + majorVersion, fullVersion := extractVersionFromLDFlags(ldflags) + if fullVersion != "" { + version = fullVersion + } else if hasVersion && hasTimestamp { + //NOTE: err is ignored, because if parsing fails + // we still use the empty Time{} struct to generate an empty date, like 00010101000000 + // for consistency with the pseudo-version format: https://go.dev/ref/mod#pseudo-versions + ts, _ := time.Parse(time.RFC3339, timestamp) + if len(version) >= 12 { + version = version[:12] } + version = module.PseudoVersion(majorVersion, fullVersion, ts, version) + } + if version != "" { main.Version = version main.PURL = packageURL(main.Name, main.Version) diff --git a/syft/pkg/cataloger/golang/parse_go_binary_test.go b/syft/pkg/cataloger/golang/parse_go_binary_test.go index 5f483b3c4f5..d578b46adaa 100644 --- a/syft/pkg/cataloger/golang/parse_go_binary_test.go +++ b/syft/pkg/cataloger/golang/parse_go_binary_test.go @@ -347,7 +347,7 @@ func TestBuildGoPkgInfo(t *testing.T) { }, }, { - name: "parse main mod and replace devel version with one from ldflags", + name: "parse main mod and replace devel version with one from ldflags with vcs. build settings", arch: archDetails, mod: &debug.BuildInfo{ GoVersion: goCompiledVersion, @@ -393,6 +393,135 @@ func TestBuildGoPkgInfo(t *testing.T) { }, }, }, + { + name: "parse main mod and replace devel version with one from ldflags without any vcs. build settings", + arch: archDetails, + mod: &debug.BuildInfo{ + GoVersion: goCompiledVersion, + Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, + Settings: []debug.BuildSetting{ + {Key: "GOARCH", Value: archDetails}, + {Key: "GOOS", Value: "darwin"}, + {Key: "GOAMD64", Value: "v1"}, + {Key: "-ldflags", Value: `build -ldflags="-w -s -extldflags '-static' -X github.com/anchore/syft/internal/version.version=0.79.0`}, + }, + }, + expected: []pkg.Package{ + { + Name: "github.com/anchore/syft", + Language: pkg.Go, + Type: pkg.GoModulePkg, + Version: "v0.79.0", + PURL: "pkg:golang/github.com/anchore/syft@v0.79.0", + Locations: file.NewLocationSet( + file.NewLocationFromCoordinates( + file.Coordinates{ + RealPath: "/a-path", + FileSystemID: "layer-id", + }, + ).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), + ), + MetadataType: pkg.GolangBinMetadataType, + Metadata: pkg.GolangBinMetadata{ + GoCompiledVersion: goCompiledVersion, + Architecture: archDetails, + BuildSettings: map[string]string{ + "GOARCH": archDetails, + "GOOS": "darwin", + "GOAMD64": "v1", + "-ldflags": `build -ldflags="-w -s -extldflags '-static' -X github.com/anchore/syft/internal/version.version=0.79.0`, + }, + MainModule: "github.com/anchore/syft", + }, + }, + }, + }, + { + name: "parse main mod and replace devel version with one from ldflags main.version without any vcs. build settings", + arch: archDetails, + mod: &debug.BuildInfo{ + GoVersion: goCompiledVersion, + Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, + Settings: []debug.BuildSetting{ + {Key: "GOARCH", Value: archDetails}, + {Key: "GOOS", Value: "darwin"}, + {Key: "GOAMD64", Value: "v1"}, + {Key: "-ldflags", Value: `build -ldflags="-w -s -extldflags '-static' -X main.version=0.79.0`}, + }, + }, + expected: []pkg.Package{ + { + Name: "github.com/anchore/syft", + Language: pkg.Go, + Type: pkg.GoModulePkg, + Version: "v0.79.0", + PURL: "pkg:golang/github.com/anchore/syft@v0.79.0", + Locations: file.NewLocationSet( + file.NewLocationFromCoordinates( + file.Coordinates{ + RealPath: "/a-path", + FileSystemID: "layer-id", + }, + ).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), + ), + MetadataType: pkg.GolangBinMetadataType, + Metadata: pkg.GolangBinMetadata{ + GoCompiledVersion: goCompiledVersion, + Architecture: archDetails, + BuildSettings: map[string]string{ + "GOARCH": archDetails, + "GOOS": "darwin", + "GOAMD64": "v1", + "-ldflags": `build -ldflags="-w -s -extldflags '-static' -X main.version=0.79.0`, + }, + MainModule: "github.com/anchore/syft", + }, + }, + }, + }, + { + name: "parse main mod and replace devel version with one from ldflags main.Version without any vcs. build settings", + arch: archDetails, + mod: &debug.BuildInfo{ + GoVersion: goCompiledVersion, + Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, + Settings: []debug.BuildSetting{ + {Key: "GOARCH", Value: archDetails}, + {Key: "GOOS", Value: "darwin"}, + {Key: "GOAMD64", Value: "v1"}, + {Key: "-ldflags", Value: `build -ldflags="-w -s -extldflags '-static' -X main.Version=0.79.0`}, + }, + }, + expected: []pkg.Package{ + { + Name: "github.com/anchore/syft", + Language: pkg.Go, + Type: pkg.GoModulePkg, + Version: "v0.79.0", + PURL: "pkg:golang/github.com/anchore/syft@v0.79.0", + Locations: file.NewLocationSet( + file.NewLocationFromCoordinates( + file.Coordinates{ + RealPath: "/a-path", + FileSystemID: "layer-id", + }, + ).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), + ), + MetadataType: pkg.GolangBinMetadataType, + Metadata: pkg.GolangBinMetadata{ + GoCompiledVersion: goCompiledVersion, + Architecture: archDetails, + BuildSettings: map[string]string{ + "GOARCH": archDetails, + "GOOS": "darwin", + "GOAMD64": "v1", + "-ldflags": `build -ldflags="-w -s -extldflags '-static' -X main.Version=0.79.0`, + }, + MainModule: "github.com/anchore/syft", + }, + }, + }, + }, { name: "parse main mod and replace devel version with a pseudo version", arch: archDetails,