fix: golang remote license search not executing when error reading local mod dir (#3549)

Signed-off-by: Keith Zantow <kzantow@gmail.com>
This commit is contained in:
Keith Zantow 2025-01-06 11:47:55 -05:00 committed by GitHub
parent 2a8c8ac832
commit a2a56dd3e9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 103 additions and 116 deletions

View File

@ -80,29 +80,38 @@ func remotesForModule(proxies []string, noProxy []string, module string) []strin
return proxies return proxies
} }
func (c *goLicenseResolver) getLicenses(ctx context.Context, scanner licenses.Scanner, resolver file.Resolver, moduleName, moduleVersion string) ([]pkg.License, error) { func (c *goLicenseResolver) getLicenses(ctx context.Context, scanner licenses.Scanner, resolver file.Resolver, moduleName, moduleVersion string) []pkg.License {
// search the scan target first, ignoring local and remote sources // search the scan target first, ignoring local and remote sources
goLicenses, err := c.findLicensesInSource(ctx, scanner, resolver, goLicenses, err := c.findLicensesInSource(ctx, scanner, resolver,
fmt.Sprintf(`**/go/pkg/mod/%s@%s/*`, processCaps(moduleName), moduleVersion), fmt.Sprintf(`**/go/pkg/mod/%s@%s/*`, processCaps(moduleName), moduleVersion),
) )
if err != nil || len(goLicenses) > 0 { if err != nil {
return toPkgLicenses(goLicenses), err log.WithFields("error", err, "module", moduleName, "version", moduleVersion).Trace("unable to read golang licenses from source")
}
if len(goLicenses) > 0 {
return toPkgLicenses(goLicenses)
} }
// look in the local host mod directory... // look in the local host mod directory...
if c.opts.SearchLocalModCacheLicenses { if c.opts.SearchLocalModCacheLicenses {
goLicenses, err = c.getLicensesFromLocal(ctx, scanner, moduleName, moduleVersion) goLicenses, err = c.getLicensesFromLocal(ctx, scanner, moduleName, moduleVersion)
if err != nil || len(goLicenses) > 0 { if err != nil {
return toPkgLicenses(goLicenses), err log.WithFields("error", err, "module", moduleName, "version", moduleVersion).Trace("unable to read golang licenses local")
}
if len(goLicenses) > 0 {
return toPkgLicenses(goLicenses)
} }
} }
// download from remote sources // download from remote sources
if c.opts.SearchRemoteLicenses { if c.opts.SearchRemoteLicenses {
goLicenses, err = c.getLicensesFromRemote(ctx, scanner, moduleName, moduleVersion) goLicenses, err = c.getLicensesFromRemote(ctx, scanner, moduleName, moduleVersion)
if err != nil {
log.WithFields("error", err, "module", moduleName, "version", moduleVersion).Debug("unable to read golang licenses remote")
}
} }
return toPkgLicenses(goLicenses), err return toPkgLicenses(goLicenses)
} }
func (c *goLicenseResolver) getLicensesFromLocal(ctx context.Context, scanner licenses.Scanner, moduleName, moduleVersion string) ([]goLicense, error) { func (c *goLicenseResolver) getLicensesFromLocal(ctx context.Context, scanner licenses.Scanner, moduleName, moduleVersion string) ([]goLicense, error) {

View File

@ -23,81 +23,11 @@ import (
"github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg"
) )
func Test_LocalLicenseSearch(t *testing.T) { func Test_LicenseSearch(t *testing.T) {
loc1 := file.NewLocation("github.com/someorg/somename@v0.3.2/LICENSE") 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") 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") loc3 := file.NewLocation("github.com/someorg/strangelicense@v1.2.3/LiCeNsE.tXt")
licenseScanner := licenses.TestingOnlyScanner()
tests := []struct {
name string
version string
expected pkg.License
}{
{
name: "github.com/someorg/somename",
version: "v0.3.2",
expected: pkg.License{
Value: "Apache-2.0",
SPDXExpression: "Apache-2.0",
Type: license.Concluded,
URLs: []string{"file://$GOPATH/pkg/mod/" + loc1.RealPath},
Locations: file.NewLocationSet(),
},
},
{
name: "github.com/CapORG/CapProject",
version: "v4.111.5",
expected: pkg.License{
Value: "MIT",
SPDXExpression: "MIT",
Type: license.Concluded,
URLs: []string{"file://$GOPATH/pkg/mod/" + loc2.RealPath},
Locations: file.NewLocationSet(),
},
},
{
name: "github.com/someorg/strangelicense",
version: "v1.2.3",
expected: pkg.License{
Value: "Apache-2.0",
SPDXExpression: "Apache-2.0",
Type: license.Concluded,
URLs: []string{"file://$GOPATH/pkg/mod/" + loc3.RealPath},
Locations: file.NewLocationSet(),
},
},
}
wd, err := os.Getwd()
require.NoError(t, err)
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
l := newGoLicenseResolver(
"",
CatalogerConfig{
SearchLocalModCacheLicenses: true,
LocalModCacheDir: filepath.Join(wd, "test-fixtures", "licenses", "pkg", "mod"),
},
)
lics, err := l.getLicenses(context.Background(), licenseScanner, fileresolver.Empty{}, test.name, test.version)
require.NoError(t, err)
require.Len(t, lics, 1)
require.Equal(t, test.expected, lics[0])
})
}
}
func Test_RemoteProxyLicenseSearch(t *testing.T) {
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")
licenseScanner := licenses.TestingOnlyScanner()
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
uri := strings.TrimPrefix(strings.TrimSuffix(r.RequestURI, ".zip"), "/") uri := strings.TrimPrefix(strings.TrimSuffix(r.RequestURI, ".zip"), "/")
@ -135,52 +65,116 @@ func Test_RemoteProxyLicenseSearch(t *testing.T) {
})) }))
defer server.Close() defer server.Close()
wd, err := os.Getwd()
require.NoError(t, err)
licenseScanner := licenses.TestingOnlyScanner()
tests := []struct { tests := []struct {
name string name string
version string version string
expected pkg.License config CatalogerConfig
expected []pkg.License
}{ }{
{ {
name: "github.com/someorg/somename", name: "github.com/someorg/somename",
version: "v0.3.2", version: "v0.3.2",
expected: pkg.License{ config: CatalogerConfig{
SearchLocalModCacheLicenses: true,
LocalModCacheDir: filepath.Join(wd, "test-fixtures", "licenses", "pkg", "mod"),
},
expected: []pkg.License{{
Value: "Apache-2.0",
SPDXExpression: "Apache-2.0",
Type: license.Concluded,
URLs: []string{"file://$GOPATH/pkg/mod/" + loc1.RealPath},
Locations: file.NewLocationSet(),
}},
},
{
name: "github.com/CapORG/CapProject",
version: "v4.111.5",
config: CatalogerConfig{
SearchLocalModCacheLicenses: true,
LocalModCacheDir: filepath.Join(wd, "test-fixtures", "licenses", "pkg", "mod"),
},
expected: []pkg.License{{
Value: "MIT",
SPDXExpression: "MIT",
Type: license.Concluded,
URLs: []string{"file://$GOPATH/pkg/mod/" + loc2.RealPath},
Locations: file.NewLocationSet(),
}},
},
{
name: "github.com/someorg/strangelicense",
version: "v1.2.3",
config: CatalogerConfig{
SearchLocalModCacheLicenses: true,
LocalModCacheDir: filepath.Join(wd, "test-fixtures", "licenses", "pkg", "mod"),
},
expected: []pkg.License{{
Value: "Apache-2.0",
SPDXExpression: "Apache-2.0",
Type: license.Concluded,
URLs: []string{"file://$GOPATH/pkg/mod/" + loc3.RealPath},
Locations: file.NewLocationSet(),
}},
},
{
name: "github.com/someorg/somename",
version: "v0.3.2",
config: CatalogerConfig{
SearchRemoteLicenses: true,
Proxies: []string{server.URL},
},
expected: []pkg.License{{
Value: "Apache-2.0", Value: "Apache-2.0",
SPDXExpression: "Apache-2.0", SPDXExpression: "Apache-2.0",
Type: license.Concluded, Type: license.Concluded,
URLs: []string{server.URL + "/github.com/someorg/somename/@v/v0.3.2.zip#" + loc1.RealPath}, URLs: []string{server.URL + "/github.com/someorg/somename/@v/v0.3.2.zip#" + loc1.RealPath},
Locations: file.NewLocationSet(), Locations: file.NewLocationSet(),
}, }},
}, },
{ {
name: "github.com/CapORG/CapProject", name: "github.com/CapORG/CapProject",
version: "v4.111.5", version: "v4.111.5",
expected: pkg.License{ config: CatalogerConfig{
SearchRemoteLicenses: true,
Proxies: []string{server.URL},
},
expected: []pkg.License{{
Value: "MIT", Value: "MIT",
SPDXExpression: "MIT", SPDXExpression: "MIT",
Type: license.Concluded, Type: license.Concluded,
URLs: []string{server.URL + "/github.com/CapORG/CapProject/@v/v4.111.5.zip#" + loc2.RealPath}, URLs: []string{server.URL + "/github.com/CapORG/CapProject/@v/v4.111.5.zip#" + loc2.RealPath},
Locations: file.NewLocationSet(), Locations: file.NewLocationSet(),
}},
},
{
name: "github.com/CapORG/CapProject",
version: "v4.111.5",
config: CatalogerConfig{
SearchLocalModCacheLicenses: true,
LocalModCacheDir: filepath.Join(wd, "test-fixtures"), // valid dir but does not find modules
SearchRemoteLicenses: true,
Proxies: []string{server.URL},
}, },
expected: []pkg.License{{
Value: "MIT",
SPDXExpression: "MIT",
Type: license.Concluded,
URLs: []string{server.URL + "/github.com/CapORG/CapProject/@v/v4.111.5.zip#" + loc2.RealPath},
Locations: file.NewLocationSet(),
}},
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
l := newGoLicenseResolver("", test.config)
l := newGoLicenseResolver( lics := l.getLicenses(context.Background(), licenseScanner, fileresolver.Empty{}, test.name, test.version)
"", require.EqualValues(t, test.expected, lics)
CatalogerConfig{
SearchRemoteLicenses: true,
Proxies: []string{server.URL},
},
)
lics, err := l.getLicenses(context.Background(), licenseScanner, fileresolver.Empty{}, test.name, test.version)
require.NoError(t, err)
require.Len(t, lics, 1)
require.Equal(t, test.expected, lics[0])
}) })
} }
} }

