mirror of
https://github.com/anchore/syft.git
synced 2026-04-05 22:30:35 +02: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:
parent
b4d717fb30
commit
f77d503892
@ -158,8 +158,8 @@ func (cfg Catalog) ToFilesConfig() filecataloging.Config {
|
||||
|
||||
func (cfg Catalog) ToLicenseConfig() cataloging.LicenseConfig {
|
||||
return cataloging.LicenseConfig{
|
||||
IncludeUnkownLicenseContent: cfg.License.IncludeUnknownLicenseContent,
|
||||
Coverage: cfg.License.LicenseCoverage,
|
||||
IncludeContent: cfg.License.Content,
|
||||
Coverage: cfg.License.Coverage,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,12 +1,22 @@
|
||||
package options
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/anchore/clio"
|
||||
"github.com/anchore/syft/syft/cataloging"
|
||||
)
|
||||
|
||||
type licenseConfig struct {
|
||||
IncludeUnknownLicenseContent bool `yaml:"include-unknown-license-content" json:"include-unknown-license-content" mapstructure:"include-unknown-license-content"`
|
||||
LicenseCoverage float64 `yaml:"license-coverage" json:"license-coverage" mapstructure:"license-coverage"`
|
||||
Content cataloging.LicenseContent `yaml:"content" json:"content" mapstructure:"content"`
|
||||
// Deprecated: please use include-license-content instead
|
||||
IncludeUnknownLicenseContent *bool `yaml:"-" json:"-" mapstructure:"include-unknown-license-content"`
|
||||
|
||||
Coverage float64 `yaml:"coverage" json:"coverage" mapstructure:"coverage"`
|
||||
// Deprecated: please use coverage instead
|
||||
LicenseCoverage *float64 `yaml:"license-coverage" json:"license-coverage" mapstructure:"license-coverage"`
|
||||
|
||||
AvailableLicenseContent []cataloging.LicenseContent `yaml:"-" json:"-" mapstructure:"-"`
|
||||
}
|
||||
|
||||
var _ interface {
|
||||
@ -14,15 +24,56 @@ var _ interface {
|
||||
} = (*licenseConfig)(nil)
|
||||
|
||||
func (o *licenseConfig) DescribeFields(descriptions clio.FieldDescriptionSet) {
|
||||
descriptions.Add(&o.IncludeUnknownLicenseContent, `include the content of a license in the SBOM when syft
|
||||
cannot determine a valid SPDX ID for the given license`)
|
||||
descriptions.Add(&o.LicenseCoverage, `adjust the percent as a fraction of the total text, in normalized words, that
|
||||
descriptions.Add(&o.Content, fmt.Sprintf("include the content of licenses in the SBOM for a given syft scan; valid values are: %s", o.AvailableLicenseContent))
|
||||
descriptions.Add(&o.IncludeUnknownLicenseContent, `deprecated: please use 'license-content' instead`)
|
||||
|
||||
descriptions.Add(&o.Coverage, `adjust the percent as a fraction of the total text, in normalized words, that
|
||||
matches any valid license for the given inputs, expressed as a percentage across all of the licenses matched.`)
|
||||
descriptions.Add(&o.LicenseCoverage, `deprecated: please use 'coverage' instead`)
|
||||
}
|
||||
|
||||
func (o *licenseConfig) PostLoad() error {
|
||||
cfg := cataloging.DefaultLicenseConfig()
|
||||
defaultContent := cfg.IncludeContent
|
||||
defaultCoverage := cfg.Coverage
|
||||
|
||||
// if both legacy and new fields are specified, error out
|
||||
if o.IncludeUnknownLicenseContent != nil && o.Content != defaultContent {
|
||||
return fmt.Errorf("both 'include-unknown-license-content' and 'content' are set, please use only 'content'")
|
||||
}
|
||||
|
||||
if o.LicenseCoverage != nil && o.Coverage != defaultCoverage {
|
||||
return fmt.Errorf("both 'license-coverage' and 'coverage' are set, please use only 'coverage'")
|
||||
}
|
||||
|
||||
// finalize the license content value
|
||||
if o.IncludeUnknownLicenseContent != nil {
|
||||
// convert 'include-unknown-license-content' -> 'license-content'
|
||||
v := cataloging.LicenseContentExcludeAll
|
||||
if *o.IncludeUnknownLicenseContent {
|
||||
v = cataloging.LicenseContentIncludeUnknown
|
||||
}
|
||||
o.Content = v
|
||||
}
|
||||
|
||||
// finalize the coverage value
|
||||
if o.LicenseCoverage != nil {
|
||||
// convert 'license-coverage' -> 'coverage'
|
||||
o.Coverage = *o.LicenseCoverage
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func defaultLicenseConfig() licenseConfig {
|
||||
cfg := cataloging.DefaultLicenseConfig()
|
||||
return licenseConfig{
|
||||
IncludeUnknownLicenseContent: false,
|
||||
LicenseCoverage: 75,
|
||||
Content: cfg.IncludeContent,
|
||||
Coverage: cfg.Coverage,
|
||||
AvailableLicenseContent: []cataloging.LicenseContent{
|
||||
cataloging.LicenseContentIncludeAll,
|
||||
cataloging.LicenseContentIncludeUnknown,
|
||||
cataloging.LicenseContentExcludeAll,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@ import (
|
||||
)
|
||||
|
||||
func TestSetContextLicenseScanner(t *testing.T) {
|
||||
scanner := testScanner(true)
|
||||
scanner := testScanner()
|
||||
ctx := context.Background()
|
||||
ctx = SetContextLicenseScanner(ctx, scanner)
|
||||
|
||||
@ -20,7 +20,7 @@ func TestSetContextLicenseScanner(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIsContextLicenseScannerSet(t *testing.T) {
|
||||
scanner := testScanner(true)
|
||||
scanner := testScanner()
|
||||
ctx := context.Background()
|
||||
require.False(t, IsContextLicenseScannerSet(ctx))
|
||||
|
||||
@ -30,7 +30,7 @@ func TestIsContextLicenseScannerSet(t *testing.T) {
|
||||
|
||||
func TestContextLicenseScanner(t *testing.T) {
|
||||
t.Run("with scanner", func(t *testing.T) {
|
||||
scanner := testScanner(true)
|
||||
scanner := testScanner()
|
||||
ctx := SetContextLicenseScanner(context.Background(), scanner)
|
||||
s, err := ContextLicenseScanner(ctx)
|
||||
if err != nil || s != scanner {
|
||||
|
||||
36
internal/licenses/find_evidence.go
Normal file
36
internal/licenses/find_evidence.go
Normal file
@ -0,0 +1,36 @@
|
||||
package licenses
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
)
|
||||
|
||||
func (s *scanner) FindEvidence(_ context.Context, reader io.Reader) (evidence []Evidence, content []byte, err error) {
|
||||
if s.scanner == nil {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
content, err = io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
cov := s.scanner(content)
|
||||
if cov.Percent < s.coverageThreshold {
|
||||
// unknown or no licenses here
|
||||
// => check return content to Search to process
|
||||
return nil, content, nil
|
||||
}
|
||||
|
||||
evidence = make([]Evidence, 0)
|
||||
for _, m := range cov.Match {
|
||||
evidence = append(evidence, Evidence{
|
||||
ID: m.ID,
|
||||
Type: m.Type,
|
||||
Start: m.Start,
|
||||
End: m.End,
|
||||
IsURL: m.IsURL,
|
||||
})
|
||||
}
|
||||
return evidence, content, nil
|
||||
}
|
||||
81
internal/licenses/find_evidence_test.go
Normal file
81
internal/licenses/find_evidence_test.go
Normal file
@ -0,0 +1,81 @@
|
||||
package licenses
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/google/licensecheck"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestDefaultScanner_FindEvidence(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
fixture string
|
||||
wantIDs []string // expected license IDs
|
||||
minMatch int // minimum # of matches required
|
||||
}{
|
||||
{
|
||||
name: "Single licenses are able to be recognized and returned Apache 2.0",
|
||||
fixture: "test-fixtures/apache-license-2.0",
|
||||
wantIDs: []string{"Apache-2.0"},
|
||||
minMatch: 1,
|
||||
},
|
||||
{
|
||||
name: "Multiple Licenses are returned as evidence with duplicates at different offset",
|
||||
fixture: "test-fixtures/multi-license",
|
||||
wantIDs: []string{
|
||||
"MIT",
|
||||
"MIT",
|
||||
"NCSA",
|
||||
"Apache-2.0",
|
||||
"Zlib",
|
||||
"Unlicense",
|
||||
"BSD-2-Clause",
|
||||
"BSD-2-Clause",
|
||||
"BSD-3-Clause",
|
||||
},
|
||||
minMatch: 2,
|
||||
},
|
||||
}
|
||||
|
||||
scanner := testScanner()
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
filePath := filepath.Clean(tc.fixture)
|
||||
f, err := os.Open(filePath)
|
||||
require.NoError(t, err)
|
||||
defer f.Close()
|
||||
|
||||
evidence, content, err := scanner.FindEvidence(context.Background(), f)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, content)
|
||||
require.GreaterOrEqual(t, len(evidence), tc.minMatch, "expected at least %d matches", tc.minMatch)
|
||||
|
||||
var foundIDs []string
|
||||
for _, ev := range evidence {
|
||||
foundIDs = append(foundIDs, ev.ID)
|
||||
}
|
||||
|
||||
require.ElementsMatch(t, tc.wantIDs, foundIDs, "expected license IDs %v, but got %v", tc.wantIDs, foundIDs)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func testScanner() Scanner {
|
||||
return &scanner{
|
||||
coverageThreshold: DefaultCoverageThreshold,
|
||||
scanner: licensecheck.Scan,
|
||||
}
|
||||
}
|
||||
|
||||
func mustOpen(fixture string) []byte {
|
||||
content, err := os.ReadFile(fixture)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return content
|
||||
}
|
||||
@ -8,32 +8,36 @@ import (
|
||||
"github.com/google/licensecheck"
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
const (
|
||||
UnknownLicensePrefix = unknownLicenseType + "_"
|
||||
DefaultCoverageThreshold = 75 // determined by experimentation
|
||||
DefaultIncludeLicenseContent = false
|
||||
|
||||
unknownLicenseType = "UNKNOWN"
|
||||
)
|
||||
|
||||
type Evidence struct {
|
||||
ID string // License identifier. (See licenses/README.md.)
|
||||
Type licensecheck.Type // The type of the license: BSD, MIT, etc.
|
||||
Start int // Start offset of match in text; match is at text[Start:End].
|
||||
End int // End offset of match in text.
|
||||
IsURL bool // Whether match is a URL.
|
||||
}
|
||||
|
||||
type Scanner interface {
|
||||
IdentifyLicenseIDs(context.Context, io.Reader) ([]string, []byte, error)
|
||||
FileSearch(context.Context, file.LocationReadCloser) ([]file.License, error)
|
||||
PkgSearch(context.Context, file.LocationReadCloser) ([]pkg.License, error)
|
||||
FindEvidence(context.Context, io.Reader) ([]Evidence, []byte, error)
|
||||
}
|
||||
|
||||
var _ Scanner = (*scanner)(nil)
|
||||
|
||||
type scanner struct {
|
||||
coverageThreshold float64 // between 0 and 100
|
||||
includeLicenseContent bool
|
||||
scanner func([]byte) licensecheck.Coverage
|
||||
}
|
||||
|
||||
type ScannerConfig struct {
|
||||
CoverageThreshold float64
|
||||
IncludeLicenseContent bool
|
||||
Scanner func([]byte) licensecheck.Coverage
|
||||
}
|
||||
|
||||
@ -45,12 +49,6 @@ func WithCoverage(coverage float64) Option {
|
||||
}
|
||||
}
|
||||
|
||||
func WithIncludeLicenseContent(includeLicenseContent bool) Option {
|
||||
return func(s *scanner) {
|
||||
s.includeLicenseContent = includeLicenseContent
|
||||
}
|
||||
}
|
||||
|
||||
// NewDefaultScanner returns a scanner that uses a new instance of the default licensecheck package scanner.
|
||||
func NewDefaultScanner(o ...Option) (Scanner, error) {
|
||||
s, err := licensecheck.NewScanner(licensecheck.BuiltinLicenses())
|
||||
@ -58,9 +56,9 @@ func NewDefaultScanner(o ...Option) (Scanner, error) {
|
||||
log.WithFields("error", err).Trace("unable to create default license scanner")
|
||||
return nil, fmt.Errorf("unable to create default license scanner: %w", err)
|
||||
}
|
||||
|
||||
newScanner := &scanner{
|
||||
coverageThreshold: DefaultCoverageThreshold,
|
||||
includeLicenseContent: DefaultIncludeLicenseContent,
|
||||
scanner: s.Scan,
|
||||
}
|
||||
|
||||
@ -79,7 +77,6 @@ func NewScanner(c *ScannerConfig) (Scanner, error) {
|
||||
|
||||
return &scanner{
|
||||
coverageThreshold: c.CoverageThreshold,
|
||||
includeLicenseContent: c.IncludeLicenseContent,
|
||||
scanner: c.Scanner,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -1,84 +0,0 @@
|
||||
package licenses
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/google/licensecheck"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestIdentifyLicenseIDs(t *testing.T) {
|
||||
type expectation struct {
|
||||
yieldError bool
|
||||
ids []string
|
||||
content []byte
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
in string
|
||||
expected expectation
|
||||
}{
|
||||
{
|
||||
name: "apache license 2.0",
|
||||
in: `test-fixtures/apache-license-2.0`,
|
||||
expected: expectation{
|
||||
yieldError: false,
|
||||
ids: []string{"Apache-2.0"},
|
||||
content: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "custom license includes content for IdentifyLicenseIDs",
|
||||
in: "test-fixtures/nvidia-software-and-cuda-supplement",
|
||||
expected: expectation{
|
||||
yieldError: false,
|
||||
ids: []string{},
|
||||
content: mustOpen("test-fixtures/nvidia-software-and-cuda-supplement"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
content, err := os.ReadFile(test.in)
|
||||
require.NoError(t, err)
|
||||
ids, content, err := testScanner(false).IdentifyLicenseIDs(context.TODO(), bytes.NewReader(content))
|
||||
if test.expected.yieldError {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, ids, len(test.expected.ids))
|
||||
require.Len(t, content, len(test.expected.content))
|
||||
|
||||
if len(test.expected.ids) > 0 {
|
||||
require.Equal(t, ids, test.expected.ids)
|
||||
}
|
||||
|
||||
if len(test.expected.content) > 0 {
|
||||
require.Equal(t, content, test.expected.content)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func testScanner(includeLicenseContent bool) Scanner {
|
||||
return &scanner{
|
||||
coverageThreshold: DefaultCoverageThreshold,
|
||||
includeLicenseContent: includeLicenseContent,
|
||||
scanner: licensecheck.Scan,
|
||||
}
|
||||
}
|
||||
|
||||
func mustOpen(fixture string) []byte {
|
||||
content, err := os.ReadFile(fixture)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return content
|
||||
}
|
||||
@ -1,123 +0,0 @@
|
||||
package licenses
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/license"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
const (
|
||||
unknownLicenseType = "UNKNOWN"
|
||||
UnknownLicensePrefix = unknownLicenseType + "_"
|
||||
)
|
||||
|
||||
func getCustomLicenseContentHash(contents []byte) string {
|
||||
hash := sha256.Sum256(contents)
|
||||
return fmt.Sprintf("%x", hash[:])
|
||||
}
|
||||
|
||||
func (s *scanner) IdentifyLicenseIDs(_ context.Context, reader io.Reader) ([]string, []byte, error) {
|
||||
if s.scanner == nil {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
content, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
cov := s.scanner(content)
|
||||
if cov.Percent < s.coverageThreshold {
|
||||
// unknown or no licenses here
|
||||
// => check return content to Search to process
|
||||
return nil, content, nil
|
||||
}
|
||||
|
||||
var ids []string
|
||||
for _, m := range cov.Match {
|
||||
ids = append(ids, m.ID)
|
||||
}
|
||||
return ids, nil, nil
|
||||
}
|
||||
|
||||
// PkgSearch scans the contents of a license file to attempt to determine the type of license it is
|
||||
func (s *scanner) PkgSearch(ctx context.Context, reader file.LocationReadCloser) (licenses []pkg.License, err error) {
|
||||
licenses = make([]pkg.License, 0)
|
||||
|
||||
ids, content, err := s.IdentifyLicenseIDs(ctx, reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// IdentifyLicenseIDs can only return a list of ID or content
|
||||
// These return values are mutually exclusive.
|
||||
// If the scanner threshold for matching scores < 75% then we return the license full content
|
||||
if len(ids) > 0 {
|
||||
for _, id := range ids {
|
||||
lic := pkg.NewLicenseFromLocations(id, reader.Location)
|
||||
lic.Type = license.Concluded
|
||||
|
||||
licenses = append(licenses, lic)
|
||||
}
|
||||
} else if len(content) > 0 {
|
||||
// harmonize line endings to unix compatible first:
|
||||
// 1. \r\n => \n (Windows => UNIX)
|
||||
// 2. \r => \n (Macintosh => UNIX)
|
||||
content = []byte(strings.ReplaceAll(strings.ReplaceAll(string(content), "\r\n", "\n"), "\r", "\n"))
|
||||
|
||||
lic := pkg.NewLicenseFromLocations(unknownLicenseType, reader.Location)
|
||||
lic.SPDXExpression = UnknownLicensePrefix + getCustomLicenseContentHash(content)
|
||||
if s.includeLicenseContent {
|
||||
lic.Contents = string(content)
|
||||
}
|
||||
lic.Type = license.Declared
|
||||
|
||||
licenses = append(licenses, lic)
|
||||
}
|
||||
|
||||
return licenses, nil
|
||||
}
|
||||
|
||||
// FileSearch scans the contents of a license file to attempt to determine the type of license it is
|
||||
func (s *scanner) FileSearch(ctx context.Context, reader file.LocationReadCloser) (licenses []file.License, err error) {
|
||||
licenses = make([]file.License, 0)
|
||||
|
||||
ids, content, err := s.IdentifyLicenseIDs(ctx, reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// IdentifyLicenseIDs can only return a list of ID or content
|
||||
// These return values are mutually exclusive.
|
||||
// If the scanner threshold for matching scores < 75% then we return the license full content
|
||||
if len(ids) > 0 {
|
||||
for _, id := range ids {
|
||||
lic := file.NewLicense(id)
|
||||
lic.Type = license.Concluded
|
||||
|
||||
licenses = append(licenses, lic)
|
||||
}
|
||||
} else if len(content) > 0 {
|
||||
// harmonize line endings to unix compatible first:
|
||||
// 1. \r\n => \n (Windows => UNIX)
|
||||
// 2. \r => \n (Macintosh => UNIX)
|
||||
content = []byte(strings.ReplaceAll(strings.ReplaceAll(string(content), "\r\n", "\n"), "\r", "\n"))
|
||||
|
||||
lic := file.NewLicense(unknownLicenseType)
|
||||
lic.SPDXExpression = UnknownLicensePrefix + getCustomLicenseContentHash(content)
|
||||
if s.includeLicenseContent {
|
||||
lic.Contents = string(content)
|
||||
}
|
||||
lic.Type = license.Declared
|
||||
|
||||
licenses = append(licenses, lic)
|
||||
}
|
||||
|
||||
return licenses, nil
|
||||
}
|
||||
@ -1,166 +0,0 @@
|
||||
package licenses
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
type bytesReadCloser struct {
|
||||
bytes.Buffer
|
||||
}
|
||||
|
||||
func (brc *bytesReadCloser) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func newBytesReadCloser(data []byte) *bytesReadCloser {
|
||||
return &bytesReadCloser{
|
||||
Buffer: *bytes.NewBuffer(data),
|
||||
}
|
||||
}
|
||||
|
||||
func TestSearchFileLicenses(t *testing.T) {
|
||||
type expectation struct {
|
||||
yieldError bool
|
||||
licenses []file.License
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
in string
|
||||
includeUnkownLicenseContent bool
|
||||
expected expectation
|
||||
}{
|
||||
{
|
||||
name: "apache license 2.0",
|
||||
in: "test-fixtures/apache-license-2.0",
|
||||
expected: expectation{
|
||||
yieldError: false,
|
||||
licenses: []file.License{
|
||||
{
|
||||
Value: "Apache-2.0",
|
||||
SPDXExpression: "Apache-2.0",
|
||||
Type: "concluded",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
content, err := os.ReadFile(test.in)
|
||||
require.NoError(t, err)
|
||||
s := testScanner(false)
|
||||
result, err := s.FileSearch(ctx, file.NewLocationReadCloser(file.NewLocation("LICENSE"), io.NopCloser(bytes.NewReader(content))))
|
||||
if test.expected.yieldError {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, result, len(test.expected.licenses))
|
||||
|
||||
if len(test.expected.licenses) > 0 {
|
||||
require.Equal(t, test.expected.licenses, result)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSearchPkgLicenses(t *testing.T) {
|
||||
type expectation struct {
|
||||
wantErr require.ErrorAssertionFunc
|
||||
licenses []pkg.License
|
||||
}
|
||||
|
||||
testLocation := file.NewLocation("LICENSE")
|
||||
tests := []struct {
|
||||
name string
|
||||
in string
|
||||
includeUnkownLicenseContent bool
|
||||
expected expectation
|
||||
}{
|
||||
{
|
||||
name: "apache license 2.0",
|
||||
in: "test-fixtures/apache-license-2.0",
|
||||
expected: expectation{
|
||||
licenses: []pkg.License{
|
||||
{
|
||||
Value: "Apache-2.0",
|
||||
SPDXExpression: "Apache-2.0",
|
||||
Type: "concluded",
|
||||
URLs: nil,
|
||||
Locations: file.NewLocationSet(testLocation),
|
||||
Contents: "",
|
||||
},
|
||||
},
|
||||
wantErr: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "custom license no content by default",
|
||||
in: "test-fixtures/nvidia-software-and-cuda-supplement",
|
||||
expected: expectation{
|
||||
licenses: []pkg.License{
|
||||
{
|
||||
Value: "UNKNOWN",
|
||||
SPDXExpression: "UNKNOWN_eebcea3ab1d1a28e671de90119ffcfb35fe86951e4af1b17af52b7a82fcf7d0a",
|
||||
Type: "declared",
|
||||
URLs: nil,
|
||||
Locations: file.NewLocationSet(testLocation),
|
||||
Contents: "",
|
||||
},
|
||||
},
|
||||
wantErr: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "custom license with content when scanner has content config",
|
||||
in: "test-fixtures/nvidia-software-and-cuda-supplement",
|
||||
includeUnkownLicenseContent: true,
|
||||
expected: expectation{
|
||||
licenses: []pkg.License{
|
||||
{
|
||||
Value: "UNKNOWN",
|
||||
SPDXExpression: "UNKNOWN_eebcea3ab1d1a28e671de90119ffcfb35fe86951e4af1b17af52b7a82fcf7d0a",
|
||||
Type: "declared",
|
||||
URLs: nil,
|
||||
Locations: file.NewLocationSet(testLocation),
|
||||
Contents: string(mustOpen("test-fixtures/nvidia-software-and-cuda-supplement")),
|
||||
},
|
||||
},
|
||||
wantErr: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
content, err := os.ReadFile(test.in)
|
||||
require.NoError(t, err)
|
||||
s := testScanner(test.includeUnkownLicenseContent)
|
||||
result, err := s.PkgSearch(ctx, file.NewLocationReadCloser(file.NewLocation("LICENSE"), io.NopCloser(bytes.NewReader(content))))
|
||||
if test.expected.wantErr != nil {
|
||||
test.expected.wantErr(t, err)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, result, len(test.expected.licenses))
|
||||
|
||||
if len(test.expected.licenses) > 0 {
|
||||
require.Equal(t, test.expected.licenses, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
445
internal/licenses/test-fixtures/multi-license
Normal file
445
internal/licenses/test-fixtures/multi-license
Normal file
@ -0,0 +1,445 @@
|
||||
Emscripten is available under 2 licenses, the MIT license and the
|
||||
University of Illinois/NCSA Open Source License.
|
||||
|
||||
Both are permissive open source licenses, with little if any
|
||||
practical difference between them.
|
||||
|
||||
The reason for offering both is that (1) the MIT license is
|
||||
well-known, while (2) the University of Illinois/NCSA Open Source
|
||||
License allows Emscripten's code to be integrated upstream into
|
||||
LLVM, which uses that license, should the opportunity arise.
|
||||
|
||||
Additionally, the binaryen project is available under the Apache License
|
||||
Version 2.0.
|
||||
|
||||
The full text of all three licenses follows.
|
||||
|
||||
==============================================================================
|
||||
|
||||
Copyright (c) 2010-2014 Emscripten authors, see AUTHORS file.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
==============================================================================
|
||||
|
||||
Copyright (c) 2010-2014 Emscripten authors, see AUTHORS file.
|
||||
All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the
|
||||
"Software"), to deal with the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimers.
|
||||
|
||||
Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimers
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
Neither the names of Mozilla,
|
||||
nor the names of its contributors may be used to endorse
|
||||
or promote products derived from this Software without specific prior
|
||||
written permission.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
|
||||
|
||||
==============================================================================
|
||||
|
||||
This program uses portions of Node.js source code located in src/library_path.js,
|
||||
in accordance with the terms of the MIT license. Node's license follows:
|
||||
|
||||
"""
|
||||
Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
deal in the Software without restriction, including without limitation the
|
||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN THE SOFTWARE.
|
||||
"""
|
||||
|
||||
==============================================================================
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
==============================================================================
|
||||
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
==============================================================================
|
||||
|
||||
Files: tools/filelock.py
|
||||
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <http://unlicense.org>
|
||||
|
||||
==============================================================================
|
||||
|
||||
Files: tools/eliminator/node_modules/uglify-js/... tools/node_modules/terser/...
|
||||
|
||||
Distributed under the BSD license:
|
||||
|
||||
Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials
|
||||
provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
|
||||
==============================================================================
|
||||
|
||||
Files: system/include/webgpu/webgpu.h
|
||||
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2019, "WebGPU native" developers
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
==============================================================================
|
||||
|
||||
Copyright (c) 2005-2011 David Schultz <das@FreeBSD.ORG>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
@ -107,6 +107,11 @@ func finalizePkgCatalogerResults(cfg CatalogingFactoryConfig, resolver file.Path
|
||||
}
|
||||
}
|
||||
|
||||
// we want to know if the user wants to preserve license content or not in the final SBOM
|
||||
// note: this looks incorrect, but pkg.License.Content is NOT used to compute the Package ID
|
||||
// this does NOT change the reproducibility of the Package ID
|
||||
applyLicenseContentRules(&p, cfg.LicenseConfig)
|
||||
|
||||
pkgs[i] = p
|
||||
}
|
||||
return pkgs, relationships
|
||||
@ -262,3 +267,29 @@ func packageFileOwnershipRelationships(p pkg.Package, resolver file.PathResolver
|
||||
}
|
||||
return relationships, nil
|
||||
}
|
||||
|
||||
func applyLicenseContentRules(p *pkg.Package, cfg cataloging.LicenseConfig) {
|
||||
if p.Licenses.Empty() {
|
||||
return
|
||||
}
|
||||
|
||||
licenses := p.Licenses.ToSlice()
|
||||
for i := range licenses {
|
||||
l := &licenses[i]
|
||||
switch cfg.IncludeContent {
|
||||
case cataloging.LicenseContentIncludeUnknown:
|
||||
// we don't have an SPDX expression, which means we didn't find an SPDX license
|
||||
// include the unknown licenses content in the final SBOM
|
||||
if l.SPDXExpression != "" {
|
||||
licenses[i].Contents = ""
|
||||
}
|
||||
case cataloging.LicenseContentExcludeAll:
|
||||
// clear it all out
|
||||
licenses[i].Contents = ""
|
||||
case cataloging.LicenseContentIncludeAll:
|
||||
// always include the content
|
||||
}
|
||||
}
|
||||
|
||||
p.Licenses = pkg.NewLicenseSet(licenses...)
|
||||
}
|
||||
|
||||
@ -117,6 +117,114 @@ func TestFilterNonCompliantPackages(t *testing.T) {
|
||||
assert.Equal(t, p2, droppedPkgs[0])
|
||||
}
|
||||
|
||||
func TestApplyLicenseContentRules(t *testing.T) {
|
||||
licenseWithSPDX := pkg.License{
|
||||
SPDXExpression: "MIT",
|
||||
Contents: "MIT license content",
|
||||
}
|
||||
licenseWithoutSPDX := pkg.License{
|
||||
Value: "License-Not-A-SPDX-Expression",
|
||||
Contents: "Non-SPDX license content",
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
inputLicenses []pkg.License
|
||||
cfg cataloging.LicenseConfig
|
||||
expectedLicenses []pkg.License
|
||||
}{
|
||||
{
|
||||
name: "LicenseContentIncludeUnknown",
|
||||
inputLicenses: []pkg.License{
|
||||
licenseWithSPDX,
|
||||
licenseWithoutSPDX,
|
||||
},
|
||||
cfg: cataloging.LicenseConfig{
|
||||
IncludeContent: cataloging.LicenseContentIncludeUnknown,
|
||||
},
|
||||
expectedLicenses: []pkg.License{
|
||||
{
|
||||
SPDXExpression: "MIT",
|
||||
Contents: "", // content cleared for SPDX license
|
||||
},
|
||||
{
|
||||
Value: "License-Not-A-SPDX-Expression",
|
||||
Contents: "Non-SPDX license content", // content preserved for non-SPDX
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "LicenseContentExcludeAll",
|
||||
inputLicenses: []pkg.License{
|
||||
licenseWithSPDX,
|
||||
licenseWithoutSPDX,
|
||||
},
|
||||
cfg: cataloging.LicenseConfig{
|
||||
IncludeContent: cataloging.LicenseContentExcludeAll,
|
||||
},
|
||||
expectedLicenses: []pkg.License{
|
||||
{
|
||||
SPDXExpression: "MIT",
|
||||
Contents: "", // content cleared
|
||||
},
|
||||
{
|
||||
Value: "License-Not-A-SPDX-Expression",
|
||||
Contents: "", // content cleared
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "IncludeLicenseContentDefault",
|
||||
inputLicenses: []pkg.License{
|
||||
licenseWithSPDX,
|
||||
licenseWithoutSPDX,
|
||||
},
|
||||
cfg: cataloging.LicenseConfig{
|
||||
IncludeContent: cataloging.LicenseContentIncludeAll,
|
||||
},
|
||||
expectedLicenses: []pkg.License{
|
||||
{
|
||||
SPDXExpression: "MIT",
|
||||
Contents: "MIT license content", // content preserved
|
||||
},
|
||||
{
|
||||
Value: "License-Not-A-SPDX-Expression",
|
||||
Contents: "Non-SPDX license content", // content preserved
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Empty licenses",
|
||||
inputLicenses: []pkg.License{},
|
||||
cfg: cataloging.LicenseConfig{
|
||||
IncludeContent: cataloging.LicenseContentIncludeAll,
|
||||
},
|
||||
expectedLicenses: []pkg.License{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
inputPkg := &pkg.Package{
|
||||
Licenses: pkg.NewLicenseSet(tt.inputLicenses...),
|
||||
}
|
||||
|
||||
inputPkg.SetID()
|
||||
originalID := inputPkg.ID()
|
||||
|
||||
applyLicenseContentRules(inputPkg, tt.cfg)
|
||||
|
||||
assert.Equal(t, originalID, inputPkg.ID(), "package ID changed unexpectedly")
|
||||
|
||||
actualLicenses := inputPkg.Licenses.ToSlice()
|
||||
expectedLicenses := pkg.NewLicenseSet(tt.expectedLicenses...).ToSlice()
|
||||
|
||||
assert.Equal(t, expectedLicenses, actualLicenses, "license contents do not match expected values")
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyComplianceRules_DropAndStub(t *testing.T) {
|
||||
p := pkg.Package{Name: "", Version: ""}
|
||||
p.SetID()
|
||||
|
||||
@ -1,15 +1,33 @@
|
||||
package cataloging
|
||||
|
||||
import "github.com/anchore/syft/internal/licenses"
|
||||
import (
|
||||
"github.com/anchore/syft/internal/licenses"
|
||||
)
|
||||
|
||||
// LicenseContent controls when license content should be included in the SBOM.
|
||||
type LicenseContent string
|
||||
|
||||
const (
|
||||
LicenseContentIncludeAll LicenseContent = "all"
|
||||
LicenseContentIncludeUnknown LicenseContent = "unknown"
|
||||
LicenseContentExcludeAll LicenseContent = "none"
|
||||
)
|
||||
|
||||
type LicenseConfig struct {
|
||||
IncludeUnkownLicenseContent bool `json:"include-unknown-license-content" yaml:"include-unknown-license-content" mapstructure:"include-unknown-license-content"`
|
||||
// IncludeUnknownLicenseContent controls whether the content of a license should be included in the SBOM when the license ID cannot be determined.
|
||||
// Deprecated: use IncludeContent instead
|
||||
IncludeUnknownLicenseContent bool `json:"-" yaml:"-" mapstructure:"-"`
|
||||
|
||||
// IncludeContent controls whether license copy discovered should be included in the SBOM.
|
||||
IncludeContent LicenseContent `json:"include-content" yaml:"include-content" mapstructure:"include-content"`
|
||||
|
||||
// Coverage is the percentage of text that must match a license for it to be considered a match.
|
||||
Coverage float64 `json:"coverage" yaml:"coverage" mapstructure:"coverage"`
|
||||
}
|
||||
|
||||
func DefaultLicenseConfig() LicenseConfig {
|
||||
return LicenseConfig{
|
||||
IncludeUnkownLicenseContent: licenses.DefaultIncludeLicenseContent,
|
||||
IncludeContent: LicenseContentExcludeAll,
|
||||
Coverage: licenses.DefaultCoverageThreshold,
|
||||
}
|
||||
}
|
||||
|
||||
@ -109,7 +109,6 @@ func setupContext(ctx context.Context, cfg *CreateSBOMConfig) (context.Context,
|
||||
func SetContextLicenseScanner(ctx context.Context, cfg cataloging.LicenseConfig) (context.Context, error) {
|
||||
// inject a single license scanner and content config for all package cataloging tasks into context
|
||||
licenseScanner, err := licenses.NewDefaultScanner(
|
||||
licenses.WithIncludeLicenseContent(cfg.IncludeUnkownLicenseContent),
|
||||
licenses.WithCoverage(cfg.Coverage),
|
||||
)
|
||||
if err != nil {
|
||||
|
||||
@ -5,7 +5,6 @@ import (
|
||||
"crypto/sha1"
|
||||
"fmt"
|
||||
"path"
|
||||
"regexp"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
@ -15,7 +14,6 @@ import (
|
||||
"github.com/spdx/tools-golang/spdx"
|
||||
|
||||
"github.com/anchore/packageurl-go"
|
||||
internallicenses "github.com/anchore/syft/internal/licenses"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/internal/mimetype"
|
||||
"github.com/anchore/syft/internal/relationship"
|
||||
@ -50,7 +48,7 @@ func ToFormatModel(s sbom.SBOM) *spdx.Document {
|
||||
name, namespace := helpers.DocumentNameAndNamespace(s.Source, s.Descriptor)
|
||||
|
||||
rels := relationship.NewIndex(s.Relationships...)
|
||||
packages := toPackages(rels, s.Artifacts.Packages, s)
|
||||
packages, otherLicenses := toPackages(rels, s.Artifacts.Packages, s)
|
||||
|
||||
allRelationships := toRelationships(rels.All())
|
||||
|
||||
@ -156,7 +154,7 @@ func ToFormatModel(s sbom.SBOM) *spdx.Document {
|
||||
Packages: packages,
|
||||
Files: toFiles(s),
|
||||
Relationships: allRelationships,
|
||||
OtherLicenses: toOtherLicenses(s.Artifacts.Packages),
|
||||
OtherLicenses: convertOtherLicense(otherLicenses),
|
||||
}
|
||||
}
|
||||
|
||||
@ -319,16 +317,18 @@ func toSPDXID(identifiable artifact.Identifiable) spdx.ElementID {
|
||||
// packages populates all Package Information from the package Collection (see https://spdx.github.io/spdx-spec/3-package-information/)
|
||||
//
|
||||
//nolint:funlen
|
||||
func toPackages(rels *relationship.Index, catalog *pkg.Collection, sbom sbom.SBOM) (results []*spdx.Package) {
|
||||
func toPackages(rels *relationship.Index, catalog *pkg.Collection, sbom sbom.SBOM) (results []*spdx.Package, otherLicenses []spdx.OtherLicense) {
|
||||
otherLicenseSet := helpers.NewSPDXOtherLicenseSet()
|
||||
for _, p := range catalog.Sorted() {
|
||||
// name should be guaranteed to be unique, but semantically useful and stable
|
||||
// name should be guaranteed to be unique but semantically useful and stable
|
||||
id := toSPDXID(p)
|
||||
|
||||
// If the Concluded License is not the same as the Declared License, a written explanation should be provided
|
||||
// in the Comments on License field (section 7.16). With respect to NOASSERTION, a written explanation in
|
||||
// the Comments on License field (section 7.16) is preferred.
|
||||
// extract these correctly to the spdx license format
|
||||
concluded, declared := helpers.License(p)
|
||||
concluded, declared, ol := helpers.License(p)
|
||||
otherLicenseSet.Add(ol...)
|
||||
|
||||
// two ways to get filesAnalyzed == true:
|
||||
// 1. syft has generated a sha1 digest for the package itself - usually in the java cataloger
|
||||
@ -487,7 +487,7 @@ func toPackages(rels *relationship.Index, catalog *pkg.Collection, sbom sbom.SBO
|
||||
PackageAttributionTexts: nil,
|
||||
})
|
||||
}
|
||||
return results
|
||||
return results, otherLicenseSet.ToSlice()
|
||||
}
|
||||
|
||||
func toPackageChecksums(p pkg.Package) ([]spdx.Checksum, bool) {
|
||||
@ -740,81 +740,14 @@ func toFileTypes(metadata *file.Metadata) (ty []string) {
|
||||
return ty
|
||||
}
|
||||
|
||||
// other licenses are for licenses from the pkg.Package that do not have a valid SPDX Expression
|
||||
// OR are an expression that is a single `License-Ref-*`
|
||||
func toOtherLicenses(catalog *pkg.Collection) []*spdx.OtherLicense {
|
||||
licenses := map[string]helpers.SPDXLicense{}
|
||||
|
||||
for p := range catalog.Enumerate() {
|
||||
declaredLicenses, concludedLicenses := helpers.ParseLicenses(p.Licenses.ToSlice())
|
||||
for _, l := range declaredLicenses {
|
||||
if l.Value != "" {
|
||||
licenses[l.ID] = l
|
||||
}
|
||||
if l.ID != "" && isLicenseRef(l.ID) {
|
||||
licenses[l.ID] = l
|
||||
}
|
||||
}
|
||||
for _, l := range concludedLicenses {
|
||||
if l.Value != "" {
|
||||
licenses[l.ID] = l
|
||||
}
|
||||
if l.ID != "" && isLicenseRef(l.ID) {
|
||||
licenses[l.ID] = l
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var result []*spdx.OtherLicense
|
||||
|
||||
var ids []string
|
||||
for licenseID := range licenses {
|
||||
ids = append(ids, licenseID)
|
||||
}
|
||||
|
||||
slices.Sort(ids)
|
||||
for _, id := range ids {
|
||||
license := licenses[id]
|
||||
value := license.Value
|
||||
fullText := license.FullText
|
||||
// handle cases where LicenseRef needs to be included in hasExtractedLicensingInfos
|
||||
if license.Value == "" {
|
||||
value, _ = strings.CutPrefix(license.ID, "LicenseRef-")
|
||||
}
|
||||
other := &spdx.OtherLicense{
|
||||
LicenseIdentifier: license.ID,
|
||||
}
|
||||
if fullText != "" {
|
||||
other.ExtractedText = fullText
|
||||
} else {
|
||||
other.ExtractedText = value
|
||||
}
|
||||
customPrefix := spdxlicense.LicenseRefPrefix + helpers.SanitizeElementID(internallicenses.UnknownLicensePrefix)
|
||||
if strings.HasPrefix(license.ID, customPrefix) {
|
||||
other.LicenseName = strings.TrimPrefix(license.ID, customPrefix)
|
||||
other.LicenseComment = strings.Trim(internallicenses.UnknownLicensePrefix, "-_")
|
||||
}
|
||||
result = append(result, other)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
var licenseRefRegEx = regexp.MustCompile(`^LicenseRef-[A-Za-z0-9_-]+$`)
|
||||
|
||||
// isSingularLicenseRef checks if the string is a singular LicenseRef-* identifier
|
||||
func isLicenseRef(s string) bool {
|
||||
// Match the input string against the regex
|
||||
return licenseRefRegEx.MatchString(s)
|
||||
}
|
||||
|
||||
// TODO: handle SPDX excludes file case
|
||||
// f file is an "excludes" file, skip it /* exclude SPDX analysis file(s) */
|
||||
// see: https://spdx.github.io/spdx-spec/v2.3/package-information/#79-package-verification-code-field
|
||||
// the above link contains the SPDX algorithm for a package verification code
|
||||
func newPackageVerificationCode(rels *relationship.Index, p pkg.Package, sbom sbom.SBOM) *spdx.PackageVerificationCode {
|
||||
// key off of the contains relationship;
|
||||
// spdx validator will fail if a package claims to contain a file but no sha1 provided
|
||||
// if a sha1 for a file is provided then the validator will fail if the package does not have
|
||||
// key off of the spdx contains relationship;
|
||||
// spdx validator will fail if a package claims to contain a file, but no sha1 provided
|
||||
// if a sha1 for a file is provided, then the validator will fail if the package does not have
|
||||
// a package verification code
|
||||
coordinates := rels.Coordinates(p, artifact.ContainsRelationship)
|
||||
var digests []file.Digest
|
||||
@ -887,3 +820,15 @@ func convertAbsoluteToRelative(absPath string) (string, error) {
|
||||
|
||||
return relPath, nil
|
||||
}
|
||||
|
||||
func convertOtherLicense(otherLicenses []spdx.OtherLicense) []*spdx.OtherLicense {
|
||||
if len(otherLicenses) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
result := make([]*spdx.OtherLicense, 0, len(otherLicenses))
|
||||
for i := range otherLicenses {
|
||||
result = append(result, &otherLicenses[i])
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
package spdxhelpers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
@ -736,7 +738,7 @@ func Test_H1Digest(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
catalog := pkg.NewCollection(test.pkg)
|
||||
pkgs := toPackages(relationship.NewIndex(), catalog, s)
|
||||
pkgs, _ := toPackages(relationship.NewIndex(), catalog, s)
|
||||
require.Len(t, pkgs, 1)
|
||||
for _, p := range pkgs {
|
||||
if test.expectedDigest == "" {
|
||||
@ -753,29 +755,31 @@ func Test_H1Digest(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_OtherLicenses(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
tests := []struct {
|
||||
name string
|
||||
pkg pkg.Package
|
||||
expected []*spdx.OtherLicense
|
||||
expected []spdx.OtherLicense
|
||||
}{
|
||||
{
|
||||
name: "no licenseRef",
|
||||
pkg: pkg.Package{
|
||||
Licenses: pkg.NewLicenseSet(),
|
||||
},
|
||||
expected: nil,
|
||||
expected: []spdx.OtherLicense{},
|
||||
},
|
||||
{
|
||||
name: "single licenseRef",
|
||||
pkg: pkg.Package{
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicense("foobar"),
|
||||
pkg.NewLicenseWithContext(ctx, "foobar"),
|
||||
),
|
||||
},
|
||||
expected: []*spdx.OtherLicense{
|
||||
expected: []spdx.OtherLicense{
|
||||
{
|
||||
LicenseIdentifier: "LicenseRef-foobar",
|
||||
ExtractedText: "foobar",
|
||||
LicenseName: "foobar",
|
||||
ExtractedText: "NOASSERTION",
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -783,18 +787,20 @@ func Test_OtherLicenses(t *testing.T) {
|
||||
name: "multiple licenseRef",
|
||||
pkg: pkg.Package{
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicense("internal made up license name"),
|
||||
pkg.NewLicense("new apple license 2.0"),
|
||||
pkg.NewLicenseWithContext(ctx, "internal made up license name"),
|
||||
pkg.NewLicenseWithContext(ctx, "new apple license 2.0"),
|
||||
),
|
||||
},
|
||||
expected: []*spdx.OtherLicense{
|
||||
expected: []spdx.OtherLicense{
|
||||
{
|
||||
LicenseIdentifier: "LicenseRef-internal-made-up-license-name",
|
||||
ExtractedText: "internal made up license name",
|
||||
ExtractedText: "NOASSERTION",
|
||||
LicenseName: "internal made up license name",
|
||||
},
|
||||
{
|
||||
LicenseIdentifier: "LicenseRef-new-apple-license-2.0",
|
||||
ExtractedText: "new apple license 2.0",
|
||||
ExtractedText: "NOASSERTION",
|
||||
LicenseName: "new apple license 2.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -802,31 +808,27 @@ func Test_OtherLicenses(t *testing.T) {
|
||||
name: "LicenseRef as a valid spdx expression",
|
||||
pkg: pkg.Package{
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicense("LicenseRef-Fedora-Public-Domain"),
|
||||
pkg.NewLicenseWithContext(ctx, "LicenseRef-Fedora-Public-Domain"),
|
||||
),
|
||||
},
|
||||
expected: []*spdx.OtherLicense{
|
||||
{
|
||||
LicenseIdentifier: "LicenseRef-Fedora-Public-Domain",
|
||||
ExtractedText: "Fedora-Public-Domain",
|
||||
},
|
||||
},
|
||||
expected: []spdx.OtherLicense{},
|
||||
},
|
||||
{
|
||||
name: "LicenseRef as a valid spdx expression does not otherize compound spdx expressions",
|
||||
pkg: pkg.Package{
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicense("(MIT AND LicenseRef-Fedora-Public-Domain)"),
|
||||
pkg.NewLicenseWithContext(ctx, "(MIT AND LicenseRef-Fedora-Public-Domain)"),
|
||||
),
|
||||
},
|
||||
expected: nil,
|
||||
expected: []spdx.OtherLicense{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
catalog := pkg.NewCollection(test.pkg)
|
||||
otherLicenses := toOtherLicenses(catalog)
|
||||
rels := relationship.NewIndex()
|
||||
_, otherLicenses := toPackages(rels, catalog, sbom.SBOM{})
|
||||
require.Len(t, otherLicenses, len(test.expected))
|
||||
require.Equal(t, test.expected, otherLicenses)
|
||||
})
|
||||
@ -902,18 +904,19 @@ func Test_toSPDXID(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_otherLicenses(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
pkg1 := pkg.Package{
|
||||
Name: "first-pkg",
|
||||
Version: "1.1",
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicense("MIT"),
|
||||
pkg.NewLicenseWithContext(ctx, "MIT"),
|
||||
),
|
||||
}
|
||||
pkg2 := pkg.Package{
|
||||
Name: "second-pkg",
|
||||
Version: "2.2",
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicense("non spdx license"),
|
||||
pkg.NewLicenseWithContext(ctx, "non spdx license"),
|
||||
),
|
||||
}
|
||||
bigText := `
|
||||
@ -923,7 +926,7 @@ func Test_otherLicenses(t *testing.T) {
|
||||
Name: "third-pkg",
|
||||
Version: "3.3",
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicense(bigText),
|
||||
pkg.NewLicenseWithContext(ctx, bigText),
|
||||
),
|
||||
}
|
||||
|
||||
@ -938,22 +941,25 @@ func Test_otherLicenses(t *testing.T) {
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "other licenses includes original text",
|
||||
name: "other licenses must include some original text",
|
||||
packages: []pkg.Package{pkg2},
|
||||
expected: []*spdx.OtherLicense{
|
||||
{
|
||||
LicenseIdentifier: "LicenseRef-non-spdx-license",
|
||||
ExtractedText: "non spdx license",
|
||||
LicenseName: "non spdx license",
|
||||
ExtractedText: "NOASSERTION",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "big licenses get hashed",
|
||||
name: "big licenses get hashed and space is trimmed",
|
||||
packages: []pkg.Package{pkg3},
|
||||
expected: []*spdx.OtherLicense{
|
||||
{
|
||||
LicenseIdentifier: "LicenseRef-e9a1e42833d3e456f147052f4d312101bd171a0798893169fe596ca6b55c049e",
|
||||
ExtractedText: bigText,
|
||||
LicenseIdentifier: "LicenseRef-3f17782eef51ae86f18fdd6832f5918e2b40f688b52c9adc07ba6ec1024ef408",
|
||||
// Carries through the syft-json license value when we shasum large texts
|
||||
LicenseName: "LicenseRef-sha256:3f17782eef51ae86f18fdd6832f5918e2b40f688b52c9adc07ba6ec1024ef408",
|
||||
ExtractedText: strings.TrimSpace(bigText),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package spdxhelpers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
@ -535,14 +536,14 @@ func parseSPDXLicenses(p *spdx.Package) []pkg.License {
|
||||
|
||||
// concluded
|
||||
if p.PackageLicenseConcluded != helpers.NOASSERTION && p.PackageLicenseConcluded != helpers.NONE && p.PackageLicenseConcluded != "" {
|
||||
l := pkg.NewLicense(cleanSPDXID(p.PackageLicenseConcluded))
|
||||
l := pkg.NewLicenseWithContext(context.TODO(), cleanSPDXID(p.PackageLicenseConcluded))
|
||||
l.Type = license.Concluded
|
||||
licenses = append(licenses, l)
|
||||
}
|
||||
|
||||
// declared
|
||||
if p.PackageLicenseDeclared != helpers.NOASSERTION && p.PackageLicenseDeclared != helpers.NONE && p.PackageLicenseDeclared != "" {
|
||||
l := pkg.NewLicense(cleanSPDXID(p.PackageLicenseDeclared))
|
||||
l := pkg.NewLicenseWithContext(context.TODO(), cleanSPDXID(p.PackageLicenseDeclared))
|
||||
l.Type = license.Declared
|
||||
licenses = append(licenses, l)
|
||||
}
|
||||
|
||||
@ -24,7 +24,7 @@
|
||||
},
|
||||
"components": [
|
||||
{
|
||||
"bom-ref": "4dd25c6ee16b729a",
|
||||
"bom-ref": "f04d218ff5ff50db",
|
||||
"type": "library",
|
||||
"name": "package-1",
|
||||
"version": "1.0.1",
|
||||
|
||||
@ -25,7 +25,7 @@
|
||||
},
|
||||
"components": [
|
||||
{
|
||||
"bom-ref": "72567175418f73f8",
|
||||
"bom-ref": "2f52f617f1548337",
|
||||
"type": "library",
|
||||
"name": "package-1",
|
||||
"version": "1.0.1",
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
</component>
|
||||
</metadata>
|
||||
<components>
|
||||
<component bom-ref="4dd25c6ee16b729a" type="library">
|
||||
<component bom-ref="f04d218ff5ff50db" type="library">
|
||||
<name>package-1</name>
|
||||
<version>1.0.1</version>
|
||||
<licenses>
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
</component>
|
||||
</metadata>
|
||||
<components>
|
||||
<component bom-ref="72567175418f73f8" type="library">
|
||||
<component bom-ref="2f52f617f1548337" type="library">
|
||||
<name>package-1</name>
|
||||
<version>1.0.1</version>
|
||||
<licenses>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"strings"
|
||||
|
||||
@ -58,11 +59,11 @@ func decodeLicenses(c *cyclonedx.Component) []pkg.License {
|
||||
// these fields are mutually exclusive in the spec
|
||||
switch {
|
||||
case l.License != nil && l.License.ID != "":
|
||||
licenses = append(licenses, pkg.NewLicenseFromURLs(l.License.ID, l.License.URL))
|
||||
licenses = append(licenses, pkg.NewLicenseFromURLsWithContext(context.TODO(), l.License.ID, l.License.URL))
|
||||
case l.License != nil && l.License.Name != "":
|
||||
licenses = append(licenses, pkg.NewLicenseFromURLs(l.License.Name, l.License.URL))
|
||||
licenses = append(licenses, pkg.NewLicenseFromURLsWithContext(context.TODO(), l.License.Name, l.License.URL))
|
||||
case l.Expression != "":
|
||||
licenses = append(licenses, pkg.NewLicense(l.Expression))
|
||||
licenses = append(licenses, pkg.NewLicenseWithContext(context.TODO(), l.Expression))
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/CycloneDX/cyclonedx-go"
|
||||
@ -12,6 +13,7 @@ import (
|
||||
)
|
||||
|
||||
func Test_encodeLicense(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
tests := []struct {
|
||||
name string
|
||||
input pkg.Package
|
||||
@ -25,7 +27,7 @@ func Test_encodeLicense(t *testing.T) {
|
||||
name: "no SPDX licenses",
|
||||
input: pkg.Package{
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicense("RandomLicense"),
|
||||
pkg.NewLicenseWithContext(ctx, "RandomLicense"),
|
||||
),
|
||||
},
|
||||
expected: &cyclonedx.Licenses{
|
||||
@ -40,8 +42,8 @@ func Test_encodeLicense(t *testing.T) {
|
||||
name: "single SPDX ID and Non SPDX ID",
|
||||
input: pkg.Package{
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicense("mit"),
|
||||
pkg.NewLicense("FOOBAR"),
|
||||
pkg.NewLicenseWithContext(ctx, "mit"),
|
||||
pkg.NewLicenseWithContext(ctx, "FOOBAR"),
|
||||
),
|
||||
},
|
||||
expected: &cyclonedx.Licenses{
|
||||
@ -61,7 +63,7 @@ func Test_encodeLicense(t *testing.T) {
|
||||
name: "with complex SPDX license expression",
|
||||
input: pkg.Package{
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicense("MIT AND GPL-3.0-only"),
|
||||
pkg.NewLicenseWithContext(ctx, "MIT AND GPL-3.0-only"),
|
||||
),
|
||||
},
|
||||
expected: &cyclonedx.Licenses{
|
||||
@ -74,8 +76,8 @@ func Test_encodeLicense(t *testing.T) {
|
||||
name: "with multiple complex SPDX license expression",
|
||||
input: pkg.Package{
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicense("MIT AND GPL-3.0-only"),
|
||||
pkg.NewLicense("MIT AND GPL-3.0-only WITH Classpath-exception-2.0"),
|
||||
pkg.NewLicenseWithContext(ctx, "MIT AND GPL-3.0-only"),
|
||||
pkg.NewLicenseWithContext(ctx, "MIT AND GPL-3.0-only WITH Classpath-exception-2.0"),
|
||||
),
|
||||
},
|
||||
expected: &cyclonedx.Licenses{
|
||||
@ -88,9 +90,9 @@ func Test_encodeLicense(t *testing.T) {
|
||||
name: "with multiple URLs and expressions",
|
||||
input: pkg.Package{
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromURLs("MIT", "https://opensource.org/licenses/MIT", "https://spdx.org/licenses/MIT.html"),
|
||||
pkg.NewLicense("MIT AND GPL-3.0-only"),
|
||||
pkg.NewLicenseFromURLs("FakeLicense", "htts://someurl.com"),
|
||||
pkg.NewLicenseFromURLsWithContext(ctx, "MIT", "https://opensource.org/licenses/MIT", "https://spdx.org/licenses/MIT.html"),
|
||||
pkg.NewLicenseWithContext(ctx, "MIT AND GPL-3.0-only"),
|
||||
pkg.NewLicenseFromURLsWithContext(ctx, "FakeLicense", "htts://someurl.com"),
|
||||
),
|
||||
},
|
||||
expected: &cyclonedx.Licenses{
|
||||
@ -123,8 +125,8 @@ func Test_encodeLicense(t *testing.T) {
|
||||
name: "with multiple values licenses are deduplicated",
|
||||
input: pkg.Package{
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicense("Apache-2"),
|
||||
pkg.NewLicense("Apache-2.0"),
|
||||
pkg.NewLicenseWithContext(ctx, "Apache-2"),
|
||||
pkg.NewLicenseWithContext(ctx, "Apache-2.0"),
|
||||
),
|
||||
},
|
||||
expected: &cyclonedx.Licenses{
|
||||
@ -139,9 +141,9 @@ func Test_encodeLicense(t *testing.T) {
|
||||
name: "with multiple URLs and single with no URLs",
|
||||
input: pkg.Package{
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicense("MIT"),
|
||||
pkg.NewLicenseFromURLs("MIT", "https://opensource.org/licenses/MIT", "https://spdx.org/licenses/MIT.html"),
|
||||
pkg.NewLicense("MIT AND GPL-3.0-only"),
|
||||
pkg.NewLicenseWithContext(ctx, "MIT"),
|
||||
pkg.NewLicenseFromURLsWithContext(ctx, "MIT", "https://opensource.org/licenses/MIT", "https://spdx.org/licenses/MIT.html"),
|
||||
pkg.NewLicenseWithContext(ctx, "MIT AND GPL-3.0-only"),
|
||||
),
|
||||
},
|
||||
expected: &cyclonedx.Licenses{
|
||||
@ -167,7 +169,7 @@ func Test_encodeLicense(t *testing.T) {
|
||||
{
|
||||
name: "single parenthesized SPDX expression",
|
||||
input: pkg.Package{
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromValues("(MIT OR Apache-2.0)")...),
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromValuesWithContext(ctx, "(MIT OR Apache-2.0)")...),
|
||||
},
|
||||
expected: &cyclonedx.Licenses{
|
||||
{
|
||||
@ -179,7 +181,7 @@ func Test_encodeLicense(t *testing.T) {
|
||||
name: "single license AND to parenthesized SPDX expression",
|
||||
// (LGPL-3.0-or-later OR GPL-2.0-or-later OR (LGPL-3.0-or-later AND GPL-2.0-or-later)) AND GFDL-1.3-invariants-or-later
|
||||
input: pkg.Package{
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromValues("(LGPL-3.0-or-later OR GPL-2.0-or-later OR (LGPL-3.0-or-later AND GPL-2.0-or-later)) AND GFDL-1.3-invariants-or-later")...),
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromValuesWithContext(ctx, "(LGPL-3.0-or-later OR GPL-2.0-or-later OR (LGPL-3.0-or-later AND GPL-2.0-or-later)) AND GFDL-1.3-invariants-or-later")...),
|
||||
},
|
||||
expected: &cyclonedx.Licenses{
|
||||
{
|
||||
@ -248,7 +250,6 @@ func TestDecodeLicenses(t *testing.T) {
|
||||
Value: "RandomLicense",
|
||||
// CycloneDX specification doesn't give a field for determining the license type
|
||||
Type: license.Declared,
|
||||
URLs: []string{},
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -268,7 +269,6 @@ func TestDecodeLicenses(t *testing.T) {
|
||||
Value: "MIT",
|
||||
SPDXExpression: "MIT",
|
||||
Type: license.Declared,
|
||||
URLs: []string{},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@ -1,16 +1,17 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/spdx/tools-golang/spdx"
|
||||
|
||||
"github.com/anchore/syft/internal/spdxlicense"
|
||||
"github.com/anchore/syft/syft/license"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
func License(p pkg.Package) (concluded, declared string) {
|
||||
func License(p pkg.Package) (concluded, declared string, otherLicenses []spdx.OtherLicense) {
|
||||
// source: https://spdx.github.io/spdx-spec/v2.3/package-information/#713-concluded-license-field
|
||||
// The options to populate this field are limited to:
|
||||
// A valid SPDX License Expression as defined in Annex D;
|
||||
@ -21,15 +22,15 @@ func License(p pkg.Package) (concluded, declared string) {
|
||||
// (iii) the SPDX file creator has intentionally provided no information (no meaning should be implied by doing so).
|
||||
|
||||
if p.Licenses.Empty() {
|
||||
return NOASSERTION, NOASSERTION
|
||||
return NOASSERTION, NOASSERTION, nil
|
||||
}
|
||||
|
||||
// take all licenses and assume an AND expression;
|
||||
// for information about license expressions see:
|
||||
// https://spdx.github.io/spdx-spec/v2.3/SPDX-license-expressions/
|
||||
pc, pd := ParseLicenses(p.Licenses.ToSlice())
|
||||
pc, pd, ol := ParseLicenses(p.Licenses.ToSlice())
|
||||
|
||||
return joinLicenses(pc), joinLicenses(pd)
|
||||
return joinLicenses(pc), joinLicenses(pd), ol
|
||||
}
|
||||
|
||||
func joinLicenses(licenses []SPDXLicense) string {
|
||||
@ -58,14 +59,32 @@ func joinLicenses(licenses []SPDXLicense) string {
|
||||
}
|
||||
|
||||
type SPDXLicense struct {
|
||||
// Valid SPDX ID OR License Value (should have LicenseRef- prefix and be sanitized)
|
||||
// OR combination of the above as a valid SPDX License Expression as defined in Annex D.
|
||||
// https://spdx.github.io/spdx-spec/v2.3/SPDX-license-expressions/
|
||||
ID string
|
||||
Value string
|
||||
FullText string
|
||||
// If the SPDX license is not on the SPDX License List
|
||||
LicenseName string
|
||||
FullText string // 0..1 (Mandatory, one) if there is a License Identifier assigned (LicenseRef).
|
||||
URLs []string
|
||||
}
|
||||
|
||||
func ParseLicenses(raw []pkg.License) (concluded, declared []SPDXLicense) {
|
||||
func ParseLicenses(raw []pkg.License) (concluded, declared []SPDXLicense, otherLicenses []spdx.OtherLicense) {
|
||||
for _, l := range raw {
|
||||
candidate := createSPDXLicense(l)
|
||||
|
||||
// isCustomLicense determines if the candidate falls under https://spdx.github.io/spdx-spec/v2.3/other-licensing-information-detected/#
|
||||
// of the SPDX spec, where:
|
||||
// - we should not have a complex SPDX expression
|
||||
// - if a single license, it should not be a known license (on the SPDX license list)
|
||||
if l.SPDXExpression == "" && strings.Contains(candidate.ID, spdxlicense.LicenseRefPrefix) {
|
||||
otherLicenses = append(otherLicenses, spdx.OtherLicense{
|
||||
LicenseIdentifier: candidate.ID,
|
||||
ExtractedText: candidate.FullText,
|
||||
LicenseName: candidate.LicenseName,
|
||||
LicenseCrossReferences: candidate.URLs,
|
||||
})
|
||||
}
|
||||
switch l.Type {
|
||||
case license.Concluded:
|
||||
concluded = append(concluded, candidate)
|
||||
@ -74,35 +93,70 @@ func ParseLicenses(raw []pkg.License) (concluded, declared []SPDXLicense) {
|
||||
}
|
||||
}
|
||||
|
||||
return concluded, declared
|
||||
return concluded, declared, otherLicenses
|
||||
}
|
||||
|
||||
func createSPDXLicense(l pkg.License) SPDXLicense {
|
||||
candidate := SPDXLicense{
|
||||
// source: https://spdx.github.io/spdx-spec/v2.3/other-licensing-information-detected/#102-extracted-text-field
|
||||
// we need to populate this field in the spdx document if we have a license ref
|
||||
// 0..1 (Mandatory, one) if there is a License Identifier assigned (LicenseRef).
|
||||
ft := NOASSERTION
|
||||
if l.Contents != "" {
|
||||
ft = l.Contents
|
||||
}
|
||||
|
||||
return SPDXLicense{
|
||||
ID: generateLicenseID(l),
|
||||
FullText: l.Contents,
|
||||
}
|
||||
|
||||
if l.SPDXExpression == "" {
|
||||
candidate.Value = l.Value
|
||||
}
|
||||
return candidate
|
||||
LicenseName: l.Value,
|
||||
FullText: ft,
|
||||
URLs: l.URLs,
|
||||
}
|
||||
}
|
||||
|
||||
// generateLicenseID generates a license ID for the given license, which is either the license value or the SPDX expression.
|
||||
func generateLicenseID(l pkg.License) string {
|
||||
if l.SPDXExpression != "" {
|
||||
return l.SPDXExpression
|
||||
}
|
||||
if l.Value != "" {
|
||||
return spdxlicense.LicenseRefPrefix + SanitizeElementID(l.Value)
|
||||
|
||||
// syft format includes the algo for the sha in the values
|
||||
// we can strip this and just make LicenseRef-<sum> for spdx consumption
|
||||
id := strings.ReplaceAll(l.Value, "sha256:", "")
|
||||
if !strings.HasPrefix(id, "LicenseRef-") {
|
||||
id = "LicenseRef-" + id
|
||||
}
|
||||
return licenseSum(l.Contents)
|
||||
return SanitizeElementID(id)
|
||||
}
|
||||
|
||||
func licenseSum(s string) string {
|
||||
if len(s) <= 64 {
|
||||
return spdxlicense.LicenseRefPrefix + SanitizeElementID(s)
|
||||
type SPDXOtherLicenseSet struct {
|
||||
set map[string]spdx.OtherLicense
|
||||
}
|
||||
hash := sha256.Sum256([]byte(s))
|
||||
return fmt.Sprintf("%s%x", spdxlicense.LicenseRefPrefix, hash)
|
||||
|
||||
func NewSPDXOtherLicenseSet() *SPDXOtherLicenseSet {
|
||||
return &SPDXOtherLicenseSet{
|
||||
set: make(map[string]spdx.OtherLicense),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SPDXOtherLicenseSet) Add(licenses ...spdx.OtherLicense) {
|
||||
for _, l := range licenses {
|
||||
s.set[l.LicenseIdentifier] = l
|
||||
}
|
||||
}
|
||||
|
||||
type ByLicenseIdentifier []spdx.OtherLicense
|
||||
|
||||
func (o ByLicenseIdentifier) Len() int { return len(o) }
|
||||
func (o ByLicenseIdentifier) Swap(i, j int) { o[i], o[j] = o[j], o[i] }
|
||||
func (o ByLicenseIdentifier) Less(i, j int) bool {
|
||||
return o[i].LicenseIdentifier < o[j].LicenseIdentifier
|
||||
}
|
||||
|
||||
func (s *SPDXOtherLicenseSet) ToSlice() []spdx.OtherLicense {
|
||||
values := make([]spdx.OtherLicense, 0, len(s.set))
|
||||
for _, v := range s.set {
|
||||
values = append(values, v)
|
||||
}
|
||||
sort.Sort(ByLicenseIdentifier(values))
|
||||
return values
|
||||
}
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/anchore/syft/internal/spdxlicense"
|
||||
@ -11,6 +12,7 @@ import (
|
||||
)
|
||||
|
||||
func Test_License(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
type expected struct {
|
||||
concluded string
|
||||
declared string
|
||||
@ -31,7 +33,7 @@ func Test_License(t *testing.T) {
|
||||
{
|
||||
name: "no SPDX licenses",
|
||||
input: pkg.Package{
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicense("made-up")),
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicenseWithContext(ctx, "made-up")),
|
||||
},
|
||||
expected: expected{
|
||||
concluded: "NOASSERTION",
|
||||
@ -41,7 +43,7 @@ func Test_License(t *testing.T) {
|
||||
{
|
||||
name: "with SPDX license",
|
||||
input: pkg.Package{
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicense("MIT")),
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicenseWithContext(ctx, "MIT")),
|
||||
},
|
||||
expected: struct {
|
||||
concluded string
|
||||
@ -55,8 +57,8 @@ func Test_License(t *testing.T) {
|
||||
name: "with SPDX license expression",
|
||||
input: pkg.Package{
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicense("MIT"),
|
||||
pkg.NewLicense("GPL-3.0-only"),
|
||||
pkg.NewLicenseWithContext(ctx, "MIT"),
|
||||
pkg.NewLicenseWithContext(ctx, "GPL-3.0-only"),
|
||||
),
|
||||
},
|
||||
expected: expected{
|
||||
@ -69,9 +71,9 @@ func Test_License(t *testing.T) {
|
||||
name: "includes valid LicenseRef-",
|
||||
input: pkg.Package{
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicense("one thing first"),
|
||||
pkg.NewLicense("two things/#$^second"),
|
||||
pkg.NewLicense("MIT"),
|
||||
pkg.NewLicenseWithContext(ctx, "one thing first"),
|
||||
pkg.NewLicenseWithContext(ctx, "two things/#$^second"),
|
||||
pkg.NewLicenseWithContext(ctx, "MIT"),
|
||||
),
|
||||
},
|
||||
expected: expected{
|
||||
@ -84,9 +86,9 @@ func Test_License(t *testing.T) {
|
||||
name: "join parentheses correctly",
|
||||
input: pkg.Package{
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicense("one thing first"),
|
||||
pkg.NewLicense("MIT AND GPL-3.0-only"),
|
||||
pkg.NewLicense("MIT OR APACHE-2.0"),
|
||||
pkg.NewLicenseWithContext(ctx, "one thing first"),
|
||||
pkg.NewLicenseWithContext(ctx, "MIT AND GPL-3.0-only"),
|
||||
pkg.NewLicenseWithContext(ctx, "MIT OR APACHE-2.0"),
|
||||
),
|
||||
},
|
||||
expected: expected{
|
||||
@ -98,7 +100,7 @@ func Test_License(t *testing.T) {
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
c, d := License(test.input)
|
||||
c, d, _ := License(test.input)
|
||||
assert.Equal(t, test.expected.concluded, c)
|
||||
assert.Equal(t, test.expected.declared, d)
|
||||
})
|
||||
@ -123,11 +125,13 @@ func TestGenerateLicenseID(t *testing.T) {
|
||||
{
|
||||
name: "Uses value if no SPDX expression",
|
||||
license: pkg.License{
|
||||
Value: "MIT",
|
||||
Value: "my-sweet-custom-license",
|
||||
},
|
||||
expected: spdxlicense.LicenseRefPrefix + "MIT",
|
||||
expected: spdxlicense.LicenseRefPrefix + "my-sweet-custom-license",
|
||||
},
|
||||
{
|
||||
// note: this is an oversight of the SPDX spec. It does NOT allow "+" in the ID even though they are
|
||||
// significant to the licenses in the expressions below
|
||||
name: "Long value is sanitized correctly",
|
||||
license: pkg.License{
|
||||
Value: "LGPLv2+ and LGPLv2+ with exceptions and GPLv2+ and GPLv2+ with exceptions and BSD and Inner-Net and ISC and Public Domain and GFDL",
|
||||
@ -135,13 +139,6 @@ func TestGenerateLicenseID(t *testing.T) {
|
||||
expected: spdxlicense.LicenseRefPrefix +
|
||||
"LGPLv2--and-LGPLv2--with-exceptions-and-GPLv2--and-GPLv2--with-exceptions-and-BSD-and-Inner-Net-and-ISC-and-Public-Domain-and-GFDL",
|
||||
},
|
||||
{
|
||||
name: "Uses hash of contents when nothing else is provided",
|
||||
license: pkg.License{
|
||||
Contents: "This is a very long custom license text that should be hashed because it's more than 64 characters long.",
|
||||
},
|
||||
expected: "", // We'll verify it starts with the correct prefix
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
@ -160,34 +157,92 @@ func TestGenerateLicenseID(t *testing.T) {
|
||||
func Test_joinLicenses(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
args []SPDXLicense
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "multiple licenses",
|
||||
args: []string{"MIT", "GPL-3.0-only"},
|
||||
args: []SPDXLicense{{ID: "MIT"}, {ID: "GPL-3.0-only"}},
|
||||
want: "MIT AND GPL-3.0-only",
|
||||
},
|
||||
{
|
||||
name: "multiple licenses with complex expressions",
|
||||
args: []string{"MIT AND Apache", "GPL-3.0-only"},
|
||||
args: []SPDXLicense{{ID: "MIT AND Apache"}, {ID: "GPL-3.0-only"}},
|
||||
want: "(MIT AND Apache) AND GPL-3.0-only",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Equalf(t, tt.want, joinLicenses(toSpdxLicenses(tt.args)), "joinLicenses(%v)", tt.args)
|
||||
assert.Equalf(t, tt.want, joinLicenses(tt.args), "joinLicenses(%v)", tt.args)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func toSpdxLicenses(ids []string) (licenses []SPDXLicense) {
|
||||
for _, l := range ids {
|
||||
license := SPDXLicense{ID: l}
|
||||
if strings.HasPrefix(l, spdxlicense.LicenseRefPrefix) {
|
||||
license.Value = l
|
||||
func TestCreateSPDXLicenseAndGenerateLicenseID(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input pkg.License
|
||||
expected SPDXLicense
|
||||
}{
|
||||
{
|
||||
name: "SPDX expression used as ID",
|
||||
input: pkg.License{
|
||||
SPDXExpression: "MIT",
|
||||
Value: "MIT",
|
||||
Contents: "",
|
||||
},
|
||||
expected: SPDXLicense{
|
||||
ID: "MIT",
|
||||
LicenseName: "MIT",
|
||||
FullText: "NOASSERTION",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "LicenseRef with contents",
|
||||
input: pkg.License{
|
||||
Value: "sha256:123abc",
|
||||
Contents: "license contents here",
|
||||
},
|
||||
expected: SPDXLicense{
|
||||
ID: "LicenseRef-123abc",
|
||||
LicenseName: "sha256:123abc",
|
||||
FullText: "license contents here",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "LicenseRef without contents",
|
||||
input: pkg.License{
|
||||
Value: "custom-license",
|
||||
Contents: "",
|
||||
},
|
||||
expected: SPDXLicense{
|
||||
ID: "LicenseRef-custom-license",
|
||||
LicenseName: "custom-license",
|
||||
FullText: "NOASSERTION",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "URL is passed through",
|
||||
input: pkg.License{
|
||||
SPDXExpression: "MIT",
|
||||
URLs: []string{
|
||||
"https://example.com/license",
|
||||
},
|
||||
},
|
||||
expected: SPDXLicense{
|
||||
ID: "MIT",
|
||||
FullText: "NOASSERTION",
|
||||
URLs: []string{"https://example.com/license"},
|
||||
},
|
||||
},
|
||||
}
|
||||
licenses = append(licenses, license)
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
license := createSPDXLicense(tt.input)
|
||||
if d := cmp.Diff(tt.expected, license); d != "" {
|
||||
t.Errorf("createSPDXLicense() mismatch (-want +got):\n%s", d)
|
||||
}
|
||||
})
|
||||
}
|
||||
return licenses
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package testutil
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
@ -98,7 +99,7 @@ func DirectoryInputWithAuthorField(t testing.TB) sbom.SBOM {
|
||||
|
||||
func newDirectoryCatalog() *pkg.Collection {
|
||||
catalog := pkg.NewCollection()
|
||||
|
||||
ctx := context.TODO()
|
||||
// populate catalog with test data
|
||||
catalog.Add(pkg.Package{
|
||||
Name: "package-1",
|
||||
@ -110,7 +111,7 @@ func newDirectoryCatalog() *pkg.Collection {
|
||||
),
|
||||
Language: pkg.Python,
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicense("MIT"),
|
||||
pkg.NewLicenseWithContext(ctx, "MIT"),
|
||||
),
|
||||
Metadata: pkg.PythonPackage{
|
||||
Name: "package-1",
|
||||
@ -149,7 +150,7 @@ func newDirectoryCatalog() *pkg.Collection {
|
||||
|
||||
func newDirectoryCatalogWithAuthorField() *pkg.Collection {
|
||||
catalog := pkg.NewCollection()
|
||||
|
||||
ctx := context.TODO()
|
||||
// populate catalog with test data
|
||||
catalog.Add(pkg.Package{
|
||||
Name: "package-1",
|
||||
@ -161,7 +162,7 @@ func newDirectoryCatalogWithAuthorField() *pkg.Collection {
|
||||
),
|
||||
Language: pkg.Python,
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicense("MIT"),
|
||||
pkg.NewLicenseWithContext(ctx, "MIT"),
|
||||
),
|
||||
Metadata: pkg.PythonPackage{
|
||||
Name: "package-1",
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package testutil
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
@ -98,7 +99,7 @@ func populateImageCatalog(catalog *pkg.Collection, img *image.Image) {
|
||||
// TODO: this helper function is coupled to the image-simple fixture, which seems like a bad idea
|
||||
_, ref1, _ := img.SquashedTree().File("/somefile-1.txt", filetree.FollowBasenameLinks)
|
||||
_, ref2, _ := img.SquashedTree().File("/somefile-2.txt", filetree.FollowBasenameLinks)
|
||||
|
||||
ctx := context.TODO()
|
||||
// populate catalog with test data
|
||||
if ref1 != nil {
|
||||
catalog.Add(pkg.Package{
|
||||
@ -111,7 +112,7 @@ func populateImageCatalog(catalog *pkg.Collection, img *image.Image) {
|
||||
FoundBy: "the-cataloger-1",
|
||||
Language: pkg.Python,
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicense("MIT"),
|
||||
pkg.NewLicenseWithContext(ctx, "MIT"),
|
||||
),
|
||||
Metadata: pkg.PythonPackage{
|
||||
Name: "package-1",
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
"packages": [
|
||||
{
|
||||
"name": "package-1",
|
||||
"SPDXID": "SPDXRef-Package-python-package-1-4dd25c6ee16b729a",
|
||||
"SPDXID": "SPDXRef-Package-python-package-1-f04d218ff5ff50db",
|
||||
"versionInfo": "1.0.1",
|
||||
"supplier": "NOASSERTION",
|
||||
"downloadLocation": "NOASSERTION",
|
||||
@ -76,7 +76,7 @@
|
||||
"relationships": [
|
||||
{
|
||||
"spdxElementId": "SPDXRef-DocumentRoot-Directory-some-path",
|
||||
"relatedSpdxElement": "SPDXRef-Package-python-package-1-4dd25c6ee16b729a",
|
||||
"relatedSpdxElement": "SPDXRef-Package-python-package-1-f04d218ff5ff50db",
|
||||
"relationshipType": "CONTAINS"
|
||||
},
|
||||
{
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
"packages": [
|
||||
{
|
||||
"name": "package-1",
|
||||
"SPDXID": "SPDXRef-Package-python-package-1-72567175418f73f8",
|
||||
"SPDXID": "SPDXRef-Package-python-package-1-2f52f617f1548337",
|
||||
"versionInfo": "1.0.1",
|
||||
"supplier": "NOASSERTION",
|
||||
"downloadLocation": "NOASSERTION",
|
||||
@ -90,7 +90,7 @@
|
||||
"relationships": [
|
||||
{
|
||||
"spdxElementId": "SPDXRef-DocumentRoot-Image-user-image-input",
|
||||
"relatedSpdxElement": "SPDXRef-Package-python-package-1-72567175418f73f8",
|
||||
"relatedSpdxElement": "SPDXRef-Package-python-package-1-2f52f617f1548337",
|
||||
"relationshipType": "CONTAINS"
|
||||
},
|
||||
{
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
"packages": [
|
||||
{
|
||||
"name": "package-1",
|
||||
"SPDXID": "SPDXRef-Package-python-package-1-72567175418f73f8",
|
||||
"SPDXID": "SPDXRef-Package-python-package-1-2f52f617f1548337",
|
||||
"versionInfo": "1.0.1",
|
||||
"supplier": "NOASSERTION",
|
||||
"downloadLocation": "NOASSERTION",
|
||||
@ -199,38 +199,38 @@
|
||||
],
|
||||
"relationships": [
|
||||
{
|
||||
"spdxElementId": "SPDXRef-Package-python-package-1-72567175418f73f8",
|
||||
"spdxElementId": "SPDXRef-Package-python-package-1-2f52f617f1548337",
|
||||
"relatedSpdxElement": "SPDXRef-File-f1-5265a4dde3edbf7c",
|
||||
"relationshipType": "CONTAINS"
|
||||
},
|
||||
{
|
||||
"spdxElementId": "SPDXRef-Package-python-package-1-72567175418f73f8",
|
||||
"spdxElementId": "SPDXRef-Package-python-package-1-2f52f617f1548337",
|
||||
"relatedSpdxElement": "SPDXRef-File-z1-f5-839d99ee67d9d174",
|
||||
"relationshipType": "CONTAINS"
|
||||
},
|
||||
{
|
||||
"spdxElementId": "SPDXRef-Package-python-package-1-72567175418f73f8",
|
||||
"spdxElementId": "SPDXRef-Package-python-package-1-2f52f617f1548337",
|
||||
"relatedSpdxElement": "SPDXRef-File-a1-f6-9c2f7510199b17f6",
|
||||
"relationshipType": "CONTAINS"
|
||||
},
|
||||
{
|
||||
"spdxElementId": "SPDXRef-Package-python-package-1-72567175418f73f8",
|
||||
"spdxElementId": "SPDXRef-Package-python-package-1-2f52f617f1548337",
|
||||
"relatedSpdxElement": "SPDXRef-File-d2-f4-c641caa71518099f",
|
||||
"relationshipType": "CONTAINS"
|
||||
},
|
||||
{
|
||||
"spdxElementId": "SPDXRef-Package-python-package-1-72567175418f73f8",
|
||||
"spdxElementId": "SPDXRef-Package-python-package-1-2f52f617f1548337",
|
||||
"relatedSpdxElement": "SPDXRef-File-d1-f3-c6f5b29dca12661f",
|
||||
"relationshipType": "CONTAINS"
|
||||
},
|
||||
{
|
||||
"spdxElementId": "SPDXRef-Package-python-package-1-72567175418f73f8",
|
||||
"spdxElementId": "SPDXRef-Package-python-package-1-2f52f617f1548337",
|
||||
"relatedSpdxElement": "SPDXRef-File-f2-f9e49132a4b96ccd",
|
||||
"relationshipType": "CONTAINS"
|
||||
},
|
||||
{
|
||||
"spdxElementId": "SPDXRef-DocumentRoot-Image-user-image-input",
|
||||
"relatedSpdxElement": "SPDXRef-Package-python-package-1-72567175418f73f8",
|
||||
"relatedSpdxElement": "SPDXRef-Package-python-package-1-2f52f617f1548337",
|
||||
"relationshipType": "CONTAINS"
|
||||
},
|
||||
{
|
||||
|
||||
@ -91,7 +91,7 @@ ExternalRef: PACKAGE-MANAGER purl pkg:deb/debian/package-2@2.0.1
|
||||
##### Package: package-1
|
||||
|
||||
PackageName: package-1
|
||||
SPDXID: SPDXRef-Package-python-package-1-72567175418f73f8
|
||||
SPDXID: SPDXRef-Package-python-package-1-2f52f617f1548337
|
||||
PackageVersion: 1.0.1
|
||||
PackageSupplier: NOASSERTION
|
||||
PackageDownloadLocation: NOASSERTION
|
||||
@ -105,13 +105,13 @@ ExternalRef: PACKAGE-MANAGER purl a-purl-1
|
||||
|
||||
##### Relationships
|
||||
|
||||
Relationship: SPDXRef-Package-python-package-1-72567175418f73f8 CONTAINS SPDXRef-File-f1-5265a4dde3edbf7c
|
||||
Relationship: SPDXRef-Package-python-package-1-72567175418f73f8 CONTAINS SPDXRef-File-z1-f5-839d99ee67d9d174
|
||||
Relationship: SPDXRef-Package-python-package-1-72567175418f73f8 CONTAINS SPDXRef-File-a1-f6-9c2f7510199b17f6
|
||||
Relationship: SPDXRef-Package-python-package-1-72567175418f73f8 CONTAINS SPDXRef-File-d2-f4-c641caa71518099f
|
||||
Relationship: SPDXRef-Package-python-package-1-72567175418f73f8 CONTAINS SPDXRef-File-d1-f3-c6f5b29dca12661f
|
||||
Relationship: SPDXRef-Package-python-package-1-72567175418f73f8 CONTAINS SPDXRef-File-f2-f9e49132a4b96ccd
|
||||
Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-python-package-1-72567175418f73f8
|
||||
Relationship: SPDXRef-Package-python-package-1-2f52f617f1548337 CONTAINS SPDXRef-File-f1-5265a4dde3edbf7c
|
||||
Relationship: SPDXRef-Package-python-package-1-2f52f617f1548337 CONTAINS SPDXRef-File-z1-f5-839d99ee67d9d174
|
||||
Relationship: SPDXRef-Package-python-package-1-2f52f617f1548337 CONTAINS SPDXRef-File-a1-f6-9c2f7510199b17f6
|
||||
Relationship: SPDXRef-Package-python-package-1-2f52f617f1548337 CONTAINS SPDXRef-File-d2-f4-c641caa71518099f
|
||||
Relationship: SPDXRef-Package-python-package-1-2f52f617f1548337 CONTAINS SPDXRef-File-d1-f3-c6f5b29dca12661f
|
||||
Relationship: SPDXRef-Package-python-package-1-2f52f617f1548337 CONTAINS SPDXRef-File-f2-f9e49132a4b96ccd
|
||||
Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-python-package-1-2f52f617f1548337
|
||||
Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-deb-package-2-4b756c6f6fb127a3
|
||||
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-DocumentRoot-Image-user-image-input
|
||||
|
||||
|
||||
@ -38,7 +38,7 @@ ExternalRef: PACKAGE-MANAGER purl pkg:deb/debian/package-2@2.0.1
|
||||
##### Package: package-1
|
||||
|
||||
PackageName: package-1
|
||||
SPDXID: SPDXRef-Package-python-package-1-4dd25c6ee16b729a
|
||||
SPDXID: SPDXRef-Package-python-package-1-f04d218ff5ff50db
|
||||
PackageVersion: 1.0.1
|
||||
PackageSupplier: NOASSERTION
|
||||
PackageDownloadLocation: NOASSERTION
|
||||
@ -52,7 +52,7 @@ ExternalRef: PACKAGE-MANAGER purl a-purl-2
|
||||
|
||||
##### Relationships
|
||||
|
||||
Relationship: SPDXRef-DocumentRoot-Directory-some-path CONTAINS SPDXRef-Package-python-package-1-4dd25c6ee16b729a
|
||||
Relationship: SPDXRef-DocumentRoot-Directory-some-path CONTAINS SPDXRef-Package-python-package-1-f04d218ff5ff50db
|
||||
Relationship: SPDXRef-DocumentRoot-Directory-some-path CONTAINS SPDXRef-Package-deb-package-2-39392bb5e270f669
|
||||
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-DocumentRoot-Directory-some-path
|
||||
|
||||
|
||||
@ -41,7 +41,7 @@ ExternalRef: PACKAGE-MANAGER purl pkg:deb/debian/package-2@2.0.1
|
||||
##### Package: package-1
|
||||
|
||||
PackageName: package-1
|
||||
SPDXID: SPDXRef-Package-python-package-1-72567175418f73f8
|
||||
SPDXID: SPDXRef-Package-python-package-1-2f52f617f1548337
|
||||
PackageVersion: 1.0.1
|
||||
PackageSupplier: NOASSERTION
|
||||
PackageDownloadLocation: NOASSERTION
|
||||
@ -55,7 +55,7 @@ ExternalRef: PACKAGE-MANAGER purl a-purl-1
|
||||
|
||||
##### Relationships
|
||||
|
||||
Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-python-package-1-72567175418f73f8
|
||||
Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-python-package-1-2f52f617f1548337
|
||||
Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-deb-package-2-4b756c6f6fb127a3
|
||||
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-DocumentRoot-Image-user-image-input
|
||||
|
||||
|
||||
@ -2,6 +2,7 @@ package syftjson
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"flag"
|
||||
"strings"
|
||||
"testing"
|
||||
@ -132,7 +133,7 @@ func TestImageEncoder(t *testing.T) {
|
||||
|
||||
func TestEncodeFullJSONDocument(t *testing.T) {
|
||||
catalog := pkg.NewCollection()
|
||||
|
||||
ctx := context.TODO()
|
||||
p1 := pkg.Package{
|
||||
Name: "package-1",
|
||||
Version: "1.0.1",
|
||||
@ -144,7 +145,7 @@ func TestEncodeFullJSONDocument(t *testing.T) {
|
||||
Type: pkg.PythonPkg,
|
||||
FoundBy: "the-cataloger-1",
|
||||
Language: pkg.Python,
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicense("MIT")),
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicenseWithContext(ctx, "MIT")),
|
||||
Metadata: pkg.PythonPackage{
|
||||
Name: "package-1",
|
||||
Version: "1.0.1",
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"artifacts": [
|
||||
{
|
||||
"id": "4dd25c6ee16b729a",
|
||||
"id": "f04d218ff5ff50db",
|
||||
"name": "package-1",
|
||||
"version": "1.0.1",
|
||||
"type": "python",
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"artifacts": [
|
||||
{
|
||||
"id": "fba4ca04d4906f25",
|
||||
"id": "951845d9a8d6b5b2",
|
||||
"name": "package-1",
|
||||
"version": "1.0.1",
|
||||
"type": "python",
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"artifacts": [
|
||||
{
|
||||
"id": "72567175418f73f8",
|
||||
"id": "2f52f617f1548337",
|
||||
"name": "package-1",
|
||||
"version": "1.0.1",
|
||||
"type": "python",
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package alpine
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
@ -14,14 +15,14 @@ import (
|
||||
|
||||
func TestApkDBCataloger(t *testing.T) {
|
||||
dbLocation := file.NewLocation("lib/apk/db/installed")
|
||||
|
||||
ctx := context.TODO()
|
||||
bashPkg := pkg.Package{
|
||||
Name: "bash",
|
||||
Version: "5.2.21-r0",
|
||||
Type: pkg.ApkPkg,
|
||||
FoundBy: "apk-db-cataloger",
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("GPL-3.0-or-later", dbLocation),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "GPL-3.0-or-later", dbLocation),
|
||||
),
|
||||
Locations: file.NewLocationSet(dbLocation),
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
@ -50,7 +51,7 @@ func TestApkDBCataloger(t *testing.T) {
|
||||
Type: pkg.ApkPkg,
|
||||
FoundBy: "apk-db-cataloger",
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("GPL-2.0-only", dbLocation),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "GPL-2.0-only", dbLocation),
|
||||
),
|
||||
Locations: file.NewLocationSet(dbLocation),
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
@ -79,7 +80,7 @@ func TestApkDBCataloger(t *testing.T) {
|
||||
Type: pkg.ApkPkg,
|
||||
FoundBy: "apk-db-cataloger",
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("MIT", dbLocation),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "MIT", dbLocation),
|
||||
),
|
||||
Locations: file.NewLocationSet(dbLocation),
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
@ -106,7 +107,7 @@ func TestApkDBCataloger(t *testing.T) {
|
||||
Type: pkg.ApkPkg,
|
||||
FoundBy: "apk-db-cataloger",
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("GPL-2.0-or-later", dbLocation),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "GPL-2.0-or-later", dbLocation),
|
||||
),
|
||||
Locations: file.NewLocationSet(dbLocation),
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package alpine
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/packageurl-go"
|
||||
@ -10,7 +11,7 @@ import (
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
func newPackage(d parsedData, release *linux.Release, dbLocation file.Location) pkg.Package {
|
||||
func newPackage(ctx context.Context, d parsedData, release *linux.Release, dbLocation file.Location) pkg.Package {
|
||||
// check if license is a valid spdx expression before splitting
|
||||
licenseStrings := []string{d.License}
|
||||
_, err := license.ParseExpression(d.License)
|
||||
@ -23,7 +24,7 @@ func newPackage(d parsedData, release *linux.Release, dbLocation file.Location)
|
||||
Name: d.Package,
|
||||
Version: d.Version,
|
||||
Locations: file.NewLocationSet(dbLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(dbLocation, licenseStrings...)...),
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocationWithContext(ctx, dbLocation, licenseStrings...)...),
|
||||
PURL: packageURL(d.ApkDBEntry, release),
|
||||
Type: pkg.ApkPkg,
|
||||
Metadata: d.ApkDBEntry,
|
||||
|
||||
@ -36,7 +36,7 @@ type parsedData struct {
|
||||
// information on specific fields, see https://wiki.alpinelinux.org/wiki/Apk_spec.
|
||||
//
|
||||
//nolint:funlen
|
||||
func parseApkDB(_ context.Context, resolver file.Resolver, env *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
func parseApkDB(ctx context.Context, resolver file.Resolver, env *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
var errs error
|
||||
@ -132,7 +132,7 @@ func parseApkDB(_ context.Context, resolver file.Resolver, env *generic.Environm
|
||||
|
||||
pkgs := make([]pkg.Package, 0, len(apks))
|
||||
for _, apk := range apks {
|
||||
pkgs = append(pkgs, newPackage(apk, r, reader.Location))
|
||||
pkgs = append(pkgs, newPackage(ctx, apk, r, reader.Location))
|
||||
}
|
||||
|
||||
return pkgs, nil, errs
|
||||
|
||||
@ -76,6 +76,7 @@ func TestExtraFileAttributes(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSinglePackageDetails(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
tests := []struct {
|
||||
fixture string
|
||||
expected pkg.Package
|
||||
@ -86,9 +87,9 @@ func TestSinglePackageDetails(t *testing.T) {
|
||||
Name: "musl-utils",
|
||||
Version: "1.1.24-r2",
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicense("MIT"),
|
||||
pkg.NewLicense("BSD"),
|
||||
pkg.NewLicense("GPL2+"),
|
||||
pkg.NewLicenseWithContext(ctx, "MIT"),
|
||||
pkg.NewLicenseWithContext(ctx, "BSD"),
|
||||
pkg.NewLicenseWithContext(ctx, "GPL2+"),
|
||||
),
|
||||
Type: pkg.ApkPkg,
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
@ -175,7 +176,7 @@ func TestSinglePackageDetails(t *testing.T) {
|
||||
Name: "alpine-baselayout-data",
|
||||
Version: "3.4.0-r0",
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicense("GPL-2.0-only"),
|
||||
pkg.NewLicenseWithContext(ctx, "GPL-2.0-only"),
|
||||
),
|
||||
Type: pkg.ApkPkg,
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
@ -219,7 +220,7 @@ func TestSinglePackageDetails(t *testing.T) {
|
||||
Name: "alpine-baselayout",
|
||||
Version: "3.2.0-r6",
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicense("GPL-2.0-only"),
|
||||
pkg.NewLicenseWithContext(ctx, "GPL-2.0-only"),
|
||||
),
|
||||
Type: pkg.ApkPkg,
|
||||
PURL: "",
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package arch
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
@ -25,6 +26,7 @@ func TestAlpmCataloger(t *testing.T) {
|
||||
emacsDbLocation := file.NewLocation("var/lib/pacman/local/emacs-29.3-3/desc")
|
||||
fuzzyDbLocation := file.NewLocation("var/lib/pacman/local/fuzzy-1.2-3/desc")
|
||||
madeupDbLocation := file.NewLocation("var/lib/pacman/local/madeup-20.30-4/desc")
|
||||
ctx := context.TODO()
|
||||
|
||||
treeSitterPkg := pkg.Package{
|
||||
Name: "tree-sitter",
|
||||
@ -32,7 +34,7 @@ func TestAlpmCataloger(t *testing.T) {
|
||||
Type: pkg.AlpmPkg,
|
||||
FoundBy: "alpm-db-cataloger",
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("MIT", treeSitterDbLocation),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "MIT", treeSitterDbLocation),
|
||||
),
|
||||
Locations: file.NewLocationSet(treeSitterDbLocation),
|
||||
Metadata: pkg.AlpmDBEntry{
|
||||
@ -58,7 +60,7 @@ func TestAlpmCataloger(t *testing.T) {
|
||||
Type: pkg.AlpmPkg,
|
||||
FoundBy: "alpm-db-cataloger",
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("GPL3", emacsDbLocation),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "GPL3", emacsDbLocation),
|
||||
),
|
||||
Locations: file.NewLocationSet(emacsDbLocation),
|
||||
Metadata: pkg.AlpmDBEntry{
|
||||
@ -123,8 +125,8 @@ func TestAlpmCataloger(t *testing.T) {
|
||||
Type: pkg.AlpmPkg,
|
||||
FoundBy: "alpm-db-cataloger",
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("LGPL3", gmpDbLocation),
|
||||
pkg.NewLicenseFromLocations("GPL", gmpDbLocation),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "LGPL3", gmpDbLocation),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "GPL", gmpDbLocation),
|
||||
),
|
||||
Locations: file.NewLocationSet(
|
||||
gmpDbLocation,
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package arch
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/packageurl-go"
|
||||
@ -9,7 +10,7 @@ import (
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
func newPackage(m *parsedData, release *linux.Release, dbLocation file.Location, otherLocations ...file.Location) pkg.Package {
|
||||
func newPackage(ctx context.Context, m *parsedData, release *linux.Release, dbLocation file.Location, otherLocations ...file.Location) pkg.Package {
|
||||
licenseCandidates := strings.Split(m.Licenses, "\n")
|
||||
|
||||
locs := file.NewLocationSet(dbLocation)
|
||||
@ -19,7 +20,7 @@ func newPackage(m *parsedData, release *linux.Release, dbLocation file.Location,
|
||||
Name: m.Package,
|
||||
Version: m.Version,
|
||||
Locations: locs,
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(dbLocation.WithoutAnnotations(), licenseCandidates...)...),
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocationWithContext(ctx, dbLocation.WithoutAnnotations(), licenseCandidates...)...),
|
||||
Type: pkg.AlpmPkg,
|
||||
PURL: packageURL(m, release),
|
||||
Metadata: m.AlpmDBEntry,
|
||||
|
||||
@ -41,7 +41,7 @@ type parsedData struct {
|
||||
}
|
||||
|
||||
// parseAlpmDB parses the arch linux pacman database flat-files and returns the packages and relationships found within.
|
||||
func parseAlpmDB(_ context.Context, resolver file.Resolver, env *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
func parseAlpmDB(ctx context.Context, resolver file.Resolver, env *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
var errs error
|
||||
|
||||
data, err := parseAlpmDBEntry(reader)
|
||||
@ -78,6 +78,7 @@ func parseAlpmDB(_ context.Context, resolver file.Resolver, env *generic.Environ
|
||||
|
||||
return []pkg.Package{
|
||||
newPackage(
|
||||
ctx,
|
||||
data,
|
||||
env.LinuxRelease,
|
||||
reader.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
package binary
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/anchore/packageurl-go"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/syft/cpe"
|
||||
@ -8,11 +10,11 @@ import (
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
func newELFPackage(metadata elfBinaryPackageNotes, locations file.LocationSet) pkg.Package {
|
||||
func newELFPackage(ctx context.Context, metadata elfBinaryPackageNotes, locations file.LocationSet) pkg.Package {
|
||||
p := pkg.Package{
|
||||
Name: metadata.Name,
|
||||
Version: metadata.Version,
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicense(metadata.License)),
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicenseWithContext(ctx, metadata.License)),
|
||||
PURL: packageURL(metadata),
|
||||
Type: pkgType(metadata.Type),
|
||||
Locations: locations,
|
||||
|
||||
@ -52,7 +52,7 @@ func (c *elfPackageCataloger) Name() string {
|
||||
return "elf-binary-package-cataloger"
|
||||
}
|
||||
|
||||
func (c *elfPackageCataloger) Catalog(_ context.Context, resolver file.Resolver) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
func (c *elfPackageCataloger) Catalog(ctx context.Context, resolver file.Resolver) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
var errs error
|
||||
locations, err := resolver.FilesByMIMEType(mimetype.ExecutableMIMETypeSet.List()...)
|
||||
if err != nil {
|
||||
@ -84,7 +84,7 @@ func (c *elfPackageCataloger) Catalog(_ context.Context, resolver file.Resolver)
|
||||
}
|
||||
|
||||
// create a package for each unique name/version pair (based on the first note found)
|
||||
pkgs = append(pkgs, newELFPackage(notes[0], noteLocations))
|
||||
pkgs = append(pkgs, newELFPackage(ctx, notes[0], noteLocations))
|
||||
}
|
||||
|
||||
// why not return relationships? We have an executable cataloger that will note the dynamic libraries imported by
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package binary
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
@ -135,6 +136,7 @@ func Test_packageURL(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_newELFPackage(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
tests := []struct {
|
||||
name string
|
||||
metadata elfBinaryPackageNotes
|
||||
@ -168,7 +170,7 @@ func Test_newELFPackage(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
actual := newELFPackage(test.metadata, file.NewLocationSet())
|
||||
actual := newELFPackage(ctx, test.metadata, file.NewLocationSet())
|
||||
if diff := cmp.Diff(test.expected, actual, cmpopts.IgnoreFields(pkg.Package{}, "id"), cmpopts.IgnoreUnexported(pkg.Package{}, file.LocationSet{}, pkg.LicenseSet{})); diff != "" {
|
||||
t.Errorf("newELFPackage() mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package bitnami
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
@ -23,14 +24,15 @@ func mustCPEs(s ...string) (c []cpe.CPE) {
|
||||
}
|
||||
|
||||
func TestBitnamiCataloger(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
postgresqlMainPkg := pkg.Package{
|
||||
Name: "postgresql",
|
||||
Version: "17.2.0-8",
|
||||
Type: pkg.BitnamiPkg,
|
||||
Locations: file.NewLocationSet(file.NewLocation("opt/bitnami/postgresql/.spdx-postgresql.spdx")),
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromType("PostgreSQL", license.Concluded),
|
||||
pkg.NewLicenseFromType("PostgreSQL", license.Declared),
|
||||
pkg.NewLicenseFromTypeWithContext(ctx, "PostgreSQL", license.Concluded),
|
||||
pkg.NewLicenseFromTypeWithContext(ctx, "PostgreSQL", license.Declared),
|
||||
),
|
||||
FoundBy: catalogerName,
|
||||
PURL: "pkg:bitnami/postgresql@17.2.0-8?arch=arm64&distro=debian-12",
|
||||
@ -56,8 +58,8 @@ func TestBitnamiCataloger(t *testing.T) {
|
||||
Type: pkg.BitnamiPkg,
|
||||
Locations: file.NewLocationSet(file.NewLocation("opt/bitnami/postgresql/.spdx-postgresql.spdx")),
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromType("LGPL-2.1-only", license.Concluded),
|
||||
pkg.NewLicenseFromType("LGPL-2.1-only", license.Declared),
|
||||
pkg.NewLicenseFromTypeWithContext(ctx, "LGPL-2.1-only", license.Concluded),
|
||||
pkg.NewLicenseFromTypeWithContext(ctx, "LGPL-2.1-only", license.Declared),
|
||||
),
|
||||
FoundBy: catalogerName,
|
||||
PURL: "pkg:bitnami/geos@3.13.0?arch=arm64&distro=debian-12",
|
||||
@ -78,8 +80,8 @@ func TestBitnamiCataloger(t *testing.T) {
|
||||
Type: pkg.BitnamiPkg,
|
||||
Locations: file.NewLocationSet(file.NewLocation("opt/bitnami/postgresql/.spdx-postgresql.spdx")),
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromType("MIT", license.Concluded),
|
||||
pkg.NewLicenseFromType("MIT", license.Declared),
|
||||
pkg.NewLicenseFromTypeWithContext(ctx, "MIT", license.Concluded),
|
||||
pkg.NewLicenseFromTypeWithContext(ctx, "MIT", license.Declared),
|
||||
),
|
||||
FoundBy: catalogerName,
|
||||
PURL: "pkg:bitnami/proj@6.3.2?arch=arm64&distro=debian-12",
|
||||
@ -100,8 +102,8 @@ func TestBitnamiCataloger(t *testing.T) {
|
||||
Type: pkg.BitnamiPkg,
|
||||
Locations: file.NewLocationSet(file.NewLocation("opt/bitnami/postgresql/.spdx-postgresql.spdx")),
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromType("MIT", license.Concluded),
|
||||
pkg.NewLicenseFromType("MIT", license.Declared),
|
||||
pkg.NewLicenseFromTypeWithContext(ctx, "MIT", license.Concluded),
|
||||
pkg.NewLicenseFromTypeWithContext(ctx, "MIT", license.Declared),
|
||||
),
|
||||
FoundBy: catalogerName,
|
||||
PURL: "pkg:bitnami/gdal@3.10.1?arch=arm64&distro=debian-12",
|
||||
@ -122,8 +124,8 @@ func TestBitnamiCataloger(t *testing.T) {
|
||||
Type: pkg.BitnamiPkg,
|
||||
Locations: file.NewLocationSet(file.NewLocation("opt/bitnami/postgresql/.spdx-postgresql.spdx")),
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromType("MIT", license.Concluded),
|
||||
pkg.NewLicenseFromType("MIT", license.Declared),
|
||||
pkg.NewLicenseFromTypeWithContext(ctx, "MIT", license.Concluded),
|
||||
pkg.NewLicenseFromTypeWithContext(ctx, "MIT", license.Declared),
|
||||
),
|
||||
FoundBy: catalogerName,
|
||||
PURL: "pkg:bitnami/json-c@0.16.20220414?arch=arm64&distro=debian-12",
|
||||
@ -144,8 +146,8 @@ func TestBitnamiCataloger(t *testing.T) {
|
||||
Type: pkg.BitnamiPkg,
|
||||
Locations: file.NewLocationSet(file.NewLocation("opt/bitnami/postgresql/.spdx-postgresql.spdx")),
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromType("0BSD", license.Concluded),
|
||||
pkg.NewLicenseFromType("0BSD", license.Declared),
|
||||
pkg.NewLicenseFromTypeWithContext(ctx, "0BSD", license.Concluded),
|
||||
pkg.NewLicenseFromTypeWithContext(ctx, "0BSD", license.Declared),
|
||||
),
|
||||
FoundBy: catalogerName,
|
||||
PURL: "pkg:bitnami/orafce@4.14.1?arch=arm64&distro=debian-12",
|
||||
@ -166,8 +168,8 @@ func TestBitnamiCataloger(t *testing.T) {
|
||||
Type: pkg.BitnamiPkg,
|
||||
Locations: file.NewLocationSet(file.NewLocation("opt/bitnami/postgresql/.spdx-postgresql.spdx")),
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromType("BSD-3-Clause", license.Concluded),
|
||||
pkg.NewLicenseFromType("BSD-3-Clause", license.Declared),
|
||||
pkg.NewLicenseFromTypeWithContext(ctx, "BSD-3-Clause", license.Concluded),
|
||||
pkg.NewLicenseFromTypeWithContext(ctx, "BSD-3-Clause", license.Declared),
|
||||
),
|
||||
FoundBy: catalogerName,
|
||||
PURL: "pkg:bitnami/pljava@1.6.8?arch=arm64&distro=debian-12",
|
||||
@ -193,8 +195,8 @@ func TestBitnamiCataloger(t *testing.T) {
|
||||
Type: pkg.BitnamiPkg,
|
||||
Locations: file.NewLocationSet(file.NewLocation("opt/bitnami/postgresql/.spdx-postgresql.spdx")),
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromType("LGPL-2.1-only", license.Concluded),
|
||||
pkg.NewLicenseFromType("LGPL-2.1-only", license.Declared),
|
||||
pkg.NewLicenseFromTypeWithContext(ctx, "LGPL-2.1-only", license.Concluded),
|
||||
pkg.NewLicenseFromTypeWithContext(ctx, "LGPL-2.1-only", license.Declared),
|
||||
),
|
||||
FoundBy: catalogerName,
|
||||
PURL: "pkg:bitnami/unixodbc@2.3.12?arch=arm64&distro=debian-12",
|
||||
@ -215,8 +217,8 @@ func TestBitnamiCataloger(t *testing.T) {
|
||||
Type: pkg.BitnamiPkg,
|
||||
Locations: file.NewLocationSet(file.NewLocation("opt/bitnami/postgresql/.spdx-postgresql.spdx")),
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromType("LGPL-3.0-only", license.Concluded),
|
||||
pkg.NewLicenseFromType("LGPL-3.0-only", license.Declared),
|
||||
pkg.NewLicenseFromTypeWithContext(ctx, "LGPL-3.0-only", license.Concluded),
|
||||
pkg.NewLicenseFromTypeWithContext(ctx, "LGPL-3.0-only", license.Declared),
|
||||
),
|
||||
FoundBy: catalogerName,
|
||||
PURL: "pkg:bitnami/psqlodbc@16.0.0?arch=arm64&distro=debian-12",
|
||||
@ -237,8 +239,8 @@ func TestBitnamiCataloger(t *testing.T) {
|
||||
Type: pkg.BitnamiPkg,
|
||||
Locations: file.NewLocationSet(file.NewLocation("opt/bitnami/postgresql/.spdx-postgresql.spdx")),
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromType("BSD-3-Clause", license.Concluded),
|
||||
pkg.NewLicenseFromType("BSD-3-Clause", license.Declared),
|
||||
pkg.NewLicenseFromTypeWithContext(ctx, "BSD-3-Clause", license.Concluded),
|
||||
pkg.NewLicenseFromTypeWithContext(ctx, "BSD-3-Clause", license.Declared),
|
||||
),
|
||||
FoundBy: catalogerName,
|
||||
PURL: "pkg:bitnami/protobuf@3.21.12?arch=arm64&distro=debian-12",
|
||||
@ -259,8 +261,8 @@ func TestBitnamiCataloger(t *testing.T) {
|
||||
Type: pkg.BitnamiPkg,
|
||||
Locations: file.NewLocationSet(file.NewLocation("opt/bitnami/postgresql/.spdx-postgresql.spdx")),
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromType("BSD-2-Clause", license.Concluded),
|
||||
pkg.NewLicenseFromType("BSD-2-Clause", license.Declared),
|
||||
pkg.NewLicenseFromTypeWithContext(ctx, "BSD-2-Clause", license.Concluded),
|
||||
pkg.NewLicenseFromTypeWithContext(ctx, "BSD-2-Clause", license.Declared),
|
||||
),
|
||||
FoundBy: catalogerName,
|
||||
PURL: "pkg:bitnami/protobuf-c@1.5.1?arch=arm64&distro=debian-12",
|
||||
@ -281,8 +283,8 @@ func TestBitnamiCataloger(t *testing.T) {
|
||||
Type: pkg.BitnamiPkg,
|
||||
Locations: file.NewLocationSet(file.NewLocation("opt/bitnami/postgresql/.spdx-postgresql.spdx")),
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromType("GPL-2.0-or-later", license.Concluded),
|
||||
pkg.NewLicenseFromType("GPL-2.0-or-later", license.Declared),
|
||||
pkg.NewLicenseFromTypeWithContext(ctx, "GPL-2.0-or-later", license.Concluded),
|
||||
pkg.NewLicenseFromTypeWithContext(ctx, "GPL-2.0-or-later", license.Declared),
|
||||
),
|
||||
FoundBy: catalogerName,
|
||||
PURL: "pkg:bitnami/postgis@3.4.4?arch=arm64&distro=debian-12",
|
||||
@ -303,8 +305,8 @@ func TestBitnamiCataloger(t *testing.T) {
|
||||
Type: pkg.BitnamiPkg,
|
||||
Locations: file.NewLocationSet(file.NewLocation("opt/bitnami/postgresql/.spdx-postgresql.spdx")),
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromType("PostgreSQL", license.Concluded),
|
||||
pkg.NewLicenseFromType("PostgreSQL", license.Declared),
|
||||
pkg.NewLicenseFromTypeWithContext(ctx, "PostgreSQL", license.Concluded),
|
||||
pkg.NewLicenseFromTypeWithContext(ctx, "PostgreSQL", license.Declared),
|
||||
),
|
||||
FoundBy: catalogerName,
|
||||
PURL: "pkg:bitnami/pgaudit@17.0.0?arch=arm64&distro=debian-12",
|
||||
@ -322,8 +324,8 @@ func TestBitnamiCataloger(t *testing.T) {
|
||||
Type: pkg.BitnamiPkg,
|
||||
Locations: file.NewLocationSet(file.NewLocation("opt/bitnami/postgresql/.spdx-postgresql.spdx")),
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromType("MIT", license.Concluded),
|
||||
pkg.NewLicenseFromType("MIT", license.Declared),
|
||||
pkg.NewLicenseFromTypeWithContext(ctx, "MIT", license.Concluded),
|
||||
pkg.NewLicenseFromTypeWithContext(ctx, "MIT", license.Declared),
|
||||
),
|
||||
FoundBy: catalogerName,
|
||||
PURL: "pkg:bitnami/pgbackrest@2.54.2?arch=arm64&distro=debian-12",
|
||||
@ -344,8 +346,8 @@ func TestBitnamiCataloger(t *testing.T) {
|
||||
Type: pkg.BitnamiPkg,
|
||||
Locations: file.NewLocationSet(file.NewLocation("opt/bitnami/postgresql/.spdx-postgresql.spdx")),
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromType("BSD-3-Clause", license.Concluded),
|
||||
pkg.NewLicenseFromType("BSD-3-Clause", license.Declared),
|
||||
pkg.NewLicenseFromTypeWithContext(ctx, "BSD-3-Clause", license.Concluded),
|
||||
pkg.NewLicenseFromTypeWithContext(ctx, "BSD-3-Clause", license.Declared),
|
||||
),
|
||||
FoundBy: catalogerName,
|
||||
PURL: "pkg:bitnami/wal2json@2.6.0?arch=arm64&distro=debian-12",
|
||||
@ -366,8 +368,8 @@ func TestBitnamiCataloger(t *testing.T) {
|
||||
Type: pkg.BitnamiPkg,
|
||||
Locations: file.NewLocationSet(file.NewLocation("opt/bitnami/postgresql/.spdx-postgresql.spdx")),
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromType("BSD-3-Clause", license.Concluded),
|
||||
pkg.NewLicenseFromType("BSD-3-Clause", license.Declared),
|
||||
pkg.NewLicenseFromTypeWithContext(ctx, "BSD-3-Clause", license.Concluded),
|
||||
pkg.NewLicenseFromTypeWithContext(ctx, "BSD-3-Clause", license.Declared),
|
||||
),
|
||||
FoundBy: catalogerName,
|
||||
PURL: "pkg:bitnami/nss_wrapper@1.1.16?arch=arm64&distro=debian-12",
|
||||
@ -402,8 +404,8 @@ func TestBitnamiCataloger(t *testing.T) {
|
||||
Type: pkg.BitnamiPkg,
|
||||
Locations: file.NewLocationSet(file.NewLocation("opt/bitnami/render-template/.spdx-render-template.spdx")),
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromType("Apache-2.0", license.Concluded),
|
||||
pkg.NewLicenseFromType("Apache-2.0", license.Declared),
|
||||
pkg.NewLicenseFromTypeWithContext(ctx, "Apache-2.0", license.Concluded),
|
||||
pkg.NewLicenseFromTypeWithContext(ctx, "Apache-2.0", license.Declared),
|
||||
),
|
||||
FoundBy: catalogerName,
|
||||
PURL: "pkg:bitnami/render-template@1.0.7-4?arch=arm64&distro=debian-12",
|
||||
@ -427,8 +429,8 @@ func TestBitnamiCataloger(t *testing.T) {
|
||||
Type: pkg.BitnamiPkg,
|
||||
Locations: file.NewLocationSet(file.NewLocation("opt/bitnami/redis/.spdx-redis.spdx")),
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromType("RSALv2", license.Concluded),
|
||||
pkg.NewLicenseFromType("RSALv2", license.Declared),
|
||||
pkg.NewLicenseFromTypeWithContext(ctx, "RSALv2", license.Concluded),
|
||||
pkg.NewLicenseFromTypeWithContext(ctx, "RSALv2", license.Declared),
|
||||
),
|
||||
FoundBy: catalogerName,
|
||||
PURL: "pkg:bitnami/redis@7.4.0-0?arch=arm64&distro=debian-12",
|
||||
|
||||
@ -13,6 +13,7 @@ import (
|
||||
)
|
||||
|
||||
func TestDpkgCataloger(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
tests := []struct {
|
||||
name string
|
||||
expected []pkg.Package
|
||||
@ -25,9 +26,9 @@ func TestDpkgCataloger(t *testing.T) {
|
||||
Version: "1.1.8-3.6",
|
||||
FoundBy: "dpkg-db-cataloger",
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("GPL-1", file.NewLocation("/usr/share/doc/libpam-runtime/copyright")),
|
||||
pkg.NewLicenseFromLocations("GPL-2", file.NewLocation("/usr/share/doc/libpam-runtime/copyright")),
|
||||
pkg.NewLicenseFromLocations("LGPL-2.1", file.NewLocation("/usr/share/doc/libpam-runtime/copyright")),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "GPL-1", file.NewLocation("/usr/share/doc/libpam-runtime/copyright")),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "GPL-2", file.NewLocation("/usr/share/doc/libpam-runtime/copyright")),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "LGPL-2.1", file.NewLocation("/usr/share/doc/libpam-runtime/copyright")),
|
||||
),
|
||||
Locations: file.NewLocationSet(
|
||||
file.NewLocation("/var/lib/dpkg/status").WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
@ -100,9 +101,9 @@ func TestDpkgCataloger(t *testing.T) {
|
||||
Version: "3.34.1-3",
|
||||
FoundBy: "dpkg-db-cataloger",
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("public-domain", file.NewLocation("/usr/share/doc/libsqlite3-0/copyright")),
|
||||
pkg.NewLicenseFromLocations("GPL-2+", file.NewLocation("/usr/share/doc/libsqlite3-0/copyright")),
|
||||
pkg.NewLicenseFromLocations("GPL-2", file.NewLocation("/usr/share/doc/libsqlite3-0/copyright")),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "public-domain", file.NewLocation("/usr/share/doc/libsqlite3-0/copyright")),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "GPL-2+", file.NewLocation("/usr/share/doc/libsqlite3-0/copyright")),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "GPL-2", file.NewLocation("/usr/share/doc/libsqlite3-0/copyright")),
|
||||
),
|
||||
Locations: file.NewLocationSet(
|
||||
file.NewLocation("/var/lib/dpkg/status.d/libsqlite3-0").WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
@ -226,6 +227,7 @@ func Test_CatalogerRelationships(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDpkgArchiveCataloger(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
tests := []struct {
|
||||
name string
|
||||
expected []pkg.Package
|
||||
@ -241,7 +243,7 @@ func TestDpkgArchiveCataloger(t *testing.T) {
|
||||
file.NewLocation("/zlib1g.deb"),
|
||||
),
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("Zlib"),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "Zlib"),
|
||||
),
|
||||
PURL: "pkg:deb/zlib1g@1%3A1.3.dfsg-3.1ubuntu2.1?arch=amd64&upstream=zlib",
|
||||
Type: pkg.DebPkg,
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package debian
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"path"
|
||||
@ -22,7 +23,7 @@ const (
|
||||
docsPath = "/usr/share/doc"
|
||||
)
|
||||
|
||||
func newDpkgPackage(d pkg.DpkgDBEntry, dbLocation file.Location, resolver file.Resolver, release *linux.Release, evidence ...file.Location) pkg.Package {
|
||||
func newDpkgPackage(ctx context.Context, d pkg.DpkgDBEntry, dbLocation file.Location, resolver file.Resolver, release *linux.Release, evidence ...file.Location) pkg.Package {
|
||||
// TODO: separate pr to license refactor, but explore extracting dpkg-specific license parsing into a separate function
|
||||
var licenses []pkg.License
|
||||
|
||||
@ -46,7 +47,7 @@ func newDpkgPackage(d pkg.DpkgDBEntry, dbLocation file.Location, resolver file.R
|
||||
mergeFileListing(resolver, dbLocation, &p)
|
||||
|
||||
// fetch additional data from the copyright file to derive the license information
|
||||
addLicenses(resolver, dbLocation, &p)
|
||||
addLicenses(ctx, resolver, dbLocation, &p)
|
||||
}
|
||||
|
||||
p.SetID()
|
||||
@ -54,11 +55,11 @@ func newDpkgPackage(d pkg.DpkgDBEntry, dbLocation file.Location, resolver file.R
|
||||
return p
|
||||
}
|
||||
|
||||
func newDebArchivePackage(location file.Location, metadata pkg.DpkgArchiveEntry, licenseStrings []string) pkg.Package {
|
||||
func newDebArchivePackage(ctx context.Context, location file.Location, metadata pkg.DpkgArchiveEntry, licenseStrings []string) pkg.Package {
|
||||
p := pkg.Package{
|
||||
Name: metadata.Package,
|
||||
Version: metadata.Version,
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromValues(licenseStrings...)...),
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromValuesWithContext(ctx, licenseStrings...)...),
|
||||
Type: pkg.DebPkg,
|
||||
PURL: packageURL(
|
||||
pkg.DpkgDBEntry(metadata),
|
||||
@ -108,7 +109,7 @@ func packageURL(m pkg.DpkgDBEntry, distro *linux.Release) string {
|
||||
).ToString()
|
||||
}
|
||||
|
||||
func addLicenses(resolver file.Resolver, dbLocation file.Location, p *pkg.Package) {
|
||||
func addLicenses(ctx context.Context, resolver file.Resolver, dbLocation file.Location, p *pkg.Package) {
|
||||
metadata, ok := p.Metadata.(pkg.DpkgDBEntry)
|
||||
if !ok {
|
||||
log.WithFields("package", p).Trace("unable to extract DPKG metadata to add licenses")
|
||||
@ -123,7 +124,7 @@ func addLicenses(resolver file.Resolver, dbLocation file.Location, p *pkg.Packag
|
||||
// attach the licenses
|
||||
licenseStrs := parseLicensesFromCopyright(copyrightReader)
|
||||
for _, licenseStr := range licenseStrs {
|
||||
p.Licenses.Add(pkg.NewLicenseFromLocations(licenseStr, copyrightLocation.WithoutAnnotations()))
|
||||
p.Licenses.Add(pkg.NewLicenseFromLocationsWithContext(ctx, licenseStr, copyrightLocation.WithoutAnnotations()))
|
||||
}
|
||||
// keep a record of the file where this was discovered
|
||||
p.Locations.Add(*copyrightLocation)
|
||||
|
||||
@ -72,7 +72,7 @@ func parseDebArchive(ctx context.Context, _ file.Resolver, _ *generic.Environmen
|
||||
}
|
||||
|
||||
return []pkg.Package{
|
||||
newDebArchivePackage(reader.Location, *metadata, licenses),
|
||||
newDebArchivePackage(ctx, reader.Location, *metadata, licenses),
|
||||
}, nil, nil
|
||||
}
|
||||
|
||||
|
||||
@ -39,7 +39,7 @@ func parseDpkgDB(ctx context.Context, resolver file.Resolver, env *generic.Envir
|
||||
dbLoc := reader.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)
|
||||
var pkgs []pkg.Package
|
||||
_ = sync.CollectSlice(&ctx, cataloging.ExecutorFile, sync.ToSeq(metadata), func(m pkg.DpkgDBEntry) (pkg.Package, error) {
|
||||
return newDpkgPackage(m, dbLoc, resolver, env.LinuxRelease, findDpkgInfoFiles(m.Package, resolver, reader.Location)...), nil
|
||||
return newDpkgPackage(ctx, m, dbLoc, resolver, env.LinuxRelease, findDpkgInfoFiles(m.Package, resolver, reader.Location)...), nil
|
||||
}, &pkgs)
|
||||
|
||||
return pkgs, nil, unknown.IfEmptyf(pkgs, "unable to determine packages")
|
||||
|
||||
@ -24,7 +24,7 @@ var (
|
||||
)
|
||||
|
||||
// parses individual CONTENTS files from the portage flat-file store (e.g. /var/db/pkg/*/*/CONTENTS).
|
||||
func parsePortageContents(_ context.Context, resolver file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
func parsePortageContents(ctx context.Context, resolver file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
cpvMatch := cpvRe.FindStringSubmatch(reader.RealPath)
|
||||
if cpvMatch == nil {
|
||||
return nil, nil, fmt.Errorf("failed to match package and version in %s", reader.RealPath)
|
||||
@ -43,7 +43,7 @@ func parsePortageContents(_ context.Context, resolver file.Resolver, _ *generic.
|
||||
|
||||
locations := file.NewLocationSet(reader.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation))
|
||||
|
||||
licenses, licenseLocations := addLicenses(resolver, reader.Location, &m)
|
||||
licenses, licenseLocations := addLicenses(ctx, resolver, reader.Location, &m)
|
||||
locations.Add(licenseLocations...)
|
||||
locations.Add(addSize(resolver, reader.Location, &m)...)
|
||||
addFiles(resolver, reader.Location, &m)
|
||||
@ -57,7 +57,6 @@ func parsePortageContents(_ context.Context, resolver file.Resolver, _ *generic.
|
||||
Type: pkg.PortagePkg,
|
||||
Metadata: m,
|
||||
}
|
||||
|
||||
p.SetID()
|
||||
|
||||
return []pkg.Package{p}, nil, nil
|
||||
@ -89,7 +88,7 @@ func addFiles(resolver file.Resolver, dbLocation file.Location, entry *pkg.Porta
|
||||
}
|
||||
}
|
||||
|
||||
func addLicenses(resolver file.Resolver, dbLocation file.Location, entry *pkg.PortageEntry) (pkg.LicenseSet, []file.Location) {
|
||||
func addLicenses(ctx context.Context, resolver file.Resolver, dbLocation file.Location, entry *pkg.PortageEntry) (pkg.LicenseSet, []file.Location) {
|
||||
parentPath := filepath.Dir(dbLocation.RealPath)
|
||||
|
||||
location := resolver.RelativeFileByPath(dbLocation, path.Join(parentPath, "LICENSE"))
|
||||
@ -108,12 +107,8 @@ func addLicenses(resolver file.Resolver, dbLocation file.Location, entry *pkg.Po
|
||||
og, spdxExpression := extractLicenses(resolver, location, licenseReader)
|
||||
entry.Licenses = og
|
||||
|
||||
return pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations(spdxExpression, *location),
|
||||
),
|
||||
[]file.Location{
|
||||
location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.SupportingEvidenceAnnotation),
|
||||
}
|
||||
return pkg.NewLicenseSet(pkg.NewLicenseFromLocationsWithContext(ctx, spdxExpression, *location)), []file.Location{
|
||||
location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.SupportingEvidenceAnnotation)}
|
||||
}
|
||||
|
||||
func addSize(resolver file.Resolver, dbLocation file.Location, entry *pkg.PortageEntry) []file.Location {
|
||||
|
||||
@ -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]
|
||||
|
||||
@ -280,7 +280,7 @@ func (j *archiveParser) discoverMainPackage(ctx context.Context) (*pkg.Package,
|
||||
func (j *archiveParser) discoverNameVersionLicense(ctx context.Context, manifest *pkg.JavaManifest) (string, string, []pkg.License, error) {
|
||||
// we use j.location because we want to associate the license declaration with where we discovered the contents in the manifest
|
||||
// TODO: when we support locations of paths within archives we should start passing the specific manifest location object instead of the top jar
|
||||
lics := pkg.NewLicensesFromLocation(j.location, selectLicenses(manifest)...)
|
||||
lics := pkg.NewLicensesFromLocationWithContext(ctx, j.location, selectLicenses(manifest)...)
|
||||
/*
|
||||
We should name and version from, in this order:
|
||||
1. pom.properties if we find exactly 1
|
||||
@ -351,10 +351,10 @@ func (j *archiveParser) findLicenseFromJavaMetadata(ctx context.Context, groupID
|
||||
}
|
||||
}
|
||||
|
||||
return toPkgLicenses(&j.location, pomLicenses)
|
||||
return toPkgLicenses(ctx, &j.location, pomLicenses)
|
||||
}
|
||||
|
||||
func toPkgLicenses(location *file.Location, licenses []maven.License) []pkg.License {
|
||||
func toPkgLicenses(ctx context.Context, location *file.Location, licenses []maven.License) []pkg.License {
|
||||
var out []pkg.License
|
||||
for _, license := range licenses {
|
||||
name := ""
|
||||
@ -365,10 +365,14 @@ func toPkgLicenses(location *file.Location, licenses []maven.License) []pkg.Lice
|
||||
if license.URL != nil {
|
||||
url = *license.URL
|
||||
}
|
||||
// note: it is possible to:
|
||||
// - have a license without a URL
|
||||
// - have license and a URL
|
||||
// - have a URL without a license (this is weird, but can happen)
|
||||
if name == "" && url == "" {
|
||||
continue
|
||||
}
|
||||
out = append(out, pkg.NewLicenseFromFields(name, url, location))
|
||||
out = append(out, pkg.NewLicenseFromFieldsWithContext(ctx, name, url, location))
|
||||
}
|
||||
return out
|
||||
}
|
||||
@ -492,7 +496,7 @@ func getDigestsFromArchive(ctx context.Context, archivePath string) ([]file.Dige
|
||||
}
|
||||
|
||||
func (j *archiveParser) getLicenseFromFileInArchive(ctx context.Context) ([]pkg.License, error) {
|
||||
var fileLicenses []pkg.License
|
||||
var out []pkg.License
|
||||
for _, filename := range licenses.FileNames() {
|
||||
licenseMatches := j.fileManifest.GlobMatch(true, "/META-INF/"+filename)
|
||||
if len(licenseMatches) == 0 {
|
||||
@ -509,19 +513,15 @@ func (j *archiveParser) getLicenseFromFileInArchive(ctx context.Context) ([]pkg.
|
||||
for _, licenseMatch := range licenseMatches {
|
||||
licenseContents := contents[licenseMatch]
|
||||
r := strings.NewReader(licenseContents)
|
||||
parsed, err := j.licenseScanner.PkgSearch(ctx, file.NewLocationReadCloser(j.location, io.NopCloser(r)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(parsed) > 0 {
|
||||
fileLicenses = append(fileLicenses, parsed...)
|
||||
lics := pkg.NewLicensesFromReadCloserWithContext(ctx, file.NewLocationReadCloser(j.location, io.NopCloser(r)))
|
||||
if len(lics) > 0 {
|
||||
out = append(out, lics...)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fileLicenses, nil
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (j *archiveParser) discoverPkgsFromNestedArchives(ctx context.Context, parentPkg *pkg.Package) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
@ -692,7 +692,7 @@ func newPackageFromMavenData(ctx context.Context, r *maven.Resolver, pomProperti
|
||||
log.WithFields("error", err, "mavenID", maven.NewID(pomProperties.GroupID, pomProperties.ArtifactID, pomProperties.Version)).Trace("error attempting to resolve licenses")
|
||||
}
|
||||
|
||||
licenseSet := pkg.NewLicenseSet(toPkgLicenses(&location, pomLicenses)...)
|
||||
licenseSet := pkg.NewLicenseSet(toPkgLicenses(ctx, &location, pomLicenses)...)
|
||||
|
||||
p := pkg.Package{
|
||||
Name: pomProperties.ArtifactID,
|
||||
|
||||
@ -14,13 +14,11 @@ import (
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"github.com/google/licensecheck"
|
||||
"github.com/gookit/color"
|
||||
"github.com/scylladb/go-set/strset"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/internal/licenses"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/license"
|
||||
@ -32,10 +30,7 @@ import (
|
||||
|
||||
func TestSearchMavenForLicenses(t *testing.T) {
|
||||
url := maventest.MockRepo(t, "internal/maven/test-fixtures/maven-repo")
|
||||
sc := &licenses.ScannerConfig{Scanner: licensecheck.Scan, CoverageThreshold: 75}
|
||||
scanner, err := licenses.NewScanner(sc)
|
||||
require.NoError(t, err)
|
||||
ctx := licenses.SetContextLicenseScanner(context.Background(), scanner)
|
||||
ctx := pkgtest.Context()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@ -88,17 +83,13 @@ func TestSearchMavenForLicenses(t *testing.T) {
|
||||
// assert licenses are discovered from upstream
|
||||
_, _, _, parsedPom := ap.discoverMainPackageFromPomInfo(context.Background())
|
||||
resolvedLicenses, _ := ap.maven.ResolveLicenses(context.Background(), parsedPom.project)
|
||||
assert.Equal(t, tc.expectedLicenses, toPkgLicenses(nil, resolvedLicenses))
|
||||
assert.Equal(t, tc.expectedLicenses, toPkgLicenses(ctx, nil, resolvedLicenses))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseJar(t *testing.T) {
|
||||
sc := &licenses.ScannerConfig{Scanner: licensecheck.Scan, CoverageThreshold: 75}
|
||||
scanner, err := licenses.NewScanner(sc)
|
||||
require.NoError(t, err)
|
||||
ctx := licenses.SetContextLicenseScanner(context.Background(), scanner)
|
||||
|
||||
ctx := pkgtest.Context()
|
||||
tests := []struct {
|
||||
name string
|
||||
fixture string
|
||||
@ -121,7 +112,7 @@ func TestParseJar(t *testing.T) {
|
||||
Version: "1.0-SNAPSHOT",
|
||||
PURL: "pkg:maven/io.jenkins.plugins/example-jenkins-plugin@1.0-SNAPSHOT",
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("MIT License", file.NewLocation("test-fixtures/java-builds/packages/example-jenkins-plugin.hpi")),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "MIT License", file.NewLocation("test-fixtures/java-builds/packages/example-jenkins-plugin.hpi")),
|
||||
),
|
||||
Language: pkg.Java,
|
||||
Type: pkg.JenkinsPluginPkg,
|
||||
@ -207,14 +198,10 @@ func TestParseJar(t *testing.T) {
|
||||
Language: pkg.Java,
|
||||
Type: pkg.JavaPkg,
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromFields(
|
||||
"Apache 2",
|
||||
"http://www.apache.org/licenses/LICENSE-2.0.txt",
|
||||
func() *file.Location {
|
||||
pkg.NewLicenseFromFieldsWithContext(ctx, "Apache 2", "http://www.apache.org/licenses/LICENSE-2.0.txt", func() *file.Location {
|
||||
l := file.NewLocation("test-fixtures/java-builds/packages/example-java-app-gradle-0.1.0.jar")
|
||||
return &l
|
||||
}(),
|
||||
),
|
||||
}()),
|
||||
),
|
||||
Metadata: pkg.JavaArchive{
|
||||
// ensure that nested packages with different names than that of the parent are appended as
|
||||
@ -306,14 +293,10 @@ func TestParseJar(t *testing.T) {
|
||||
Version: "2.9.2",
|
||||
PURL: "pkg:maven/joda-time/joda-time@2.9.2",
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromFields(
|
||||
"Apache 2",
|
||||
"http://www.apache.org/licenses/LICENSE-2.0.txt",
|
||||
func() *file.Location {
|
||||
pkg.NewLicenseFromFieldsWithContext(ctx, "Apache 2", "http://www.apache.org/licenses/LICENSE-2.0.txt", func() *file.Location {
|
||||
l := file.NewLocation("test-fixtures/java-builds/packages/example-java-app-maven-0.1.0.jar")
|
||||
return &l
|
||||
}(),
|
||||
),
|
||||
}()),
|
||||
),
|
||||
Language: pkg.Java,
|
||||
Type: pkg.JavaPkg,
|
||||
@ -369,7 +352,7 @@ func TestParseJar(t *testing.T) {
|
||||
defer cleanupFn()
|
||||
require.NoError(t, err)
|
||||
|
||||
actual, _, err := parser.parse(context.Background(), nil)
|
||||
actual, _, err := parser.parse(ctx, nil)
|
||||
if test.wantErr != nil {
|
||||
test.wantErr(t, err)
|
||||
} else {
|
||||
@ -432,11 +415,18 @@ func TestParseJar(t *testing.T) {
|
||||
metadata.Manifest.Main = newMain
|
||||
}
|
||||
}
|
||||
|
||||
// write censored data back
|
||||
a.Metadata = metadata
|
||||
|
||||
pkgtest.AssertPackagesEqual(t, e, a)
|
||||
// we can't use cmpopts.IgnoreFields for the license contents because of the set structure
|
||||
// drop the license contents from the comparison
|
||||
licenses := a.Licenses.ToSlice()
|
||||
for i := range licenses {
|
||||
licenses[i].Contents = ""
|
||||
}
|
||||
a.Licenses = pkg.NewLicenseSet(licenses...)
|
||||
|
||||
pkgtest.AssertPackagesEqual(t, e, a, cmpopts.IgnoreFields(pkg.License{}, "Contents"))
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -1102,6 +1092,7 @@ func Test_artifactIDMatchesFilename(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_parseJavaArchive_regressions(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
apiAll := pkg.Package{
|
||||
Name: "api-all",
|
||||
Version: "2.0.0",
|
||||
@ -1192,7 +1183,8 @@ func Test_parseJavaArchive_regressions(t *testing.T) {
|
||||
PURL: "pkg:maven/com.fasterxml.jackson.core/jackson-core@2.15.2",
|
||||
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/jar-metadata/cache/jackson-core-2.15.2.jar")),
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicensesFromLocation(
|
||||
pkg.NewLicensesFromLocationWithContext(
|
||||
ctx,
|
||||
file.NewLocation("test-fixtures/jar-metadata/cache/jackson-core-2.15.2.jar"),
|
||||
"https://www.apache.org/licenses/LICENSE-2.0.txt",
|
||||
)...,
|
||||
@ -1246,7 +1238,8 @@ func Test_parseJavaArchive_regressions(t *testing.T) {
|
||||
PURL: "pkg:maven/com.fasterxml.jackson.core/jackson-core@2.15.2",
|
||||
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/jar-metadata/cache/com.fasterxml.jackson.core.jackson-core-2.15.2.jar")),
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicensesFromLocation(
|
||||
pkg.NewLicensesFromLocationWithContext(
|
||||
ctx,
|
||||
file.NewLocation("test-fixtures/jar-metadata/cache/com.fasterxml.jackson.core.jackson-core-2.15.2.jar"),
|
||||
"https://www.apache.org/licenses/LICENSE-2.0.txt",
|
||||
)...,
|
||||
@ -1380,11 +1373,7 @@ func Test_parseJavaArchive_regressions(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_deterministicMatchingPomProperties(t *testing.T) {
|
||||
sc := &licenses.ScannerConfig{Scanner: licensecheck.Scan, CoverageThreshold: 75}
|
||||
scanner, err := licenses.NewScanner(sc)
|
||||
require.NoError(t, err)
|
||||
ctx := licenses.SetContextLicenseScanner(context.Background(), scanner)
|
||||
|
||||
ctx := pkgtest.Context()
|
||||
tests := []struct {
|
||||
fixture string
|
||||
expected maven.ID
|
||||
|
||||
@ -117,7 +117,7 @@ func newPackageFromMavenPom(ctx context.Context, r *maven.Resolver, pom *maven.P
|
||||
if err != nil {
|
||||
log.Tracef("error resolving licenses: %v", err)
|
||||
}
|
||||
licenses := toPkgLicenses(&location, pomLicenses)
|
||||
licenses := toPkgLicenses(ctx, &location, pomLicenses)
|
||||
|
||||
m := pkg.JavaArchive{
|
||||
PomProject: &pkg.JavaPomProject{
|
||||
@ -240,7 +240,7 @@ func newPackageFromDependency(ctx context.Context, r *maven.Resolver, pom *maven
|
||||
var pomProject *pkg.JavaPomProject
|
||||
if dependencyPom != nil {
|
||||
depLicenses, _ := r.ResolveLicenses(ctx, dependencyPom)
|
||||
licenses = append(licenses, toPkgLicenses(nil, depLicenses)...)
|
||||
licenses = append(licenses, toPkgLicenses(ctx, nil, depLicenses)...)
|
||||
pomProject = &pkg.JavaPomProject{
|
||||
Parent: pomParent(ctx, r, dependencyPom),
|
||||
GroupID: id.GroupID,
|
||||
|
||||
@ -194,6 +194,7 @@ func Test_parseCommonsTextPomXMLProject(t *testing.T) {
|
||||
func Test_parsePomXMLProject(t *testing.T) {
|
||||
// TODO: ideally we would have the path to the contained pom.xml, not the jar
|
||||
jarLocation := file.NewLocation("path/to/archive.jar")
|
||||
ctx := context.TODO()
|
||||
tests := []struct {
|
||||
name string
|
||||
project *pkg.JavaPomProject
|
||||
@ -270,7 +271,7 @@ func Test_parsePomXMLProject(t *testing.T) {
|
||||
|
||||
licenses, err := r.ResolveLicenses(context.Background(), pom)
|
||||
//assert.NoError(t, err)
|
||||
assert.Equal(t, test.licenses, toPkgLicenses(&jarLocation, licenses))
|
||||
assert.Equal(t, test.licenses, toPkgLicenses(ctx, &jarLocation, licenses))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package javascript
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/anchore/syft/syft/file"
|
||||
@ -9,6 +10,7 @@ import (
|
||||
)
|
||||
|
||||
func Test_JavascriptCataloger(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
locationSet := file.NewLocationSet(file.NewLocation("package-lock.json"))
|
||||
expectedPkgs := []pkg.Package{
|
||||
{
|
||||
@ -20,7 +22,7 @@ func Test_JavascriptCataloger(t *testing.T) {
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("MIT", file.NewLocation("package-lock.json")),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "MIT", file.NewLocation("package-lock.json")),
|
||||
),
|
||||
Metadata: pkg.NpmPackageLockEntry{Resolved: "https://registry.npmjs.org/@actions/core/-/core-1.6.0.tgz", Integrity: "sha512-NB1UAZomZlCV/LmJqkLhNTqtKfFXJZAUPcfl/zqG7EfsQdeUJtaWO98SGbuQ3pydJ3fHl2CvI/51OKYlCYYcaw=="},
|
||||
},
|
||||
@ -43,7 +45,7 @@ func Test_JavascriptCataloger(t *testing.T) {
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("MIT", file.NewLocation("package-lock.json")),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "MIT", file.NewLocation("package-lock.json")),
|
||||
),
|
||||
Metadata: pkg.NpmPackageLockEntry{Resolved: "https://registry.npmjs.org/cowsay/-/cowsay-1.4.0.tgz", Integrity: "sha512-rdg5k5PsHFVJheO/pmE3aDg2rUDDTfPJau6yYkZYlHFktUz+UxbE+IgnUAEyyCyv4noL5ltxXD0gZzmHPCy/9g=="},
|
||||
},
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package javascript
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
@ -17,13 +18,13 @@ import (
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
func newPackageJSONPackage(u packageJSON, indexLocation file.Location) pkg.Package {
|
||||
func newPackageJSONPackage(ctx context.Context, u packageJSON, indexLocation file.Location) pkg.Package {
|
||||
licenseCandidates, err := u.licensesFromJSON()
|
||||
if err != nil {
|
||||
log.Debugf("unable to extract licenses from javascript package.json: %+v", err)
|
||||
}
|
||||
|
||||
license := pkg.NewLicensesFromLocation(indexLocation, licenseCandidates...)
|
||||
license := pkg.NewLicensesFromLocationWithContext(ctx, indexLocation, licenseCandidates...)
|
||||
p := pkg.Package{
|
||||
Name: u.Name,
|
||||
Version: u.Version,
|
||||
@ -48,7 +49,7 @@ func newPackageJSONPackage(u packageJSON, indexLocation file.Location) pkg.Packa
|
||||
return p
|
||||
}
|
||||
|
||||
func newPackageLockV1Package(cfg CatalogerConfig, resolver file.Resolver, location file.Location, name string, u lockDependency) pkg.Package {
|
||||
func newPackageLockV1Package(ctx context.Context, cfg CatalogerConfig, resolver file.Resolver, location file.Location, name string, u lockDependency) pkg.Package {
|
||||
version := u.Version
|
||||
|
||||
const aliasPrefixPackageLockV1 = "npm:"
|
||||
@ -69,7 +70,7 @@ func newPackageLockV1Package(cfg CatalogerConfig, resolver file.Resolver, locati
|
||||
if cfg.SearchRemoteLicenses {
|
||||
license, err := getLicenseFromNpmRegistry(cfg.NPMBaseURL, name, version)
|
||||
if err == nil && license != "" {
|
||||
licenses := pkg.NewLicensesFromValues(license)
|
||||
licenses := pkg.NewLicensesFromValuesWithContext(ctx, license)
|
||||
licenseSet = pkg.NewLicenseSet(licenses...)
|
||||
}
|
||||
if err != nil {
|
||||
@ -78,6 +79,7 @@ func newPackageLockV1Package(cfg CatalogerConfig, resolver file.Resolver, locati
|
||||
}
|
||||
|
||||
return finalizeLockPkg(
|
||||
ctx,
|
||||
resolver,
|
||||
location,
|
||||
pkg.Package{
|
||||
@ -93,15 +95,15 @@ func newPackageLockV1Package(cfg CatalogerConfig, resolver file.Resolver, locati
|
||||
)
|
||||
}
|
||||
|
||||
func newPackageLockV2Package(cfg CatalogerConfig, resolver file.Resolver, location file.Location, name string, u lockPackage) pkg.Package {
|
||||
func newPackageLockV2Package(ctx context.Context, cfg CatalogerConfig, resolver file.Resolver, location file.Location, name string, u lockPackage) pkg.Package {
|
||||
var licenseSet pkg.LicenseSet
|
||||
|
||||
if u.License != nil {
|
||||
licenseSet = pkg.NewLicenseSet(pkg.NewLicensesFromLocation(location, u.License...)...)
|
||||
licenseSet = pkg.NewLicenseSet(pkg.NewLicensesFromLocationWithContext(ctx, location, u.License...)...)
|
||||
} else if cfg.SearchRemoteLicenses {
|
||||
license, err := getLicenseFromNpmRegistry(cfg.NPMBaseURL, name, u.Version)
|
||||
if err == nil && license != "" {
|
||||
licenses := pkg.NewLicensesFromValues(license)
|
||||
licenses := pkg.NewLicensesFromValuesWithContext(ctx, license)
|
||||
licenseSet = pkg.NewLicenseSet(licenses...)
|
||||
}
|
||||
if err != nil {
|
||||
@ -110,6 +112,7 @@ func newPackageLockV2Package(cfg CatalogerConfig, resolver file.Resolver, locati
|
||||
}
|
||||
|
||||
return finalizeLockPkg(
|
||||
ctx,
|
||||
resolver,
|
||||
location,
|
||||
pkg.Package{
|
||||
@ -125,8 +128,9 @@ func newPackageLockV2Package(cfg CatalogerConfig, resolver file.Resolver, locati
|
||||
)
|
||||
}
|
||||
|
||||
func newPnpmPackage(resolver file.Resolver, location file.Location, name, version string) pkg.Package {
|
||||
func newPnpmPackage(ctx context.Context, resolver file.Resolver, location file.Location, name, version string) pkg.Package {
|
||||
return finalizeLockPkg(
|
||||
ctx,
|
||||
resolver,
|
||||
location,
|
||||
pkg.Package{
|
||||
@ -140,13 +144,13 @@ func newPnpmPackage(resolver file.Resolver, location file.Location, name, versio
|
||||
)
|
||||
}
|
||||
|
||||
func newYarnLockPackage(cfg CatalogerConfig, resolver file.Resolver, location file.Location, name, version string, resolved string, integrity string) pkg.Package {
|
||||
func newYarnLockPackage(ctx context.Context, cfg CatalogerConfig, resolver file.Resolver, location file.Location, name, version string, resolved string, integrity string) pkg.Package {
|
||||
var licenseSet pkg.LicenseSet
|
||||
|
||||
if cfg.SearchRemoteLicenses {
|
||||
license, err := getLicenseFromNpmRegistry(cfg.NPMBaseURL, name, version)
|
||||
if err == nil && license != "" {
|
||||
licenses := pkg.NewLicensesFromValues(license)
|
||||
licenses := pkg.NewLicensesFromValuesWithContext(ctx, license)
|
||||
licenseSet = pkg.NewLicenseSet(licenses...)
|
||||
}
|
||||
if err != nil {
|
||||
@ -154,6 +158,7 @@ func newYarnLockPackage(cfg CatalogerConfig, resolver file.Resolver, location fi
|
||||
}
|
||||
}
|
||||
return finalizeLockPkg(
|
||||
ctx,
|
||||
resolver,
|
||||
location,
|
||||
pkg.Package{
|
||||
@ -226,9 +231,9 @@ func getLicenseFromNpmRegistry(baseURL, packageName, version string) (string, er
|
||||
return license.License, nil
|
||||
}
|
||||
|
||||
func finalizeLockPkg(resolver file.Resolver, location file.Location, p pkg.Package) pkg.Package {
|
||||
func finalizeLockPkg(ctx context.Context, resolver file.Resolver, location file.Location, p pkg.Package) pkg.Package {
|
||||
licenseCandidate := addLicenses(p.Name, resolver, location)
|
||||
p.Licenses.Add(pkg.NewLicensesFromLocation(location, licenseCandidate...)...)
|
||||
p.Licenses.Add(pkg.NewLicensesFromLocationWithContext(ctx, location, licenseCandidate...)...)
|
||||
p.SetID()
|
||||
return p
|
||||
}
|
||||
|
||||
@ -51,7 +51,7 @@ type repository struct {
|
||||
var authorPattern = regexp.MustCompile(`^\s*(?P<name>[^<(]*)(\s+<(?P<email>.*)>)?(\s\((?P<url>.*)\))?\s*$`)
|
||||
|
||||
// parsePackageJSON parses a package.json and returns the discovered JavaScript packages.
|
||||
func parsePackageJSON(_ context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
func parsePackageJSON(ctx context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
var pkgs []pkg.Package
|
||||
dec := json.NewDecoder(reader)
|
||||
|
||||
@ -67,7 +67,7 @@ func parsePackageJSON(_ context.Context, _ file.Resolver, _ *generic.Environment
|
||||
// a compliance filter later will remove these packages based on compliance rules
|
||||
pkgs = append(
|
||||
pkgs,
|
||||
newPackageJSONPackage(p, reader.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
|
||||
newPackageJSONPackage(ctx, p, reader.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package javascript
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@ -11,6 +12,7 @@ import (
|
||||
)
|
||||
|
||||
func TestParsePackageJSON(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
tests := []struct {
|
||||
Fixture string
|
||||
ExpectedPkg pkg.Package
|
||||
@ -24,7 +26,7 @@ func TestParsePackageJSON(t *testing.T) {
|
||||
Type: pkg.NpmPkg,
|
||||
Language: pkg.JavaScript,
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("Artistic-2.0", file.NewLocation("test-fixtures/pkg-json/package.json")),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "Artistic-2.0", file.NewLocation("test-fixtures/pkg-json/package.json")),
|
||||
),
|
||||
Metadata: pkg.NpmPackage{
|
||||
Name: "npm",
|
||||
@ -45,7 +47,7 @@ func TestParsePackageJSON(t *testing.T) {
|
||||
Type: pkg.NpmPkg,
|
||||
Language: pkg.JavaScript,
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("ISC", file.NewLocation("test-fixtures/pkg-json/package-license-object.json")),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "ISC", file.NewLocation("test-fixtures/pkg-json/package-license-object.json")),
|
||||
),
|
||||
Metadata: pkg.NpmPackage{
|
||||
Name: "npm",
|
||||
@ -65,8 +67,8 @@ func TestParsePackageJSON(t *testing.T) {
|
||||
PURL: "pkg:npm/npm@6.14.6",
|
||||
Type: pkg.NpmPkg,
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("MIT", file.NewLocation("test-fixtures/pkg-json/package-license-objects.json")),
|
||||
pkg.NewLicenseFromLocations("Apache-2.0", file.NewLocation("test-fixtures/pkg-json/package-license-objects.json")),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "MIT", file.NewLocation("test-fixtures/pkg-json/package-license-objects.json")),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "Apache-2.0", file.NewLocation("test-fixtures/pkg-json/package-license-objects.json")),
|
||||
),
|
||||
Language: pkg.JavaScript,
|
||||
Metadata: pkg.NpmPackage{
|
||||
@ -123,7 +125,7 @@ func TestParsePackageJSON(t *testing.T) {
|
||||
PURL: "pkg:npm/npm@6.14.6",
|
||||
Type: pkg.NpmPkg,
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("Artistic-2.0", file.NewLocation("test-fixtures/pkg-json/package-nested-author.json")),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "Artistic-2.0", file.NewLocation("test-fixtures/pkg-json/package-nested-author.json")),
|
||||
),
|
||||
Language: pkg.JavaScript,
|
||||
Metadata: pkg.NpmPackage{
|
||||
@ -144,7 +146,7 @@ func TestParsePackageJSON(t *testing.T) {
|
||||
PURL: "pkg:npm/function-bind@1.1.1",
|
||||
Type: pkg.NpmPkg,
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("MIT", file.NewLocation("test-fixtures/pkg-json/package-repo-string.json")),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "MIT", file.NewLocation("test-fixtures/pkg-json/package-repo-string.json")),
|
||||
),
|
||||
Language: pkg.JavaScript,
|
||||
Metadata: pkg.NpmPackage{
|
||||
@ -165,7 +167,7 @@ func TestParsePackageJSON(t *testing.T) {
|
||||
PURL: "pkg:npm/npm@6.14.6",
|
||||
Type: pkg.NpmPkg,
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("Artistic-2.0", file.NewLocation("test-fixtures/pkg-json/package-private.json")),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "Artistic-2.0", file.NewLocation("test-fixtures/pkg-json/package-private.json")),
|
||||
),
|
||||
Language: pkg.JavaScript,
|
||||
Metadata: pkg.NpmPackage{
|
||||
@ -187,7 +189,7 @@ func TestParsePackageJSON(t *testing.T) {
|
||||
PURL: "pkg:npm/npm@6.14.6",
|
||||
Type: pkg.NpmPkg,
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("Artistic-2.0", file.NewLocation("test-fixtures/pkg-json/package-author-non-standard.json")),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "Artistic-2.0", file.NewLocation("test-fixtures/pkg-json/package-author-non-standard.json")),
|
||||
),
|
||||
Language: pkg.JavaScript,
|
||||
Metadata: pkg.NpmPackage{
|
||||
|
||||
@ -55,7 +55,7 @@ func newGenericPackageLockAdapter(cfg CatalogerConfig) genericPackageLockAdapter
|
||||
}
|
||||
|
||||
// parsePackageLock parses a package-lock.json and returns the discovered JavaScript packages.
|
||||
func (a genericPackageLockAdapter) parsePackageLock(_ context.Context, resolver file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
func (a genericPackageLockAdapter) parsePackageLock(ctx context.Context, resolver file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
// in the case we find package-lock.json files in the node_modules directories, skip those
|
||||
// as the whole purpose of the lock file is for the specific dependencies of the root project
|
||||
if pathContainsNodeModulesDirectory(reader.Path()) {
|
||||
@ -81,7 +81,7 @@ func (a genericPackageLockAdapter) parsePackageLock(_ context.Context, resolver
|
||||
continue
|
||||
}
|
||||
|
||||
pkgs = append(pkgs, newPackageLockV1Package(a.cfg, resolver, reader.Location, name, pkgMeta))
|
||||
pkgs = append(pkgs, newPackageLockV1Package(ctx, a.cfg, resolver, reader.Location, name, pkgMeta))
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,7 +106,7 @@ func (a genericPackageLockAdapter) parsePackageLock(_ context.Context, resolver
|
||||
|
||||
pkgs = append(
|
||||
pkgs,
|
||||
newPackageLockV2Package(a.cfg, resolver, reader.Location, getNameFromPath(name), pkgMeta),
|
||||
newPackageLockV2Package(ctx, a.cfg, resolver, reader.Location, getNameFromPath(name), pkgMeta),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package javascript
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
@ -111,6 +112,7 @@ func TestParsePackageLock(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestParsePackageLockV2(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
fixture := "test-fixtures/pkg-lock/package-lock-2.json"
|
||||
var expectedRelationships []artifact.Relationship
|
||||
expectedPkgs := []pkg.Package{
|
||||
@ -129,7 +131,7 @@ func TestParsePackageLockV2(t *testing.T) {
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("MIT", file.NewLocation(fixture)),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "MIT", file.NewLocation(fixture)),
|
||||
),
|
||||
Metadata: pkg.NpmPackageLockEntry{Resolved: "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", Integrity: "sha1-XxnSuFqY6VWANvajysyIGUIPBc8="},
|
||||
},
|
||||
@ -140,7 +142,7 @@ func TestParsePackageLockV2(t *testing.T) {
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("MIT", file.NewLocation(fixture)),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "MIT", file.NewLocation(fixture)),
|
||||
),
|
||||
Metadata: pkg.NpmPackageLockEntry{Resolved: "https://registry.npmjs.org/@types/react/-/react-18.0.17.tgz", Integrity: "sha1-RYPZwyLWfv5LOak10iPtzHBQzPQ="},
|
||||
},
|
||||
@ -151,7 +153,7 @@ func TestParsePackageLockV2(t *testing.T) {
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("MIT", file.NewLocation(fixture)),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "MIT", file.NewLocation(fixture)),
|
||||
),
|
||||
Metadata: pkg.NpmPackageLockEntry{Resolved: "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", Integrity: "sha1-GmL4lSVyPd4kuhsBsJK/XfitTTk="},
|
||||
},
|
||||
@ -162,7 +164,7 @@ func TestParsePackageLockV2(t *testing.T) {
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("MIT", file.NewLocation(fixture)),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "MIT", file.NewLocation(fixture)),
|
||||
),
|
||||
Metadata: pkg.NpmPackageLockEntry{Resolved: "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz", Integrity: "sha1-TdysNxjXh8+d8NG30VAzklyPKfI="},
|
||||
},
|
||||
@ -227,6 +229,7 @@ func TestParsePackageLockV3(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestParsePackageLockAlias(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
var expectedRelationships []artifact.Relationship
|
||||
commonPkgs := []pkg.Package{
|
||||
{
|
||||
@ -266,7 +269,7 @@ func TestParsePackageLockAlias(t *testing.T) {
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("ISC", file.NewLocation(packageLockV2)),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "ISC", file.NewLocation(packageLockV2)),
|
||||
),
|
||||
Metadata: pkg.NpmPackageLockEntry{},
|
||||
}
|
||||
@ -288,6 +291,7 @@ func TestParsePackageLockAlias(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestParsePackageLockLicenseWithArray(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
fixture := "test-fixtures/pkg-lock/array-license-package-lock.json"
|
||||
var expectedRelationships []artifact.Relationship
|
||||
expectedPkgs := []pkg.Package{
|
||||
@ -297,7 +301,7 @@ func TestParsePackageLockLicenseWithArray(t *testing.T) {
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("ISC", file.NewLocation(fixture)),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "ISC", file.NewLocation(fixture)),
|
||||
),
|
||||
PURL: "pkg:npm/tmp@1.0.0",
|
||||
Metadata: pkg.NpmPackageLockEntry{},
|
||||
@ -309,8 +313,8 @@ func TestParsePackageLockLicenseWithArray(t *testing.T) {
|
||||
Type: pkg.NpmPkg,
|
||||
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("MIT", file.NewLocation(fixture)),
|
||||
pkg.NewLicenseFromLocations("Apache2", file.NewLocation(fixture)),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "MIT", file.NewLocation(fixture)),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "Apache2", file.NewLocation(fixture)),
|
||||
),
|
||||
PURL: "pkg:npm/pause-stream@0.0.11",
|
||||
Metadata: pkg.NpmPackageLockEntry{},
|
||||
@ -321,7 +325,7 @@ func TestParsePackageLockLicenseWithArray(t *testing.T) {
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("MIT", file.NewLocation(fixture)),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "MIT", file.NewLocation(fixture)),
|
||||
),
|
||||
PURL: "pkg:npm/through@2.3.8",
|
||||
Metadata: pkg.NpmPackageLockEntry{},
|
||||
|
||||
@ -27,7 +27,7 @@ type pnpmLockYaml struct {
|
||||
Packages map[string]interface{} `json:"packages" yaml:"packages"`
|
||||
}
|
||||
|
||||
func parsePnpmLock(_ context.Context, resolver file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
func parsePnpmLock(ctx context.Context, resolver file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
bytes, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to load pnpm-lock.yaml file: %w", err)
|
||||
@ -66,7 +66,7 @@ func parsePnpmLock(_ context.Context, resolver file.Resolver, _ *generic.Environ
|
||||
continue
|
||||
}
|
||||
|
||||
pkgs = append(pkgs, newPnpmPackage(resolver, reader.Location, name, version))
|
||||
pkgs = append(pkgs, newPnpmPackage(ctx, resolver, reader.Location, name, version))
|
||||
}
|
||||
|
||||
packageNameRegex := regexp.MustCompile(`^/?([^(]*)(?:\(.*\))*$`)
|
||||
@ -90,7 +90,7 @@ func parsePnpmLock(_ context.Context, resolver file.Resolver, _ *generic.Environ
|
||||
continue
|
||||
}
|
||||
|
||||
pkgs = append(pkgs, newPnpmPackage(resolver, reader.Location, name, version))
|
||||
pkgs = append(pkgs, newPnpmPackage(ctx, resolver, reader.Location, name, version))
|
||||
}
|
||||
|
||||
pkg.Sort(pkgs)
|
||||
|
||||
@ -59,7 +59,7 @@ func newGenericYarnLockAdapter(cfg CatalogerConfig) genericYarnLockAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
func (a genericYarnLockAdapter) parseYarnLock(_ context.Context, resolver file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
func (a genericYarnLockAdapter) parseYarnLock(ctx context.Context, resolver file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
// in the case we find yarn.lock files in the node_modules directories, skip those
|
||||
// as the whole purpose of the lock file is for the specific dependencies of the project
|
||||
if pathContainsNodeModulesDirectory(reader.Path()) {
|
||||
@ -78,7 +78,7 @@ func (a genericYarnLockAdapter) parseYarnLock(_ context.Context, resolver file.R
|
||||
if packageName := findPackageName(line); packageName != "" {
|
||||
// When we find a new package, check if we have unsaved identifiers
|
||||
if currentPackage != "" && currentVersion != "" && !parsedPackages.Has(currentPackage+"@"+currentVersion) {
|
||||
pkgs = append(pkgs, newYarnLockPackage(a.cfg, resolver, reader.Location, currentPackage, currentVersion, currentResolved, currentIntegrity))
|
||||
pkgs = append(pkgs, newYarnLockPackage(ctx, a.cfg, resolver, reader.Location, currentPackage, currentVersion, currentResolved, currentIntegrity))
|
||||
parsedPackages.Add(currentPackage + "@" + currentVersion)
|
||||
}
|
||||
|
||||
@ -90,7 +90,7 @@ func (a genericYarnLockAdapter) parseYarnLock(_ context.Context, resolver file.R
|
||||
currentPackage = packageName
|
||||
currentVersion = version
|
||||
} else if integrity := findIntegrity(line); integrity != "" && !parsedPackages.Has(currentPackage+"@"+currentVersion) {
|
||||
pkgs = append(pkgs, newYarnLockPackage(a.cfg, resolver, reader.Location, currentPackage, currentVersion, currentResolved, integrity))
|
||||
pkgs = append(pkgs, newYarnLockPackage(ctx, a.cfg, resolver, reader.Location, currentPackage, currentVersion, currentResolved, integrity))
|
||||
parsedPackages.Add(currentPackage + "@" + currentVersion)
|
||||
|
||||
// Cleanup to indicate no unsaved identifiers
|
||||
@ -103,7 +103,7 @@ func (a genericYarnLockAdapter) parseYarnLock(_ context.Context, resolver file.R
|
||||
|
||||
// check if we have valid unsaved data after end-of-file has reached
|
||||
if currentPackage != "" && currentVersion != "" && !parsedPackages.Has(currentPackage+"@"+currentVersion) {
|
||||
pkgs = append(pkgs, newYarnLockPackage(a.cfg, resolver, reader.Location, currentPackage, currentVersion, currentResolved, currentIntegrity))
|
||||
pkgs = append(pkgs, newYarnLockPackage(ctx, a.cfg, resolver, reader.Location, currentPackage, currentVersion, currentResolved, currentIntegrity))
|
||||
parsedPackages.Add(currentPackage + "@" + currentVersion)
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package javascript
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@ -235,6 +236,7 @@ type handlerPath struct {
|
||||
}
|
||||
|
||||
func TestSearchYarnForLicenses(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
fixture := "test-fixtures/yarn-remote/yarn.lock"
|
||||
locations := file.NewLocationSet(file.NewLocation(fixture))
|
||||
mux, url, teardown := setup()
|
||||
@ -262,7 +264,7 @@ func TestSearchYarnForLicenses(t *testing.T) {
|
||||
Version: "7.10.4",
|
||||
Locations: locations,
|
||||
PURL: "pkg:npm/%40babel/code-frame@7.10.4",
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicense("MIT")),
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicenseWithContext(ctx, "MIT")),
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
Metadata: pkg.YarnLockEntry{
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package kernel
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
@ -11,6 +12,7 @@ import (
|
||||
)
|
||||
|
||||
func Test_KernelCataloger(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
kernelPkg := pkg.Package{
|
||||
Name: "linux-kernel",
|
||||
Version: "6.0.7-301.fc37.x86_64",
|
||||
@ -49,7 +51,7 @@ func Test_KernelCataloger(t *testing.T) {
|
||||
),
|
||||
),
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("GPL v2",
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "GPL v2",
|
||||
file.NewVirtualLocation(
|
||||
"/lib/modules/6.0.7-301.fc37.x86_64/kernel/drivers/tty/ttynull.ko",
|
||||
"/lib/modules/6.0.7-301.fc37.x86_64/kernel/drivers/tty/ttynull.ko",
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package kernel
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/packageurl-go"
|
||||
@ -40,12 +41,12 @@ func newLinuxKernelPackage(metadata pkg.LinuxKernel, archiveLocation file.Locati
|
||||
return p
|
||||
}
|
||||
|
||||
func newLinuxKernelModulePackage(metadata pkg.LinuxKernelModule, kmLocation file.Location) pkg.Package {
|
||||
func newLinuxKernelModulePackage(ctx context.Context, metadata pkg.LinuxKernelModule, kmLocation file.Location) pkg.Package {
|
||||
p := pkg.Package{
|
||||
Name: metadata.Name,
|
||||
Version: metadata.Version,
|
||||
Locations: file.NewLocationSet(kmLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(kmLocation, metadata.License)...),
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocationWithContext(ctx, kmLocation, metadata.License)...),
|
||||
PURL: packageURL(metadata.Name, metadata.Version),
|
||||
Type: pkg.LinuxKernelModulePkg,
|
||||
Metadata: metadata,
|
||||
|
||||
@ -15,7 +15,7 @@ import (
|
||||
|
||||
const modinfoName = ".modinfo"
|
||||
|
||||
func parseLinuxKernelModuleFile(_ context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
func parseLinuxKernelModuleFile(ctx context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
unionReader, err := unionreader.GetUnionReader(reader)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to get union reader for file: %w", err)
|
||||
@ -32,6 +32,7 @@ func parseLinuxKernelModuleFile(_ context.Context, _ file.Resolver, _ *generic.E
|
||||
|
||||
return []pkg.Package{
|
||||
newLinuxKernelModulePackage(
|
||||
ctx,
|
||||
*metadata,
|
||||
reader.Location,
|
||||
),
|
||||
|
||||
@ -1,13 +1,15 @@
|
||||
package lua
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/anchore/packageurl-go"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
func newLuaRocksPackage(u luaRocksPackage, indexLocation file.Location) pkg.Package {
|
||||
license := pkg.NewLicensesFromLocation(indexLocation, u.License)
|
||||
func newLuaRocksPackage(ctx context.Context, u luaRocksPackage, indexLocation file.Location) pkg.Package {
|
||||
license := pkg.NewLicensesFromLocationWithContext(ctx, indexLocation, u.License)
|
||||
p := pkg.Package{
|
||||
Name: u.Name,
|
||||
Version: u.Version,
|
||||
|
||||
@ -27,7 +27,7 @@ type repository struct {
|
||||
}
|
||||
|
||||
// parseRockspec parses a package.rockspec and returns the discovered Lua packages.
|
||||
func parseRockspec(_ context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
func parseRockspec(ctx context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
doc, err := parseRockspecData(reader)
|
||||
if err != nil {
|
||||
log.WithFields("error", err).Trace("unable to parse Rockspec app")
|
||||
@ -64,6 +64,7 @@ func parseRockspec(_ context.Context, _ file.Resolver, _ *generic.Environment, r
|
||||
}
|
||||
|
||||
p := newLuaRocksPackage(
|
||||
ctx,
|
||||
luaRocksPackage{
|
||||
Name: name,
|
||||
Version: version,
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package lua
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/anchore/syft/syft/file"
|
||||
@ -9,6 +10,7 @@ import (
|
||||
)
|
||||
|
||||
func TestParseRockspec(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
tests := []struct {
|
||||
Fixture string
|
||||
ExpectedPkg pkg.Package
|
||||
@ -22,7 +24,7 @@ func TestParseRockspec(t *testing.T) {
|
||||
Type: pkg.LuaRocksPkg,
|
||||
Language: pkg.Lua,
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("Apache-2.0", file.NewLocation("test-fixtures/rockspec/kong-3.7.0-0.rockspec")),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "Apache-2.0", file.NewLocation("test-fixtures/rockspec/kong-3.7.0-0.rockspec")),
|
||||
),
|
||||
Metadata: pkg.LuaRocksPackage{
|
||||
Name: "kong",
|
||||
@ -43,7 +45,7 @@ func TestParseRockspec(t *testing.T) {
|
||||
Type: pkg.LuaRocksPkg,
|
||||
Language: pkg.Lua,
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("MIT/X11", file.NewLocation("test-fixtures/rockspec/lpeg-1.0.2-1.rockspec")),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "MIT/X11", file.NewLocation("test-fixtures/rockspec/lpeg-1.0.2-1.rockspec")),
|
||||
),
|
||||
Metadata: pkg.LuaRocksPackage{
|
||||
Name: "LPeg",
|
||||
@ -64,7 +66,7 @@ func TestParseRockspec(t *testing.T) {
|
||||
Type: pkg.LuaRocksPkg,
|
||||
Language: pkg.Lua,
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("MIT", file.NewLocation("test-fixtures/rockspec/kong-pgmoon-1.16.2-1.rockspec")),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "MIT", file.NewLocation("test-fixtures/rockspec/kong-pgmoon-1.16.2-1.rockspec")),
|
||||
),
|
||||
Metadata: pkg.LuaRocksPackage{
|
||||
Name: "kong-pgmoon",
|
||||
@ -85,7 +87,7 @@ func TestParseRockspec(t *testing.T) {
|
||||
Type: pkg.LuaRocksPkg,
|
||||
Language: pkg.Lua,
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("MIT/X11", file.NewLocation("test-fixtures/rockspec/luasyslog-2.0.1-1.rockspec")),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "MIT/X11", file.NewLocation("test-fixtures/rockspec/luasyslog-2.0.1-1.rockspec")),
|
||||
),
|
||||
Metadata: pkg.LuaRocksPackage{
|
||||
Name: "luasyslog",
|
||||
|
||||
@ -1,16 +1,18 @@
|
||||
package ocaml
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/anchore/packageurl-go"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
func newOpamPackage(m pkg.OpamPackage, fileLocation file.Location) pkg.Package {
|
||||
func newOpamPackage(ctx context.Context, m pkg.OpamPackage, fileLocation file.Location) pkg.Package {
|
||||
p := pkg.Package{
|
||||
Name: m.Name,
|
||||
Version: m.Version,
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(fileLocation, m.Licenses...)...),
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocationWithContext(ctx, fileLocation, m.Licenses...)...),
|
||||
PURL: opamPackageURL(m.Name, m.Version),
|
||||
Locations: file.NewLocationSet(fileLocation),
|
||||
Type: pkg.OpamPkg,
|
||||
|
||||
@ -16,7 +16,7 @@ import (
|
||||
)
|
||||
|
||||
//nolint:funlen
|
||||
func parseOpamPackage(_ context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
func parseOpamPackage(ctx context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
var pkgs []pkg.Package
|
||||
|
||||
opamVersionRe := regexp.MustCompile(`(?m)opam-version:\s*"[0-9]+\.[0-9]+"`)
|
||||
@ -94,6 +94,7 @@ func parseOpamPackage(_ context.Context, _ file.Resolver, _ *generic.Environment
|
||||
pkgs = append(
|
||||
pkgs,
|
||||
newOpamPackage(
|
||||
ctx,
|
||||
entry,
|
||||
reader.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
),
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package ocaml
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@ -17,7 +18,7 @@ func TestParseOpamPackage(t *testing.T) {
|
||||
|
||||
fixture2 := "test-fixtures/alcotest.opam"
|
||||
location2 := file.NewLocation(fixture2)
|
||||
|
||||
ctx := context.TODO()
|
||||
tests := []struct {
|
||||
fixture string
|
||||
want []pkg.Package
|
||||
@ -31,10 +32,7 @@ func TestParseOpamPackage(t *testing.T) {
|
||||
PURL: "pkg:opam/ocaml-base-compiler@4.14.0",
|
||||
Locations: file.NewLocationSet(location1),
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicensesFromLocation(
|
||||
location1,
|
||||
"LGPL-2.1-or-later WITH OCaml-LGPL-linking-exception",
|
||||
)...,
|
||||
pkg.NewLicensesFromLocationWithContext(ctx, location1, "LGPL-2.1-or-later WITH OCaml-LGPL-linking-exception")...,
|
||||
),
|
||||
Language: pkg.OCaml,
|
||||
Type: pkg.OpamPkg,
|
||||
@ -60,7 +58,8 @@ func TestParseOpamPackage(t *testing.T) {
|
||||
PURL: "pkg:opam/alcotest@1.5.0",
|
||||
Locations: file.NewLocationSet(location2),
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicensesFromLocation(
|
||||
pkg.NewLicensesFromLocationWithContext(
|
||||
ctx,
|
||||
location2,
|
||||
"ISC",
|
||||
)...,
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package php
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/packageurl-go"
|
||||
@ -8,12 +9,12 @@ import (
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
func newComposerLockPackage(pd parsedLockData, indexLocation file.Location) pkg.Package {
|
||||
func newComposerLockPackage(ctx context.Context, pd parsedLockData, indexLocation file.Location) pkg.Package {
|
||||
p := pkg.Package{
|
||||
Name: pd.Name,
|
||||
Version: pd.Version,
|
||||
Locations: file.NewLocationSet(indexLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(indexLocation, pd.License...)...),
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocationWithContext(ctx, indexLocation, pd.License...)...),
|
||||
PURL: packageURLFromComposer(pd.Name, pd.Version),
|
||||
Language: pkg.PHP,
|
||||
Type: pkg.PhpComposerPkg,
|
||||
@ -24,12 +25,12 @@ func newComposerLockPackage(pd parsedLockData, indexLocation file.Location) pkg.
|
||||
return p
|
||||
}
|
||||
|
||||
func newComposerInstalledPackage(pd parsedInstalledData, indexLocation file.Location) pkg.Package {
|
||||
func newComposerInstalledPackage(ctx context.Context, pd parsedInstalledData, indexLocation file.Location) pkg.Package {
|
||||
p := pkg.Package{
|
||||
Name: pd.Name,
|
||||
Version: pd.Version,
|
||||
Locations: file.NewLocationSet(indexLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(indexLocation, pd.License...)...),
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocationWithContext(ctx, indexLocation, pd.License...)...),
|
||||
PURL: packageURLFromComposer(pd.Name, pd.Version),
|
||||
Language: pkg.PHP,
|
||||
Type: pkg.PhpComposerPkg,
|
||||
@ -40,12 +41,12 @@ func newComposerInstalledPackage(pd parsedInstalledData, indexLocation file.Loca
|
||||
return p
|
||||
}
|
||||
|
||||
func newPearPackage(pd peclPearData, indexLocation file.Location) pkg.Package {
|
||||
func newPearPackage(ctx context.Context, pd peclPearData, indexLocation file.Location) pkg.Package {
|
||||
p := pkg.Package{
|
||||
Name: pd.Name,
|
||||
Version: pd.Version,
|
||||
Locations: file.NewLocationSet(indexLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(indexLocation, pd.License...)...),
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocationWithContext(ctx, indexLocation, pd.License...)...),
|
||||
PURL: packageURLFromPear(pd.Name, pd.Channel, pd.Version),
|
||||
Language: pkg.PHP,
|
||||
Type: pkg.PhpPearPkg,
|
||||
@ -56,12 +57,12 @@ func newPearPackage(pd peclPearData, indexLocation file.Location) pkg.Package {
|
||||
return p
|
||||
}
|
||||
|
||||
func newPeclPackage(pd peclPearData, indexLocation file.Location) pkg.Package {
|
||||
func newPeclPackage(ctx context.Context, pd peclPearData, indexLocation file.Location) pkg.Package {
|
||||
p := pkg.Package{
|
||||
Name: pd.Name,
|
||||
Version: pd.Version,
|
||||
Locations: file.NewLocationSet(indexLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(indexLocation, pd.License...)...),
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocationWithContext(ctx, indexLocation, pd.License...)...),
|
||||
PURL: packageURLFromPear(pd.Name, pd.Channel, pd.Version),
|
||||
Language: pkg.PHP,
|
||||
Type: pkg.PhpPeclPkg,
|
||||
|
||||
@ -27,7 +27,7 @@ type composerLock struct {
|
||||
}
|
||||
|
||||
// parseComposerLock is a parser function for Composer.lock contents, returning "Default" php packages discovered.
|
||||
func parseComposerLock(_ context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
func parseComposerLock(ctx context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
pkgs := make([]pkg.Package, 0)
|
||||
dec := json.NewDecoder(reader)
|
||||
|
||||
@ -42,6 +42,7 @@ func parseComposerLock(_ context.Context, _ file.Resolver, _ *generic.Environmen
|
||||
pkgs = append(
|
||||
pkgs,
|
||||
newComposerLockPackage(
|
||||
ctx,
|
||||
pd,
|
||||
reader.Location,
|
||||
),
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package php
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
@ -10,6 +11,7 @@ import (
|
||||
)
|
||||
|
||||
func TestParseComposerFileLock(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
var expectedRelationships []artifact.Relationship
|
||||
fixture := "test-fixtures/composer.lock"
|
||||
locations := file.NewLocationSet(file.NewLocation(fixture))
|
||||
@ -20,7 +22,7 @@ func TestParseComposerFileLock(t *testing.T) {
|
||||
PURL: "pkg:composer/adoy/fastcgi-client@1.0.2",
|
||||
Locations: locations,
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("MIT", file.NewLocation(fixture)),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "MIT", file.NewLocation(fixture)),
|
||||
),
|
||||
Language: pkg.PHP,
|
||||
Type: pkg.PhpComposerPkg,
|
||||
@ -60,7 +62,7 @@ func TestParseComposerFileLock(t *testing.T) {
|
||||
PURL: "pkg:composer/alcaeus/mongo-php-adapter@1.1.11",
|
||||
Language: pkg.PHP,
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("MIT", file.NewLocation(fixture)),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "MIT", file.NewLocation(fixture)),
|
||||
),
|
||||
Type: pkg.PhpComposerPkg,
|
||||
Metadata: pkg.PhpComposerLockEntry{
|
||||
|
||||
@ -48,7 +48,7 @@ func (w *installedJSONComposerV2) UnmarshalJSON(data []byte) error {
|
||||
}
|
||||
|
||||
// parseInstalledJSON is a parser function for Composer.lock contents, returning "Default" php packages discovered.
|
||||
func parseInstalledJSON(_ context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
func parseInstalledJSON(ctx context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
var pkgs []pkg.Package
|
||||
dec := json.NewDecoder(reader)
|
||||
|
||||
@ -63,6 +63,7 @@ func parseInstalledJSON(_ context.Context, _ file.Resolver, _ *generic.Environme
|
||||
pkgs = append(
|
||||
pkgs,
|
||||
newComposerInstalledPackage(
|
||||
ctx,
|
||||
pd,
|
||||
reader.Location,
|
||||
),
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package php
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
@ -10,6 +11,7 @@ import (
|
||||
)
|
||||
|
||||
func TestParseInstalledJsonComposerV1(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
fixtures := []string{
|
||||
"test-fixtures/vendor/composer_1/installed.json",
|
||||
"test-fixtures/vendor/composer_2/installed.json",
|
||||
@ -24,7 +26,7 @@ func TestParseInstalledJsonComposerV1(t *testing.T) {
|
||||
Language: pkg.PHP,
|
||||
Type: pkg.PhpComposerPkg,
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicense("MIT"),
|
||||
pkg.NewLicenseWithContext(ctx, "MIT"),
|
||||
),
|
||||
Metadata: pkg.PhpComposerInstalledEntry{
|
||||
Name: "asm89/stack-cors",
|
||||
@ -73,7 +75,7 @@ func TestParseInstalledJsonComposerV1(t *testing.T) {
|
||||
Language: pkg.PHP,
|
||||
Type: pkg.PhpComposerPkg,
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicense("MIT"),
|
||||
pkg.NewLicenseWithContext(ctx, "MIT"),
|
||||
),
|
||||
Metadata: pkg.PhpComposerInstalledEntry{
|
||||
Name: "behat/mink",
|
||||
|
||||
@ -34,7 +34,7 @@ func (p *peclPearData) ToPecl() pkg.PhpPeclEntry {
|
||||
return pkg.PhpPeclEntry(p.ToPear())
|
||||
}
|
||||
|
||||
func parsePecl(_ context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
func parsePecl(ctx context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
m, err := parsePeclPearSerialized(reader)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@ -42,10 +42,10 @@ func parsePecl(_ context.Context, _ file.Resolver, _ *generic.Environment, reade
|
||||
if m == nil {
|
||||
return nil, nil, unknown.New(reader.Location, fmt.Errorf("no pecl package found"))
|
||||
}
|
||||
return []pkg.Package{newPeclPackage(*m, reader.Location)}, nil, nil
|
||||
return []pkg.Package{newPeclPackage(ctx, *m, reader.Location)}, nil, nil
|
||||
}
|
||||
|
||||
func parsePear(_ context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
func parsePear(ctx context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
m, err := parsePeclPearSerialized(reader)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@ -53,7 +53,7 @@ func parsePear(_ context.Context, _ file.Resolver, _ *generic.Environment, reade
|
||||
if m == nil {
|
||||
return nil, nil, unknown.New(reader.Location, fmt.Errorf("no pear package found"))
|
||||
}
|
||||
return []pkg.Package{newPearPackage(*m, reader.Location)}, nil, nil
|
||||
return []pkg.Package{newPearPackage(ctx, *m, reader.Location)}, nil, nil
|
||||
}
|
||||
|
||||
// parsePeclPearSerialized is a parser function for Pear metadata contents, returning "Default" php packages discovered.
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package php
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
@ -10,6 +11,7 @@ import (
|
||||
)
|
||||
|
||||
func TestParsePear(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
tests := []struct {
|
||||
name string
|
||||
fixture string
|
||||
@ -26,7 +28,7 @@ func TestParsePear(t *testing.T) {
|
||||
PURL: "pkg:pear/pecl.php.net/memcached@3.2.0",
|
||||
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/memcached-v6-format.reg")),
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("PHP License", file.NewLocation("test-fixtures/memcached-v6-format.reg")),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "PHP License", file.NewLocation("test-fixtures/memcached-v6-format.reg")),
|
||||
),
|
||||
Language: pkg.PHP,
|
||||
Type: pkg.PhpPearPkg,
|
||||
@ -49,7 +51,7 @@ func TestParsePear(t *testing.T) {
|
||||
PURL: "pkg:pear/pecl.php.net/memcached@3.2.0",
|
||||
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/memcached-v5-format.reg")),
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("PHP License", file.NewLocation("test-fixtures/memcached-v5-format.reg")),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "PHP License", file.NewLocation("test-fixtures/memcached-v5-format.reg")),
|
||||
),
|
||||
Language: pkg.PHP,
|
||||
Type: pkg.PhpPearPkg,
|
||||
@ -71,6 +73,7 @@ func TestParsePear(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestParsePecl(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
tests := []struct {
|
||||
name string
|
||||
fixture string
|
||||
@ -87,7 +90,7 @@ func TestParsePecl(t *testing.T) {
|
||||
PURL: "pkg:pear/pecl.php.net/memcached@3.2.0",
|
||||
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/memcached-v6-format.reg")),
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("PHP License", file.NewLocation("test-fixtures/memcached-v6-format.reg")),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "PHP License", file.NewLocation("test-fixtures/memcached-v6-format.reg")),
|
||||
),
|
||||
Language: pkg.PHP,
|
||||
Type: pkg.PhpPeclPkg, // important!
|
||||
@ -110,7 +113,7 @@ func TestParsePecl(t *testing.T) {
|
||||
PURL: "pkg:pear/pecl.php.net/memcached@3.2.0",
|
||||
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/memcached-v5-format.reg")),
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("PHP License", file.NewLocation("test-fixtures/memcached-v5-format.reg")),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "PHP License", file.NewLocation("test-fixtures/memcached-v5-format.reg")),
|
||||
),
|
||||
Language: pkg.PHP,
|
||||
Type: pkg.PhpPeclPkg, // important!
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
package python
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
@ -13,6 +15,7 @@ import (
|
||||
)
|
||||
|
||||
func Test_PackageCataloger(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
tests := []struct {
|
||||
name string
|
||||
fixture string
|
||||
@ -55,7 +58,7 @@ func Test_PackageCataloger(t *testing.T) {
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
// here we only used the license that was declared in the METADATA file, we did not go searching for other licenses
|
||||
// this is the better source of truth when there is no explicit LicenseFile given
|
||||
pkg.NewLicenseFromLocations("BSD License", file.NewLocation("dist-name/dist-info/METADATA")),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "BSD License", file.NewLocation("dist-name/dist-info/METADATA")),
|
||||
),
|
||||
FoundBy: "python-installed-package-cataloger",
|
||||
Metadata: pkg.PythonPackage{
|
||||
@ -94,7 +97,7 @@ func Test_PackageCataloger(t *testing.T) {
|
||||
file.NewLocation("egg-name/egg-info/top_level.txt"),
|
||||
),
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("Apache 2.0", file.NewLocation("egg-name/egg-info/PKG-INFO")),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "Apache 2.0", file.NewLocation("egg-name/egg-info/PKG-INFO")),
|
||||
),
|
||||
FoundBy: "python-installed-package-cataloger",
|
||||
Metadata: pkg.PythonPackage{
|
||||
@ -138,7 +141,7 @@ func Test_PackageCataloger(t *testing.T) {
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
// here we only used the license that was declared in the METADATA file, we did not go searching for other licenses
|
||||
// this is the better source of truth when there is no explicit LicenseFile given
|
||||
pkg.NewLicenseFromLocations("BSD License", file.NewLocation("dist-name/DIST-INFO/METADATA")),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "BSD License", file.NewLocation("dist-name/DIST-INFO/METADATA")),
|
||||
),
|
||||
FoundBy: "python-installed-package-cataloger",
|
||||
Metadata: pkg.PythonPackage{
|
||||
@ -174,7 +177,7 @@ func Test_PackageCataloger(t *testing.T) {
|
||||
file.NewLocation("egg-name/EGG-INFO/top_level.txt"),
|
||||
),
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("Apache 2.0", file.NewLocation("egg-name/EGG-INFO/PKG-INFO")),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "Apache 2.0", file.NewLocation("egg-name/EGG-INFO/PKG-INFO")),
|
||||
),
|
||||
FoundBy: "python-installed-package-cataloger",
|
||||
Metadata: pkg.PythonPackage{
|
||||
@ -220,6 +223,7 @@ func Test_PackageCataloger(t *testing.T) {
|
||||
Value: "BSD-3-Clause",
|
||||
SPDXExpression: "BSD-3-Clause",
|
||||
Type: "concluded",
|
||||
Contents: mustContentsFromLocation(t, "test-fixtures/site-packages/license/with-license-file-declared.dist-info/LICENSE.txt", 0, 1475),
|
||||
// we read the path from the LicenseFile field in the METADATA file, then read the license file directly
|
||||
Locations: file.NewLocationSet(file.NewLocation("with-license-file-declared.dist-info/LICENSE.txt")),
|
||||
},
|
||||
@ -266,7 +270,7 @@ func Test_PackageCataloger(t *testing.T) {
|
||||
Value: "BSD-3-Clause",
|
||||
SPDXExpression: "BSD-3-Clause",
|
||||
Type: "concluded",
|
||||
// we discover license files automatically
|
||||
Contents: mustContentsFromLocation(t, "test-fixtures/site-packages/license/with-license-file-declared.dist-info/LICENSE.txt", 0, 1475),
|
||||
Locations: file.NewLocationSet(file.NewLocation("without-license-file-declared.dist-info/LICENSE.txt")),
|
||||
},
|
||||
),
|
||||
@ -312,7 +316,7 @@ func Test_PackageCataloger(t *testing.T) {
|
||||
file.NewLocation("dist-info/RECORD"),
|
||||
),
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("BSD License", file.NewLocation("dist-info/METADATA")),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "BSD License", file.NewLocation("dist-info/METADATA")),
|
||||
),
|
||||
FoundBy: "python-installed-package-cataloger",
|
||||
Metadata: pkg.PythonPackage{
|
||||
@ -349,7 +353,7 @@ func Test_PackageCataloger(t *testing.T) {
|
||||
file.NewLocation("METADATA"),
|
||||
),
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("BSD License", file.NewLocation("METADATA")),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "BSD License", file.NewLocation("METADATA")),
|
||||
),
|
||||
FoundBy: "python-installed-package-cataloger",
|
||||
Metadata: pkg.PythonPackage{
|
||||
@ -378,7 +382,7 @@ func Test_PackageCataloger(t *testing.T) {
|
||||
file.NewLocation("test.egg-info"),
|
||||
),
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("Apache 2.0", file.NewLocation("test.egg-info")),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "Apache 2.0", file.NewLocation("test.egg-info")),
|
||||
),
|
||||
FoundBy: "python-installed-package-cataloger",
|
||||
Metadata: pkg.PythonPackage{
|
||||
@ -398,10 +402,10 @@ func Test_PackageCataloger(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
pkgtest.NewCatalogTester().
|
||||
(pkgtest.NewCatalogTester().
|
||||
FromDirectory(t, test.fixture).
|
||||
Expects(test.expectedPackages, nil).
|
||||
TestCataloger(t, NewInstalledPackageCataloger())
|
||||
TestCataloger(t, NewInstalledPackageCataloger()))
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -802,3 +806,26 @@ func stringPackage(p pkg.Package) string {
|
||||
|
||||
return fmt.Sprintf("%s @ %s (%s)", p.Name, p.Version, loc)
|
||||
}
|
||||
|
||||
func mustContentsFromLocation(t *testing.T, contentsPath string, offset ...int) string {
|
||||
t.Helper() // Marks this function as a test helper for cleaner error reporting
|
||||
contents, err := os.ReadFile(contentsPath)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read file %s: %v", contentsPath, err)
|
||||
}
|
||||
|
||||
if len(offset) == 0 {
|
||||
return string(contents)
|
||||
}
|
||||
|
||||
if len(offset) != 2 {
|
||||
t.Fatalf("invalid offset provided, expected two integers: start and end")
|
||||
}
|
||||
start, end := offset[0], offset[1]
|
||||
|
||||
if start < 0 || end > len(contents) || start > end {
|
||||
t.Fatalf("invalid offset range: start=%d, end=%d, content length=%d", start, end, len(contents))
|
||||
}
|
||||
|
||||
return string(contents[start:end])
|
||||
}
|
||||
|
||||
@ -25,10 +25,6 @@ import (
|
||||
// parseWheelOrEgg takes the primary metadata file reference and returns the python package it represents. Contained
|
||||
// fields are governed by the PyPA core metadata specification (https://packaging.python.org/en/latest/specifications/core-metadata/).
|
||||
func parseWheelOrEgg(ctx context.Context, resolver file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
licenseScanner, err := licenses.ContextLicenseScanner(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
pd, sources, err := assembleEggOrWheelMetadata(resolver, reader.Location)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@ -46,7 +42,7 @@ func parseWheelOrEgg(ctx context.Context, resolver file.Resolver, _ *generic.Env
|
||||
pkgs := []pkg.Package{
|
||||
newPackageForPackage(
|
||||
*pd,
|
||||
findLicenses(ctx, licenseScanner, resolver, *pd),
|
||||
findLicenses(ctx, resolver, *pd),
|
||||
sources...,
|
||||
),
|
||||
}
|
||||
@ -253,7 +249,7 @@ func assembleEggOrWheelMetadata(resolver file.Resolver, metadataLocation file.Lo
|
||||
return &pd, sources, nil
|
||||
}
|
||||
|
||||
func findLicenses(ctx context.Context, scanner licenses.Scanner, resolver file.Resolver, m parsedData) pkg.LicenseSet {
|
||||
func findLicenses(ctx context.Context, resolver file.Resolver, m parsedData) pkg.LicenseSet {
|
||||
var licenseSet pkg.LicenseSet
|
||||
|
||||
licenseLocations := file.NewLocationSet()
|
||||
@ -268,9 +264,9 @@ func findLicenses(ctx context.Context, scanner licenses.Scanner, resolver file.R
|
||||
|
||||
switch {
|
||||
case m.LicenseExpression != "" || m.Licenses != "":
|
||||
licenseSet = getLicenseSetFromValues(licenseLocations.ToSlice(), m.LicenseExpression, m.Licenses)
|
||||
licenseSet = getLicenseSetFromValues(ctx, licenseLocations.ToSlice(), m.LicenseExpression, m.Licenses)
|
||||
case !licenseLocations.Empty():
|
||||
licenseSet = getLicenseSetFromFiles(ctx, scanner, resolver, licenseLocations.ToSlice()...)
|
||||
licenseSet = getLicenseSetFromFiles(ctx, resolver, licenseLocations.ToSlice()...)
|
||||
|
||||
default:
|
||||
// search for known license paths from RECORDS file
|
||||
@ -302,14 +298,14 @@ func findLicenses(ctx context.Context, scanner licenses.Scanner, resolver file.R
|
||||
locationSet.Add(locs...)
|
||||
}
|
||||
|
||||
licenseSet = getLicenseSetFromFiles(ctx, scanner, resolver, locationSet.ToSlice()...)
|
||||
licenseSet = getLicenseSetFromFiles(ctx, resolver, locationSet.ToSlice()...)
|
||||
}
|
||||
return licenseSet
|
||||
}
|
||||
|
||||
func getLicenseSetFromValues(locations []file.Location, licenseValues ...string) pkg.LicenseSet {
|
||||
func getLicenseSetFromValues(ctx context.Context, locations []file.Location, licenseValues ...string) pkg.LicenseSet {
|
||||
if len(locations) == 0 {
|
||||
return pkg.NewLicenseSet(pkg.NewLicensesFromValues(licenseValues...)...)
|
||||
return pkg.NewLicenseSet(pkg.NewLicensesFromValuesWithContext(ctx, licenseValues...)...)
|
||||
}
|
||||
|
||||
licenseSet := pkg.NewLicenseSet()
|
||||
@ -318,31 +314,25 @@ func getLicenseSetFromValues(locations []file.Location, licenseValues ...string)
|
||||
continue
|
||||
}
|
||||
|
||||
licenseSet.Add(pkg.NewLicenseFromLocations(value, locations...))
|
||||
licenseSet.Add(pkg.NewLicenseFromLocationsWithContext(ctx, value, locations...))
|
||||
}
|
||||
return licenseSet
|
||||
}
|
||||
|
||||
func getLicenseSetFromFiles(ctx context.Context, scanner licenses.Scanner, resolver file.Resolver, locations ...file.Location) pkg.LicenseSet {
|
||||
func getLicenseSetFromFiles(ctx context.Context, resolver file.Resolver, locations ...file.Location) pkg.LicenseSet {
|
||||
licenseSet := pkg.NewLicenseSet()
|
||||
for _, loc := range locations {
|
||||
licenseSet.Add(getLicenseSetFromFile(ctx, scanner, resolver, loc)...)
|
||||
licenseSet.Add(getLicenseSetFromFile(ctx, resolver, loc)...)
|
||||
}
|
||||
return licenseSet
|
||||
}
|
||||
|
||||
func getLicenseSetFromFile(ctx context.Context, scanner licenses.Scanner, resolver file.Resolver, location file.Location) []pkg.License {
|
||||
func getLicenseSetFromFile(ctx context.Context, resolver file.Resolver, location file.Location) []pkg.License {
|
||||
metadataContents, err := resolver.FileContentsByLocation(location)
|
||||
if err != nil {
|
||||
log.WithFields("error", err, "path", location.Path()).Trace("unable to read file contents")
|
||||
return nil
|
||||
}
|
||||
defer internal.CloseAndLogError(metadataContents, location.Path())
|
||||
parsed, err := scanner.PkgSearch(ctx, file.NewLocationReadCloser(location, metadataContents))
|
||||
if err != nil {
|
||||
log.WithFields("error", err, "path", location.Path()).Trace("unable to parse a license from the file")
|
||||
return nil
|
||||
}
|
||||
|
||||
return parsed
|
||||
return pkg.NewLicensesFromReadCloserWithContext(ctx, file.NewLocationReadCloser(location, metadataContents))
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package r
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
@ -10,13 +11,14 @@ import (
|
||||
)
|
||||
|
||||
func TestRPackageCataloger(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
expectedPkgs := []pkg.Package{
|
||||
{
|
||||
Name: "base",
|
||||
Version: "4.3.0",
|
||||
FoundBy: "r-package-cataloger",
|
||||
Locations: file.NewLocationSet(file.NewLocation("base/DESCRIPTION")),
|
||||
Licenses: pkg.NewLicenseSet([]pkg.License{pkg.NewLicense("Part of R 4.3.0")}...),
|
||||
Licenses: pkg.NewLicenseSet([]pkg.License{pkg.NewLicenseWithContext(ctx, "Part of R 4.3.0")}...),
|
||||
Language: pkg.R,
|
||||
Type: pkg.Rpkg,
|
||||
PURL: "pkg:cran/base@4.3.0",
|
||||
@ -34,7 +36,7 @@ func TestRPackageCataloger(t *testing.T) {
|
||||
Version: "1.5.0.9000",
|
||||
FoundBy: "r-package-cataloger",
|
||||
Locations: file.NewLocationSet(file.NewLocation("stringr/DESCRIPTION")),
|
||||
Licenses: pkg.NewLicenseSet([]pkg.License{pkg.NewLicense("MIT")}...),
|
||||
Licenses: pkg.NewLicenseSet([]pkg.License{pkg.NewLicenseWithContext(ctx, "MIT")}...),
|
||||
Language: pkg.R,
|
||||
Type: pkg.Rpkg,
|
||||
PURL: "pkg:cran/stringr@1.5.0.9000",
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package r
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/packageurl-go"
|
||||
@ -8,13 +9,13 @@ import (
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
func newPackage(pd parseData, locations ...file.Location) pkg.Package {
|
||||
func newPackage(ctx context.Context, pd parseData, locations ...file.Location) pkg.Package {
|
||||
locationSet := file.NewLocationSet()
|
||||
for _, loc := range locations {
|
||||
locationSet.Add(loc.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation))
|
||||
}
|
||||
|
||||
licenses := parseLicenseData(pd.License)
|
||||
licenses := parseLicenseData(ctx, pd.License)
|
||||
|
||||
result := pkg.Package{
|
||||
Name: pd.Package,
|
||||
@ -44,7 +45,7 @@ func packageURL(m parseData) string {
|
||||
// Multiple licences can be specified separated by ‘|’
|
||||
// (surrounded by spaces) in which case the user can choose any of the above cases.
|
||||
// https://cran.rstudio.com/doc/manuals/r-devel/R-exts.html#Licensing
|
||||
func parseLicenseData(license string, locations ...file.Location) []pkg.License {
|
||||
func parseLicenseData(ctx context.Context, license string, locations ...file.Location) []pkg.License {
|
||||
licenses := make([]pkg.License, 0)
|
||||
|
||||
// check if multiple licenses are separated by |
|
||||
@ -56,7 +57,7 @@ func parseLicenseData(license string, locations ...file.Location) []pkg.License
|
||||
licenseVersion := strings.SplitN(l, " ", 2)
|
||||
if len(licenseVersion) == 2 {
|
||||
l = strings.Join([]string{licenseVersion[0], parseVersion(licenseVersion[1])}, "")
|
||||
licenses = append(licenses, pkg.NewLicenseFromLocations(l, locations...))
|
||||
licenses = append(licenses, pkg.NewLicenseFromLocationsWithContext(ctx, l, locations...))
|
||||
continue
|
||||
}
|
||||
}
|
||||
@ -65,7 +66,7 @@ func parseLicenseData(license string, locations ...file.Location) []pkg.License
|
||||
if strings.Contains(l, "+") && strings.Contains(l, "LICENSE") {
|
||||
splitField := strings.Split(l, " ")
|
||||
if len(splitField) > 0 {
|
||||
licenses = append(licenses, pkg.NewLicenseFromLocations(splitField[0], locations...))
|
||||
licenses = append(licenses, pkg.NewLicenseFromLocationsWithContext(ctx, splitField[0], locations...))
|
||||
continue
|
||||
}
|
||||
}
|
||||
@ -77,7 +78,7 @@ func parseLicenseData(license string, locations ...file.Location) []pkg.License
|
||||
|
||||
// no specific case found for the above so assume case 2
|
||||
// check if the common name in case 2 is valid SDPX otherwise value will be populated
|
||||
licenses = append(licenses, pkg.NewLicenseFromLocations(l, locations...))
|
||||
licenses = append(licenses, pkg.NewLicenseFromLocationsWithContext(ctx, l, locations...))
|
||||
continue
|
||||
}
|
||||
return licenses
|
||||
|
||||
@ -1,12 +1,14 @@
|
||||
package r
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
func Test_NewPackageLicenses(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
testCases := []struct {
|
||||
name string
|
||||
pd parseData
|
||||
@ -20,7 +22,7 @@ func Test_NewPackageLicenses(t *testing.T) {
|
||||
License: "MIT",
|
||||
},
|
||||
[]pkg.License{
|
||||
pkg.NewLicense("MIT"),
|
||||
pkg.NewLicenseWithContext(ctx, "MIT"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -31,7 +33,7 @@ func Test_NewPackageLicenses(t *testing.T) {
|
||||
License: "LGPL (== 2.0)",
|
||||
},
|
||||
[]pkg.License{
|
||||
pkg.NewLicense("LGPL2.0"),
|
||||
pkg.NewLicenseWithContext(ctx, "LGPL2.0"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -42,7 +44,7 @@ func Test_NewPackageLicenses(t *testing.T) {
|
||||
License: "LGPL (>= 2.0, < 3)",
|
||||
},
|
||||
[]pkg.License{
|
||||
pkg.NewLicense("LGPL2.0+"),
|
||||
pkg.NewLicenseWithContext(ctx, "LGPL2.0+"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -53,7 +55,7 @@ func Test_NewPackageLicenses(t *testing.T) {
|
||||
License: "GPL-2 + file LICENSE",
|
||||
},
|
||||
[]pkg.License{
|
||||
pkg.NewLicense("GPL-2"),
|
||||
pkg.NewLicenseWithContext(ctx, "GPL-2"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -64,7 +66,7 @@ func Test_NewPackageLicenses(t *testing.T) {
|
||||
License: "Mozilla Public License",
|
||||
},
|
||||
[]pkg.License{
|
||||
pkg.NewLicense("Mozilla Public License"),
|
||||
pkg.NewLicenseWithContext(ctx, "Mozilla Public License"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -75,15 +77,15 @@ func Test_NewPackageLicenses(t *testing.T) {
|
||||
License: "GPL-2 | file LICENSE | LGPL (>= 2.0)",
|
||||
},
|
||||
[]pkg.License{
|
||||
pkg.NewLicense("GPL-2"),
|
||||
pkg.NewLicense("LGPL2.0+"),
|
||||
pkg.NewLicenseWithContext(ctx, "GPL-2"),
|
||||
pkg.NewLicenseWithContext(ctx, "LGPL2.0+"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range testCases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := parseLicenseData(tt.pd.License)
|
||||
got := parseLicenseData(ctx, tt.pd.License)
|
||||
if len(got) != len(tt.want) {
|
||||
t.Errorf("unexpected number of licenses: got=%d, want=%d", len(got), len(tt.want))
|
||||
}
|
||||
|
||||
@ -29,10 +29,10 @@ License: Part of R 4.3.0
|
||||
License: Unlimited
|
||||
*/
|
||||
|
||||
func parseDescriptionFile(_ context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
func parseDescriptionFile(ctx context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
values := extractFieldsFromDescriptionFile(reader)
|
||||
m := parseDataFromDescriptionMap(values)
|
||||
p := newPackage(m, []file.Location{reader.Location}...)
|
||||
p := newPackage(ctx, m, []file.Location{reader.Location}...)
|
||||
if p.Name == "" || p.Version == "" {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package redhat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
@ -15,7 +16,7 @@ import (
|
||||
)
|
||||
|
||||
func Test_DBCataloger(t *testing.T) {
|
||||
|
||||
ctx := context.TODO()
|
||||
dbLocation := file.NewLocation("/var/lib/rpm/rpmdb.sqlite")
|
||||
locations := file.NewLocationSet(dbLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation))
|
||||
|
||||
@ -24,7 +25,7 @@ func Test_DBCataloger(t *testing.T) {
|
||||
Version: "11-13.el9",
|
||||
Type: pkg.RpmPkg,
|
||||
Locations: locations,
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicenseFromLocations("Public Domain", dbLocation)),
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicenseFromLocationsWithContext(ctx, "Public Domain", dbLocation)),
|
||||
FoundBy: "rpm-db-cataloger",
|
||||
PURL: "pkg:rpm/basesystem@11-13.el9?arch=noarch&upstream=basesystem-11-13.el9.src.rpm",
|
||||
Metadata: pkg.RpmDBEntry{
|
||||
@ -54,7 +55,7 @@ func Test_DBCataloger(t *testing.T) {
|
||||
Version: "5.1.8-6.el9_1",
|
||||
Type: pkg.RpmPkg,
|
||||
Locations: locations,
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicenseFromLocations("GPLv3+", dbLocation)),
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicenseFromLocationsWithContext(ctx, "GPLv3+", dbLocation)),
|
||||
FoundBy: "rpm-db-cataloger",
|
||||
PURL: "pkg:rpm/bash@5.1.8-6.el9_1?arch=x86_64&upstream=bash-5.1.8-6.el9_1.src.rpm",
|
||||
Metadata: pkg.RpmDBEntry{
|
||||
@ -106,7 +107,7 @@ func Test_DBCataloger(t *testing.T) {
|
||||
Version: "3.16-2.el9",
|
||||
Type: pkg.RpmPkg,
|
||||
Locations: locations,
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicenseFromLocations("Public Domain", dbLocation)),
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicenseFromLocationsWithContext(ctx, "Public Domain", dbLocation)),
|
||||
FoundBy: "rpm-db-cataloger",
|
||||
PURL: "pkg:rpm/filesystem@3.16-2.el9?arch=x86_64&upstream=filesystem-3.16-2.el9.src.rpm",
|
||||
Metadata: pkg.RpmDBEntry{
|
||||
@ -246,7 +247,6 @@ func Test_RPMFileCataloger_Globs(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_denySelfReferences(t *testing.T) {
|
||||
|
||||
a := pkg.Package{
|
||||
Name: "a",
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package redhat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -11,11 +12,11 @@ import (
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
func newDBPackage(dbOrRpmLocation file.Location, m pkg.RpmDBEntry, distro *linux.Release, licenses []string) pkg.Package {
|
||||
func newDBPackage(ctx context.Context, dbOrRpmLocation file.Location, m pkg.RpmDBEntry, distro *linux.Release, licenses []string) pkg.Package {
|
||||
p := pkg.Package{
|
||||
Name: m.Name,
|
||||
Version: toELVersion(m.Epoch, m.Version, m.Release),
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(dbOrRpmLocation, licenses...)...),
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocationWithContext(ctx, dbOrRpmLocation, licenses...)...),
|
||||
PURL: packageURL(m.Name, m.Arch, m.Epoch, m.SourceRpm, m.Version, m.Release, distro),
|
||||
Locations: file.NewLocationSet(dbOrRpmLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
|
||||
Type: pkg.RpmPkg,
|
||||
@ -26,11 +27,11 @@ func newDBPackage(dbOrRpmLocation file.Location, m pkg.RpmDBEntry, distro *linux
|
||||
return p
|
||||
}
|
||||
|
||||
func newArchivePackage(archiveLocation file.Location, m pkg.RpmArchive, licenses []string) pkg.Package {
|
||||
func newArchivePackage(ctx context.Context, archiveLocation file.Location, m pkg.RpmArchive, licenses []string) pkg.Package {
|
||||
p := pkg.Package{
|
||||
Name: m.Name,
|
||||
Version: toELVersion(m.Epoch, m.Version, m.Release),
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(archiveLocation, licenses...)...),
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocationWithContext(ctx, archiveLocation, licenses...)...),
|
||||
PURL: packageURL(m.Name, m.Arch, m.Epoch, m.SourceRpm, m.Version, m.Release, nil),
|
||||
Locations: file.NewLocationSet(archiveLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
|
||||
Type: pkg.RpmPkg,
|
||||
|
||||
@ -16,7 +16,7 @@ import (
|
||||
)
|
||||
|
||||
// parseRpmArchive parses a single RPM
|
||||
func parseRpmArchive(_ context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
func parseRpmArchive(ctx context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
rpm, err := rpmutils.ReadRpm(reader)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("RPM file found but unable to read: %s (%w)", reader.RealPath, err)
|
||||
@ -56,7 +56,7 @@ func parseRpmArchive(_ context.Context, _ file.Resolver, _ *generic.Environment,
|
||||
Files: mapFiles(files, digestAlgorithm),
|
||||
}
|
||||
|
||||
return []pkg.Package{newArchivePackage(reader.Location, metadata, licenses)}, nil, nil
|
||||
return []pkg.Package{newArchivePackage(ctx, reader.Location, metadata, licenses)}, nil, nil
|
||||
}
|
||||
|
||||
func getDigestAlgorithm(location file.Location, header *rpmutils.RpmHeader) string {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package redhat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/anchore/syft/syft/file"
|
||||
@ -9,6 +10,7 @@ import (
|
||||
)
|
||||
|
||||
func TestParseRpmFiles(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
abcRpmLocation := file.NewLocation("abc-1.01-9.hg20160905.el7.x86_64.rpm")
|
||||
zorkRpmLocation := file.NewLocation("zork-1.0.3-1.el7.x86_64.rpm")
|
||||
tests := []struct {
|
||||
@ -26,7 +28,7 @@ func TestParseRpmFiles(t *testing.T) {
|
||||
FoundBy: "rpm-archive-cataloger",
|
||||
Type: pkg.RpmPkg,
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("MIT", abcRpmLocation),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "MIT", abcRpmLocation),
|
||||
),
|
||||
Metadata: pkg.RpmArchive{
|
||||
Name: "abc",
|
||||
@ -54,7 +56,7 @@ func TestParseRpmFiles(t *testing.T) {
|
||||
FoundBy: "rpm-archive-cataloger",
|
||||
Type: pkg.RpmPkg,
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("Public Domain", zorkRpmLocation),
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "Public Domain", zorkRpmLocation),
|
||||
),
|
||||
Metadata: pkg.RpmArchive{
|
||||
Name: "zork",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user