Skip to content

Commit

Permalink
Guess go main module version based on binary contents (#2608)
Browse files Browse the repository at this point in the history
* guess go main module version based on binary contents

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* add configuration options for golang main module version heuristics

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* fix test setup for go bin cataloger

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* fix unit test

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* fix incorrect test assert ordering

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* handle error from seek

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

---------

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
  • Loading branch information
wagoodman authored Feb 9, 2024
1 parent 737c4e4 commit 84576b9
Show file tree
Hide file tree
Showing 9 changed files with 300 additions and 85 deletions.
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -691,6 +691,24 @@ golang:
# SYFT_GOLANG_NOPROXY env var
no-proxy: ""

# the go main module version discovered from binaries built with the go compiler will
# always show (devel) as the version. Use these options to control heuristics to guess
# a more accurate version from the binary.
main-module-version:

# look for LD flags that appear to be setting a version (e.g. -X main.version=1.0.0)
# SYFT_GOLANG_MAIN_MODULE_VERSION_FROM_LD_FLAGS env var
from-ld-flags: true

# use the build settings (e.g. vcs.version & vcs.time) to craft a v0 pseudo version
# (e.g. v0.0.0-20220308212642-53e6d0aaf6fb) when a more accurate version cannot be found otherwise.
# SYFT_GOLANG_MAIN_MODULE_VERSION_FROM_BUILD_SETTINGS env var
from-build-settings: true

# search for semver-like strings in the binary contents.
# SYFT_GOLANG_MAIN_MODULE_VERSION_FROM_CONTENTS env var
from-contents: true

java:
maven-url: "https://repo1.maven.org/maven2"
max-parent-recursive-depth: 5
Expand Down
9 changes: 8 additions & 1 deletion cmd/syft/internal/options/catalog.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ func DefaultCatalog() Catalog {
Scope: source.SquashedScope.String(),
Package: defaultPackageConfig(),
LinuxKernel: defaultLinuxKernelConfig(),
Golang: defaultGolangConfig(),
File: defaultFileConfig(),
Relationships: defaultRelationshipsConfig(),
Source: defaultSourceConfig(),
Expand Down Expand Up @@ -131,7 +132,13 @@ func (cfg Catalog) ToPackagesConfig() pkgcataloging.Config {
WithLocalModCacheDir(cfg.Golang.LocalModCacheDir).
WithSearchRemoteLicenses(cfg.Golang.SearchRemoteLicenses).
WithProxy(cfg.Golang.Proxy).
WithNoProxy(cfg.Golang.NoProxy),
WithNoProxy(cfg.Golang.NoProxy).
WithMainModuleVersion(
golang.DefaultMainModuleVersionConfig().
WithFromContents(cfg.Golang.MainModuleVersion.FromContents).
WithFromBuildSettings(cfg.Golang.MainModuleVersion.FromBuildSettings).
WithFromLDFlags(cfg.Golang.MainModuleVersion.FromLDFlags),
),
JavaScript: javascript.DefaultCatalogerConfig().
WithSearchRemoteLicenses(cfg.JavaScript.SearchRemoteLicenses).
WithNpmBaseURL(cfg.JavaScript.NpmBaseURL),
Expand Down
39 changes: 34 additions & 5 deletions cmd/syft/internal/options/golang.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,38 @@
package options

import (
"strings"

"github.com/anchore/syft/syft/pkg/cataloger/golang"
)

type golangConfig struct {
SearchLocalModCacheLicenses bool `json:"search-local-mod-cache-licenses" yaml:"search-local-mod-cache-licenses" mapstructure:"search-local-mod-cache-licenses"`
LocalModCacheDir string `json:"local-mod-cache-dir" yaml:"local-mod-cache-dir" mapstructure:"local-mod-cache-dir"`
SearchRemoteLicenses bool `json:"search-remote-licenses" yaml:"search-remote-licenses" mapstructure:"search-remote-licenses"`
Proxy string `json:"proxy" yaml:"proxy" mapstructure:"proxy"`
NoProxy string `json:"no-proxy" yaml:"no-proxy" mapstructure:"no-proxy"`
SearchLocalModCacheLicenses bool `json:"search-local-mod-cache-licenses" yaml:"search-local-mod-cache-licenses" mapstructure:"search-local-mod-cache-licenses"`
LocalModCacheDir string `json:"local-mod-cache-dir" yaml:"local-mod-cache-dir" mapstructure:"local-mod-cache-dir"`
SearchRemoteLicenses bool `json:"search-remote-licenses" yaml:"search-remote-licenses" mapstructure:"search-remote-licenses"`
Proxy string `json:"proxy" yaml:"proxy" mapstructure:"proxy"`
NoProxy string `json:"no-proxy" yaml:"no-proxy" mapstructure:"no-proxy"`
MainModuleVersion golangMainModuleVersionConfig `json:"main-module-version" yaml:"main-module-version" mapstructure:"main-module-version"`
}

type golangMainModuleVersionConfig struct {
FromLDFlags bool `json:"from-ld-flags" yaml:"from-ld-flags" mapstructure:"from-ld-flags"`
FromContents bool `json:"from-contents" yaml:"from-contents" mapstructure:"from-contents"`
FromBuildSettings bool `json:"from-build-settings" yaml:"from-build-settings" mapstructure:"from-build-settings"`
}

func defaultGolangConfig() golangConfig {
def := golang.DefaultCatalogerConfig()
return golangConfig{
SearchLocalModCacheLicenses: def.SearchLocalModCacheLicenses,
LocalModCacheDir: def.LocalModCacheDir,
SearchRemoteLicenses: def.SearchRemoteLicenses,
Proxy: strings.Join(def.Proxies, ","),
NoProxy: strings.Join(def.NoProxy, ","),
MainModuleVersion: golangMainModuleVersionConfig{
FromLDFlags: def.MainModuleVersion.FromLDFlags,
FromContents: def.MainModuleVersion.FromContents,
FromBuildSettings: def.MainModuleVersion.FromBuildSettings,
},
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ func Test_packageCatalogerExports(t *testing.T) {
for pkg, expected := range expectAtLeast {
actual, ok := exports[pkg]
require.True(t, ok, pkg)
require.True(t, expected.IsSubset(actual.Names()), pkg)
if !assert.True(t, actual.Names().IsSubset(expected), pkg) {
t.Logf("missing: %s", strset.SymmetricDifference(expected, actual.Names()))
}
}

}
Expand Down
8 changes: 4 additions & 4 deletions syft/pkg/cataloger/golang/cataloger.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ func NewGoModuleFileCataloger(opts CatalogerConfig) pkg.Cataloger {

// NewGoModuleBinaryCataloger returns a new cataloger object that searches within binaries built by the go compiler.
func NewGoModuleBinaryCataloger(opts CatalogerConfig) pkg.Cataloger {
c := goBinaryCataloger{
licenses: newGoLicenses(binaryCatalogerName, opts),
}
return &progressingCataloger{
cataloger: generic.NewCataloger(binaryCatalogerName).
WithParserByMimeTypes(c.parseGoBinary, mimetype.ExecutableMIMETypeSet.List()...),
WithParserByMimeTypes(
newGoBinaryCataloger(opts).parseGoBinary,
mimetype.ExecutableMIMETypeSet.List()...,
),
}
}

Expand Down
49 changes: 43 additions & 6 deletions syft/pkg/cataloger/golang/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,28 @@ var (
)

type CatalogerConfig struct {
SearchLocalModCacheLicenses bool `yaml:"search-local-mod-cache-licenses" json:"search-local-mod-cache-licenses" mapstructure:"search-local-mod-cache-licenses"`
LocalModCacheDir string `yaml:"local-mod-cache-dir" json:"local-mod-cache-dir" mapstructure:"local-mod-cache-dir"`
SearchRemoteLicenses bool `yaml:"search-remote-licenses" json:"search-remote-licenses" mapstructure:"search-remote-licenses"`
Proxies []string `yaml:"proxies,omitempty" json:"proxies,omitempty" mapstructure:"proxies"`
NoProxy []string `yaml:"no-proxy,omitempty" json:"no-proxy,omitempty" mapstructure:"no-proxy"`
SearchLocalModCacheLicenses bool `yaml:"search-local-mod-cache-licenses" json:"search-local-mod-cache-licenses" mapstructure:"search-local-mod-cache-licenses"`
LocalModCacheDir string `yaml:"local-mod-cache-dir" json:"local-mod-cache-dir" mapstructure:"local-mod-cache-dir"`
SearchRemoteLicenses bool `yaml:"search-remote-licenses" json:"search-remote-licenses" mapstructure:"search-remote-licenses"`
Proxies []string `yaml:"proxies,omitempty" json:"proxies,omitempty" mapstructure:"proxies"`
NoProxy []string `yaml:"no-proxy,omitempty" json:"no-proxy,omitempty" mapstructure:"no-proxy"`
MainModuleVersion MainModuleVersionConfig `yaml:"main-module-version" json:"main-module-version" mapstructure:"main-module-version"`
}

type MainModuleVersionConfig struct {
FromLDFlags bool `yaml:"from-ld-flags" json:"from-ld-flags" mapstructure:"from-ld-flags"`
FromContents bool `yaml:"from-contents" json:"from-contents" mapstructure:"from-contents"`
FromBuildSettings bool `yaml:"from-build-settings" json:"from-build-settings" mapstructure:"from-build-settings"`
}

// DefaultCatalogerConfig create a CatalogerConfig with default options, which includes:
// - setting the default remote proxy if none is provided
// - setting the default no proxy if none is provided
// - setting the default local module cache dir if none is provided
func DefaultCatalogerConfig() CatalogerConfig {
g := CatalogerConfig{}
g := CatalogerConfig{
MainModuleVersion: DefaultMainModuleVersionConfig(),
}

// first process the proxy settings
if len(g.Proxies) == 0 {
Expand Down Expand Up @@ -76,6 +85,14 @@ func DefaultCatalogerConfig() CatalogerConfig {
return g
}

func DefaultMainModuleVersionConfig() MainModuleVersionConfig {
return MainModuleVersionConfig{
FromLDFlags: true,
FromContents: true,
FromBuildSettings: true,
}
}

func (g CatalogerConfig) WithSearchLocalModCacheLicenses(input bool) CatalogerConfig {
g.SearchLocalModCacheLicenses = input
return g
Expand Down Expand Up @@ -112,3 +129,23 @@ func (g CatalogerConfig) WithNoProxy(input string) CatalogerConfig {
g.NoProxy = strings.Split(input, ",")
return g
}

func (g CatalogerConfig) WithMainModuleVersion(input MainModuleVersionConfig) CatalogerConfig {
g.MainModuleVersion = input
return g
}

func (g MainModuleVersionConfig) WithFromLDFlags(input bool) MainModuleVersionConfig {
g.FromLDFlags = input
return g
}

func (g MainModuleVersionConfig) WithFromContents(input bool) MainModuleVersionConfig {
g.FromContents = input
return g
}

func (g MainModuleVersionConfig) WithFromBuildSettings(input bool) MainModuleVersionConfig {
g.FromBuildSettings = input
return g
}
4 changes: 3 additions & 1 deletion syft/pkg/cataloger/golang/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"github.com/stretchr/testify/assert"
)

func Test_Options(t *testing.T) {
func Test_Config(t *testing.T) {
type opts struct {
local bool
cacheDir string
Expand Down Expand Up @@ -51,6 +51,7 @@ func Test_Options(t *testing.T) {
SearchRemoteLicenses: false,
Proxies: []string{"https://my.proxy"},
NoProxy: []string{"my.private", "no.proxy"},
MainModuleVersion: DefaultMainModuleVersionConfig(),
},
},
{
Expand All @@ -74,6 +75,7 @@ func Test_Options(t *testing.T) {
SearchRemoteLicenses: true,
Proxies: []string{"https://alt.proxy", "direct"},
NoProxy: []string{"alt.no.proxy"},
MainModuleVersion: DefaultMainModuleVersionConfig(),
},
},
}
Expand Down
Loading

0 comments on commit 84576b9

Please sign in to comment.