mirror of
https://github.com/anchore/syft.git
synced 2025-11-17 16:33:21 +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"
|
"io"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -97,17 +98,19 @@ func createModuleRelationships(main pkg.Package, deps []pkg.Package) []artifact.
|
|||||||
return relationships
|
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) {
|
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 {
|
if mod == nil {
|
||||||
return nil, pkgs
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var empty debug.Module
|
if missingMainModule(mod) {
|
||||||
if mod.Main == empty && mod.Path != "" {
|
mod.Main = createMainModuleFromPath(mod)
|
||||||
mod.Main = createMainModuleFromPath(mod.Path)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var pkgs []pkg.Package
|
||||||
for _, dep := range mod.Deps {
|
for _, dep := range mod.Deps {
|
||||||
if dep == nil {
|
if dep == nil {
|
||||||
continue
|
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
|
return nil, pkgs
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,6 +142,16 @@ func (c *goBinaryCataloger) buildGoPkgInfo(resolver file.Resolver, location file
|
|||||||
return &main, pkgs
|
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 {
|
func (c *goBinaryCataloger) makeGoMainPackage(resolver file.Resolver, mod *extendedBuildInfo, arch string, location file.Location, reader io.ReadSeekCloser) pkg.Package {
|
||||||
gbs := getBuildSettings(mod.Settings)
|
gbs := getBuildSettings(mod.Settings)
|
||||||
gover, experiments := getExperimentsFromVersion(mod.GoVersion)
|
gover, experiments := getExperimentsFromVersion(mod.GoVersion)
|
||||||
@ -358,8 +371,29 @@ func getExperimentsFromVersion(version string) (string, []string) {
|
|||||||
return version, experiments
|
return version, experiments
|
||||||
}
|
}
|
||||||
|
|
||||||
func createMainModuleFromPath(path string) (mod debug.Module) {
|
func createMainModuleFromPath(existing *extendedBuildInfo) debug.Module {
|
||||||
mod.Path = path
|
// search for a main module candidate within the dependencies
|
||||||
mod.Version = devel
|
var mainModuleCandidates []debug.Module
|
||||||
return
|
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",
|
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),
|
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 {
|
for _, test := range tests {
|
||||||
@ -1092,6 +1181,12 @@ func Test_extractVersionFromLDFlags(t *testing.T) {
|
|||||||
wantMajorVersion: "6",
|
wantMajorVersion: "6",
|
||||||
wantFullVersion: "v6.1.7",
|
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
|
// 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
|
return builds
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user