View File

@ -119,11 +119,7 @@ func (c *goBinaryCataloger) buildGoPkgInfo(ctx context.Context, licenseScanner l
continue continue
} }
lics, err := c.licenseResolver.getLicenses(ctx, licenseScanner, resolver, dep.Path, dep.Version) lics := c.licenseResolver.getLicenses(ctx, licenseScanner, resolver, dep.Path, dep.Version)
if err != nil {
log.Tracef("error getting licenses for golang package: %s %v", dep.Path, err)
}
gover, experiments := getExperimentsFromVersion(mod.GoVersion) gover, experiments := getExperimentsFromVersion(mod.GoVersion)
p := c.newGoBinaryPackage( p := c.newGoBinaryPackage(
dep, dep,
@ -162,12 +158,7 @@ func missingMainModule(mod *extendedBuildInfo) bool {
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, licenseScanner licenses.Scanner, resolver file.Resolver, mod *extendedBuildInfo, arch string, location file.Location, reader io.ReadSeekCloser) pkg.Package {
gbs := getBuildSettings(mod.Settings) gbs := getBuildSettings(mod.Settings)
lics := c.licenseResolver.getLicenses(ctx, licenseScanner, resolver, mod.Main.Path, mod.Main.Version)
lics, err := c.licenseResolver.getLicenses(ctx, licenseScanner, resolver, mod.Main.Path, mod.Main.Version)
if err != nil {
log.Tracef("error getting licenses for golang package: %s %v", mod.Main.Path, err)
}
gover, experiments := getExperimentsFromVersion(mod.GoVersion) gover, experiments := getExperimentsFromVersion(mod.GoVersion)
main := c.newGoBinaryPackage( main := c.newGoBinaryPackage(
&mod.Main, &mod.Main,

View File

@ -53,11 +53,7 @@ func (c *goModCataloger) parseGoModFile(ctx context.Context, resolver file.Resol
} }
for _, m := range f.Require { for _, m := range f.Require {
lics, err := c.licenseResolver.getLicenses(ctx, licenseScanner, resolver, m.Mod.Path, m.Mod.Version) lics := c.licenseResolver.getLicenses(ctx, licenseScanner, resolver, m.Mod.Path, m.Mod.Version)
if err != nil {
log.Tracef("error getting licenses for package: %s %v", m.Mod.Path, err)
}
packages[m.Mod.Path] = pkg.Package{ packages[m.Mod.Path] = pkg.Package{
Name: m.Mod.Path, Name: m.Mod.Path,
Version: m.Mod.Version, Version: m.Mod.Version,
@ -74,10 +70,7 @@ func (c *goModCataloger) parseGoModFile(ctx context.Context, resolver file.Resol
// remove any old packages and replace with new ones... // remove any old packages and replace with new ones...
for _, m := range f.Replace { for _, m := range f.Replace {
lics, err := c.licenseResolver.getLicenses(ctx, licenseScanner, resolver, m.New.Path, m.New.Version) lics := c.licenseResolver.getLicenses(ctx, licenseScanner, resolver, m.New.Path, m.New.Version)
if err != nil {
log.Tracef("error getting licenses for package: %s %v", m.New.Path, err)
}
// the old path and new path may be the same, in which case this is a noop, // 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. // but if they're different we need to remove the old package.