mirror of
https://github.com/anchore/syft.git
synced 2025-11-17 08:23:15 +01:00
better go mod detection from partial package builds (#3060)
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
parent
ca945d16e0
commit
9573f557d1
@ -11,6 +11,7 @@ import (
|
||||
"io"
|
||||
"regexp"
|
||||
"runtime/debug"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@ -97,17 +98,19 @@ func createModuleRelationships(main pkg.Package, deps []pkg.Package) []artifact.
|
||||
return relationships
|
||||
}
|
||||
|
||||
var emptyModule debug.Module
|
||||
var moduleFromPartialPackageBuild = debug.Module{Path: "command-line-arguments"}
|
||||
|
||||
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 nil, pkgs
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var empty debug.Module
|
||||
if mod.Main == empty && mod.Path != "" {
|
||||
mod.Main = createMainModuleFromPath(mod.Path)
|
||||
if missingMainModule(mod) {
|
||||
mod.Main = createMainModuleFromPath(mod)
|
||||
}
|
||||
|
||||
var pkgs []pkg.Package
|
||||
for _, dep := range mod.Deps {
|
||||
if dep == nil {
|
||||
continue
|
||||
@ -130,7 +133,7 @@ func (c *goBinaryCataloger) buildGoPkgInfo(resolver file.Resolver, location file
|
||||
}
|
||||
}
|
||||
|
||||
if mod.Main == empty {
|
||||
if mod.Main == emptyModule {
|
||||
return nil, pkgs
|
||||
}
|
||||
|
||||
@ -139,6 +142,16 @@ func (c *goBinaryCataloger) buildGoPkgInfo(resolver file.Resolver, location file
|
||||
return &main, pkgs
|
||||
}
|
||||
|
||||
func missingMainModule(mod *extendedBuildInfo) bool {
|
||||
if mod.Main == emptyModule && mod.Path != "" {
|
||||
return true
|
||||
}
|
||||
// special case: when invoking go build with a source file and not a package (directory) then you will
|
||||
// see "command-line-arguments" as the main module path... even though that's not the main module. In this
|
||||
// circumstance, we should treat the main module as missing and search for it within the dependencies.
|
||||
return mod.Main == moduleFromPartialPackageBuild
|
||||
}
|
||||
|
||||
func (c *goBinaryCataloger) makeGoMainPackage(resolver file.Resolver, mod *extendedBuildInfo, arch string, location file.Location, reader io.ReadSeekCloser) pkg.Package {
|
||||
gbs := getBuildSettings(mod.Settings)
|
||||
gover, experiments := getExperimentsFromVersion(mod.GoVersion)
|
||||
@ -358,8 +371,29 @@ func getExperimentsFromVersion(version string) (string, []string) {
|
||||
return version, experiments
|
||||
}
|
||||
|
||||
func createMainModuleFromPath(path string) (mod debug.Module) {
|
||||
mod.Path = path
|
||||
mod.Version = devel
|
||||
return
|
||||
func createMainModuleFromPath(existing *extendedBuildInfo) debug.Module {
|
||||
// search for a main module candidate within the dependencies
|
||||
var mainModuleCandidates []debug.Module
|
||||
var usedIndex int
|
||||
for i, dep := range existing.Deps {
|
||||
if dep == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if dep.Version == devel {
|
||||
usedIndex = i
|
||||
mainModuleCandidates = append(mainModuleCandidates, *dep)
|
||||
}
|
||||
}
|
||||
if len(mainModuleCandidates) == 1 {
|
||||
// we need to prune the dependency from module list
|
||||
existing.Deps = slices.Delete(existing.Deps, usedIndex, usedIndex+1)
|
||||
return mainModuleCandidates[0]
|
||||
}
|
||||
|
||||
// otherwise craft a main module from the path (a bit of a cop out, but allows us to have a main module)
|
||||
return debug.Module{
|
||||
Path: existing.Path,
|
||||
Version: devel,
|
||||
}
|
||||
}
|
||||
|
||||
@ -210,7 +210,7 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "buildGoPkgInfo parses a blank mod and returns no packages",
|
||||
mod: &extendedBuildInfo{&debug.BuildInfo{}, nil, ""},
|
||||
mod: &extendedBuildInfo{BuildInfo: &debug.BuildInfo{}, cryptoSettings: nil, arch: ""},
|
||||
expected: []pkg.Package(nil),
|
||||
},
|
||||
{
|
||||
@ -946,6 +946,95 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
||||
},
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "parse a mod from path (partial build of package)",
|
||||
mod: &extendedBuildInfo{
|
||||
BuildInfo: &debug.BuildInfo{
|
||||
GoVersion: "go1.22.2",
|
||||
Main: debug.Module{Path: "command-line-arguments"},
|
||||
Settings: []debug.BuildSetting{
|
||||
{
|
||||
Key: "-ldflags",
|
||||
Value: `build -ldflags="-w -s -X github.com/kuskoman/logstash-exporter/config.Version=v1.7.0 -X github.com/kuskoman/logstash-exporter/config.GitCommit=db696dbcfe5a91d288d5ad44ce8ccbea97e65978 -X github.com/kuskoman/logstash-exporter/config.BuildDate=2024-07-17T08:12:17Z"`,
|
||||
},
|
||||
{Key: "GOARCH", Value: archDetails},
|
||||
{Key: "GOOS", Value: "darwin"},
|
||||
{Key: "GOAMD64", Value: "v1"},
|
||||
},
|
||||
Deps: []*debug.Module{
|
||||
{
|
||||
Path: "github.com/kuskoman/something-else",
|
||||
Version: "v1.2.3",
|
||||
},
|
||||
{
|
||||
Path: "github.com/kuskoman/logstash-exporter",
|
||||
Version: "(devel)",
|
||||
},
|
||||
},
|
||||
},
|
||||
arch: archDetails,
|
||||
},
|
||||
expected: []pkg.Package{
|
||||
{
|
||||
Name: "github.com/kuskoman/something-else",
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
Version: "v1.2.3",
|
||||
PURL: "pkg:golang/github.com/kuskoman/something-else@v1.2.3",
|
||||
Locations: file.NewLocationSet(
|
||||
file.NewLocationFromCoordinates(
|
||||
file.Coordinates{
|
||||
RealPath: "/a-path",
|
||||
FileSystemID: "layer-id",
|
||||
},
|
||||
).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
),
|
||||
Metadata: pkg.GolangBinaryBuildinfoEntry{
|
||||
GoCompiledVersion: "go1.22.2",
|
||||
Architecture: archDetails,
|
||||
MainModule: "github.com/kuskoman/logstash-exporter", // correctly attached the main module
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "github.com/kuskoman/logstash-exporter",
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
Version: "v1.7.0",
|
||||
PURL: "pkg:golang/github.com/kuskoman/logstash-exporter@v1.7.0",
|
||||
Locations: file.NewLocationSet(
|
||||
file.NewLocationFromCoordinates(
|
||||
file.Coordinates{
|
||||
RealPath: "/a-path",
|
||||
FileSystemID: "layer-id",
|
||||
},
|
||||
).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
),
|
||||
Metadata: pkg.GolangBinaryBuildinfoEntry{
|
||||
GoCompiledVersion: "go1.22.2",
|
||||
BuildSettings: []pkg.KeyValue{
|
||||
{
|
||||
Key: "-ldflags",
|
||||
Value: `build -ldflags="-w -s -X github.com/kuskoman/logstash-exporter/config.Version=v1.7.0 -X github.com/kuskoman/logstash-exporter/config.GitCommit=db696dbcfe5a91d288d5ad44ce8ccbea97e65978 -X github.com/kuskoman/logstash-exporter/config.BuildDate=2024-07-17T08:12:17Z"`,
|
||||
},
|
||||
{
|
||||
Key: "GOARCH",
|
||||
Value: "amd64",
|
||||
},
|
||||
{
|
||||
Key: "GOOS",
|
||||
Value: "darwin",
|
||||
},
|
||||
{
|
||||
Key: "GOAMD64",
|
||||
Value: "v1",
|
||||
},
|
||||
},
|
||||
Architecture: archDetails,
|
||||
MainModule: "github.com/kuskoman/logstash-exporter",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
@ -1092,6 +1181,12 @@ func Test_extractVersionFromLDFlags(t *testing.T) {
|
||||
wantMajorVersion: "6",
|
||||
wantFullVersion: "v6.1.7",
|
||||
},
|
||||
{
|
||||
name: "logstash-exporter",
|
||||
ldflags: `build -ldflags="-w -s -X github.com/kuskoman/logstash-exporter/config.Version=v1.7.0 -X github.com/kuskoman/logstash-exporter/config.GitCommit=db696dbcfe5a91d288d5ad44ce8ccbea97e65978 -X github.com/kuskoman/logstash-exporter/config.BuildDate=2024-07-17T08:12:17Z"`,
|
||||
wantMajorVersion: "1",
|
||||
wantFullVersion: "v1.7.0",
|
||||
},
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// negative cases
|
||||
{
|
||||
|
||||
@ -56,7 +56,7 @@ func scanFile(reader unionreader.UnionReader, filename string) []*extendedBuildI
|
||||
}
|
||||
}
|
||||
|
||||
builds = append(builds, &extendedBuildInfo{bi, v, arch})
|
||||
builds = append(builds, &extendedBuildInfo{BuildInfo: bi, cryptoSettings: v, arch: arch})
|
||||
}
|
||||
return builds
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user