detect license ID from full text when incidentally provided as a value (#3876)

---------
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
Christopher Angelo Phillips
2025-05-13 16:37:18 -04:00
committed by GitHub
co-authored by Alex Goodman
parent b4d717fb30
commit f77d503892
117 changed files with 2082 additions and 1327 deletions
+1 -1
View File
@@ -31,5 +31,5 @@ func NewGoModuleBinaryCataloger(opts CatalogerConfig) pkg.Cataloger {
newGoBinaryCataloger(opts).parseGoBinary,
mimetype.ExecutableMIMETypeSet.List()...,
).
WithProcessors(stdlibProcessor)
WithResolvingProcessors(stdlibProcessor)
}
+32 -105
View File
@@ -26,25 +26,15 @@ import (
"github.com/anchore/syft/internal/licenses"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/license"
"github.com/anchore/syft/syft/pkg"
)
type goLicense struct {
Value string `json:"val,omitempty"`
SPDXExpression string `json:"spdx,omitempty"`
Type license.Type `json:"type,omitempty"`
URLs []string `json:"urls,omitempty"`
Locations []string `json:"locations,omitempty"`
Contents string `json:"contents,omitempty"`
}
type goLicenseResolver struct {
catalogerName string
opts CatalogerConfig
localModCacheDir fs.FS
localVendorDir fs.FS
licenseCache cache.Resolver[[]goLicense]
licenseCache cache.Resolver[[]pkg.License]
lowerLicenseFileNames *strset.Set
}
@@ -73,7 +63,7 @@ func newGoLicenseResolver(catalogerName string, opts CatalogerConfig) goLicenseR
opts: opts,
localModCacheDir: localModCacheDir,
localVendorDir: localVendorDir,
licenseCache: cache.GetResolverCachingErrors[[]goLicense]("golang", "v1"),
licenseCache: cache.GetResolverCachingErrors[[]pkg.License]("golang", "v2"),
lowerLicenseFileNames: strset.New(lowercaseLicenseFiles()...),
}
}
@@ -97,52 +87,52 @@ func remotesForModule(proxies []string, noProxy []string, module string) []strin
return proxies
}
func (c *goLicenseResolver) getLicenses(ctx context.Context, scanner licenses.Scanner, resolver file.Resolver, moduleName, moduleVersion string) []pkg.License {
func (c *goLicenseResolver) getLicenses(ctx context.Context, resolver file.Resolver, moduleName, moduleVersion string) []pkg.License {
// search the scan target first, ignoring local and remote sources
goLicenses, err := c.findLicensesInSource(ctx, scanner, resolver,
pkgLicenses, err := c.findLicensesInSource(ctx, resolver,
fmt.Sprintf(`**/go/pkg/mod/%s@%s/*`, processCaps(moduleName), moduleVersion),
)
if err != nil {
log.WithFields("error", err, "module", moduleName, "version", moduleVersion).Trace("unable to read golang licenses from source")
}
if len(goLicenses) > 0 {
return toPkgLicenses(goLicenses)
if len(pkgLicenses) > 0 {
return pkgLicenses
}
// look in the local host mod directory...
if c.opts.SearchLocalModCacheLicenses {
goLicenses, err = c.getLicensesFromLocal(ctx, scanner, moduleName, moduleVersion)
pkgLicenses, err = c.getLicensesFromLocal(ctx, moduleName, moduleVersion)
if err != nil {
log.WithFields("error", err, "module", moduleName, "version", moduleVersion).Trace("unable to read golang licenses local")
}
if len(goLicenses) > 0 {
return toPkgLicenses(goLicenses)
if len(pkgLicenses) > 0 {
return pkgLicenses
}
}
// look in the local vendor directory...
if c.opts.SearchLocalVendorLicenses {
goLicenses, err = c.getLicensesFromLocalVendor(ctx, scanner, moduleName)
pkgLicenses, err = c.getLicensesFromLocalVendor(ctx, moduleName)
if err != nil {
log.WithFields("error", err, "module", moduleName, "version", moduleVersion).Trace("unable to read golang licenses vendor")
}
if len(goLicenses) > 0 {
return toPkgLicenses(goLicenses)
if len(pkgLicenses) > 0 {
return pkgLicenses
}
}
// download from remote sources
if c.opts.SearchRemoteLicenses {
goLicenses, err = c.getLicensesFromRemote(ctx, scanner, moduleName, moduleVersion)
pkgLicenses, err = c.getLicensesFromRemote(ctx, moduleName, moduleVersion)
if err != nil {
log.WithFields("error", err, "module", moduleName, "version", moduleVersion).Debug("unable to read golang licenses remote")
}
}
return toPkgLicenses(goLicenses)
return pkgLicenses
}
func (c *goLicenseResolver) getLicensesFromLocal(ctx context.Context, scanner licenses.Scanner, moduleName, moduleVersion string) ([]goLicense, error) {
func (c *goLicenseResolver) getLicensesFromLocal(ctx context.Context, moduleName, moduleVersion string) ([]pkg.License, error) {
if c.localModCacheDir == nil {
return nil, nil
}
@@ -158,10 +148,10 @@ func (c *goLicenseResolver) getLicensesFromLocal(ctx context.Context, scanner li
// if we're running against a directory on the filesystem, it may not include the
// user's homedir / GOPATH, so we defer to using the localModCacheResolver
// we use $GOPATH/pkg/mod to avoid leaking information about the user's system
return c.findLicensesInFS(ctx, scanner, "file://$GOPATH/pkg/mod/"+subdir+"/", dir)
return c.findLicensesInFS(ctx, "file://$GOPATH/pkg/mod/"+subdir+"/", dir)
}
func (c *goLicenseResolver) getLicensesFromLocalVendor(ctx context.Context, scanner licenses.Scanner, moduleName string) ([]goLicense, error) {
func (c *goLicenseResolver) getLicensesFromLocalVendor(ctx context.Context, moduleName string) ([]pkg.License, error) {
if c.localVendorDir == nil {
return nil, nil
}
@@ -177,11 +167,11 @@ func (c *goLicenseResolver) getLicensesFromLocalVendor(ctx context.Context, scan
// if we're running against a directory on the filesystem, it may not include the
// user's homedir / GOPATH, so we defer to using the localModCacheResolver
// we use $GOPATH/pkg/mod to avoid leaking information about the user's system
return c.findLicensesInFS(ctx, scanner, "file://$GO_VENDOR/"+subdir+"/", dir)
return c.findLicensesInFS(ctx, "file://$GO_VENDOR/"+subdir+"/", dir)
}
func (c *goLicenseResolver) getLicensesFromRemote(ctx context.Context, scanner licenses.Scanner, moduleName, moduleVersion string) ([]goLicense, error) {
return c.licenseCache.Resolve(fmt.Sprintf("%s/%s", moduleName, moduleVersion), func() ([]goLicense, error) {
func (c *goLicenseResolver) getLicensesFromRemote(ctx context.Context, moduleName, moduleVersion string) ([]pkg.License, error) {
return c.licenseCache.Resolve(fmt.Sprintf("%s/%s", moduleName, moduleVersion), func() ([]pkg.License, error) {
proxies := remotesForModule(c.opts.Proxies, c.opts.NoProxy, moduleName)
urlPrefix, fsys, err := getModule(proxies, moduleName, moduleVersion)
@@ -189,12 +179,12 @@ func (c *goLicenseResolver) getLicensesFromRemote(ctx context.Context, scanner l
return nil, err
}
return c.findLicensesInFS(ctx, scanner, urlPrefix, fsys)
return c.findLicensesInFS(ctx, urlPrefix, fsys)
})
}
func (c *goLicenseResolver) findLicensesInFS(ctx context.Context, scanner licenses.Scanner, urlPrefix string, fsys fs.FS) ([]goLicense, error) {
var out []goLicense
func (c *goLicenseResolver) findLicensesInFS(ctx context.Context, urlPrefix string, fsys fs.FS) ([]pkg.License, error) {
var out []pkg.License
err := fs.WalkDir(fsys, ".", func(filePath string, d fs.DirEntry, err error) error {
if err != nil {
log.Debugf("error reading %s#%s: %v", urlPrefix, filePath, err)
@@ -213,18 +203,13 @@ func (c *goLicenseResolver) findLicensesInFS(ctx context.Context, scanner licens
return nil
}
defer internal.CloseAndLogError(rdr, filePath)
parsed, err := scanner.PkgSearch(ctx, file.NewLocationReadCloser(file.NewLocation(filePath), rdr))
if err != nil {
log.Debugf("error parsing license file %s: %v", filePath, err)
return nil
}
licenses := pkg.NewLicensesFromReadCloserWithContext(ctx, file.NewLocationReadCloser(file.NewLocation(filePath), rdr))
// since these licenses are found in an external fs.FS, not in the scanned source,
// get rid of the locations but keep information about the where the license was found
// by prepending the urlPrefix to the internal path for an accurate representation
for _, l := range toGoLicenses(parsed) {
for _, l := range licenses {
l.URLs = []string{urlPrefix + filePath}
l.Locations = nil
l.Locations = file.NewLocationSet()
out = append(out, l)
}
return nil
@@ -232,15 +217,15 @@ func (c *goLicenseResolver) findLicensesInFS(ctx context.Context, scanner licens
return out, err
}
func (c *goLicenseResolver) findLicensesInSource(ctx context.Context, scanner licenses.Scanner, resolver file.Resolver, globMatch string) ([]goLicense, error) {
var out []goLicense
func (c *goLicenseResolver) findLicensesInSource(ctx context.Context, resolver file.Resolver, globMatch string) ([]pkg.License, error) {
var out []pkg.License
locations, err := resolver.FilesByGlob(globMatch)
if err != nil {
return nil, err
}
for _, l := range locations {
parsed, err := c.parseLicenseFromLocation(ctx, scanner, l, resolver)
parsed, err := c.parseLicenseFromLocation(ctx, l, resolver)
if err != nil {
return nil, err
}
@@ -258,8 +243,8 @@ func (c *goLicenseResolver) findLicensesInSource(ctx context.Context, scanner li
return out, nil
}
func (c *goLicenseResolver) parseLicenseFromLocation(ctx context.Context, scanner licenses.Scanner, l file.Location, resolver file.Resolver) ([]goLicense, error) {
var out []goLicense
func (c *goLicenseResolver) parseLicenseFromLocation(ctx context.Context, l file.Location, resolver file.Resolver) ([]pkg.License, error) {
var out []pkg.License
fileName := path.Base(l.RealPath)
if c.lowerLicenseFileNames.Has(strings.ToLower(fileName)) {
contents, err := resolver.FileContentsByLocation(l)
@@ -267,12 +252,7 @@ func (c *goLicenseResolver) parseLicenseFromLocation(ctx context.Context, scanne
return nil, err
}
defer internal.CloseAndLogError(contents, l.RealPath)
parsed, err := scanner.PkgSearch(ctx, file.NewLocationReadCloser(l, contents))
if err != nil {
return nil, err
}
out = append(out, toGoLicenses(parsed)...)
out = pkg.NewLicensesFromReadCloserWithContext(ctx, file.NewLocationReadCloser(l, contents))
}
return out, nil
}
@@ -281,13 +261,6 @@ func moduleDir(moduleName, moduleVersion string) string {
return fmt.Sprintf("%s@%s", processCaps(moduleName), moduleVersion)
}
func requireCollection[T any](licenses []T) []T {
if licenses == nil {
return make([]T, 0)
}
return licenses
}
var capReplacer = regexp.MustCompile("[A-Z]")
func processCaps(s string) string {
@@ -440,49 +413,3 @@ func (l noLicensesFound) Error() string {
}
var _ error = (*noLicensesFound)(nil)
func toPkgLicenses(goLicenses []goLicense) []pkg.License {
var out []pkg.License
for _, l := range goLicenses {
out = append(out, pkg.License{
Value: l.Value,
SPDXExpression: l.SPDXExpression,
Type: l.Type,
URLs: l.URLs,
Locations: toPkgLocations(l.Locations),
Contents: l.Contents,
})
}
return requireCollection(out)
}
func toPkgLocations(goLocations []string) file.LocationSet {
out := file.NewLocationSet()
for _, l := range goLocations {
out.Add(file.NewLocation(l))
}
return out
}
func toGoLicenses(pkgLicenses []pkg.License) []goLicense {
var out []goLicense
for _, l := range pkgLicenses {
out = append(out, goLicense{
Value: l.Value,
SPDXExpression: l.SPDXExpression,
Type: l.Type,
URLs: l.URLs,
Locations: toGoLocations(l.Locations),
Contents: l.Contents,
})
}
return out
}
func toGoLocations(locations file.LocationSet) []string {
var out []string
for _, l := range locations.ToSlice() {
out = append(out, l.RealPath)
}
return out
}
+31 -40
View File
@@ -14,17 +14,18 @@ import (
"strings"
"testing"
"github.com/google/licensecheck"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/internal/licenses"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/internal/fileresolver"
"github.com/anchore/syft/syft/license"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
)
func Test_LicenseSearch(t *testing.T) {
ctx := pkgtest.Context()
loc1 := file.NewLocation("github.com/someorg/somename@v0.3.2/LICENSE")
loc2 := file.NewLocation("github.com/!cap!o!r!g/!cap!project@v4.111.5/LICENSE.txt")
loc3 := file.NewLocation("github.com/someorg/strangelicense@v1.2.3/LiCeNsE.tXt")
@@ -71,13 +72,6 @@ func Test_LicenseSearch(t *testing.T) {
localVendorDir := filepath.Join(wd, "test-fixtures", "licenses-vendor")
sc := &licenses.ScannerConfig{
CoverageThreshold: 75,
Scanner: licensecheck.Scan,
}
licenseScanner, err := licenses.NewScanner(sc)
require.NoError(t, err)
tests := []struct {
name string
version string
@@ -95,6 +89,7 @@ func Test_LicenseSearch(t *testing.T) {
Value: "Apache-2.0",
SPDXExpression: "Apache-2.0",
Type: license.Concluded,
Contents: mustContentsFromLocation(t, loc1),
URLs: []string{"file://$GOPATH/pkg/mod/" + loc1.RealPath},
Locations: file.NewLocationSet(),
}},
@@ -110,6 +105,7 @@ func Test_LicenseSearch(t *testing.T) {
Value: "MIT",
SPDXExpression: "MIT",
Type: license.Concluded,
Contents: mustContentsFromLocation(t, loc2, 23, 1105),
URLs: []string{"file://$GOPATH/pkg/mod/" + loc2.RealPath},
Locations: file.NewLocationSet(),
}},
@@ -125,6 +121,7 @@ func Test_LicenseSearch(t *testing.T) {
Value: "Apache-2.0",
SPDXExpression: "Apache-2.0",
Type: license.Concluded,
Contents: mustContentsFromLocation(t, loc3),
URLs: []string{"file://$GOPATH/pkg/mod/" + loc3.RealPath},
Locations: file.NewLocationSet(),
}},
@@ -139,6 +136,7 @@ func Test_LicenseSearch(t *testing.T) {
expected: []pkg.License{{
Value: "Apache-2.0",
SPDXExpression: "Apache-2.0",
Contents: mustContentsFromLocation(t, loc1),
Type: license.Concluded,
URLs: []string{server.URL + "/github.com/someorg/somename/@v/v0.3.2.zip#" + loc1.RealPath},
Locations: file.NewLocationSet(),
@@ -154,6 +152,7 @@ func Test_LicenseSearch(t *testing.T) {
expected: []pkg.License{{
Value: "MIT",
SPDXExpression: "MIT",
Contents: mustContentsFromLocation(t, loc2, 23, 1105), // offset for correct scanner contents
Type: license.Concluded,
URLs: []string{server.URL + "/github.com/CapORG/CapProject/@v/v4.111.5.zip#" + loc2.RealPath},
Locations: file.NewLocationSet(),
@@ -171,6 +170,7 @@ func Test_LicenseSearch(t *testing.T) {
expected: []pkg.License{{
Value: "MIT",
SPDXExpression: "MIT",
Contents: mustContentsFromLocation(t, loc2, 23, 1105), // offset for correct scanner contents
Type: license.Concluded,
URLs: []string{server.URL + "/github.com/CapORG/CapProject/@v/v4.111.5.zip#" + loc2.RealPath},
Locations: file.NewLocationSet(),
@@ -187,6 +187,7 @@ func Test_LicenseSearch(t *testing.T) {
Value: "Apache-2.0",
SPDXExpression: "Apache-2.0",
Type: license.Concluded,
Contents: mustContentsFromLocation(t, loc1),
URLs: []string{"file://$GO_VENDOR/github.com/someorg/somename/LICENSE"},
Locations: file.NewLocationSet(),
}},
@@ -201,6 +202,7 @@ func Test_LicenseSearch(t *testing.T) {
expected: []pkg.License{{
Value: "MIT",
SPDXExpression: "MIT",
Contents: mustContentsFromLocation(t, loc2, 23, 1105), // offset for correct scanner contents
Type: license.Concluded,
URLs: []string{"file://$GO_VENDOR/github.com/!cap!o!r!g/!cap!project/LICENSE.txt"},
Locations: file.NewLocationSet(),
@@ -216,6 +218,7 @@ func Test_LicenseSearch(t *testing.T) {
expected: []pkg.License{{
Value: "Apache-2.0",
SPDXExpression: "Apache-2.0",
Contents: mustContentsFromLocation(t, loc1),
Type: license.Concluded,
URLs: []string{"file://$GO_VENDOR/github.com/someorg/strangelicense/LiCeNsE.tXt"},
Locations: file.NewLocationSet(),
@@ -226,7 +229,7 @@ func Test_LicenseSearch(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
l := newGoLicenseResolver("", test.config)
lics := l.getLicenses(context.Background(), licenseScanner, fileresolver.Empty{}, test.name, test.version)
lics := l.getLicenses(ctx, fileresolver.Empty{}, test.name, test.version)
require.EqualValues(t, test.expected, lics)
})
}
@@ -301,10 +304,7 @@ func Test_findVersionPath(t *testing.T) {
func Test_walkDirErrors(t *testing.T) {
resolver := newGoLicenseResolver("", CatalogerConfig{})
sc := &licenses.ScannerConfig{Scanner: licensecheck.Scan, CoverageThreshold: 75}
scanner, err := licenses.NewScanner(sc)
require.NoError(t, err)
_, err = resolver.findLicensesInFS(context.Background(), scanner, "somewhere", badFS{})
_, err := resolver.findLicensesInFS(context.Background(), "somewhere", badFS{})
require.Error(t, err)
}
@@ -321,10 +321,7 @@ func Test_noLocalGoModDir(t *testing.T) {
validTmp := t.TempDir()
require.NoError(t, os.MkdirAll(filepath.Join(validTmp, "mod@ver"), 0700|os.ModeDir))
sc := &licenses.ScannerConfig{Scanner: licensecheck.Scan, CoverageThreshold: 75}
licenseScanner, err := licenses.NewScanner(sc)
require.NoError(t, err)
ctx := pkgtest.Context()
tests := []struct {
name string
dir string
@@ -358,35 +355,29 @@ func Test_noLocalGoModDir(t *testing.T) {
SearchLocalModCacheLicenses: true,
LocalModCacheDir: test.dir,
})
_, err := resolver.getLicensesFromLocal(context.Background(), licenseScanner, "mod", "ver")
_, err := resolver.getLicensesFromLocal(ctx, "mod", "ver")
test.wantErr(t, err)
})
}
}
func TestLicenseConversion(t *testing.T) {
inputLicenses := []pkg.License{
{
Value: "Apache-2.0",
SPDXExpression: "Apache-2.0",
Type: "concluded",
URLs: nil,
Locations: file.NewLocationSet(file.NewLocation("LICENSE")),
Contents: "",
},
{
Value: "UNKNOWN",
SPDXExpression: "UNKNOWN_4d1cffe420916f2b706300ab63fcafaf35226a0ad3725cb9f95b26036cefae32",
Type: "declared",
URLs: nil,
Locations: file.NewLocationSet(file.NewLocation("LICENSE2")),
Contents: "NVIDIA Software License Agreement and CUDA Supplement to Software License Agreement",
},
func mustContentsFromLocation(t *testing.T, loc file.Location, offset ...int) string {
t.Helper()
contentsPath := "test-fixtures/licenses/pkg/mod/" + loc.RealPath
contents, err := os.ReadFile(contentsPath)
require.NoErrorf(t, err, "could not open contents for fixture at %s", contentsPath)
if len(offset) == 0 {
return string(contents)
}
goLicenses := toGoLicenses(inputLicenses)
require.Equal(t, 2, len(offset), "invalid offset provided, expected two integers: start and end")
result := toPkgLicenses(goLicenses)
start, end := offset[0], offset[1]
require.GreaterOrEqual(t, start, 0, "offset start must be >= 0")
require.LessOrEqual(t, end, len(contents), "offset end must be <= content length")
require.LessOrEqual(t, start, end, "offset start must be <= end")
require.Equal(t, inputLicenses, result)
return string(contents[start:end])
}
+6 -12
View File
@@ -18,7 +18,6 @@ import (
"golang.org/x/mod/module"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/internal/licenses"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
@@ -63,11 +62,6 @@ func newGoBinaryCataloger(opts CatalogerConfig) *goBinaryCataloger {
func (c *goBinaryCataloger) parseGoBinary(ctx context.Context, resolver file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
var pkgs []pkg.Package
licenseScanner, err := licenses.ContextLicenseScanner(ctx)
if err != nil {
return nil, nil, err
}
unionReader, err := unionreader.GetUnionReader(reader.ReadCloser)
if err != nil {
return nil, nil, err
@@ -79,7 +73,7 @@ func (c *goBinaryCataloger) parseGoBinary(ctx context.Context, resolver file.Res
var rels []artifact.Relationship
for _, mod := range mods {
var depPkgs []pkg.Package
mainPkg, depPkgs := c.buildGoPkgInfo(ctx, licenseScanner, resolver, reader.Location, mod, mod.arch, unionReader)
mainPkg, depPkgs := c.buildGoPkgInfo(ctx, resolver, reader.Location, mod, mod.arch, unionReader)
if mainPkg != nil {
rels = createModuleRelationships(*mainPkg, depPkgs)
pkgs = append(pkgs, *mainPkg)
@@ -107,7 +101,7 @@ func createModuleRelationships(main pkg.Package, deps []pkg.Package) []artifact.
var emptyModule debug.Module
var moduleFromPartialPackageBuild = debug.Module{Path: "command-line-arguments"}
func (c *goBinaryCataloger) buildGoPkgInfo(ctx context.Context, licenseScanner licenses.Scanner, resolver file.Resolver, location file.Location, mod *extendedBuildInfo, arch string, reader io.ReadSeekCloser) (*pkg.Package, []pkg.Package) {
func (c *goBinaryCataloger) buildGoPkgInfo(ctx context.Context, resolver file.Resolver, location file.Location, mod *extendedBuildInfo, arch string, reader io.ReadSeekCloser) (*pkg.Package, []pkg.Package) {
if mod == nil {
return nil, nil
}
@@ -122,7 +116,7 @@ func (c *goBinaryCataloger) buildGoPkgInfo(ctx context.Context, licenseScanner l
continue
}
lics := c.licenseResolver.getLicenses(ctx, licenseScanner, resolver, dep.Path, dep.Version)
lics := c.licenseResolver.getLicenses(ctx, resolver, dep.Path, dep.Version)
gover, experiments := getExperimentsFromVersion(mod.GoVersion)
m := newBinaryMetadata(
@@ -150,7 +144,7 @@ func (c *goBinaryCataloger) buildGoPkgInfo(ctx context.Context, licenseScanner l
return nil, pkgs
}
main := c.makeGoMainPackage(ctx, licenseScanner, resolver, mod, arch, location, reader)
main := c.makeGoMainPackage(ctx, resolver, mod, arch, location, reader)
return &main, pkgs
}
@@ -165,9 +159,9 @@ func missingMainModule(mod *extendedBuildInfo) bool {
return mod.Main == moduleFromPartialPackageBuild
}
func (c *goBinaryCataloger) makeGoMainPackage(ctx context.Context, licenseScanner licenses.Scanner, resolver file.Resolver, mod *extendedBuildInfo, arch string, location file.Location, reader io.ReadSeekCloser) pkg.Package {
func (c *goBinaryCataloger) makeGoMainPackage(ctx context.Context, resolver file.Resolver, mod *extendedBuildInfo, arch string, location file.Location, reader io.ReadSeekCloser) pkg.Package {
gbs := getBuildSettings(mod.Settings)
lics := c.licenseResolver.getLicenses(ctx, licenseScanner, resolver, mod.Main.Path, mod.Main.Version)
lics := c.licenseResolver.getLicenses(ctx, resolver, mod.Main.Path, mod.Main.Version)
gover, experiments := getExperimentsFromVersion(mod.GoVersion)
m := newBinaryMetadata(
@@ -15,11 +15,9 @@ import (
"syscall"
"testing"
"github.com/google/licensecheck"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/internal/licenses"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/internal/fileresolver"
"github.com/anchore/syft/syft/internal/unionreader"
@@ -170,10 +168,6 @@ func TestBuildGoPkgInfo(t *testing.T) {
},
}
sc := &licenses.ScannerConfig{Scanner: licensecheck.Scan, CoverageThreshold: 75}
licenseScanner, err := licenses.NewScanner(sc)
require.NoError(t, err)
tests := []struct {
name string
mod *extendedBuildInfo
@@ -1074,7 +1068,7 @@ func TestBuildGoPkgInfo(t *testing.T) {
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)
mainPkg, pkgs := c.buildGoPkgInfo(context.Background(), fileresolver.Empty{}, location, test.mod, test.mod.arch, reader)
if mainPkg != nil {
pkgs = append(pkgs, *mainPkg)
}
+2 -8
View File
@@ -11,7 +11,6 @@ import (
"golang.org/x/mod/modfile"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/internal/licenses"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
@@ -35,11 +34,6 @@ func newGoModCataloger(opts CatalogerConfig) *goModCataloger {
func (c *goModCataloger) parseGoModFile(ctx context.Context, resolver file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
packages := make(map[string]pkg.Package)
licenseScanner, err := licenses.ContextLicenseScanner(ctx)
if err != nil {
return nil, nil, fmt.Errorf("unable to create default license scanner: %w", err)
}
contents, err := io.ReadAll(reader)
if err != nil {
return nil, nil, fmt.Errorf("failed to read go module: %w", err)
@@ -56,7 +50,7 @@ func (c *goModCataloger) parseGoModFile(ctx context.Context, resolver file.Resol
}
for _, m := range f.Require {
lics := c.licenseResolver.getLicenses(ctx, licenseScanner, resolver, m.Mod.Path, m.Mod.Version)
lics := c.licenseResolver.getLicenses(ctx, resolver, m.Mod.Path, m.Mod.Version)
packages[m.Mod.Path] = pkg.Package{
Name: m.Mod.Path,
Version: m.Mod.Version,
@@ -73,7 +67,7 @@ func (c *goModCataloger) parseGoModFile(ctx context.Context, resolver file.Resol
// remove any old packages and replace with new ones...
for _, m := range f.Replace {
lics := c.licenseResolver.getLicenses(ctx, licenseScanner, resolver, m.New.Path, m.New.Version)
lics := c.licenseResolver.getLicenses(ctx, resolver, m.New.Path, m.New.Version)
// the old path and new path may be the same, in which case this is a noop,
// but if they're different we need to remove the old package.
+7 -6
View File
@@ -1,6 +1,7 @@
package golang
import (
"context"
"fmt"
"strings"
@@ -11,12 +12,12 @@ import (
"github.com/anchore/syft/syft/pkg"
)
func stdlibProcessor(pkgs []pkg.Package, relationships []artifact.Relationship, err error) ([]pkg.Package, []artifact.Relationship, error) {
compilerPkgs, newRelationships := stdlibPackageAndRelationships(pkgs)
func stdlibProcessor(ctx context.Context, _ file.Resolver, pkgs []pkg.Package, relationships []artifact.Relationship, err error) ([]pkg.Package, []artifact.Relationship, error) {
compilerPkgs, newRelationships := stdlibPackageAndRelationships(ctx, pkgs)
return append(pkgs, compilerPkgs...), append(relationships, newRelationships...), err
}
func stdlibPackageAndRelationships(pkgs []pkg.Package) ([]pkg.Package, []artifact.Relationship) {
func stdlibPackageAndRelationships(ctx context.Context, pkgs []pkg.Package) ([]pkg.Package, []artifact.Relationship) {
var goCompilerPkgs []pkg.Package
var relationships []artifact.Relationship
totalLocations := file.NewLocationSet()
@@ -32,7 +33,7 @@ func stdlibPackageAndRelationships(pkgs []pkg.Package) ([]pkg.Package, []artifac
continue
}
stdLibPkg := newGoStdLib(mValue.GoCompiledVersion, goPkg.Locations)
stdLibPkg := newGoStdLib(ctx, mValue.GoCompiledVersion, goPkg.Locations)
if stdLibPkg == nil {
continue
}
@@ -49,7 +50,7 @@ func stdlibPackageAndRelationships(pkgs []pkg.Package) ([]pkg.Package, []artifac
return goCompilerPkgs, relationships
}
func newGoStdLib(version string, location file.LocationSet) *pkg.Package {
func newGoStdLib(ctx context.Context, version string, location file.LocationSet) *pkg.Package {
stdlibCpe, err := generateStdlibCpe(version)
if err != nil {
return nil
@@ -60,7 +61,7 @@ func newGoStdLib(version string, location file.LocationSet) *pkg.Package {
PURL: packageURL("stdlib", strings.TrimPrefix(version, "go")),
CPEs: []cpe.CPE{stdlibCpe},
Locations: location,
Licenses: pkg.NewLicenseSet(pkg.NewLicense("BSD-3-Clause")),
Licenses: pkg.NewLicenseSet(pkg.NewLicenseWithContext(ctx, "BSD-3-Clause")),
Language: pkg.Go,
Type: pkg.GoModulePkg,
Metadata: pkg.GolangBinaryBuildinfoEntry{
@@ -1,6 +1,7 @@
package golang
import (
"context"
"testing"
"github.com/google/go-cmp/cmp"
@@ -15,7 +16,7 @@ import (
)
func Test_stdlibPackageAndRelationships(t *testing.T) {
ctx := context.Background()
tests := []struct {
name string
pkgs []pkg.Package
@@ -87,7 +88,7 @@ func Test_stdlibPackageAndRelationships(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotPkgs, gotRels := stdlibPackageAndRelationships(tt.pkgs)
gotPkgs, gotRels := stdlibPackageAndRelationships(ctx, tt.pkgs)
assert.Len(t, gotPkgs, tt.wantPkgs)
assert.Len(t, gotRels, tt.wantRels)
})
@@ -97,6 +98,7 @@ func Test_stdlibPackageAndRelationships(t *testing.T) {
func Test_stdlibPackageAndRelationships_values(t *testing.T) {
loc := file.NewLocation("/bin/my-app")
locSet := file.NewLocationSet(loc)
ctx := context.TODO()
p := pkg.Package{
Name: "github.com/something/go",
Version: "1.0.0",
@@ -114,7 +116,7 @@ func Test_stdlibPackageAndRelationships_values(t *testing.T) {
PURL: packageURL("stdlib", "1.22.2"),
Language: pkg.Go,
Type: pkg.GoModulePkg,
Licenses: pkg.NewLicenseSet(pkg.NewLicense("BSD-3-Clause")),
Licenses: pkg.NewLicenseSet(pkg.NewLicenseWithContext(ctx, "BSD-3-Clause")),
CPEs: []cpe.CPE{
{
Attributes: cpe.MustAttributes("cpe:2.3:a:golang:go:1.22.2:-:*:*:*:*:*:*"),
@@ -135,7 +137,7 @@ func Test_stdlibPackageAndRelationships_values(t *testing.T) {
Type: artifact.DependencyOfRelationship,
}
gotPkgs, gotRels := stdlibPackageAndRelationships([]pkg.Package{p})
gotPkgs, gotRels := stdlibPackageAndRelationships(ctx, []pkg.Package{p})
require.Len(t, gotPkgs, 1)
gotPkg := gotPkgs[0]