mirror of
https://github.com/anchore/syft.git
synced 2026-11-05 08:56:41 +01:00
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:
co-authored by
Alex Goodman
parent
b4d717fb30
commit
f77d503892
@@ -31,5 +31,5 @@ func NewGoModuleBinaryCataloger(opts CatalogerConfig) pkg.Cataloger {
|
||||
newGoBinaryCataloger(opts).parseGoBinary,
|
||||
mimetype.ExecutableMIMETypeSet.List()...,
|
||||
).
|
||||
WithProcessors(stdlibProcessor)
|
||||
WithResolvingProcessors(stdlibProcessor)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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])
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user