From c41fbec85f7be632321f634e75784ecbacd70e32 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Tue, 21 Mar 2023 12:46:13 -0400 Subject: [PATCH 01/99] feat: add new syft/license package Signed-off-by: Christopher Phillips --- syft/license/license.go | 52 ++++++++++++++++++++++++++++++++++++ syft/license/license_test.go | 1 + 2 files changed, 53 insertions(+) create mode 100644 syft/license/license.go create mode 100644 syft/license/license_test.go diff --git a/syft/license/license.go b/syft/license/license.go new file mode 100644 index 00000000000..ad930ba0518 --- /dev/null +++ b/syft/license/license.go @@ -0,0 +1,52 @@ +package license + +import ( + "github.com/anchore/syft/syft/source" +) + +type Type string + +const ( + Declared Type = "declared" + Concluded Type = "concluded" +) + +type PackageLicense struct { + Value string + SPDXExpression string + Type Type + URL string // external sources + Location source.Location // on disk declaration +} + +type FileLicense struct { + Value string + SPDXExpression string + Location source.Location // on disk declaration + *LicenseEvidence +} + +type LicenseEvidence struct { + Confidence int + Offset int + Extent int +} + +func NewPackageLicense(value string, url string, location source.Location) PackageLicense { + // TODO: validate value as an SPDX expression + return PackageLicense{ + Value: value, + URL: url, + Location: location, + Type: Declared, + } +} + +func NewFileLicense(value string, location source.Location) FileLicense { + // TODO: validate value as an SPDX expression + // TODO: run location against classifier to form evidence + return FileLicense{ + Value: value, + Location: location, + } +} diff --git a/syft/license/license_test.go b/syft/license/license_test.go new file mode 100644 index 00000000000..1f0d8ff2c18 --- /dev/null +++ b/syft/license/license_test.go @@ -0,0 +1 @@ +package license From dcc8aaee03942ce343b969782117441bf8fc0c3a Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Tue, 21 Mar 2023 13:16:32 -0400 Subject: [PATCH 02/99] refactor: refactor package structure to preven import loop Signed-off-by: Christopher Phillips --- syft/file/license.go | 21 +++++++++++++++++ syft/license/license.go | 40 +------------------------------ syft/pkg/license.go | 26 ++++++++++++++++++++ syft/pkg/package.go | 2 +- syft/pkg/package_test.go | 51 ++++++---------------------------------- 5 files changed, 56 insertions(+), 84 deletions(-) create mode 100644 syft/file/license.go create mode 100644 syft/pkg/license.go diff --git a/syft/file/license.go b/syft/file/license.go new file mode 100644 index 00000000000..d4eb5add985 --- /dev/null +++ b/syft/file/license.go @@ -0,0 +1,21 @@ +package file + +import ( + "github.com/anchore/syft/syft/license" +) + +type License struct { + Value string + SPDXExpression string + Type license.Type + LicenseEvidence *license.Evidence // evidence from license classifier +} + +func NewLicense(value string) License { + // TODO: validate value as an SPDX expression + // TODO: run location against classifier to form evidence + return License{ + Value: value, + Type: license.Concluded, + } +} diff --git a/syft/license/license.go b/syft/license/license.go index ad930ba0518..f0340d6df01 100644 --- a/syft/license/license.go +++ b/syft/license/license.go @@ -1,9 +1,5 @@ package license -import ( - "github.com/anchore/syft/syft/source" -) - type Type string const ( @@ -11,42 +7,8 @@ const ( Concluded Type = "concluded" ) -type PackageLicense struct { - Value string - SPDXExpression string - Type Type - URL string // external sources - Location source.Location // on disk declaration -} - -type FileLicense struct { - Value string - SPDXExpression string - Location source.Location // on disk declaration - *LicenseEvidence -} - -type LicenseEvidence struct { +type Evidence struct { Confidence int Offset int Extent int } - -func NewPackageLicense(value string, url string, location source.Location) PackageLicense { - // TODO: validate value as an SPDX expression - return PackageLicense{ - Value: value, - URL: url, - Location: location, - Type: Declared, - } -} - -func NewFileLicense(value string, location source.Location) FileLicense { - // TODO: validate value as an SPDX expression - // TODO: run location against classifier to form evidence - return FileLicense{ - Value: value, - Location: location, - } -} diff --git a/syft/pkg/license.go b/syft/pkg/license.go new file mode 100644 index 00000000000..560f9a567ea --- /dev/null +++ b/syft/pkg/license.go @@ -0,0 +1,26 @@ +package pkg + +import ( + "github.com/anchore/syft/syft/license" + "github.com/anchore/syft/syft/source" +) + +type PackageLicense struct { + Value string + SPDXExpression string + Type license.Type + URL string // external sources + Location source.Location // on disk declaration +} + +func NewPackageLicense(value string, url string, location source.Location) PackageLicense { + // TODO: validate value as an SPDX expression + // TODO: how do we express other places where a license is declared + // EX: we got this from the go module cache at path /x/y/z on disk + return PackageLicense{ + Value: value, + URL: url, + Location: location, + Type: license.Declared, + } +} diff --git a/syft/pkg/package.go b/syft/pkg/package.go index 88351f2174d..a9eb3dd00ab 100644 --- a/syft/pkg/package.go +++ b/syft/pkg/package.go @@ -22,7 +22,7 @@ type Package struct { Version string // the version of the package FoundBy string `hash:"ignore" cyclonedx:"foundBy"` // the specific cataloger that discovered this package Locations source.LocationSet // the locations that lead to the discovery of this package (note: this is not necessarily the locations that make up this package) - Licenses []string // licenses discovered with the package metadata + Licenses []License // licenses discovered with the package metadata Language Language `hash:"ignore" cyclonedx:"language"` // the language ecosystem this package belongs to (e.g. JavaScript, Python, etc) Type Type `cyclonedx:"type"` // the package type (e.g. Npm, Yarn, Python, Rpm, Deb, etc) CPEs []cpe.CPE `hash:"ignore"` // all possible Common Platform Enumerators (note: this is NOT included in the definition of the ID since all fields on a CPE are derived from other fields) diff --git a/syft/pkg/package_test.go b/syft/pkg/package_test.go index 51854c061ed..d5b4b4edb46 100644 --- a/syft/pkg/package_test.go +++ b/syft/pkg/package_test.go @@ -9,6 +9,7 @@ import ( "github.com/anchore/stereoscope/pkg/file" "github.com/anchore/syft/syft/cpe" + "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/source" ) @@ -27,10 +28,7 @@ func TestIDUniqueness(t *testing.T) { Locations: source.NewLocationSet( originalLocation, ), - Licenses: []string{ - "cc0-1.0", - "MIT", - }, + Licenses: license.PackageLicense{}, Language: "math", Type: PythonPkg, CPEs: []cpe.CPE{ @@ -80,18 +78,6 @@ func TestIDUniqueness(t *testing.T) { }, expectedIDComparison: assert.Equal, }, - { - name: "licenses order is ignored", - transform: func(pkg Package) Package { - // note: same as the original package, only a different order - pkg.Licenses = []string{ - "MIT", - "cc0-1.0", - } - return pkg - }, - expectedIDComparison: assert.Equal, - }, { name: "name is reflected", transform: func(pkg Package) Package { @@ -143,14 +129,6 @@ func TestIDUniqueness(t *testing.T) { }, expectedIDComparison: assert.NotEqual, }, - { - name: "licenses is reflected", - transform: func(pkg Package) Package { - pkg.Licenses = []string{"new!"} - return pkg - }, - expectedIDComparison: assert.NotEqual, - }, { name: "type is reflected", transform: func(pkg Package) Package { @@ -264,10 +242,7 @@ func TestPackage_Merge(t *testing.T) { Locations: source.NewLocationSet( originalLocation, ), - Licenses: []string{ - "cc0-1.0", - "MIT", - }, + Licenses: license.PackageLicense{}, Language: "math", Type: PythonPkg, CPEs: []cpe.CPE{ @@ -292,10 +267,7 @@ func TestPackage_Merge(t *testing.T) { Locations: source.NewLocationSet( similarLocation, // NOTE: difference; we have a different layer but the same path ), - Licenses: []string{ - "cc0-1.0", - "MIT", - }, + Licenses: license.PackageLicense{}, Language: "math", Type: PythonPkg, CPEs: []cpe.CPE{ @@ -321,10 +293,7 @@ func TestPackage_Merge(t *testing.T) { originalLocation, similarLocation, // NOTE: merge! ), - Licenses: []string{ - "cc0-1.0", - "MIT", - }, + Licenses: license.PackageLicense{}, Language: "math", Type: PythonPkg, CPEs: []cpe.CPE{ @@ -353,10 +322,7 @@ func TestPackage_Merge(t *testing.T) { Locations: source.NewLocationSet( originalLocation, ), - Licenses: []string{ - "cc0-1.0", - "MIT", - }, + Licenses: license.PackageLicense{}, Language: "math", Type: PythonPkg, CPEs: []cpe.CPE{ @@ -381,10 +347,7 @@ func TestPackage_Merge(t *testing.T) { Locations: source.NewLocationSet( originalLocation, ), - Licenses: []string{ - "cc0-1.0", - "MIT", - }, + Licenses: license.PackageLicense{}, Language: "math", Type: PythonPkg, CPEs: []cpe.CPE{ From 1e9c39a12e48deefe0b828d858c0f125e8ac2d7c Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Tue, 21 Mar 2023 13:50:11 -0400 Subject: [PATCH 03/99] refactor: update license sections with TODO/Updates for branch ^--^ ^------------^ | | | +-> Summary in present tense. | +-------> Type: chore, docs, feat, fix, refactor, style, or test. Signed-off-by: Christopher Phillips --- .../common/cyclonedxhelpers/licenses.go | 36 ++------ syft/formats/common/spdxhelpers/license.go | 51 ----------- .../common/spdxhelpers/license_test.go | 85 ------------------- .../common/spdxhelpers/to_format_model.go | 43 ++-------- .../common/spdxhelpers/to_syft_model.go | 11 ++- syft/formats/syftjson/to_format_model.go | 10 +-- syft/formats/syftjson/to_syft_model.go | 11 +-- syft/pkg/alpm_metadata.go | 1 - syft/pkg/apk_metadata.go | 1 - syft/pkg/apk_metadata_test.go | 2 - syft/pkg/cataloger/alpm/cataloger_test.go | 4 +- syft/pkg/cataloger/alpm/package.go | 6 +- syft/pkg/cataloger/alpm/package_test.go | 68 +++++++++------ syft/pkg/cataloger/alpm/parse_alpm_db.go | 13 ++- syft/pkg/cataloger/apkdb/package.go | 5 +- syft/pkg/cataloger/apkdb/package_test.go | 79 ++++++++++------- syft/pkg/cataloger/apkdb/parse_apk_db.go | 17 ++-- syft/pkg/cataloger/deb/package.go | 4 +- syft/pkg/cataloger/java/archive_parser.go | 4 +- syft/pkg/cataloger/javascript/package.go | 14 +-- .../javascript/parse_package_json.go | 10 +-- .../portage/parse_portage_contents.go | 9 +- syft/pkg/cataloger/python/package.go | 7 +- syft/pkg/cataloger/rpm/package.go | 6 +- syft/pkg/cataloger/rpm/parse_rpm.go | 7 +- syft/pkg/cataloger/ruby/package.go | 3 +- syft/pkg/cataloger/ruby/parse_gemspec.go | 7 +- syft/pkg/gem_metadata.go | 1 - syft/pkg/license.go | 6 +- syft/pkg/npm_package_json_metadata.go | 1 - syft/pkg/package.go | 1 + syft/pkg/package_test.go | 7 -- syft/pkg/php_composer_json_metadata.go | 1 - syft/pkg/python_package_metadata.go | 1 - syft/pkg/rpm_metadata.go | 1 - syft/sbom/sbom.go | 1 + 36 files changed, 181 insertions(+), 353 deletions(-) delete mode 100644 syft/formats/common/spdxhelpers/license.go delete mode 100644 syft/formats/common/spdxhelpers/license_test.go diff --git a/syft/formats/common/cyclonedxhelpers/licenses.go b/syft/formats/common/cyclonedxhelpers/licenses.go index 7518b0976b1..d515b2f43eb 100644 --- a/syft/formats/common/cyclonedxhelpers/licenses.go +++ b/syft/formats/common/cyclonedxhelpers/licenses.go @@ -3,44 +3,20 @@ package cyclonedxhelpers import ( "github.com/CycloneDX/cyclonedx-go" - "github.com/anchore/syft/internal/spdxlicense" "github.com/anchore/syft/syft/pkg" ) +// TODO: update this to only return valid cyclonedx expression types +// This should be a function that just surfaces licenses already validated in the package struct func encodeLicenses(p pkg.Package) *cyclonedx.Licenses { - lc := cyclonedx.Licenses{} - for _, licenseName := range p.Licenses { - if value, other, exists := spdxlicense.ID(licenseName); exists { - lc = append(lc, cyclonedx.LicenseChoice{ - License: &cyclonedx.License{ - ID: value, - Name: other, - }, - }) - } - } - if len(lc) > 0 { - return &lc - } return nil } -func decodeLicenses(c *cyclonedx.Component) (out []string) { +func decodeLicenses(c *cyclonedx.Component) []pkg.License { if c.Licenses != nil { - for _, l := range *c.Licenses { - if l.License != nil { - var lic string - switch { - case l.License.ID != "": - lic = l.License.ID - case l.License.Name != "": - lic = l.License.Name - default: - continue - } - out = append(out, lic) - } + for range *c.Licenses { + // TODO: switch on if it's a license or expression } } - return + return nil } diff --git a/syft/formats/common/spdxhelpers/license.go b/syft/formats/common/spdxhelpers/license.go deleted file mode 100644 index 68220183831..00000000000 --- a/syft/formats/common/spdxhelpers/license.go +++ /dev/null @@ -1,51 +0,0 @@ -package spdxhelpers - -import ( - "strings" - - "github.com/anchore/syft/internal/spdxlicense" - "github.com/anchore/syft/syft/pkg" -) - -func License(p pkg.Package) string { - // source: https://spdx.github.io/spdx-spec/3-package-information/#313-concluded-license - // The options to populate this field are limited to: - // A valid SPDX License Expression as defined in Appendix IV; - // NONE, if the SPDX file creator concludes there is no license available for this package; or - // NOASSERTION if: - // (i) the SPDX file creator has attempted to but cannot reach a reasonable objective determination; - // (ii) the SPDX file creator has made no attempt to determine this field; or - // (iii) the SPDX file creator has intentionally provided no information (no meaning should be implied by doing so). - - if len(p.Licenses) == 0 { - return NONE - } - - // take all licenses and assume an AND expression; for information about license expressions see https://spdx.github.io/spdx-spec/appendix-IV-SPDX-license-expressions/ - parsedLicenses := parseLicenses(p.Licenses) - - for i, v := range parsedLicenses { - if strings.HasPrefix(v, spdxlicense.LicenseRefPrefix) { - parsedLicenses[i] = SanitizeElementID(v) - } - } - - if len(parsedLicenses) == 0 { - return NOASSERTION - } - - return strings.Join(parsedLicenses, " AND ") -} - -func parseLicenses(raw []string) (parsedLicenses []string) { - for _, l := range raw { - if value, other, exists := spdxlicense.ID(l); exists { - parsed := value - if other != "" { - parsed = spdxlicense.LicenseRefPrefix + other - } - parsedLicenses = append(parsedLicenses, parsed) - } - } - return -} diff --git a/syft/formats/common/spdxhelpers/license_test.go b/syft/formats/common/spdxhelpers/license_test.go deleted file mode 100644 index 980c0f0dd23..00000000000 --- a/syft/formats/common/spdxhelpers/license_test.go +++ /dev/null @@ -1,85 +0,0 @@ -package spdxhelpers - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/anchore/syft/syft/pkg" -) - -func Test_License(t *testing.T) { - tests := []struct { - name string - input pkg.Package - expected string - }{ - { - name: "no licenses", - input: pkg.Package{}, - expected: NONE, - }, - { - name: "no SPDX licenses", - input: pkg.Package{ - Licenses: []string{ - "made-up", - }, - }, - expected: "LicenseRef-made-up", - }, - { - name: "with SPDX license", - input: pkg.Package{ - Licenses: []string{ - "MIT", - }, - }, - expected: "MIT", - }, - { - name: "with SPDX license expression", - input: pkg.Package{ - Licenses: []string{ - "MIT", - "GPL-3.0", - }, - }, - expected: "MIT AND GPL-3.0-only", - }, - { - name: "cap insensitive", - input: pkg.Package{ - Licenses: []string{ - "gpl-3.0", - }, - }, - expected: "GPL-3.0-only", - }, - { - name: "debian to spdx conversion", - input: pkg.Package{ - Licenses: []string{ - "GPL-2", - }, - }, - expected: "GPL-2.0-only", - }, - { - name: "includes valid LicenseRef-", - input: pkg.Package{ - Licenses: []string{ - "one thing first", - "two things/#$^second", - "MIT", - }, - }, - expected: "LicenseRef-one-thing-first AND LicenseRef-two-things----second AND MIT", - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - assert.Equal(t, test.expected, License(test.input)) - }) - } -} diff --git a/syft/formats/common/spdxhelpers/to_format_model.go b/syft/formats/common/spdxhelpers/to_format_model.go index a6f3f8d4379..24b18dccc39 100644 --- a/syft/formats/common/spdxhelpers/to_format_model.go +++ b/syft/formats/common/spdxhelpers/to_format_model.go @@ -150,7 +150,8 @@ func toPackages(catalog *pkg.Catalog, sbom sbom.SBOM) (results []*spdx.Package) // 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. - license := License(p) + // TODO: package licenses are validated on construction at the cataloger level + // extract these correctly to the spdx license format // two ways to get filesAnalyzed == true: // 1. syft has generated a sha1 digest for the package itself - usually in the java cataloger @@ -250,23 +251,7 @@ func toPackages(catalog *pkg.Catalog, sbom sbom.SBOM) (results []*spdx.Package) // Cardinality: optional, one PackageSourceInfo: SourceInfo(p), - // 7.13: Concluded License: SPDX License Expression, "NONE" or "NOASSERTION" - // Cardinality: mandatory, one - // Purpose: Contain the license the SPDX file creator has concluded as governing the - // package or alternative values, if the governing license cannot be determined. - PackageLicenseConcluded: license, - - // 7.14: All Licenses Info from Files: SPDX License Expression, "NONE" or "NOASSERTION" - // Cardinality: mandatory, one or many if filesAnalyzed is true / omitted; - // zero (must be omitted) if filesAnalyzed is false - PackageLicenseInfoFromFiles: nil, - - // 7.15: Declared License: SPDX License Expression, "NONE" or "NOASSERTION" - // Cardinality: mandatory, one - // Purpose: List the licenses that have been declared by the authors of the package. - // Any license information that does not originate from the package authors, e.g. license - // information from a third party repository, should not be included in this field. - PackageLicenseDeclared: license, + // TODO 7.13 ----- 7.16 // 7.16: Comments on License // Cardinality: optional, one @@ -512,26 +497,10 @@ func toFileTypes(metadata *source.FileMetadata) (ty []string) { return ty } +// TODO: other licenses are for licenses from the pkg.Package that do not have an SPDXExpression +// field. The expression is only filled given a validated Value field. func toOtherLicenses(catalog *pkg.Catalog) []*spdx.OtherLicense { - licenses := map[string]bool{} - for _, p := range catalog.Sorted() { - for _, license := range parseLicenses(p.Licenses) { - if strings.HasPrefix(license, spdxlicense.LicenseRefPrefix) { - licenses[license] = true - } - } - } - var result []*spdx.OtherLicense - for license := range licenses { - // separate the actual ID from the prefix - name := strings.TrimPrefix(license, spdxlicense.LicenseRefPrefix) - result = append(result, &spdx.OtherLicense{ - LicenseIdentifier: SanitizeElementID(license), - LicenseName: name, - ExtractedText: NONE, // we probably should have some extracted text here, but this is good enough for now - }) - } - return result + return nil } // TODO: handle SPDX excludes file case diff --git a/syft/formats/common/spdxhelpers/to_syft_model.go b/syft/formats/common/spdxhelpers/to_syft_model.go index 4993eea7e87..6cb2e8c7640 100644 --- a/syft/formats/common/spdxhelpers/to_syft_model.go +++ b/syft/formats/common/spdxhelpers/to_syft_model.go @@ -271,10 +271,11 @@ func toSyftPackage(p *spdx.Package) *pkg.Package { info := extractPkgInfo(p) metadataType, metadata := extractMetadata(p, info) sP := pkg.Package{ - Type: info.typ, - Name: p.PackageName, - Version: p.PackageVersion, - Licenses: parseLicense(p.PackageLicenseDeclared), + Type: info.typ, + Name: p.PackageName, + Version: p.PackageVersion, + // TODO: this should be updated to account for concluded and declared + // Licenses: parseLicense(p.PackageLicenseDeclared), CPEs: extractCPEs(p), PURL: info.purl.String(), Language: info.lang, @@ -312,7 +313,6 @@ func extractMetadata(p *spdx.Package, info pkgInfo) (pkg.MetadataType, interface OriginPackage: upstreamName, Maintainer: supplier, Version: p.PackageVersion, - License: p.PackageLicenseDeclared, Architecture: arch, URL: p.PackageHomePage, Description: p.PackageDescription, @@ -335,7 +335,6 @@ func extractMetadata(p *spdx.Package, info pkgInfo) (pkg.MetadataType, interface Epoch: epoch, Arch: arch, SourceRpm: upstreamValue, - License: license, Vendor: originator, } case pkg.DebPkg: diff --git a/syft/formats/syftjson/to_format_model.go b/syft/formats/syftjson/to_format_model.go index b5067d6aef6..c5ac418b642 100644 --- a/syft/formats/syftjson/to_format_model.go +++ b/syft/formats/syftjson/to_format_model.go @@ -190,10 +190,11 @@ func toPackageModel(p pkg.Package) model.Package { cpes[i] = cpe.String(c) } - var licenses = make([]string, 0) - if p.Licenses != nil { - licenses = p.Licenses - } + // TODO: update user facing package to use latest license model + //var licenses = make([]string, 0) + //if p.Licenses != nil { + // licenses = p.Licenses + //} locations := p.Locations.ToSlice() var coordinates = make([]source.Coordinates, len(locations)) @@ -209,7 +210,6 @@ func toPackageModel(p pkg.Package) model.Package { Type: p.Type, FoundBy: p.FoundBy, Locations: coordinates, - Licenses: licenses, Language: p.Language, CPEs: cpes, PURL: p.PURL, diff --git a/syft/formats/syftjson/to_syft_model.go b/syft/formats/syftjson/to_syft_model.go index 0418a41c187..4fd3007e472 100644 --- a/syft/formats/syftjson/to_syft_model.go +++ b/syft/formats/syftjson/to_syft_model.go @@ -207,12 +207,13 @@ func toSyftPackage(p model.Package, idAliases map[string]string) pkg.Package { locations[i] = source.NewLocationFromCoordinates(c) } + // TODO: sync pkg and user facing license struct out := pkg.Package{ - Name: p.Name, - Version: p.Version, - FoundBy: p.FoundBy, - Locations: source.NewLocationSet(locations...), - Licenses: p.Licenses, + Name: p.Name, + Version: p.Version, + FoundBy: p.FoundBy, + Locations: source.NewLocationSet(locations...), + // Licenses: p.Licenses, Language: p.Language, Type: p.Type, CPEs: cpes, diff --git a/syft/pkg/alpm_metadata.go b/syft/pkg/alpm_metadata.go index 3e807be9b10..0e59d1ca2e9 100644 --- a/syft/pkg/alpm_metadata.go +++ b/syft/pkg/alpm_metadata.go @@ -21,7 +21,6 @@ type AlpmMetadata struct { Architecture string `mapstructure:"arch" json:"architecture"` Size int `mapstructure:"size" json:"size" cyclonedx:"size"` Packager string `mapstructure:"packager" json:"packager"` - License string `mapstructure:"license" json:"license"` URL string `mapstructure:"url" json:"url"` Validation string `mapstructure:"validation" json:"validation"` Reason int `mapstructure:"reason" json:"reason"` diff --git a/syft/pkg/apk_metadata.go b/syft/pkg/apk_metadata.go index 21abd50df50..4a3b55ab7f1 100644 --- a/syft/pkg/apk_metadata.go +++ b/syft/pkg/apk_metadata.go @@ -27,7 +27,6 @@ type ApkMetadata struct { OriginPackage string `mapstructure:"o" json:"originPackage" cyclonedx:"originPackage"` Maintainer string `mapstructure:"m" json:"maintainer"` Version string `mapstructure:"V" json:"version"` - License string `mapstructure:"L" json:"license"` Architecture string `mapstructure:"A" json:"architecture"` URL string `mapstructure:"U" json:"url"` Description string `mapstructure:"T" json:"description"` diff --git a/syft/pkg/apk_metadata_test.go b/syft/pkg/apk_metadata_test.go index a5e5f4121dd..82c1d58ca60 100644 --- a/syft/pkg/apk_metadata_test.go +++ b/syft/pkg/apk_metadata_test.go @@ -47,7 +47,6 @@ func TestApkMetadata_UnmarshalJSON(t *testing.T) { OriginPackage: "pax-utils", Maintainer: "Natanael Copa ", Version: "1.3.4-r0", - License: "GPL-2.0-only", Architecture: "x86_64", URL: "https://wiki.gentoo.org/wiki/Hardened/PaX_Utilities", Description: "Scan ELF binaries for stuff", @@ -86,7 +85,6 @@ func TestApkMetadata_UnmarshalJSON(t *testing.T) { OriginPackage: "pax-utils", Maintainer: "Natanael Copa ", Version: "1.3.4-r0", - License: "GPL-2.0-only", Architecture: "x86_64", URL: "https://wiki.gentoo.org/wiki/Hardened/PaX_Utilities", Description: "Scan ELF binaries for stuff", diff --git a/syft/pkg/cataloger/alpm/cataloger_test.go b/syft/pkg/cataloger/alpm/cataloger_test.go index 65447d66d8b..4a4ce1539e3 100644 --- a/syft/pkg/cataloger/alpm/cataloger_test.go +++ b/syft/pkg/cataloger/alpm/cataloger_test.go @@ -7,6 +7,7 @@ import ( "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" "github.com/anchore/syft/syft/source" @@ -20,7 +21,7 @@ func TestAlpmCataloger(t *testing.T) { Version: "6.2.1-2", Type: pkg.AlpmPkg, FoundBy: "alpmdb-cataloger", - Licenses: []string{"LGPL3", "GPL"}, + Licenses: license.PackageLicense{}, Locations: source.NewLocationSet(source.NewLocation("var/lib/pacman/local/gmp-6.2.1-2/desc")), CPEs: nil, PURL: "", @@ -33,7 +34,6 @@ func TestAlpmCataloger(t *testing.T) { Architecture: "x86_64", Size: 1044438, Packager: "Antonio Rojas ", - License: "LGPL3\nGPL", URL: "https://gmplib.org/", Validation: "pgp", Reason: 1, diff --git a/syft/pkg/cataloger/alpm/package.go b/syft/pkg/cataloger/alpm/package.go index 1a5bdf13135..37b69cb3b84 100644 --- a/syft/pkg/cataloger/alpm/package.go +++ b/syft/pkg/cataloger/alpm/package.go @@ -2,19 +2,17 @@ package alpm import ( "github.com/anchore/packageurl-go" - "github.com/anchore/syft/internal" "github.com/anchore/syft/syft/linux" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/source" ) -func newPackage(m pkg.AlpmMetadata, release *linux.Release, locations ...source.Location) pkg.Package { +func newPackage(m alpmData, release *linux.Release, locations ...source.Location) pkg.Package { p := pkg.Package{ Name: m.Package, Version: m.Version, Locations: source.NewLocationSet(locations...), Type: pkg.AlpmPkg, - Licenses: internal.SplitAny(m.License, " \n"), PURL: packageURL(m, release), MetadataType: pkg.AlpmMetadataType, Metadata: m, @@ -23,7 +21,7 @@ func newPackage(m pkg.AlpmMetadata, release *linux.Release, locations ...source. return p } -func packageURL(m pkg.AlpmMetadata, distro *linux.Release) string { +func packageURL(m alpmData, distro *linux.Release) string { if distro == nil || distro.ID != "arch" { // note: there is no namespace variation (like with debian ID_LIKE for ubuntu ID, for example) return "" diff --git a/syft/pkg/cataloger/alpm/package_test.go b/syft/pkg/cataloger/alpm/package_test.go index bb88a749cf3..e4ac1bbd2c6 100644 --- a/syft/pkg/cataloger/alpm/package_test.go +++ b/syft/pkg/cataloger/alpm/package_test.go @@ -13,16 +13,19 @@ import ( func Test_PackageURL(t *testing.T) { tests := []struct { name string - metadata pkg.AlpmMetadata + metadata alpmData distro linux.Release expected string }{ { name: "bad distro id", - metadata: pkg.AlpmMetadata{ - Package: "p", - Version: "v", - Architecture: "a", + metadata: alpmData{ + "", + pkg.AlpmMetadata{ + Package: "p", + Version: "v", + Architecture: "a", + }, }, distro: linux.Release{ ID: "something-else", @@ -32,10 +35,13 @@ func Test_PackageURL(t *testing.T) { }, { name: "gocase", - metadata: pkg.AlpmMetadata{ - Package: "p", - Version: "v", - Architecture: "a", + metadata: alpmData{ + "", + pkg.AlpmMetadata{ + Package: "p", + Version: "v", + Architecture: "a", + }, }, distro: linux.Release{ ID: "arch", @@ -45,9 +51,12 @@ func Test_PackageURL(t *testing.T) { }, { name: "missing architecture", - metadata: pkg.AlpmMetadata{ - Package: "p", - Version: "v", + metadata: alpmData{ + "", + pkg.AlpmMetadata{ + Package: "p", + Version: "v", + }, }, distro: linux.Release{ ID: "arch", @@ -55,10 +64,13 @@ func Test_PackageURL(t *testing.T) { expected: "pkg:alpm/arch/p@v?distro=arch", }, { - metadata: pkg.AlpmMetadata{ - Package: "python", - Version: "3.10.0", - Architecture: "any", + metadata: alpmData{ + "", + pkg.AlpmMetadata{ + Package: "python", + Version: "3.10.0", + Architecture: "any", + }, }, distro: linux.Release{ ID: "arch", @@ -67,10 +79,13 @@ func Test_PackageURL(t *testing.T) { expected: "pkg:alpm/arch/python@3.10.0?arch=any&distro=arch-rolling", }, { - metadata: pkg.AlpmMetadata{ - Package: "g plus plus", - Version: "v84", - Architecture: "x86_64", + metadata: alpmData{ + "", + pkg.AlpmMetadata{ + Package: "g plus plus", + Version: "v84", + Architecture: "x86_64", + }, }, distro: linux.Release{ ID: "arch", @@ -80,11 +95,14 @@ func Test_PackageURL(t *testing.T) { }, { name: "add source information as qualifier", - metadata: pkg.AlpmMetadata{ - Package: "p", - Version: "v", - Architecture: "a", - BasePackage: "origin", + metadata: alpmData{ + "", + pkg.AlpmMetadata{ + Package: "p", + Version: "v", + Architecture: "a", + BasePackage: "origin", + }, }, distro: linux.Release{ ID: "arch", diff --git a/syft/pkg/cataloger/alpm/parse_alpm_db.go b/syft/pkg/cataloger/alpm/parse_alpm_db.go index 9de9620b429..57d45353440 100644 --- a/syft/pkg/cataloger/alpm/parse_alpm_db.go +++ b/syft/pkg/cataloger/alpm/parse_alpm_db.go @@ -31,6 +31,11 @@ var ( } ) +type alpmData struct { + License string `mapstructure:"license" json:"license"` + pkg.AlpmMetadata +} + func parseAlpmDB(resolver source.FileResolver, env *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { metadata, err := parseAlpmDBEntry(reader) if err != nil { @@ -48,7 +53,7 @@ func parseAlpmDB(resolver source.FileResolver, env *generic.Environment, reader return nil, nil, err } - // The replace the files found the the pacman database with the files from the mtree These contain more metadata and + // The replace the files found the pacman database with the files from the mtree These contain more metadata and // thus more useful. metadata.Files = pkgFiles @@ -74,7 +79,7 @@ func parseAlpmDB(resolver source.FileResolver, env *generic.Environment, reader }, nil, nil } -func parseAlpmDBEntry(reader io.Reader) (*pkg.AlpmMetadata, error) { +func parseAlpmDBEntry(reader io.Reader) (*alpmData, error) { scanner := newScanner(reader) metadata, err := parseDatabase(scanner) if err != nil { @@ -124,8 +129,8 @@ func getFileReader(path string, resolver source.FileResolver) (io.Reader, error) return dbContentReader, nil } -func parseDatabase(b *bufio.Scanner) (*pkg.AlpmMetadata, error) { - var entry pkg.AlpmMetadata +func parseDatabase(b *bufio.Scanner) (*alpmData, error) { + var entry alpmData var err error pkgFields := make(map[string]interface{}) for b.Scan() { diff --git a/syft/pkg/cataloger/apkdb/package.go b/syft/pkg/cataloger/apkdb/package.go index bad434d558d..dbe12256e3f 100644 --- a/syft/pkg/cataloger/apkdb/package.go +++ b/syft/pkg/cataloger/apkdb/package.go @@ -9,12 +9,11 @@ import ( "github.com/anchore/syft/syft/source" ) -func newPackage(d pkg.ApkMetadata, release *linux.Release, locations ...source.Location) pkg.Package { +func newPackage(d apkData, release *linux.Release, locations ...source.Location) pkg.Package { p := pkg.Package{ Name: d.Package, Version: d.Version, Locations: source.NewLocationSet(locations...), - Licenses: strings.Split(d.License, " "), PURL: packageURL(d, release), Type: pkg.ApkPkg, MetadataType: pkg.ApkMetadataType, @@ -27,7 +26,7 @@ func newPackage(d pkg.ApkMetadata, release *linux.Release, locations ...source.L } // packageURL returns the PURL for the specific Alpine package (see https://github.com/package-url/purl-spec) -func packageURL(m pkg.ApkMetadata, distro *linux.Release) string { +func packageURL(m apkData, distro *linux.Release) string { if distro == nil { return "" } diff --git a/syft/pkg/cataloger/apkdb/package_test.go b/syft/pkg/cataloger/apkdb/package_test.go index 8a25ebb9862..bf45143ed59 100644 --- a/syft/pkg/cataloger/apkdb/package_test.go +++ b/syft/pkg/cataloger/apkdb/package_test.go @@ -15,16 +15,19 @@ import ( func Test_PackageURL(t *testing.T) { tests := []struct { name string - metadata pkg.ApkMetadata + metadata apkData distro linux.Release expected string }{ { name: "non-alpine distro", - metadata: pkg.ApkMetadata{ - Package: "p", - Version: "v", - Architecture: "a", + metadata: apkData{ + "", + pkg.ApkMetadata{ + Package: "p", + Version: "v", + Architecture: "a", + }, }, distro: linux.Release{ ID: "something else", @@ -34,10 +37,13 @@ func Test_PackageURL(t *testing.T) { }, { name: "gocase", - metadata: pkg.ApkMetadata{ - Package: "p", - Version: "v", - Architecture: "a", + metadata: apkData{ + "", + pkg.ApkMetadata{ + Package: "p", + Version: "v", + Architecture: "a", + }, }, distro: linux.Release{ ID: "alpine", @@ -47,9 +53,12 @@ func Test_PackageURL(t *testing.T) { }, { name: "missing architecture", - metadata: pkg.ApkMetadata{ - Package: "p", - Version: "v", + metadata: apkData{ + "", + pkg.ApkMetadata{ + Package: "p", + Version: "v", + }, }, distro: linux.Release{ ID: "alpine", @@ -59,10 +68,13 @@ func Test_PackageURL(t *testing.T) { }, // verify #351 { - metadata: pkg.ApkMetadata{ - Package: "g++", - Version: "v84", - Architecture: "am86", + metadata: apkData{ + "", + pkg.ApkMetadata{ + Package: "g++", + Version: "v84", + Architecture: "am86", + }, }, distro: linux.Release{ ID: "alpine", @@ -71,10 +83,13 @@ func Test_PackageURL(t *testing.T) { expected: "pkg:apk/alpine/g++@v84?arch=am86&distro=alpine-3.4.6", }, { - metadata: pkg.ApkMetadata{ - Package: "g plus plus", - Version: "v84", - Architecture: "am86", + metadata: apkData{ + "", + pkg.ApkMetadata{ + Package: "g plus plus", + Version: "v84", + Architecture: "am86", + }, }, distro: linux.Release{ ID: "alpine", @@ -84,11 +99,14 @@ func Test_PackageURL(t *testing.T) { }, { name: "add source information as qualifier", - metadata: pkg.ApkMetadata{ - Package: "p", - Version: "v", - Architecture: "a", - OriginPackage: "origin", + metadata: apkData{ + "", + pkg.ApkMetadata{ + Package: "p", + Version: "v", + Architecture: "a", + OriginPackage: "origin", + }, }, distro: linux.Release{ ID: "alpine", @@ -98,10 +116,13 @@ func Test_PackageURL(t *testing.T) { }, { name: "wolfi distro", - metadata: pkg.ApkMetadata{ - Package: "p", - Version: "v", - Architecture: "a", + metadata: apkData{ + "", + pkg.ApkMetadata{ + Package: "p", + Version: "v", + Architecture: "a", + }, }, distro: linux.Release{ ID: "wolfi", diff --git a/syft/pkg/cataloger/apkdb/parse_apk_db.go b/syft/pkg/cataloger/apkdb/parse_apk_db.go index 1587a9eaaf0..25e627c9773 100644 --- a/syft/pkg/cataloger/apkdb/parse_apk_db.go +++ b/syft/pkg/cataloger/apkdb/parse_apk_db.go @@ -26,6 +26,11 @@ var ( repoRegex = regexp.MustCompile(`(?m)^https://.*\.alpinelinux\.org/alpine/v([^/]+)/([a-zA-Z0-9_]+)$`) ) +type apkData struct { + License string `mapstructure:"L" json:"license"` + pkg.ApkMetadata +} + // parseApkDB parses packages from a given APK installed DB file. For more // information on specific fields, see https://wiki.alpinelinux.org/wiki/Apk_spec. // @@ -33,15 +38,15 @@ var ( func parseApkDB(resolver source.FileResolver, env *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { scanner := bufio.NewScanner(reader) - var apks []pkg.ApkMetadata - var currentEntry pkg.ApkMetadata + var apks []apkData + var currentEntry apkData entryParsingInProgress := false fileParsingCtx := newApkFileParsingContext() // creating a dedicated append-like function here instead of using `append(...)` // below since there is nontrivial logic to be performed for each finalized apk // entry. - appendApk := func(p pkg.ApkMetadata) { + appendApk := func(p apkData) { if files := fileParsingCtx.files; len(files) >= 1 { // attached accumulated files to current package p.Files = files @@ -68,7 +73,7 @@ func parseApkDB(resolver source.FileResolver, env *generic.Environment, reader s entryParsingInProgress = false // zero-out currentEntry for use by any future entry - currentEntry = pkg.ApkMetadata{} + currentEntry = apkData{} continue } @@ -201,7 +206,7 @@ type apkField struct { } //nolint:funlen -func (f apkField) apply(p *pkg.ApkMetadata, ctx *apkFileParsingContext) { +func (f apkField) apply(p *apkData, ctx *apkFileParsingContext) { switch f.name { // APKINDEX field parsing @@ -347,7 +352,7 @@ func parseListValue(value string) []string { return nil } -func nilFieldsToEmptySlice(p *pkg.ApkMetadata) { +func nilFieldsToEmptySlice(p *apkData) { if p.Dependencies == nil { p.Dependencies = []string{} } diff --git a/syft/pkg/cataloger/deb/package.go b/syft/pkg/cataloger/deb/package.go index 3f10dbe3fe1..b512bc6bfb6 100644 --- a/syft/pkg/cataloger/deb/package.go +++ b/syft/pkg/cataloger/deb/package.go @@ -93,8 +93,8 @@ func addLicenses(resolver source.FileResolver, dbLocation source.Location, p *pk if copyrightReader != nil && copyrightLocation != nil { defer internal.CloseAndLogError(copyrightReader, copyrightLocation.VirtualPath) // attach the licenses - p.Licenses = parseLicensesFromCopyright(copyrightReader) - + // TODO: update this to use new license model/validation + _ = parseLicensesFromCopyright(copyrightReader) // keep a record of the file where this was discovered p.Locations.Add(*copyrightLocation) } diff --git a/syft/pkg/cataloger/java/archive_parser.go b/syft/pkg/cataloger/java/archive_parser.go index 3150351c8cc..c8fe05de093 100644 --- a/syft/pkg/cataloger/java/archive_parser.go +++ b/syft/pkg/cataloger/java/archive_parser.go @@ -184,10 +184,12 @@ func (j *archiveParser) discoverMainPackage() (*pkg.Package, error) { log.Warnf("failed to create digest for file=%q: %+v", j.archivePath, err) } + // TODO: update this to use the new license selection/validation logic + _ = selectLicense(manifest) + return &pkg.Package{ Name: selectName(manifest, j.fileInfo), Version: selectVersion(manifest, j.fileInfo), - Licenses: selectLicense(manifest), Language: pkg.Java, Locations: source.NewLocationSet(j.location), Type: j.fileInfo.pkgType(), diff --git a/syft/pkg/cataloger/javascript/package.go b/syft/pkg/cataloger/javascript/package.go index 34729e4215b..18aecbe8909 100644 --- a/syft/pkg/cataloger/javascript/package.go +++ b/syft/pkg/cataloger/javascript/package.go @@ -13,7 +13,7 @@ import ( ) func newPackageJSONPackage(u packageJSON, locations ...source.Location) pkg.Package { - licenses, err := u.licensesFromJSON() + _, err := u.licensesFromJSON() if err != nil { log.Warnf("unable to extract licenses from javascript package.json: %+v", err) } @@ -21,7 +21,6 @@ func newPackageJSONPackage(u packageJSON, locations ...source.Location) pkg.Pack p := pkg.Package{ Name: u.Name, Version: u.Version, - Licenses: licenses, PURL: packageURL(u.Name, u.Version), Locations: source.NewLocationSet(locations...), Language: pkg.JavaScript, @@ -33,7 +32,6 @@ func newPackageJSONPackage(u packageJSON, locations ...source.Location) pkg.Pack Author: u.Author.AuthorString(), Homepage: u.Homepage, URL: u.Repository.URL, - Licenses: licenses, Private: u.Private, }, } @@ -76,12 +74,6 @@ func newPackageLockV1Package(resolver source.FileResolver, location source.Locat } func newPackageLockV2Package(resolver source.FileResolver, location source.Location, name string, u lockPackage) pkg.Package { - var licenses []string - - if u.License != nil { - licenses = u.License - } - return finalizeLockPkg( resolver, location, @@ -92,7 +84,6 @@ func newPackageLockV2Package(resolver source.FileResolver, location source.Locat PURL: packageURL(name, u.Version), Language: pkg.JavaScript, Type: pkg.NpmPkg, - Licenses: licenses, MetadataType: pkg.NpmPackageLockJSONMetadataType, Metadata: pkg.NpmPackageLockJSONMetadata{Resolved: u.Resolved, Integrity: u.Integrity}, }, @@ -130,7 +121,6 @@ func newYarnLockPackage(resolver source.FileResolver, location source.Location, } func finalizeLockPkg(resolver source.FileResolver, location source.Location, p pkg.Package) pkg.Package { - p.Licenses = append(p.Licenses, addLicenses(p.Name, resolver, location)...) p.SetID() return p } @@ -139,13 +129,13 @@ func addLicenses(name string, resolver source.FileResolver, location source.Loca if resolver == nil { return allLicenses } + dir := path.Dir(location.RealPath) pkgPath := []string{dir, "node_modules"} pkgPath = append(pkgPath, strings.Split(name, "/")...) pkgPath = append(pkgPath, "package.json") pkgFile := path.Join(pkgPath...) locations, err := resolver.FilesByPath(pkgFile) - if err != nil { log.Debugf("an error occurred attempting to read: %s - %+v", pkgFile, err) return allLicenses diff --git a/syft/pkg/cataloger/javascript/parse_package_json.go b/syft/pkg/cataloger/javascript/parse_package_json.go index edf403c7ae1..736e998a917 100644 --- a/syft/pkg/cataloger/javascript/parse_package_json.go +++ b/syft/pkg/cataloger/javascript/parse_package_json.go @@ -137,7 +137,7 @@ func (r *repository) UnmarshalJSON(b []byte) error { return nil } -type license struct { +type npmPackageLicense struct { Type string `json:"type"` URL string `json:"url"` } @@ -151,7 +151,7 @@ func licenseFromJSON(b []byte) (string, error) { } // then try as object (this format is deprecated) - var licenseObject license + var licenseObject npmPackageLicense err = json.Unmarshal(b, &licenseObject) if err == nil { return licenseObject.Type, nil @@ -175,7 +175,7 @@ func (p packageJSON) licensesFromJSON() ([]string, error) { // The "licenses" field is deprecated. It should be inspected as a last resort. if multiLicense != nil && err == nil { - mapLicenses := func(licenses []license) []string { + mapLicenses := func(licenses []npmPackageLicense) []string { mappedLicenses := make([]string, len(licenses)) for i, l := range licenses { mappedLicenses[i] = l.Type @@ -189,8 +189,8 @@ func (p packageJSON) licensesFromJSON() ([]string, error) { return nil, err } -func licensesFromJSON(b []byte) ([]license, error) { - var licenseObject []license +func licensesFromJSON(b []byte) ([]npmPackageLicense, error) { + var licenseObject []npmPackageLicense err := json.Unmarshal(b, &licenseObject) if err == nil { return licenseObject, nil diff --git a/syft/pkg/cataloger/portage/parse_portage_contents.go b/syft/pkg/cataloger/portage/parse_portage_contents.go index dba65ace0dd..43609b6d574 100644 --- a/syft/pkg/cataloger/portage/parse_portage_contents.go +++ b/syft/pkg/cataloger/portage/parse_portage_contents.go @@ -6,7 +6,6 @@ import ( "path" "path/filepath" "regexp" - "sort" "strconv" "strings" @@ -48,7 +47,8 @@ func parsePortageContents(resolver source.FileResolver, _ *generic.Environment, Files: make([]pkg.PortageFileRecord, 0), }, } - addLicenses(resolver, reader.Location, &p) + // TODO: update this to use latest license parsing and validation tooling + // addLicenses(resolver, reader.Location, &p) addSize(resolver, reader.Location, &p) addFiles(resolver, reader.Location, &p) @@ -114,9 +114,8 @@ func addLicenses(resolver source.FileResolver, dbLocation source.Location, p *pk findings.Add(token) } } - licenses := findings.ToSlice() - sort.Strings(licenses) - p.Licenses = licenses + _ = findings.ToSlice() + // sort.Strings(licenses) p.Locations.Add(*location) } diff --git a/syft/pkg/cataloger/python/package.go b/syft/pkg/cataloger/python/package.go index 0cb1e3f71ab..a12bcdb5453 100644 --- a/syft/pkg/cataloger/python/package.go +++ b/syft/pkg/cataloger/python/package.go @@ -41,17 +41,12 @@ func newPackageForIndexWithMetadata(name, version string, metadata pkg.PythonPip } func newPackageForPackage(m pkg.PythonPackageMetadata, sources ...source.Location) pkg.Package { - var licenses []string - if m.License != "" { - licenses = []string{m.License} - } - + // TODO: lift licenses out of metadata as part of construction p := pkg.Package{ Name: m.Name, Version: m.Version, PURL: packageURL(m.Name, m.Version, &m), Locations: source.NewLocationSet(sources...), - Licenses: licenses, Language: pkg.Python, Type: pkg.PythonPkg, MetadataType: pkg.PythonPackageMetadataType, diff --git a/syft/pkg/cataloger/rpm/package.go b/syft/pkg/cataloger/rpm/package.go index cf277e7804b..91292f4ac88 100644 --- a/syft/pkg/cataloger/rpm/package.go +++ b/syft/pkg/cataloger/rpm/package.go @@ -24,15 +24,12 @@ func newPackage(dbLocation source.Location, metadata pkg.RpmMetadata, distro *li Metadata: metadata, } - if metadata.License != "" { - p.Licenses = append(p.Licenses, metadata.License) - } - p.SetID() return p } func newMetadataFromEntry(entry rpmdb.PackageInfo, files []pkg.RpmdbFileRecord) pkg.RpmMetadata { + // TODO: use entry to populate the pkg.RpmMetadata struct in package constructor return pkg.RpmMetadata{ Name: entry.Name, Version: entry.Version, @@ -41,7 +38,6 @@ func newMetadataFromEntry(entry rpmdb.PackageInfo, files []pkg.RpmdbFileRecord) Release: entry.Release, SourceRpm: entry.SourceRpm, Vendor: entry.Vendor, - License: entry.License, Size: entry.Size, ModularityLabel: entry.Modularitylabel, Files: files, diff --git a/syft/pkg/cataloger/rpm/parse_rpm.go b/syft/pkg/cataloger/rpm/parse_rpm.go index 687735faf04..60b4657945c 100644 --- a/syft/pkg/cataloger/rpm/parse_rpm.go +++ b/syft/pkg/cataloger/rpm/parse_rpm.go @@ -3,7 +3,6 @@ package rpm import ( "fmt" "strconv" - "strings" rpmdb "github.com/knqyf263/go-rpmdb/pkg" "github.com/sassoftware/go-rpmutils" @@ -27,13 +26,16 @@ func parseRpm(_ source.FileResolver, _ *generic.Environment, reader source.Locat return nil, nil, err } - licenses, _ := rpm.Header.GetStrings(rpmutils.LICENSE) + _, _ = rpm.Header.GetStrings(rpmutils.LICENSE) sourceRpm, _ := rpm.Header.GetString(rpmutils.SOURCERPM) vendor, _ := rpm.Header.GetString(rpmutils.VENDOR) digestAlgorithm := getDigestAlgorithm(rpm.Header) size, _ := rpm.Header.InstalledSize() files, _ := rpm.Header.GetFiles() + // TODO: UPDATE THIS TO USE LATEST LICENSE PARSING AND VALIDATION TOOLING + // License: strings.Join(licenses, " AND "), // TODO: AND conjunction is not necessarily correct, but we don't have a way to represent multiple licenses yet + metadata := pkg.RpmMetadata{ Name: nevra.Name, Version: nevra.Version, @@ -42,7 +44,6 @@ func parseRpm(_ source.FileResolver, _ *generic.Environment, reader source.Locat Release: nevra.Release, SourceRpm: sourceRpm, Vendor: vendor, - License: strings.Join(licenses, " AND "), // TODO: AND conjunction is not necessarily correct, but we don't have a way to represent multiple licenses yet Size: int(size), Files: mapFiles(files, digestAlgorithm), } diff --git a/syft/pkg/cataloger/ruby/package.go b/syft/pkg/cataloger/ruby/package.go index 408653c2518..17ecf57c2f0 100644 --- a/syft/pkg/cataloger/ruby/package.go +++ b/syft/pkg/cataloger/ruby/package.go @@ -21,13 +21,12 @@ func newGemfileLockPackage(name, version string, locations ...source.Location) p return p } -func newGemspecPackage(m pkg.GemMetadata, locations ...source.Location) pkg.Package { +func newGemspecPackage(m gemData, locations ...source.Location) pkg.Package { p := pkg.Package{ Name: m.Name, Version: m.Version, Locations: source.NewLocationSet(locations...), PURL: packageURL(m.Name, m.Version), - Licenses: m.Licenses, Language: pkg.Ruby, Type: pkg.GemPkg, MetadataType: pkg.GemMetadataType, diff --git a/syft/pkg/cataloger/ruby/parse_gemspec.go b/syft/pkg/cataloger/ruby/parse_gemspec.go index 08bc9fe96de..7ea9e0c2862 100644 --- a/syft/pkg/cataloger/ruby/parse_gemspec.go +++ b/syft/pkg/cataloger/ruby/parse_gemspec.go @@ -20,6 +20,11 @@ var _ generic.Parser = parseGemFileLockEntries type postProcessor func(string) []string +type gemData struct { + Licenses []string `mapstructure:"licenses" json:"licenses,omitempty"` + pkg.GemMetadata +} + // match example: Al\u003Ex ---> 003E var unicodePattern = regexp.MustCompile(`\\u(?P[0-9A-F]{4})`) @@ -90,7 +95,7 @@ func parseGemSpecEntries(_ source.FileResolver, _ *generic.Environment, reader s } if fields["name"] != "" && fields["version"] != "" { - var metadata pkg.GemMetadata + var metadata gemData if err := mapstructure.Decode(fields, &metadata); err != nil { return nil, nil, fmt.Errorf("unable to decode gem metadata: %w", err) } diff --git a/syft/pkg/gem_metadata.go b/syft/pkg/gem_metadata.go index 51a07a14880..ffe95a04738 100644 --- a/syft/pkg/gem_metadata.go +++ b/syft/pkg/gem_metadata.go @@ -6,6 +6,5 @@ type GemMetadata struct { Version string `mapstructure:"version" json:"version"` Files []string `mapstructure:"files" json:"files,omitempty"` Authors []string `mapstructure:"authors" json:"authors,omitempty"` - Licenses []string `mapstructure:"licenses" json:"licenses,omitempty"` Homepage string `mapstructure:"homepage" json:"homepage,omitempty"` } diff --git a/syft/pkg/license.go b/syft/pkg/license.go index 560f9a567ea..d1e253ad0de 100644 --- a/syft/pkg/license.go +++ b/syft/pkg/license.go @@ -5,7 +5,7 @@ import ( "github.com/anchore/syft/syft/source" ) -type PackageLicense struct { +type License struct { Value string SPDXExpression string Type license.Type @@ -13,11 +13,11 @@ type PackageLicense struct { Location source.Location // on disk declaration } -func NewPackageLicense(value string, url string, location source.Location) PackageLicense { +func NewLicense(value string, url string, location source.Location) License { // TODO: validate value as an SPDX expression // TODO: how do we express other places where a license is declared // EX: we got this from the go module cache at path /x/y/z on disk - return PackageLicense{ + return License{ Value: value, URL: url, Location: location, diff --git a/syft/pkg/npm_package_json_metadata.go b/syft/pkg/npm_package_json_metadata.go index ae150e12fbd..1066a27575f 100644 --- a/syft/pkg/npm_package_json_metadata.go +++ b/syft/pkg/npm_package_json_metadata.go @@ -6,7 +6,6 @@ type NpmPackageJSONMetadata struct { Version string `mapstructure:"version" json:"version"` Files []string `mapstructure:"files" json:"files,omitempty"` Author string `mapstructure:"author" json:"author"` - Licenses []string `mapstructure:"licenses" json:"licenses"` Homepage string `mapstructure:"homepage" json:"homepage"` Description string `mapstructure:"description" json:"description"` URL string `mapstructure:"url" json:"url"` diff --git a/syft/pkg/package.go b/syft/pkg/package.go index a9eb3dd00ab..07b1b7d2f44 100644 --- a/syft/pkg/package.go +++ b/syft/pkg/package.go @@ -16,6 +16,7 @@ import ( // Package represents an application or library that has been bundled into a distributable format. // TODO: if we ignore FoundBy for ID generation should we merge the field to show it was found in two places? +// TODO: should cyclonedx tags exist on the struct? Why don't we use the model.Package type? type Package struct { id artifact.ID `hash:"ignore"` Name string // the package name diff --git a/syft/pkg/package_test.go b/syft/pkg/package_test.go index d5b4b4edb46..1b96cd38905 100644 --- a/syft/pkg/package_test.go +++ b/syft/pkg/package_test.go @@ -9,7 +9,6 @@ import ( "github.com/anchore/stereoscope/pkg/file" "github.com/anchore/syft/syft/cpe" - "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/source" ) @@ -28,7 +27,6 @@ func TestIDUniqueness(t *testing.T) { Locations: source.NewLocationSet( originalLocation, ), - Licenses: license.PackageLicense{}, Language: "math", Type: PythonPkg, CPEs: []cpe.CPE{ @@ -242,7 +240,6 @@ func TestPackage_Merge(t *testing.T) { Locations: source.NewLocationSet( originalLocation, ), - Licenses: license.PackageLicense{}, Language: "math", Type: PythonPkg, CPEs: []cpe.CPE{ @@ -267,7 +264,6 @@ func TestPackage_Merge(t *testing.T) { Locations: source.NewLocationSet( similarLocation, // NOTE: difference; we have a different layer but the same path ), - Licenses: license.PackageLicense{}, Language: "math", Type: PythonPkg, CPEs: []cpe.CPE{ @@ -293,7 +289,6 @@ func TestPackage_Merge(t *testing.T) { originalLocation, similarLocation, // NOTE: merge! ), - Licenses: license.PackageLicense{}, Language: "math", Type: PythonPkg, CPEs: []cpe.CPE{ @@ -322,7 +317,6 @@ func TestPackage_Merge(t *testing.T) { Locations: source.NewLocationSet( originalLocation, ), - Licenses: license.PackageLicense{}, Language: "math", Type: PythonPkg, CPEs: []cpe.CPE{ @@ -347,7 +341,6 @@ func TestPackage_Merge(t *testing.T) { Locations: source.NewLocationSet( originalLocation, ), - Licenses: license.PackageLicense{}, Language: "math", Type: PythonPkg, CPEs: []cpe.CPE{ diff --git a/syft/pkg/php_composer_json_metadata.go b/syft/pkg/php_composer_json_metadata.go index dc0a6e80416..ab1b85a3a91 100644 --- a/syft/pkg/php_composer_json_metadata.go +++ b/syft/pkg/php_composer_json_metadata.go @@ -13,7 +13,6 @@ type PhpComposerJSONMetadata struct { Type string `json:"type,omitempty"` NotificationURL string `json:"notification-url,omitempty"` Bin []string `json:"bin,omitempty"` - License []string `json:"license,omitempty"` Authors []PhpComposerAuthors `json:"authors,omitempty"` Description string `json:"description,omitempty"` Homepage string `json:"homepage,omitempty"` diff --git a/syft/pkg/python_package_metadata.go b/syft/pkg/python_package_metadata.go index 97a8ab80696..36d7fe88740 100644 --- a/syft/pkg/python_package_metadata.go +++ b/syft/pkg/python_package_metadata.go @@ -31,7 +31,6 @@ type PythonDirectURLOriginInfo struct { type PythonPackageMetadata struct { Name string `json:"name" mapstruct:"Name"` Version string `json:"version" mapstruct:"Version"` - License string `json:"license" mapstruct:"License"` Author string `json:"author" mapstruct:"Author"` AuthorEmail string `json:"authorEmail" mapstruct:"Authoremail"` Platform string `json:"platform" mapstruct:"Platform"` diff --git a/syft/pkg/rpm_metadata.go b/syft/pkg/rpm_metadata.go index 1491b4900d7..b430cc2b760 100644 --- a/syft/pkg/rpm_metadata.go +++ b/syft/pkg/rpm_metadata.go @@ -27,7 +27,6 @@ type RpmMetadata struct { Release string `json:"release" cyclonedx:"release"` SourceRpm string `json:"sourceRpm" cyclonedx:"sourceRpm"` Size int `json:"size" cyclonedx:"size"` - License string `json:"license"` Vendor string `json:"vendor"` ModularityLabel string `json:"modularityLabel"` Files []RpmdbFileRecord `json:"files"` diff --git a/syft/sbom/sbom.go b/syft/sbom/sbom.go index ac6a7335ce9..a3cdad38d2a 100644 --- a/syft/sbom/sbom.go +++ b/syft/sbom/sbom.go @@ -24,6 +24,7 @@ type Artifacts struct { FileMetadata map[source.Coordinates]source.FileMetadata FileDigests map[source.Coordinates][]file.Digest FileContents map[source.Coordinates]string + FileLicenses map[source.Coordinates][]file.License Secrets map[source.Coordinates][]file.SearchResult LinuxDistribution *linux.Release } From 7fa15d8d864afe88ea03f9dfd22d87c6f56e9d1b Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Wed, 22 Mar 2023 10:54:24 -0400 Subject: [PATCH 04/99] feat: update license package with basic spdx parse Signed-off-by: Christopher Phillips --- go.mod | 3 +++ go.sum | 2 ++ syft/file/license.go | 13 ++++++++++--- syft/license/license.go | 11 +++++++++++ syft/pkg/license.go | 17 ++++++++++++----- 5 files changed, 38 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 9905fc888cb..c670186d686 100644 --- a/go.mod +++ b/go.mod @@ -54,6 +54,7 @@ require ( github.com/anchore/go-logger v0.0.0-20220728155337-03b66a5207d8 github.com/anchore/stereoscope v0.0.0-20230317134707-7928713c391e github.com/docker/docker v23.0.1+incompatible + github.com/github/go-spdx/v2 v2.1.2 github.com/google/go-containerregistry v0.14.0 github.com/invopop/jsonschema v0.7.0 github.com/knqyf263/go-rpmdb v0.0.0-20221030135625-4082a22221ce @@ -158,3 +159,5 @@ retract ( v0.53.2 v0.53.1 // Published accidentally with incorrect license in depdencies ) + +replace github.com/github/go-spdx/v2 => ../go-spdx diff --git a/go.sum b/go.sum index 1918776fa5b..10060dd4f11 100644 --- a/go.sum +++ b/go.sum @@ -190,6 +190,8 @@ github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbS github.com/gabriel-vasile/mimetype v1.4.0 h1:Cn9dkdYsMIu56tGho+fqzh7XmvY2YyGU0FnbhiOsEro= github.com/gabriel-vasile/mimetype v1.4.0/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/github/go-spdx/v2 v2.1.2 h1:p+Tv0yMgcuO0/vnMe9Qh4tmUgYhI6AsLVlakZ/Sx+DM= +github.com/github/go-spdx/v2 v2.1.2/go.mod h1:hMCrsFgT0QnCwn7G8gxy/MxMpy67WgZrwFeISTn0o6w= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= diff --git a/syft/file/license.go b/syft/file/license.go index d4eb5add985..96a766d8abd 100644 --- a/syft/file/license.go +++ b/syft/file/license.go @@ -1,6 +1,7 @@ package file import ( + "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/license" ) @@ -12,10 +13,16 @@ type License struct { } func NewLicense(value string) License { - // TODO: validate value as an SPDX expression + // TODO: enhance license package with more helpers for validation + spdxExpression, err := license.ParseExpression(value) + if err != nil { + log.Trace("unable to parse license expression: %w", err) + } + // TODO: run location against classifier to form evidence return License{ - Value: value, - Type: license.Concluded, + Value: value, + SPDXExpression: spdxExpression, + Type: license.Concluded, } } diff --git a/syft/license/license.go b/syft/license/license.go index f0340d6df01..4d7ec1f8100 100644 --- a/syft/license/license.go +++ b/syft/license/license.go @@ -1,5 +1,8 @@ +// package license provides common methods for working with SPDX license data package license +import "github.com/github/go-spdx/v2/spdxexp" + type Type string const ( @@ -12,3 +15,11 @@ type Evidence struct { Offset int Extent int } + +func ParseExpression(expression string) (string, error) { + node, err := spdxexp.Parse(expression) + if err != nil { + return "", err + } + return *node.ReconstructedLicenseString(), nil +} diff --git a/syft/pkg/license.go b/syft/pkg/license.go index d1e253ad0de..e142c560358 100644 --- a/syft/pkg/license.go +++ b/syft/pkg/license.go @@ -1,6 +1,7 @@ package pkg import ( + "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/source" ) @@ -14,13 +15,19 @@ type License struct { } func NewLicense(value string, url string, location source.Location) License { - // TODO: validate value as an SPDX expression + // TODO: check license package for validation enhancements + spdxExpression, err := license.ParseExpression(value) + if err != nil { + // TODO: warn/trace error with package context? + log.Trace("unable to parse license expression: %w", err) + } // TODO: how do we express other places where a license is declared // EX: we got this from the go module cache at path /x/y/z on disk return License{ - Value: value, - URL: url, - Location: location, - Type: license.Declared, + Value: value, + SPDXExpression: spdxExpression, + URL: url, + Location: location, + Type: license.Declared, } } From b480eb59d90609cd895a08d5ed66aaea6f5f92ca Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Wed, 22 Mar 2023 11:18:36 -0400 Subject: [PATCH 05/99] feat: update license construction for apk/alpm Signed-off-by: Christopher Phillips --- syft/pkg/cataloger/alpm/package.go | 8 ++++++++ syft/pkg/cataloger/alpm/parse_alpm_db.go | 3 ++- syft/pkg/cataloger/apkdb/package.go | 8 ++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/syft/pkg/cataloger/alpm/package.go b/syft/pkg/cataloger/alpm/package.go index 37b69cb3b84..92b4c7572c9 100644 --- a/syft/pkg/cataloger/alpm/package.go +++ b/syft/pkg/cataloger/alpm/package.go @@ -8,10 +8,18 @@ import ( ) func newPackage(m alpmData, release *linux.Release, locations ...source.Location) pkg.Package { + // ALPM only passes a single location + // We use this as the "declared" license location + var licenseLocation source.Location + if len(locations) > 0 { + licenseLocation = locations[0] + } + p := pkg.Package{ Name: m.Package, Version: m.Version, Locations: source.NewLocationSet(locations...), + Licenses: []pkg.License{pkg.NewLicense(m.License, "", licenseLocation)}, Type: pkg.AlpmPkg, PURL: packageURL(m, release), MetadataType: pkg.AlpmMetadataType, diff --git a/syft/pkg/cataloger/alpm/parse_alpm_db.go b/syft/pkg/cataloger/alpm/parse_alpm_db.go index 57d45353440..0ecc17be227 100644 --- a/syft/pkg/cataloger/alpm/parse_alpm_db.go +++ b/syft/pkg/cataloger/alpm/parse_alpm_db.go @@ -53,8 +53,9 @@ func parseAlpmDB(resolver source.FileResolver, env *generic.Environment, reader return nil, nil, err } - // The replace the files found the pacman database with the files from the mtree These contain more metadata and + // replace the files found the pacman database with the files from the mtree These contain more metadata and // thus more useful. + // TODO: probably want to use MTREE and PKGINFO here metadata.Files = pkgFiles // We only really do this to get any backup database entries from the files database diff --git a/syft/pkg/cataloger/apkdb/package.go b/syft/pkg/cataloger/apkdb/package.go index dbe12256e3f..017161f8465 100644 --- a/syft/pkg/cataloger/apkdb/package.go +++ b/syft/pkg/cataloger/apkdb/package.go @@ -10,10 +10,18 @@ import ( ) func newPackage(d apkData, release *linux.Release, locations ...source.Location) pkg.Package { + // apkdb only passes a single location in its constructor + var licenseLocation source.Location + if len(locations) > 0 { + licenseLocation = locations[0] + } + licenses := []pkg.License{pkg.NewLicense(d.License, "", licenseLocation)} + p := pkg.Package{ Name: d.Package, Version: d.Version, Locations: source.NewLocationSet(locations...), + Licenses: licenses, PURL: packageURL(d, release), Type: pkg.ApkPkg, MetadataType: pkg.ApkMetadataType, From 1e5b1ee257bd22f19c81bf350b2db68c4b9f5735 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Tue, 4 Apr 2023 13:30:44 -0400 Subject: [PATCH 06/99] test: update format model and test util Signed-off-by: Christopher Phillips --- syft/formats/internal/testutils/utils.go | 19 +++++++++++++++++-- syft/formats/syftjson/to_format_model.go | 4 ++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/syft/formats/internal/testutils/utils.go b/syft/formats/internal/testutils/utils.go index f214c4f07f3..516cb092438 100644 --- a/syft/formats/internal/testutils/utils.go +++ b/syft/formats/internal/testutils/utils.go @@ -17,6 +17,7 @@ import ( "github.com/anchore/stereoscope/pkg/imagetest" "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/cpe" + "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/linux" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/sbom" @@ -174,7 +175,14 @@ func populateImageCatalog(catalog *pkg.Catalog, img *image.Image) { FoundBy: "the-cataloger-1", Language: pkg.Python, MetadataType: pkg.PythonPackageMetadataType, - Licenses: []string{"MIT"}, + Licenses: []pkg.License{ + { + Value: "MIT", + SPDXExpression: "MIT", + Type: license.Declared, + URL: "", + }, + }, Metadata: pkg.PythonPackageMetadata{ Name: "package-1", Version: "1.0.1", @@ -249,7 +257,14 @@ func newDirectoryCatalog() *pkg.Catalog { ), Language: pkg.Python, MetadataType: pkg.PythonPackageMetadataType, - Licenses: []string{"MIT"}, + Licenses: []pkg.License{ + { + Value: "MIT", + SPDXExpression: "MIT", + Type: license.Declared, + URL: "", + }, + }, Metadata: pkg.PythonPackageMetadata{ Name: "package-1", Version: "1.0.1", diff --git a/syft/formats/syftjson/to_format_model.go b/syft/formats/syftjson/to_format_model.go index c5ac418b642..c9675757d66 100644 --- a/syft/formats/syftjson/to_format_model.go +++ b/syft/formats/syftjson/to_format_model.go @@ -191,8 +191,8 @@ func toPackageModel(p pkg.Package) model.Package { } // TODO: update user facing package to use latest license model - //var licenses = make([]string, 0) - //if p.Licenses != nil { + // var licenses = make([]string, 0) + // if p.Licenses != nil { // licenses = p.Licenses //} From 37183bb3884065eb00cc095c4a073feaef2adbe9 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Thu, 6 Apr 2023 11:37:32 -0400 Subject: [PATCH 07/99] fix: library code can return nil and no error Signed-off-by: Christopher Phillips --- syft/license/license.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/syft/license/license.go b/syft/license/license.go index 4d7ec1f8100..9de546265d3 100644 --- a/syft/license/license.go +++ b/syft/license/license.go @@ -21,5 +21,8 @@ func ParseExpression(expression string) (string, error) { if err != nil { return "", err } + if node == nil { + return "", nil + } return *node.ReconstructedLicenseString(), nil } From bb95aa32a96dd041b84e7f596ff48c332f655ecf Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Thu, 6 Apr 2023 12:05:00 -0400 Subject: [PATCH 08/99] test: update schema and test code to compile Signed-off-by: Christopher Phillips --- go.sum | 2 - internal/constants.go | 2 +- schema/json/schema-8.0.0.json | 1610 +++++++++++++++++ .../common/cyclonedxhelpers/component_test.go | 2 - .../common/cyclonedxhelpers/licenses.go | 14 +- .../common/cyclonedxhelpers/licenses_test.go | 21 +- .../common/spdxhelpers/to_format_model.go | 2 +- .../spdxhelpers/to_format_model_test.go | 14 +- .../common/spdxhelpers/to_syft_model.go | 11 - syft/formats/syftjson/encoder_test.go | 2 +- syft/pkg/cataloger/alpm/cataloger_test.go | 3 +- syft/pkg/cataloger/apkdb/parse_apk_db_test.go | 8 +- syft/pkg/cataloger/deb/cataloger_test.go | 2 +- .../pkg/cataloger/java/archive_parser_test.go | 6 +- .../cataloger/javascript/cataloger_test.go | 4 +- syft/pkg/cataloger/javascript/package.go | 108 +- .../javascript/parse_package_json_test.go | 22 +- .../javascript/parse_package_lock_test.go | 16 +- .../cataloger/php/parse_composer_lock_test.go | 7 +- .../php/parse_installed_json_test.go | 8 +- syft/pkg/cataloger/portage/cataloger_test.go | 2 +- .../portage/parse_portage_contents.go | 57 +- syft/pkg/cataloger/python/cataloger_test.go | 70 +- syft/pkg/cataloger/rpm/parse_rpm_db_test.go | 6 +- syft/pkg/cataloger/rpm/parse_rpm_test.go | 6 +- syft/pkg/cataloger/ruby/parse_gemspec_test.go | 3 +- syft/pkg/cataloger/sbom/cataloger_test.go | 28 +- syft/pkg/package_test.go | 7 - 28 files changed, 1790 insertions(+), 253 deletions(-) create mode 100644 schema/json/schema-8.0.0.json diff --git a/go.sum b/go.sum index 10060dd4f11..1918776fa5b 100644 --- a/go.sum +++ b/go.sum @@ -190,8 +190,6 @@ github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbS github.com/gabriel-vasile/mimetype v1.4.0 h1:Cn9dkdYsMIu56tGho+fqzh7XmvY2YyGU0FnbhiOsEro= github.com/gabriel-vasile/mimetype v1.4.0/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/github/go-spdx/v2 v2.1.2 h1:p+Tv0yMgcuO0/vnMe9Qh4tmUgYhI6AsLVlakZ/Sx+DM= -github.com/github/go-spdx/v2 v2.1.2/go.mod h1:hMCrsFgT0QnCwn7G8gxy/MxMpy67WgZrwFeISTn0o6w= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= diff --git a/internal/constants.go b/internal/constants.go index 05e847cafd0..b7be2824039 100644 --- a/internal/constants.go +++ b/internal/constants.go @@ -6,5 +6,5 @@ const ( // JSONSchemaVersion is the current schema version output by the JSON encoder // This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment. - JSONSchemaVersion = "7.0.1" + JSONSchemaVersion = "8.0.0" ) diff --git a/schema/json/schema-8.0.0.json b/schema/json/schema-8.0.0.json new file mode 100644 index 00000000000..aaae4eee4d9 --- /dev/null +++ b/schema/json/schema-8.0.0.json @@ -0,0 +1,1610 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/anchore/syft/syft/formats/syftjson/model/document", + "$ref": "#/$defs/Document", + "$defs": { + "AlpmFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "type": { + "type": "string" + }, + "uid": { + "type": "string" + }, + "gid": { + "type": "string" + }, + "time": { + "type": "string", + "format": "date-time" + }, + "size": { + "type": "string" + }, + "link": { + "type": "string" + }, + "digest": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object" + }, + "AlpmMetadata": { + "properties": { + "basepackage": { + "type": "string" + }, + "package": { + "type": "string" + }, + "version": { + "type": "string" + }, + "description": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "packager": { + "type": "string" + }, + "url": { + "type": "string" + }, + "validation": { + "type": "string" + }, + "reason": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/AlpmFileRecord" + }, + "type": "array" + }, + "backup": { + "items": { + "$ref": "#/$defs/AlpmFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "basepackage", + "package", + "version", + "description", + "architecture", + "size", + "packager", + "url", + "validation", + "reason", + "files", + "backup" + ] + }, + "ApkFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "ownerUid": { + "type": "string" + }, + "ownerGid": { + "type": "string" + }, + "permissions": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "ApkMetadata": { + "properties": { + "package": { + "type": "string" + }, + "originPackage": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "version": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "url": { + "type": "string" + }, + "description": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "installedSize": { + "type": "integer" + }, + "pullDependencies": { + "items": { + "type": "string" + }, + "type": "array" + }, + "provides": { + "items": { + "type": "string" + }, + "type": "array" + }, + "pullChecksum": { + "type": "string" + }, + "gitCommitOfApkPort": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/ApkFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "originPackage", + "maintainer", + "version", + "architecture", + "url", + "description", + "size", + "installedSize", + "pullDependencies", + "provides", + "pullChecksum", + "gitCommitOfApkPort", + "files" + ] + }, + "BinaryMetadata": { + "properties": { + "matches": { + "items": { + "$ref": "#/$defs/ClassifierMatch" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "matches" + ] + }, + "CargoPackageMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "type": "string" + }, + "checksum": { + "type": "string" + }, + "dependencies": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "checksum", + "dependencies" + ] + }, + "ClassifierMatch": { + "properties": { + "classifier": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Location" + } + }, + "type": "object", + "required": [ + "classifier", + "location" + ] + }, + "CocoapodsMetadata": { + "properties": { + "checksum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "checksum" + ] + }, + "ConanLockMetadata": { + "properties": { + "ref": { + "type": "string" + }, + "package_id": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "requires": { + "type": "string" + }, + "build_requires": { + "type": "string" + }, + "py_requires": { + "type": "string" + }, + "options": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "path": { + "type": "string" + }, + "context": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "ConanMetadata": { + "properties": { + "ref": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "Coordinates": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "DartPubMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "hosted_url": { + "type": "string" + }, + "vcs_url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Descriptor": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "configuration": true + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Digest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algorithm", + "value" + ] + }, + "Document": { + "properties": { + "artifacts": { + "items": { + "$ref": "#/$defs/Package" + }, + "type": "array" + }, + "artifactRelationships": { + "items": { + "$ref": "#/$defs/Relationship" + }, + "type": "array" + }, + "files": { + "items": { + "$ref": "#/$defs/File" + }, + "type": "array" + }, + "secrets": { + "items": { + "$ref": "#/$defs/Secrets" + }, + "type": "array" + }, + "source": { + "$ref": "#/$defs/Source" + }, + "distro": { + "$ref": "#/$defs/LinuxRelease" + }, + "descriptor": { + "$ref": "#/$defs/Descriptor" + }, + "schema": { + "$ref": "#/$defs/Schema" + } + }, + "type": "object", + "required": [ + "artifacts", + "artifactRelationships", + "source", + "distro", + "descriptor", + "schema" + ] + }, + "DotnetDepsMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "path": { + "type": "string" + }, + "sha512": { + "type": "string" + }, + "hashPath": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "path", + "sha512", + "hashPath" + ] + }, + "DpkgFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "isConfigFile": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "path", + "isConfigFile" + ] + }, + "DpkgMetadata": { + "properties": { + "package": { + "type": "string" + }, + "source": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "installedSize": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/DpkgFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "source", + "version", + "sourceVersion", + "architecture", + "maintainer", + "installedSize", + "files" + ] + }, + "File": { + "properties": { + "id": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Coordinates" + }, + "metadata": { + "$ref": "#/$defs/FileMetadataEntry" + }, + "contents": { + "type": "string" + }, + "digests": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "id", + "location" + ] + }, + "FileMetadataEntry": { + "properties": { + "mode": { + "type": "integer" + }, + "type": { + "type": "string" + }, + "linkDestination": { + "type": "string" + }, + "userID": { + "type": "integer" + }, + "groupID": { + "type": "integer" + }, + "mimeType": { + "type": "string" + } + }, + "type": "object", + "required": [ + "mode", + "type", + "userID", + "groupID", + "mimeType" + ] + }, + "GemMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "type": "string" + }, + "type": "array" + }, + "homepage": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "GolangBinMetadata": { + "properties": { + "goBuildSettings": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "goCompiledVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "h1Digest": { + "type": "string" + }, + "mainModule": { + "type": "string" + } + }, + "type": "object", + "required": [ + "goCompiledVersion", + "architecture" + ] + }, + "GolangModMetadata": { + "properties": { + "h1Digest": { + "type": "string" + } + }, + "type": "object" + }, + "HackageMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "snapshotURL": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "IDLikes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "JavaManifest": { + "properties": { + "main": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "namedSections": { + "patternProperties": { + ".*": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "JavaMetadata": { + "properties": { + "virtualPath": { + "type": "string" + }, + "manifest": { + "$ref": "#/$defs/JavaManifest" + }, + "pomProperties": { + "$ref": "#/$defs/PomProperties" + }, + "pomProject": { + "$ref": "#/$defs/PomProject" + }, + "digest": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "virtualPath" + ] + }, + "KbPackageMetadata": { + "properties": { + "product_id": { + "type": "string" + }, + "kb": { + "type": "string" + } + }, + "type": "object", + "required": [ + "product_id", + "kb" + ] + }, + "LinuxRelease": { + "properties": { + "prettyName": { + "type": "string" + }, + "name": { + "type": "string" + }, + "id": { + "type": "string" + }, + "idLike": { + "$ref": "#/$defs/IDLikes" + }, + "version": { + "type": "string" + }, + "versionID": { + "type": "string" + }, + "versionCodename": { + "type": "string" + }, + "buildID": { + "type": "string" + }, + "imageID": { + "type": "string" + }, + "imageVersion": { + "type": "string" + }, + "variant": { + "type": "string" + }, + "variantID": { + "type": "string" + }, + "homeURL": { + "type": "string" + }, + "supportURL": { + "type": "string" + }, + "bugReportURL": { + "type": "string" + }, + "privacyPolicyURL": { + "type": "string" + }, + "cpeName": { + "type": "string" + }, + "supportEnd": { + "type": "string" + } + }, + "type": "object" + }, + "Location": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + }, + "virtualPath": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "MixLockMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "pkgHashExt": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "pkgHash", + "pkgHashExt" + ] + }, + "NpmPackageJSONMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + }, + "author": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + }, + "private": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "homepage", + "description", + "url", + "private" + ] + }, + "NpmPackageLockJSONMetadata": { + "properties": { + "resolved": { + "type": "string" + }, + "integrity": { + "type": "string" + } + }, + "type": "object", + "required": [ + "resolved", + "integrity" + ] + }, + "Package": { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "type": { + "type": "string" + }, + "foundBy": { + "type": "string" + }, + "locations": { + "items": { + "$ref": "#/$defs/Coordinates" + }, + "type": "array" + }, + "licenses": { + "items": { + "type": "string" + }, + "type": "array" + }, + "language": { + "type": "string" + }, + "cpes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "purl": { + "type": "string" + }, + "metadataType": { + "type": "string" + }, + "metadata": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "#/$defs/AlpmMetadata" + }, + { + "$ref": "#/$defs/ApkMetadata" + }, + { + "$ref": "#/$defs/BinaryMetadata" + }, + { + "$ref": "#/$defs/CargoPackageMetadata" + }, + { + "$ref": "#/$defs/CocoapodsMetadata" + }, + { + "$ref": "#/$defs/ConanLockMetadata" + }, + { + "$ref": "#/$defs/ConanMetadata" + }, + { + "$ref": "#/$defs/DartPubMetadata" + }, + { + "$ref": "#/$defs/DotnetDepsMetadata" + }, + { + "$ref": "#/$defs/DpkgMetadata" + }, + { + "$ref": "#/$defs/GemMetadata" + }, + { + "$ref": "#/$defs/GolangBinMetadata" + }, + { + "$ref": "#/$defs/GolangModMetadata" + }, + { + "$ref": "#/$defs/HackageMetadata" + }, + { + "$ref": "#/$defs/JavaMetadata" + }, + { + "$ref": "#/$defs/KbPackageMetadata" + }, + { + "$ref": "#/$defs/MixLockMetadata" + }, + { + "$ref": "#/$defs/NpmPackageJSONMetadata" + }, + { + "$ref": "#/$defs/NpmPackageLockJSONMetadata" + }, + { + "$ref": "#/$defs/PhpComposerJSONMetadata" + }, + { + "$ref": "#/$defs/PortageMetadata" + }, + { + "$ref": "#/$defs/PythonPackageMetadata" + }, + { + "$ref": "#/$defs/PythonPipfileLockMetadata" + }, + { + "$ref": "#/$defs/RebarLockMetadata" + }, + { + "$ref": "#/$defs/RpmMetadata" + } + ] + } + }, + "type": "object", + "required": [ + "id", + "name", + "version", + "type", + "foundBy", + "locations", + "licenses", + "language", + "cpes", + "purl" + ] + }, + "PhpComposerAuthors": { + "properties": { + "name": { + "type": "string" + }, + "email": { + "type": "string" + }, + "homepage": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name" + ] + }, + "PhpComposerExternalReference": { + "properties": { + "type": { + "type": "string" + }, + "url": { + "type": "string" + }, + "reference": { + "type": "string" + }, + "shasum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "type", + "url", + "reference" + ] + }, + "PhpComposerJSONMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "dist": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "require": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "provide": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "require-dev": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "suggest": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "type": { + "type": "string" + }, + "notification-url": { + "type": "string" + }, + "bin": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "$ref": "#/$defs/PhpComposerAuthors" + }, + "type": "array" + }, + "description": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "keywords": { + "items": { + "type": "string" + }, + "type": "array" + }, + "time": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "dist" + ] + }, + "PomParent": { + "properties": { + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "type": "object", + "required": [ + "groupId", + "artifactId", + "version" + ] + }, + "PomProject": { + "properties": { + "path": { + "type": "string" + }, + "parent": { + "$ref": "#/$defs/PomParent" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "groupId", + "artifactId", + "version", + "name" + ] + }, + "PomProperties": { + "properties": { + "path": { + "type": "string" + }, + "name": { + "type": "string" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "extraFields": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path", + "name", + "groupId", + "artifactId", + "version" + ] + }, + "PortageFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "PortageMetadata": { + "properties": { + "installedSize": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/PortageFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "installedSize", + "files" + ] + }, + "PythonDirectURLOriginInfo": { + "properties": { + "url": { + "type": "string" + }, + "commitId": { + "type": "string" + }, + "vcs": { + "type": "string" + } + }, + "type": "object", + "required": [ + "url" + ] + }, + "PythonFileDigest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algorithm", + "value" + ] + }, + "PythonFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/PythonFileDigest" + }, + "size": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "PythonPackageMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "author": { + "type": "string" + }, + "authorEmail": { + "type": "string" + }, + "platform": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/PythonFileRecord" + }, + "type": "array" + }, + "sitePackagesRootPath": { + "type": "string" + }, + "topLevelPackages": { + "items": { + "type": "string" + }, + "type": "array" + }, + "directUrlOrigin": { + "$ref": "#/$defs/PythonDirectURLOriginInfo" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "authorEmail", + "platform", + "sitePackagesRootPath" + ] + }, + "PythonPipfileLockMetadata": { + "properties": { + "hashes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "index": { + "type": "string" + } + }, + "type": "object", + "required": [ + "hashes", + "index" + ] + }, + "RebarLockMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "pkgHashExt": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "pkgHash", + "pkgHashExt" + ] + }, + "Relationship": { + "properties": { + "parent": { + "type": "string" + }, + "child": { + "type": "string" + }, + "type": { + "type": "string" + }, + "metadata": true + }, + "type": "object", + "required": [ + "parent", + "child", + "type" + ] + }, + "RpmMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "epoch": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "architecture": { + "type": "string" + }, + "release": { + "type": "string" + }, + "sourceRpm": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "vendor": { + "type": "string" + }, + "modularityLabel": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/RpmdbFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "epoch", + "architecture", + "release", + "sourceRpm", + "size", + "vendor", + "modularityLabel", + "files" + ] + }, + "RpmdbFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "mode": { + "type": "integer" + }, + "size": { + "type": "integer" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "userName": { + "type": "string" + }, + "groupName": { + "type": "string" + }, + "flags": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "mode", + "size", + "digest", + "userName", + "groupName", + "flags" + ] + }, + "Schema": { + "properties": { + "version": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "version", + "url" + ] + }, + "SearchResult": { + "properties": { + "classification": { + "type": "string" + }, + "lineNumber": { + "type": "integer" + }, + "lineOffset": { + "type": "integer" + }, + "seekPosition": { + "type": "integer" + }, + "length": { + "type": "integer" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "classification", + "lineNumber", + "lineOffset", + "seekPosition", + "length" + ] + }, + "Secrets": { + "properties": { + "location": { + "$ref": "#/$defs/Coordinates" + }, + "secrets": { + "items": { + "$ref": "#/$defs/SearchResult" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "location", + "secrets" + ] + }, + "Source": { + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string" + }, + "target": true + }, + "type": "object", + "required": [ + "id", + "type", + "target" + ] + } + } +} diff --git a/syft/formats/common/cyclonedxhelpers/component_test.go b/syft/formats/common/cyclonedxhelpers/component_test.go index 01bbaf97aed..79ceda489c8 100644 --- a/syft/formats/common/cyclonedxhelpers/component_test.go +++ b/syft/formats/common/cyclonedxhelpers/component_test.go @@ -36,7 +36,6 @@ func Test_encodeComponentProperties(t *testing.T) { OriginPackage: "libc-dev", Maintainer: "Natanael Copa ", Version: "0.7.2-r0", - License: "BSD", Architecture: "x86_64", URL: "http://alpinelinux.org", Description: "Meta package to pull in correct libc", @@ -140,7 +139,6 @@ func Test_encodeComponentProperties(t *testing.T) { Version: "0.9.2", SourceRpm: "dive-0.9.2-1.src.rpm", Size: 12406784, - License: "MIT", Vendor: "", Files: []pkg.RpmdbFileRecord{}, }, diff --git a/syft/formats/common/cyclonedxhelpers/licenses.go b/syft/formats/common/cyclonedxhelpers/licenses.go index d515b2f43eb..d28d27ec0be 100644 --- a/syft/formats/common/cyclonedxhelpers/licenses.go +++ b/syft/formats/common/cyclonedxhelpers/licenses.go @@ -8,15 +8,15 @@ import ( // TODO: update this to only return valid cyclonedx expression types // This should be a function that just surfaces licenses already validated in the package struct -func encodeLicenses(p pkg.Package) *cyclonedx.Licenses { +func encodeLicenses(_ pkg.Package) *cyclonedx.Licenses { return nil } -func decodeLicenses(c *cyclonedx.Component) []pkg.License { - if c.Licenses != nil { - for range *c.Licenses { - // TODO: switch on if it's a license or expression - } - } +func decodeLicenses(_ *cyclonedx.Component) []pkg.License { + // if c.Licenses != nil { + // for range *c.Licenses { + // // TODO: switch on if it's a license or expression + // } + //} return nil } diff --git a/syft/formats/common/cyclonedxhelpers/licenses_test.go b/syft/formats/common/cyclonedxhelpers/licenses_test.go index 64999e60cfc..65d9ece7d3b 100644 --- a/syft/formats/common/cyclonedxhelpers/licenses_test.go +++ b/syft/formats/common/cyclonedxhelpers/licenses_test.go @@ -23,9 +23,7 @@ func Test_encodeLicense(t *testing.T) { { name: "no SPDX licenses", input: pkg.Package{ - Licenses: []string{ - "made-up", - }, + Licenses: []pkg.License{}, }, expected: &cyclonedx.Licenses{ {License: &cyclonedx.License{Name: "made-up"}}, @@ -34,9 +32,7 @@ func Test_encodeLicense(t *testing.T) { { name: "with SPDX license", input: pkg.Package{ - Licenses: []string{ - "MIT", - }, + Licenses: []pkg.License{}, }, expected: &cyclonedx.Licenses{ {License: &cyclonedx.License{ID: "MIT"}}, @@ -45,10 +41,7 @@ func Test_encodeLicense(t *testing.T) { { name: "with SPDX license expression", input: pkg.Package{ - Licenses: []string{ - "MIT", - "GPL-3.0", - }, + Licenses: []pkg.License{}, }, expected: &cyclonedx.Licenses{ {License: &cyclonedx.License{ID: "MIT"}}, @@ -58,9 +51,7 @@ func Test_encodeLicense(t *testing.T) { { name: "cap insensitive", input: pkg.Package{ - Licenses: []string{ - "gpl-3.0", - }, + Licenses: []pkg.License{}, }, expected: &cyclonedx.Licenses{ {License: &cyclonedx.License{ID: "GPL-3.0-only"}}, @@ -69,9 +60,7 @@ func Test_encodeLicense(t *testing.T) { { name: "debian to spdx conversion", input: pkg.Package{ - Licenses: []string{ - "GPL-2", - }, + Licenses: []pkg.License{}, }, expected: &cyclonedx.Licenses{ {License: &cyclonedx.License{ID: "GPL-2.0-only"}}, diff --git a/syft/formats/common/spdxhelpers/to_format_model.go b/syft/formats/common/spdxhelpers/to_format_model.go index 24b18dccc39..a41969a3791 100644 --- a/syft/formats/common/spdxhelpers/to_format_model.go +++ b/syft/formats/common/spdxhelpers/to_format_model.go @@ -499,7 +499,7 @@ func toFileTypes(metadata *source.FileMetadata) (ty []string) { // TODO: other licenses are for licenses from the pkg.Package that do not have an SPDXExpression // field. The expression is only filled given a validated Value field. -func toOtherLicenses(catalog *pkg.Catalog) []*spdx.OtherLicense { +func toOtherLicenses(_ *pkg.Catalog) []*spdx.OtherLicense { return nil } diff --git a/syft/formats/common/spdxhelpers/to_format_model_test.go b/syft/formats/common/spdxhelpers/to_format_model_test.go index 4e3032202c3..29475316ccd 100644 --- a/syft/formats/common/spdxhelpers/to_format_model_test.go +++ b/syft/formats/common/spdxhelpers/to_format_model_test.go @@ -441,18 +441,14 @@ func Test_OtherLicenses(t *testing.T) { { name: "no licenseRef", pkg: pkg.Package{ - Licenses: []string{ - "MIT", - }, + Licenses: []pkg.License{}, }, expected: nil, }, { name: "single licenseRef", pkg: pkg.Package{ - Licenses: []string{ - "un known", - }, + Licenses: []pkg.License{}, }, expected: []*spdx.OtherLicense{ { @@ -465,11 +461,7 @@ func Test_OtherLicenses(t *testing.T) { { name: "multiple licenseRef", pkg: pkg.Package{ - Licenses: []string{ - "un known", - "not known %s", - "MIT", - }, + Licenses: []pkg.License{}, }, expected: []*spdx.OtherLicense{ { diff --git a/syft/formats/common/spdxhelpers/to_syft_model.go b/syft/formats/common/spdxhelpers/to_syft_model.go index 6cb2e8c7640..b991fd33c06 100644 --- a/syft/formats/common/spdxhelpers/to_syft_model.go +++ b/syft/formats/common/spdxhelpers/to_syft_model.go @@ -325,10 +325,6 @@ func extractMetadata(p *spdx.Package, info pkgInfo) (pkg.MetadataType, interface } else { epoch = &converted } - license := p.PackageLicenseDeclared - if license == "" { - license = p.PackageLicenseConcluded - } return pkg.RpmMetadataType, pkg.RpmMetadata{ Name: p.PackageName, Version: p.PackageVersion, @@ -394,10 +390,3 @@ func extractCPEs(p *spdx.Package) (cpes []cpe.CPE) { } return cpes } - -func parseLicense(l string) []string { - if l == NOASSERTION || l == NONE { - return nil - } - return strings.Split(l, " AND ") -} diff --git a/syft/formats/syftjson/encoder_test.go b/syft/formats/syftjson/encoder_test.go index 185658f7b77..f13e30602d3 100644 --- a/syft/formats/syftjson/encoder_test.go +++ b/syft/formats/syftjson/encoder_test.go @@ -63,7 +63,7 @@ func TestEncodeFullJSONDocument(t *testing.T) { FoundBy: "the-cataloger-1", Language: pkg.Python, MetadataType: pkg.PythonPackageMetadataType, - Licenses: []string{"MIT"}, + Licenses: []pkg.License{}, Metadata: pkg.PythonPackageMetadata{ Name: "package-1", Version: "1.0.1", diff --git a/syft/pkg/cataloger/alpm/cataloger_test.go b/syft/pkg/cataloger/alpm/cataloger_test.go index 4a4ce1539e3..be2ca79e7d1 100644 --- a/syft/pkg/cataloger/alpm/cataloger_test.go +++ b/syft/pkg/cataloger/alpm/cataloger_test.go @@ -7,7 +7,6 @@ import ( "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/file" - "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" "github.com/anchore/syft/syft/source" @@ -21,7 +20,7 @@ func TestAlpmCataloger(t *testing.T) { Version: "6.2.1-2", Type: pkg.AlpmPkg, FoundBy: "alpmdb-cataloger", - Licenses: license.PackageLicense{}, + Licenses: []pkg.License{}, Locations: source.NewLocationSet(source.NewLocation("var/lib/pacman/local/gmp-6.2.1-2/desc")), CPEs: nil, PURL: "", diff --git a/syft/pkg/cataloger/apkdb/parse_apk_db_test.go b/syft/pkg/cataloger/apkdb/parse_apk_db_test.go index 60f40c98a58..2c0e6fb15fe 100644 --- a/syft/pkg/cataloger/apkdb/parse_apk_db_test.go +++ b/syft/pkg/cataloger/apkdb/parse_apk_db_test.go @@ -90,7 +90,6 @@ func TestSinglePackageDetails(t *testing.T) { Version: "1.1.24-r2", Description: "the musl c library (libc) implementation", Maintainer: "Timo Teräs ", - License: "MIT BSD GPL2+", Architecture: "x86_64", URL: "https://musl.libc.org/", Size: 37944, @@ -170,7 +169,6 @@ func TestSinglePackageDetails(t *testing.T) { Version: "3.4.0-r0", Description: "Alpine base dir structure and init scripts", Maintainer: "Natanael Copa ", - License: "GPL-2.0-only", Architecture: "x86_64", URL: "https://git.alpinelinux.org/cgit/aports/tree/main/alpine-baselayout", Size: 11664, @@ -207,7 +205,6 @@ func TestSinglePackageDetails(t *testing.T) { Version: "3.2.0-r6", Description: "Alpine base dir structure and init scripts", Maintainer: "Natanael Copa ", - License: "GPL-2.0-only", Architecture: "x86_64", URL: "https://git.alpinelinux.org/cgit/aports/tree/main/alpine-baselayout", Size: 19917, @@ -674,7 +671,7 @@ func TestMultiplePackages(t *testing.T) { { Name: "libc-utils", Version: "0.7.2-r0", - Licenses: []string{"BSD"}, + Licenses: []pkg.License{}, Type: pkg.ApkPkg, PURL: "pkg:apk/alpine/libc-utils@0.7.2-r0?arch=x86_64&upstream=libc-dev&distro=alpine-3.12", Locations: fixtureLocationSet, @@ -684,7 +681,6 @@ func TestMultiplePackages(t *testing.T) { OriginPackage: "libc-dev", Maintainer: "Natanael Copa ", Version: "0.7.2-r0", - License: "BSD", Architecture: "x86_64", URL: "http://alpinelinux.org", Description: "Meta package to pull in correct libc", @@ -700,7 +696,6 @@ func TestMultiplePackages(t *testing.T) { { Name: "musl-utils", Version: "1.1.24-r2", - Licenses: []string{"MIT", "BSD", "GPL2+"}, Type: pkg.ApkPkg, PURL: "pkg:apk/alpine/musl-utils@1.1.24-r2?arch=x86_64&upstream=musl&distro=alpine-3.12", Locations: fixtureLocationSet, @@ -711,7 +706,6 @@ func TestMultiplePackages(t *testing.T) { Version: "1.1.24-r2", Description: "the musl c library (libc) implementation", Maintainer: "Timo Teräs ", - License: "MIT BSD GPL2+", Architecture: "x86_64", URL: "https://musl.libc.org/", Size: 37944, diff --git a/syft/pkg/cataloger/deb/cataloger_test.go b/syft/pkg/cataloger/deb/cataloger_test.go index 8fac459c613..958db023cd7 100644 --- a/syft/pkg/cataloger/deb/cataloger_test.go +++ b/syft/pkg/cataloger/deb/cataloger_test.go @@ -15,7 +15,7 @@ func TestDpkgCataloger(t *testing.T) { Name: "libpam-runtime", Version: "1.1.8-3.6", FoundBy: "dpkgdb-cataloger", - Licenses: []string{"GPL-1", "GPL-2", "LGPL-2.1"}, + Licenses: []pkg.License{}, Locations: source.NewLocationSet( source.NewVirtualLocation("/var/lib/dpkg/status", "/var/lib/dpkg/status"), source.NewVirtualLocation("/var/lib/dpkg/info/libpam-runtime.md5sums", "/var/lib/dpkg/info/libpam-runtime.md5sums"), diff --git a/syft/pkg/cataloger/java/archive_parser_test.go b/syft/pkg/cataloger/java/archive_parser_test.go index cd2affb50ce..a64fbff2f68 100644 --- a/syft/pkg/cataloger/java/archive_parser_test.go +++ b/syft/pkg/cataloger/java/archive_parser_test.go @@ -99,7 +99,7 @@ func TestParseJar(t *testing.T) { Name: "example-jenkins-plugin", Version: "1.0-SNAPSHOT", PURL: "pkg:maven/io.jenkins.plugins/example-jenkins-plugin@1.0-SNAPSHOT", - Licenses: []string{"MIT License"}, + Licenses: []pkg.License{}, Language: pkg.Java, Type: pkg.JenkinsPluginPkg, MetadataType: pkg.JavaMetadataType, @@ -150,7 +150,7 @@ func TestParseJar(t *testing.T) { Name: "example-java-app-gradle", Version: "0.1.0", PURL: "pkg:maven/example-java-app-gradle/example-java-app-gradle@0.1.0", - Licenses: []string{}, + Licenses: []pkg.License{}, Language: pkg.Java, Type: pkg.JavaPkg, MetadataType: pkg.JavaMetadataType, @@ -176,7 +176,7 @@ func TestParseJar(t *testing.T) { Name: "example-java-app-maven", Version: "0.1.0", PURL: "pkg:maven/org.anchore/example-java-app-maven@0.1.0", - Licenses: []string{}, + Licenses: []pkg.License{}, Language: pkg.Java, Type: pkg.JavaPkg, MetadataType: pkg.JavaMetadataType, diff --git a/syft/pkg/cataloger/javascript/cataloger_test.go b/syft/pkg/cataloger/javascript/cataloger_test.go index 6e0ba16b613..b7383b1b37b 100644 --- a/syft/pkg/cataloger/javascript/cataloger_test.go +++ b/syft/pkg/cataloger/javascript/cataloger_test.go @@ -19,7 +19,7 @@ func Test_JavascriptCataloger(t *testing.T) { Locations: locationSet, Language: pkg.JavaScript, Type: pkg.NpmPkg, - Licenses: []string{"MIT"}, + Licenses: []pkg.License{}, MetadataType: pkg.NpmPackageLockJSONMetadataType, Metadata: pkg.NpmPackageLockJSONMetadata{Resolved: "https://registry.npmjs.org/@actions/core/-/core-1.6.0.tgz", Integrity: "sha512-NB1UAZomZlCV/LmJqkLhNTqtKfFXJZAUPcfl/zqG7EfsQdeUJtaWO98SGbuQ3pydJ3fHl2CvI/51OKYlCYYcaw=="}, }, @@ -42,7 +42,7 @@ func Test_JavascriptCataloger(t *testing.T) { Locations: locationSet, Language: pkg.JavaScript, Type: pkg.NpmPkg, - Licenses: []string{"MIT"}, + Licenses: []pkg.License{}, MetadataType: pkg.NpmPackageLockJSONMetadataType, Metadata: pkg.NpmPackageLockJSONMetadata{Resolved: "https://registry.npmjs.org/cowsay/-/cowsay-1.4.0.tgz", Integrity: "sha512-rdg5k5PsHFVJheO/pmE3aDg2rUDDTfPJau6yYkZYlHFktUz+UxbE+IgnUAEyyCyv4noL5ltxXD0gZzmHPCy/9g=="}, }, diff --git a/syft/pkg/cataloger/javascript/package.go b/syft/pkg/cataloger/javascript/package.go index 18aecbe8909..3972cb43e6a 100644 --- a/syft/pkg/cataloger/javascript/package.go +++ b/syft/pkg/cataloger/javascript/package.go @@ -1,9 +1,6 @@ package javascript import ( - "encoding/json" - "io" - "path" "strings" "github.com/anchore/packageurl-go" @@ -120,62 +117,63 @@ func newYarnLockPackage(resolver source.FileResolver, location source.Location, ) } -func finalizeLockPkg(resolver source.FileResolver, location source.Location, p pkg.Package) pkg.Package { +// TODO: update license extraction to use the new license finder +func finalizeLockPkg(_ source.FileResolver, _ source.Location, p pkg.Package) pkg.Package { p.SetID() return p } -func addLicenses(name string, resolver source.FileResolver, location source.Location) (allLicenses []string) { - if resolver == nil { - return allLicenses - } - - dir := path.Dir(location.RealPath) - pkgPath := []string{dir, "node_modules"} - pkgPath = append(pkgPath, strings.Split(name, "/")...) - pkgPath = append(pkgPath, "package.json") - pkgFile := path.Join(pkgPath...) - locations, err := resolver.FilesByPath(pkgFile) - if err != nil { - log.Debugf("an error occurred attempting to read: %s - %+v", pkgFile, err) - return allLicenses - } - - if len(locations) == 0 { - return allLicenses - } - - for _, l := range locations { - contentReader, err := resolver.FileContentsByLocation(l) - if err != nil { - log.Debugf("error getting file content reader for %s: %v", pkgFile, err) - return allLicenses - } - - contents, err := io.ReadAll(contentReader) - if err != nil { - log.Debugf("error reading file contents for %s: %v", pkgFile, err) - return allLicenses - } - - var pkgJSON packageJSON - err = json.Unmarshal(contents, &pkgJSON) - if err != nil { - log.Debugf("error parsing %s: %v", pkgFile, err) - return allLicenses - } - - licenses, err := pkgJSON.licensesFromJSON() - if err != nil { - log.Debugf("error getting licenses from %s: %v", pkgFile, err) - return allLicenses - } - - allLicenses = append(allLicenses, licenses...) - } - - return allLicenses -} +// func addLicenses(name string, resolver source.FileResolver, location source.Location) (allLicenses []string) { +// if resolver == nil { +// return allLicenses +// } +// +// dir := path.Dir(location.RealPath) +// pkgPath := []string{dir, "node_modules"} +// pkgPath = append(pkgPath, strings.Split(name, "/")...) +// pkgPath = append(pkgPath, "package.json") +// pkgFile := path.Join(pkgPath...) +// locations, err := resolver.FilesByPath(pkgFile) +// if err != nil { +// log.Debugf("an error occurred attempting to read: %s - %+v", pkgFile, err) +// return allLicenses +// } +// +// if len(locations) == 0 { +// return allLicenses +// } +// +// for _, l := range locations { +// contentReader, err := resolver.FileContentsByLocation(l) +// if err != nil { +// log.Debugf("error getting file content reader for %s: %v", pkgFile, err) +// return allLicenses +// } +// +// contents, err := io.ReadAll(contentReader) +// if err != nil { +// log.Debugf("error reading file contents for %s: %v", pkgFile, err) +// return allLicenses +// } +// +// var pkgJSON packageJSON +// err = json.Unmarshal(contents, &pkgJSON) +// if err != nil { +// log.Debugf("error parsing %s: %v", pkgFile, err) +// return allLicenses +// } +// +// licenses, err := pkgJSON.licensesFromJSON() +// if err != nil { +// log.Debugf("error getting licenses from %s: %v", pkgFile, err) +// return allLicenses +// } +// +// allLicenses = append(allLicenses, licenses...) +// } +// +// return allLicenses +//} // packageURL returns the PURL for the specific NPM package (see https://github.com/package-url/purl-spec) func packageURL(name, version string) string { diff --git a/syft/pkg/cataloger/javascript/parse_package_json_test.go b/syft/pkg/cataloger/javascript/parse_package_json_test.go index 9fe3722331c..ed0909c0d7f 100644 --- a/syft/pkg/cataloger/javascript/parse_package_json_test.go +++ b/syft/pkg/cataloger/javascript/parse_package_json_test.go @@ -22,7 +22,7 @@ func TestParsePackageJSON(t *testing.T) { Version: "6.14.6", PURL: "pkg:npm/npm@6.14.6", Type: pkg.NpmPkg, - Licenses: []string{"Artistic-2.0"}, + Licenses: []pkg.License{}, Language: pkg.JavaScript, MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ @@ -31,7 +31,6 @@ func TestParsePackageJSON(t *testing.T) { Author: "Isaac Z. Schlueter (http://blog.izs.me)", Homepage: "https://docs.npmjs.com/", URL: "https://github.com/npm/cli", - Licenses: []string{"Artistic-2.0"}, }, }, }, @@ -42,7 +41,7 @@ func TestParsePackageJSON(t *testing.T) { Version: "6.14.6", PURL: "pkg:npm/npm@6.14.6", Type: pkg.NpmPkg, - Licenses: []string{"ISC"}, + Licenses: []pkg.License{}, Language: pkg.JavaScript, MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ @@ -51,7 +50,6 @@ func TestParsePackageJSON(t *testing.T) { Author: "Isaac Z. Schlueter (http://blog.izs.me)", Homepage: "https://docs.npmjs.com/", URL: "https://github.com/npm/cli", - Licenses: []string{"ISC"}, }, }, }, @@ -62,7 +60,7 @@ func TestParsePackageJSON(t *testing.T) { Version: "6.14.6", PURL: "pkg:npm/npm@6.14.6", Type: pkg.NpmPkg, - Licenses: []string{"MIT", "Apache-2.0"}, + Licenses: []pkg.License{}, Language: pkg.JavaScript, MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ @@ -71,7 +69,6 @@ func TestParsePackageJSON(t *testing.T) { Author: "Isaac Z. Schlueter (http://blog.izs.me)", Homepage: "https://docs.npmjs.com/", URL: "https://github.com/npm/cli", - Licenses: []string{"MIT", "Apache-2.0"}, }, }, }, @@ -91,7 +88,6 @@ func TestParsePackageJSON(t *testing.T) { Author: "Isaac Z. Schlueter (http://blog.izs.me)", Homepage: "https://docs.npmjs.com/", URL: "https://github.com/npm/cli", - Licenses: nil, }, }, }, @@ -102,7 +98,7 @@ func TestParsePackageJSON(t *testing.T) { Version: "6.14.6", PURL: "pkg:npm/npm@6.14.6", Type: pkg.NpmPkg, - Licenses: []string{}, + Licenses: []pkg.License{}, Language: pkg.JavaScript, MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ @@ -111,7 +107,6 @@ func TestParsePackageJSON(t *testing.T) { Author: "Isaac Z. Schlueter (http://blog.izs.me)", Homepage: "https://docs.npmjs.com/", URL: "https://github.com/npm/cli", - Licenses: []string{}, }, }, }, @@ -122,7 +117,7 @@ func TestParsePackageJSON(t *testing.T) { Version: "6.14.6", PURL: "pkg:npm/npm@6.14.6", Type: pkg.NpmPkg, - Licenses: []string{"Artistic-2.0"}, + Licenses: []pkg.License{}, Language: pkg.JavaScript, MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ @@ -131,7 +126,6 @@ func TestParsePackageJSON(t *testing.T) { Author: "Isaac Z. Schlueter (http://blog.izs.me)", Homepage: "https://docs.npmjs.com/", URL: "https://github.com/npm/cli", - Licenses: []string{"Artistic-2.0"}, }, }, }, @@ -142,7 +136,7 @@ func TestParsePackageJSON(t *testing.T) { Version: "1.1.1", PURL: "pkg:npm/function-bind@1.1.1", Type: pkg.NpmPkg, - Licenses: []string{"MIT"}, + Licenses: []pkg.License{}, Language: pkg.JavaScript, MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ @@ -151,7 +145,6 @@ func TestParsePackageJSON(t *testing.T) { Author: "Raynos ", Homepage: "https://github.com/Raynos/function-bind", URL: "git://github.com/Raynos/function-bind.git", - Licenses: []string{"MIT"}, }, }, }, @@ -162,7 +155,7 @@ func TestParsePackageJSON(t *testing.T) { Version: "6.14.6", PURL: "pkg:npm/npm@6.14.6", Type: pkg.NpmPkg, - Licenses: []string{"Artistic-2.0"}, + Licenses: []pkg.License{}, Language: pkg.JavaScript, MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ @@ -171,7 +164,6 @@ func TestParsePackageJSON(t *testing.T) { Author: "Isaac Z. Schlueter (http://blog.izs.me)", Homepage: "https://docs.npmjs.com/", URL: "https://github.com/npm/cli", - Licenses: []string{"Artistic-2.0"}, Private: true, }, }, diff --git a/syft/pkg/cataloger/javascript/parse_package_lock_test.go b/syft/pkg/cataloger/javascript/parse_package_lock_test.go index 75601d43712..ba563218b50 100644 --- a/syft/pkg/cataloger/javascript/parse_package_lock_test.go +++ b/syft/pkg/cataloger/javascript/parse_package_lock_test.go @@ -139,7 +139,7 @@ func TestParsePackageLockV2(t *testing.T) { PURL: "pkg:npm/%40types/prop-types@15.7.5", Language: pkg.JavaScript, Type: pkg.NpmPkg, - Licenses: []string{"MIT"}, + Licenses: []pkg.License{}, MetadataType: "NpmPackageLockJsonMetadata", Metadata: pkg.NpmPackageLockJSONMetadata{Resolved: "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", Integrity: "sha1-XxnSuFqY6VWANvajysyIGUIPBc8="}, }, @@ -149,7 +149,7 @@ func TestParsePackageLockV2(t *testing.T) { PURL: "pkg:npm/%40types/react@18.0.17", Language: pkg.JavaScript, Type: pkg.NpmPkg, - Licenses: []string{"MIT"}, + Licenses: []pkg.License{}, MetadataType: "NpmPackageLockJsonMetadata", Metadata: pkg.NpmPackageLockJSONMetadata{Resolved: "https://registry.npmjs.org/@types/react/-/react-18.0.17.tgz", Integrity: "sha1-RYPZwyLWfv5LOak10iPtzHBQzPQ="}, }, @@ -159,7 +159,7 @@ func TestParsePackageLockV2(t *testing.T) { PURL: "pkg:npm/%40types/scheduler@0.16.2", Language: pkg.JavaScript, Type: pkg.NpmPkg, - Licenses: []string{"MIT"}, + Licenses: []pkg.License{}, MetadataType: "NpmPackageLockJsonMetadata", Metadata: pkg.NpmPackageLockJSONMetadata{Resolved: "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", Integrity: "sha1-GmL4lSVyPd4kuhsBsJK/XfitTTk="}, }, @@ -169,7 +169,7 @@ func TestParsePackageLockV2(t *testing.T) { PURL: "pkg:npm/csstype@3.1.0", Language: pkg.JavaScript, Type: pkg.NpmPkg, - Licenses: []string{"MIT"}, + Licenses: []pkg.License{}, MetadataType: "NpmPackageLockJsonMetadata", Metadata: pkg.NpmPackageLockJSONMetadata{Resolved: "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz", Integrity: "sha1-TdysNxjXh8+d8NG30VAzklyPKfI="}, }, @@ -274,7 +274,7 @@ func TestParsePackageLockAlias(t *testing.T) { PURL: "pkg:npm/alias-check@1.0.0", Language: pkg.JavaScript, Type: pkg.NpmPkg, - Licenses: []string{"ISC"}, + Licenses: []pkg.License{}, MetadataType: "NpmPackageLockJsonMetadata", Metadata: pkg.NpmPackageLockJSONMetadata{}, } @@ -305,7 +305,7 @@ func TestParsePackageLockLicenseWithArray(t *testing.T) { { Name: "tmp", Version: "1.0.0", - Licenses: []string{"ISC"}, + Licenses: []pkg.License{}, Language: pkg.JavaScript, Type: pkg.NpmPkg, PURL: "pkg:npm/tmp@1.0.0", @@ -315,7 +315,7 @@ func TestParsePackageLockLicenseWithArray(t *testing.T) { { Name: "pause-stream", Version: "0.0.11", - Licenses: []string{"MIT", "Apache2"}, + Licenses: []pkg.License{}, Language: pkg.JavaScript, Type: pkg.NpmPkg, PURL: "pkg:npm/pause-stream@0.0.11", @@ -325,7 +325,7 @@ func TestParsePackageLockLicenseWithArray(t *testing.T) { { Name: "through", Version: "2.3.8", - Licenses: []string{"MIT"}, + Licenses: []pkg.License{}, Language: pkg.JavaScript, Type: pkg.NpmPkg, PURL: "pkg:npm/through@2.3.8", diff --git a/syft/pkg/cataloger/php/parse_composer_lock_test.go b/syft/pkg/cataloger/php/parse_composer_lock_test.go index f4704970709..8d33ca71c6a 100644 --- a/syft/pkg/cataloger/php/parse_composer_lock_test.go +++ b/syft/pkg/cataloger/php/parse_composer_lock_test.go @@ -9,6 +9,7 @@ import ( "github.com/anchore/syft/syft/source" ) +// TODO: add license parsing onto the package func TestParseComposerFileLock(t *testing.T) { var expectedRelationships []artifact.Relationship fixture := "test-fixtures/composer.lock" @@ -37,9 +38,6 @@ func TestParseComposerFileLock(t *testing.T) { }, Type: "library", NotificationURL: "https://packagist.org/downloads/", - License: []string{ - "MIT", - }, Authors: []pkg.PhpComposerAuthors{ { Name: "Pierrick Charron", @@ -91,9 +89,6 @@ func TestParseComposerFileLock(t *testing.T) { }, Type: "library", NotificationURL: "https://packagist.org/downloads/", - License: []string{ - "MIT", - }, Authors: []pkg.PhpComposerAuthors{ { Name: "alcaeus", diff --git a/syft/pkg/cataloger/php/parse_installed_json_test.go b/syft/pkg/cataloger/php/parse_installed_json_test.go index b2788f506fd..d1a8b3977f5 100644 --- a/syft/pkg/cataloger/php/parse_installed_json_test.go +++ b/syft/pkg/cataloger/php/parse_installed_json_test.go @@ -24,6 +24,7 @@ func TestParseInstalledJsonComposerV1(t *testing.T) { Language: pkg.PHP, Type: pkg.PhpComposerPkg, MetadataType: pkg.PhpComposerJSONMetadataType, + Licenses: []pkg.License{}, Metadata: pkg.PhpComposerJSONMetadata{ Name: "asm89/stack-cors", Version: "1.3.0", @@ -49,9 +50,6 @@ func TestParseInstalledJsonComposerV1(t *testing.T) { Time: "2019-12-24T22:41:47+00:00", Type: "library", NotificationURL: "https://packagist.org/downloads/", - License: []string{ - "MIT", - }, Authors: []pkg.PhpComposerAuthors{ { Name: "Alexander", @@ -73,6 +71,7 @@ func TestParseInstalledJsonComposerV1(t *testing.T) { PURL: "pkg:composer/behat/mink@v1.8.1", Language: pkg.PHP, Type: pkg.PhpComposerPkg, + Licenses: []pkg.License{}, MetadataType: pkg.PhpComposerJSONMetadataType, Metadata: pkg.PhpComposerJSONMetadata{ Name: "behat/mink", @@ -106,9 +105,6 @@ func TestParseInstalledJsonComposerV1(t *testing.T) { Time: "2020-03-11T15:45:53+00:00", Type: "library", NotificationURL: "https://packagist.org/downloads/", - License: []string{ - "MIT", - }, Authors: []pkg.PhpComposerAuthors{ { Name: "Konstantin Kudryashov", diff --git a/syft/pkg/cataloger/portage/cataloger_test.go b/syft/pkg/cataloger/portage/cataloger_test.go index 3f4494b29c1..711dcafdd4a 100644 --- a/syft/pkg/cataloger/portage/cataloger_test.go +++ b/syft/pkg/cataloger/portage/cataloger_test.go @@ -23,7 +23,7 @@ func TestPortageCataloger(t *testing.T) { source.NewLocation("var/db/pkg/app-containers/skopeo-1.5.1/LICENSE"), source.NewLocation("var/db/pkg/app-containers/skopeo-1.5.1/SIZE"), ), - Licenses: []string{"Apache-2.0", "BSD", "BSD-2", "CC-BY-SA-4.0", "ISC", "MIT"}, + Licenses: []pkg.License{}, Type: pkg.PortagePkg, MetadataType: pkg.PortageMetadataType, Metadata: pkg.PortageMetadata{ diff --git a/syft/pkg/cataloger/portage/parse_portage_contents.go b/syft/pkg/cataloger/portage/parse_portage_contents.go index 43609b6d574..d78a68b5b0d 100644 --- a/syft/pkg/cataloger/portage/parse_portage_contents.go +++ b/syft/pkg/cataloger/portage/parse_portage_contents.go @@ -9,7 +9,6 @@ import ( "strconv" "strings" - "github.com/anchore/syft/internal" "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/file" @@ -90,34 +89,34 @@ func addFiles(resolver source.FileResolver, dbLocation source.Location, p *pkg.P p.Locations.Add(dbLocation) } -func addLicenses(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) { - parentPath := filepath.Dir(dbLocation.RealPath) - - location := resolver.RelativeFileByPath(dbLocation, path.Join(parentPath, "LICENSE")) - - if location == nil { - return - } - - licenseReader, err := resolver.FileContentsByLocation(*location) - if err != nil { - log.WithFields("path", dbLocation.RealPath).Warnf("failed to fetch portage LICENSE: %+v", err) - return - } - - findings := internal.NewStringSet() - scanner := bufio.NewScanner(licenseReader) - scanner.Split(bufio.ScanWords) - for scanner.Scan() { - token := scanner.Text() - if token != "||" && token != "(" && token != ")" { - findings.Add(token) - } - } - _ = findings.ToSlice() - // sort.Strings(licenses) - p.Locations.Add(*location) -} +// func addLicenses(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) { +// parentPath := filepath.Dir(dbLocation.RealPath) +// +// location := resolver.RelativeFileByPath(dbLocation, path.Join(parentPath, "LICENSE")) +// +// if location == nil { +// return +// } +// +// licenseReader, err := resolver.FileContentsByLocation(*location) +// if err != nil { +// log.WithFields("path", dbLocation.RealPath).Warnf("failed to fetch portage LICENSE: %+v", err) +// return +// } +// +// findings := internal.NewStringSet() +// scanner := bufio.NewScanner(licenseReader) +// scanner.Split(bufio.ScanWords) +// for scanner.Scan() { +// token := scanner.Text() +// if token != "||" && token != "(" && token != ")" { +// findings.Add(token) +// } +// } +// _ = findings.ToSlice() +// // sort.Strings(licenses) +// p.Locations.Add(*location) +//} func addSize(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) { parentPath := filepath.Dir(dbLocation.RealPath) diff --git a/syft/pkg/cataloger/python/cataloger_test.go b/syft/pkg/cataloger/python/cataloger_test.go index 8515daf4b41..68d7ae51e5b 100644 --- a/syft/pkg/cataloger/python/cataloger_test.go +++ b/syft/pkg/cataloger/python/cataloger_test.go @@ -40,18 +40,18 @@ func Test_PackageCataloger(t *testing.T) { "test-fixtures/egg-info/top_level.txt", }, expectedPackage: pkg.Package{ - Name: "requests", - Version: "2.22.0", - PURL: "pkg:pypi/requests@2.22.0", - Type: pkg.PythonPkg, - Language: pkg.Python, - Licenses: []string{"Apache 2.0"}, + Name: "requests", + Version: "2.22.0", + PURL: "pkg:pypi/requests@2.22.0", + Type: pkg.PythonPkg, + Language: pkg.Python, + Licenses: []pkg.License{}, + //Licenses: []string{"Apache 2.0"}, FoundBy: "python-package-cataloger", MetadataType: pkg.PythonPackageMetadataType, Metadata: pkg.PythonPackageMetadata{ Name: "requests", Version: "2.22.0", - License: "Apache 2.0", Platform: "UNKNOWN", Author: "Kenneth Reitz", AuthorEmail: "me@kennethreitz.org", @@ -77,18 +77,18 @@ func Test_PackageCataloger(t *testing.T) { "test-fixtures/dist-info/direct_url.json", }, expectedPackage: pkg.Package{ - Name: "Pygments", - Version: "2.6.1", - PURL: "pkg:pypi/Pygments@2.6.1?vcs_url=git+https://github.com/python-test/test.git%40aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - Type: pkg.PythonPkg, - Language: pkg.Python, - Licenses: []string{"BSD License"}, + Name: "Pygments", + Version: "2.6.1", + PURL: "pkg:pypi/Pygments@2.6.1?vcs_url=git+https://github.com/python-test/test.git%40aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + Type: pkg.PythonPkg, + Language: pkg.Python, + Licenses: []pkg.License{}, + //Licenses: []string{"BSD License"}, FoundBy: "python-package-cataloger", MetadataType: pkg.PythonPackageMetadataType, Metadata: pkg.PythonPackageMetadata{ Name: "Pygments", Version: "2.6.1", - License: "BSD License", Platform: "any", Author: "Georg Brandl", AuthorEmail: "georg@python.org", @@ -114,18 +114,18 @@ func Test_PackageCataloger(t *testing.T) { "test-fixtures/malformed-record/dist-info/RECORD", }, expectedPackage: pkg.Package{ - Name: "Pygments", - Version: "2.6.1", - PURL: "pkg:pypi/Pygments@2.6.1", - Type: pkg.PythonPkg, - Language: pkg.Python, - Licenses: []string{"BSD License"}, + Name: "Pygments", + Version: "2.6.1", + PURL: "pkg:pypi/Pygments@2.6.1", + Type: pkg.PythonPkg, + Language: pkg.Python, + Licenses: []pkg.License{}, + //Licenses: []string{"BSD License"}, FoundBy: "python-package-cataloger", MetadataType: pkg.PythonPackageMetadataType, Metadata: pkg.PythonPackageMetadata{ Name: "Pygments", Version: "2.6.1", - License: "BSD License", Platform: "any", Author: "Georg Brandl", AuthorEmail: "georg@python.org", @@ -145,18 +145,18 @@ func Test_PackageCataloger(t *testing.T) { name: "partial dist-info directory", fixtures: []string{"test-fixtures/partial.dist-info/METADATA"}, expectedPackage: pkg.Package{ - Name: "Pygments", - Version: "2.6.1", - PURL: "pkg:pypi/Pygments@2.6.1", - Type: pkg.PythonPkg, - Language: pkg.Python, - Licenses: []string{"BSD License"}, + Name: "Pygments", + Version: "2.6.1", + PURL: "pkg:pypi/Pygments@2.6.1", + Type: pkg.PythonPkg, + Language: pkg.Python, + Licenses: []pkg.License{}, + //Licenses: []string{"BSD License"}, FoundBy: "python-package-cataloger", MetadataType: pkg.PythonPackageMetadataType, Metadata: pkg.PythonPackageMetadata{ Name: "Pygments", Version: "2.6.1", - License: "BSD License", Platform: "any", Author: "Georg Brandl", AuthorEmail: "georg@python.org", @@ -168,18 +168,18 @@ func Test_PackageCataloger(t *testing.T) { name: "egg-info regular file", fixtures: []string{"test-fixtures/test.egg-info"}, expectedPackage: pkg.Package{ - Name: "requests", - Version: "2.22.0", - PURL: "pkg:pypi/requests@2.22.0", - Type: pkg.PythonPkg, - Language: pkg.Python, - Licenses: []string{"Apache 2.0"}, + Name: "requests", + Version: "2.22.0", + PURL: "pkg:pypi/requests@2.22.0", + Type: pkg.PythonPkg, + Language: pkg.Python, + Licenses: []pkg.License{}, + //Licenses: []string{"Apache 2.0"}, FoundBy: "python-package-cataloger", MetadataType: pkg.PythonPackageMetadataType, Metadata: pkg.PythonPackageMetadata{ Name: "requests", Version: "2.22.0", - License: "Apache 2.0", Platform: "UNKNOWN", Author: "Kenneth Reitz", AuthorEmail: "me@kennethreitz.org", diff --git a/syft/pkg/cataloger/rpm/parse_rpm_db_test.go b/syft/pkg/cataloger/rpm/parse_rpm_db_test.go index 111195fd34b..4d71b9846b6 100644 --- a/syft/pkg/cataloger/rpm/parse_rpm_db_test.go +++ b/syft/pkg/cataloger/rpm/parse_rpm_db_test.go @@ -97,7 +97,7 @@ func TestParseRpmDB(t *testing.T) { Locations: source.NewLocationSet(source.NewLocation("test-fixtures/Packages")), Type: pkg.RpmPkg, MetadataType: pkg.RpmMetadataType, - Licenses: []string{"MIT"}, + Licenses: []pkg.License{}, Metadata: pkg.RpmMetadata{ Name: "dive", Epoch: nil, @@ -106,7 +106,6 @@ func TestParseRpmDB(t *testing.T) { Version: "0.9.2", SourceRpm: "dive-0.9.2-1.src.rpm", Size: 12406784, - License: "MIT", Vendor: "", Files: []pkg.RpmdbFileRecord{}, }, @@ -125,7 +124,7 @@ func TestParseRpmDB(t *testing.T) { Locations: source.NewLocationSet(source.NewLocation("test-fixtures/Packages")), Type: pkg.RpmPkg, MetadataType: pkg.RpmMetadataType, - Licenses: []string{"MIT"}, + Licenses: []pkg.License{}, Metadata: pkg.RpmMetadata{ Name: "dive", Epoch: nil, @@ -134,7 +133,6 @@ func TestParseRpmDB(t *testing.T) { Version: "0.9.2", SourceRpm: "dive-0.9.2-1.src.rpm", Size: 12406784, - License: "MIT", Vendor: "", Files: []pkg.RpmdbFileRecord{ { diff --git a/syft/pkg/cataloger/rpm/parse_rpm_test.go b/syft/pkg/cataloger/rpm/parse_rpm_test.go index 1d082e69392..84c8988c8d3 100644 --- a/syft/pkg/cataloger/rpm/parse_rpm_test.go +++ b/syft/pkg/cataloger/rpm/parse_rpm_test.go @@ -25,7 +25,7 @@ func TestParseRpmFiles(t *testing.T) { FoundBy: "rpm-file-cataloger", Type: pkg.RpmPkg, MetadataType: pkg.RpmMetadataType, - Licenses: []string{"MIT"}, + Licenses: []pkg.License{}, Metadata: pkg.RpmMetadata{ Name: "abc", Epoch: intRef(0), @@ -34,7 +34,6 @@ func TestParseRpmFiles(t *testing.T) { Version: "1.01", SourceRpm: "abc-1.01-9.hg20160905.el7.src.rpm", Size: 17396, - License: "MIT", Vendor: "Fedora Project", Files: []pkg.RpmdbFileRecord{ {"/usr/bin/abc", 33261, 7120, file.Digest{"sha256", "8f8495a65c66762b60afa0c3949d81b275ca6fa0601696caba5af762f455d0b9"}, "root", "root", ""}, @@ -53,7 +52,7 @@ func TestParseRpmFiles(t *testing.T) { FoundBy: "rpm-file-cataloger", Type: pkg.RpmPkg, MetadataType: pkg.RpmMetadataType, - Licenses: []string{"Public Domain"}, + Licenses: []pkg.License{}, Metadata: pkg.RpmMetadata{ Name: "zork", Epoch: intRef(0), @@ -62,7 +61,6 @@ func TestParseRpmFiles(t *testing.T) { Version: "1.0.3", SourceRpm: "zork-1.0.3-1.el7.src.rpm", Size: 262367, - License: "Public Domain", Vendor: "Fedora Project", Files: []pkg.RpmdbFileRecord{ {"/usr/bin/zork", 33261, 115440, file.Digest{"sha256", "31b2ffc20b676a8fff795a45308f584273b9c47e8f7e196b4f36220b2734b472"}, "root", "root", ""}, diff --git a/syft/pkg/cataloger/ruby/parse_gemspec_test.go b/syft/pkg/cataloger/ruby/parse_gemspec_test.go index 6b378ce351c..327fbea9368 100644 --- a/syft/pkg/cataloger/ruby/parse_gemspec_test.go +++ b/syft/pkg/cataloger/ruby/parse_gemspec_test.go @@ -19,7 +19,7 @@ func TestParseGemspec(t *testing.T) { PURL: "pkg:gem/bundler@2.1.4", Locations: locations, Type: pkg.GemPkg, - Licenses: []string{"MIT"}, + Licenses: []pkg.License{}, Language: pkg.Ruby, MetadataType: pkg.GemMetadataType, Metadata: pkg.GemMetadata{ @@ -27,7 +27,6 @@ func TestParseGemspec(t *testing.T) { Version: "2.1.4", Files: []string{"exe/bundle", "exe/bundler"}, Authors: []string{"André Arko", "Samuel Giddins", "Colby Swandale", "Hiroshi Shibata", "David Rodríguez", "Grey Baker", "Stephanie Morillo", "Chris Morris", "James Wen", "Tim Moore", "André Medeiros", "Jessica Lynn Suttles", "Terence Lee", "Carl Lerche", "Yehuda Katz"}, - Licenses: []string{"MIT"}, Homepage: "https://bundler.io", }, } diff --git a/syft/pkg/cataloger/sbom/cataloger_test.go b/syft/pkg/cataloger/sbom/cataloger_test.go index ea372d50ccc..5e770f3e4d4 100644 --- a/syft/pkg/cataloger/sbom/cataloger_test.go +++ b/syft/pkg/cataloger/sbom/cataloger_test.go @@ -39,7 +39,7 @@ func Test_parseSBOM(t *testing.T) { Version: "3.2.0-r23", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []string{"GPL-2.0-only"}, + Licenses: []pkg.License{}, FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/alpine-baselayout@3.2.0-r23?arch=x86_64&upstream=alpine-baselayout&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -56,7 +56,7 @@ func Test_parseSBOM(t *testing.T) { Version: "3.2.0-r23", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []string{"GPL-2.0-only"}, + Licenses: []pkg.License{}, FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/alpine-baselayout-data@3.2.0-r23?arch=x86_64&upstream=alpine-baselayout&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -77,7 +77,7 @@ func Test_parseSBOM(t *testing.T) { Version: "2.4-r1", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []string{"MIT"}, + Licenses: []pkg.License{}, FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/alpine-keys@2.4-r1?arch=x86_64&upstream=alpine-keys&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -94,7 +94,7 @@ func Test_parseSBOM(t *testing.T) { Version: "2.12.9-r3", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []string{"GPL-2.0-only"}, + Licenses: []pkg.License{}, FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/apk-tools@2.12.9-r3?arch=x86_64&upstream=apk-tools&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -111,7 +111,7 @@ func Test_parseSBOM(t *testing.T) { Version: "1.35.0-r17", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []string{"GPL-2.0-only"}, + Licenses: []pkg.License{}, FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/busybox@1.35.0-r17?arch=x86_64&upstream=busybox&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -123,7 +123,7 @@ func Test_parseSBOM(t *testing.T) { Version: "20220614-r0", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []string{"MPL-2.0", "AND", "MIT"}, + Licenses: []pkg.License{}, FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/ca-certificates-bundle@20220614-r0?arch=x86_64&upstream=ca-certificates&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -144,7 +144,7 @@ func Test_parseSBOM(t *testing.T) { Version: "0.7.2-r3", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []string{"BSD-2-Clause", "AND", "BSD-3-Clause"}, + Licenses: []pkg.License{}, FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/libc-utils@0.7.2-r3?arch=x86_64&upstream=libc-dev&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -161,7 +161,7 @@ func Test_parseSBOM(t *testing.T) { Version: "1.1.1s-r0", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []string{"OpenSSL"}, + Licenses: []pkg.License{}, FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/libcrypto1.1@1.1.1s-r0?arch=x86_64&upstream=openssl&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -173,7 +173,7 @@ func Test_parseSBOM(t *testing.T) { Version: "1.1.1s-r0", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []string{"OpenSSL"}, + Licenses: []pkg.License{}, FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/libssl1.1@1.1.1s-r0?arch=x86_64&upstream=openssl&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -185,7 +185,7 @@ func Test_parseSBOM(t *testing.T) { Version: "1.2.3-r1", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []string{"MIT"}, + Licenses: []pkg.License{}, FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/musl@1.2.3-r1?arch=x86_64&upstream=musl&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -197,7 +197,7 @@ func Test_parseSBOM(t *testing.T) { Version: "1.2.3-r1", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []string{"MIT", "BSD", "GPL2+"}, + Licenses: []pkg.License{}, FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/musl-utils@1.2.3-r1?arch=x86_64&upstream=musl&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -214,7 +214,7 @@ func Test_parseSBOM(t *testing.T) { Version: "1.3.4-r0", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []string{"GPL-2.0-only"}, + Licenses: []pkg.License{}, FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/scanelf@1.3.4-r0?arch=x86_64&upstream=pax-utils&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -226,7 +226,7 @@ func Test_parseSBOM(t *testing.T) { Version: "1.35.0-r17", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []string{"GPL-2.0-only"}, + Licenses: []pkg.License{}, FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/ssl_client@1.35.0-r17?arch=x86_64&upstream=busybox&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -243,7 +243,7 @@ func Test_parseSBOM(t *testing.T) { Version: "1.2.12-r3", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []string{"Zlib"}, + Licenses: []pkg.License{}, FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/zlib@1.2.12-r3?arch=x86_64&upstream=zlib&distro=alpine-3.16.3", CPEs: mustCPEs( diff --git a/syft/pkg/package_test.go b/syft/pkg/package_test.go index 1b96cd38905..72137a89b23 100644 --- a/syft/pkg/package_test.go +++ b/syft/pkg/package_test.go @@ -37,7 +37,6 @@ func TestIDUniqueness(t *testing.T) { Metadata: PythonPackageMetadata{ Name: "pi", Version: "3.14", - License: "cc0-1.0", Author: "Archimedes", AuthorEmail: "Archimedes@circles.io", Platform: "universe", @@ -66,7 +65,6 @@ func TestIDUniqueness(t *testing.T) { pkg.Metadata = PythonPackageMetadata{ Name: "pi", Version: "3.14", - License: "cc0-1.0", Author: "Archimedes", AuthorEmail: "Archimedes@circles.io", Platform: "universe", @@ -250,7 +248,6 @@ func TestPackage_Merge(t *testing.T) { Metadata: PythonPackageMetadata{ Name: "pi", Version: "3.14", - License: "cc0-1.0", Author: "Archimedes", AuthorEmail: "Archimedes@circles.io", Platform: "universe", @@ -274,7 +271,6 @@ func TestPackage_Merge(t *testing.T) { Metadata: PythonPackageMetadata{ Name: "pi", Version: "3.14", - License: "cc0-1.0", Author: "Archimedes", AuthorEmail: "Archimedes@circles.io", Platform: "universe", @@ -300,7 +296,6 @@ func TestPackage_Merge(t *testing.T) { Metadata: PythonPackageMetadata{ Name: "pi", Version: "3.14", - License: "cc0-1.0", Author: "Archimedes", AuthorEmail: "Archimedes@circles.io", Platform: "universe", @@ -327,7 +322,6 @@ func TestPackage_Merge(t *testing.T) { Metadata: PythonPackageMetadata{ Name: "pi", Version: "3.14", - License: "cc0-1.0", Author: "Archimedes", AuthorEmail: "Archimedes@circles.io", Platform: "universe", @@ -351,7 +345,6 @@ func TestPackage_Merge(t *testing.T) { Metadata: PythonPackageMetadata{ Name: "pi", Version: "3.14", - License: "cc0-1.0", Author: "Archimedes", AuthorEmail: "Archimedes@circles.io", Platform: "universe", From d13cb0845be665a468a20e7b2ef0fd5770fa84ae Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Tue, 11 Apr 2023 12:14:40 -0400 Subject: [PATCH 09/99] test: fix panic in alpm test Signed-off-by: Christopher Phillips --- syft/pkg/cataloger/alpm/cataloger_test.go | 1 - syft/pkg/cataloger/alpm/parse_alpm_db.go | 15 +++++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/syft/pkg/cataloger/alpm/cataloger_test.go b/syft/pkg/cataloger/alpm/cataloger_test.go index be2ca79e7d1..f505da66c77 100644 --- a/syft/pkg/cataloger/alpm/cataloger_test.go +++ b/syft/pkg/cataloger/alpm/cataloger_test.go @@ -13,7 +13,6 @@ import ( ) func TestAlpmCataloger(t *testing.T) { - expectedPkgs := []pkg.Package{ { Name: "gmp", diff --git a/syft/pkg/cataloger/alpm/parse_alpm_db.go b/syft/pkg/cataloger/alpm/parse_alpm_db.go index 0ecc17be227..cc10e0040e9 100644 --- a/syft/pkg/cataloger/alpm/parse_alpm_db.go +++ b/syft/pkg/cataloger/alpm/parse_alpm_db.go @@ -131,7 +131,7 @@ func getFileReader(path string, resolver source.FileResolver) (io.Reader, error) } func parseDatabase(b *bufio.Scanner) (*alpmData, error) { - var entry alpmData + var entry pkg.AlpmMetadata var err error pkgFields := make(map[string]interface{}) for b.Scan() { @@ -183,6 +183,7 @@ func parseDatabase(b *bufio.Scanner) (*alpmData, error) { pkgFields[key] = value } } + if err := mapstructure.Decode(pkgFields, &entry); err != nil { return nil, fmt.Errorf("unable to parse ALPM metadata: %w", err) } @@ -193,7 +194,17 @@ func parseDatabase(b *bufio.Scanner) (*alpmData, error) { if entry.Backup == nil { entry.Backup = make([]pkg.AlpmFileRecord, 0) } - return &entry, nil + + var lic string + // TODO: this value can be a list of licenses separated by /n + if l, ok := pkgFields["license"]; ok { + lic = l.(string) + } + + return &alpmData{ + lic, + entry, + }, nil } func parseMtree(r io.Reader) ([]pkg.AlpmFileRecord, error) { From 4b2bc13b17b1e1659f2ff0192d7c2a0376b6b602 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Tue, 11 Apr 2023 12:32:13 -0400 Subject: [PATCH 10/99] fix: update license construction alpm Signed-off-by: Christopher Phillips --- syft/pkg/cataloger/alpm/package.go | 7 ++++- syft/pkg/cataloger/alpm/package_test.go | 12 +++---- syft/pkg/cataloger/alpm/parse_alpm_db.go | 40 +++++++++++++++++------- 3 files changed, 41 insertions(+), 18 deletions(-) diff --git a/syft/pkg/cataloger/alpm/package.go b/syft/pkg/cataloger/alpm/package.go index 92b4c7572c9..f06d761d2d7 100644 --- a/syft/pkg/cataloger/alpm/package.go +++ b/syft/pkg/cataloger/alpm/package.go @@ -15,11 +15,16 @@ func newPackage(m alpmData, release *linux.Release, locations ...source.Location licenseLocation = locations[0] } + var licenses []pkg.License + for _, l := range m.Licenses { + licenses = append(licenses, pkg.NewLicense(l, "", licenseLocation)) + } + p := pkg.Package{ Name: m.Package, Version: m.Version, Locations: source.NewLocationSet(locations...), - Licenses: []pkg.License{pkg.NewLicense(m.License, "", licenseLocation)}, + Licenses: licenses, Type: pkg.AlpmPkg, PURL: packageURL(m, release), MetadataType: pkg.AlpmMetadataType, diff --git a/syft/pkg/cataloger/alpm/package_test.go b/syft/pkg/cataloger/alpm/package_test.go index e4ac1bbd2c6..813e18be6ee 100644 --- a/syft/pkg/cataloger/alpm/package_test.go +++ b/syft/pkg/cataloger/alpm/package_test.go @@ -20,7 +20,7 @@ func Test_PackageURL(t *testing.T) { { name: "bad distro id", metadata: alpmData{ - "", + []string{""}, pkg.AlpmMetadata{ Package: "p", Version: "v", @@ -36,7 +36,7 @@ func Test_PackageURL(t *testing.T) { { name: "gocase", metadata: alpmData{ - "", + []string{""}, pkg.AlpmMetadata{ Package: "p", Version: "v", @@ -52,7 +52,7 @@ func Test_PackageURL(t *testing.T) { { name: "missing architecture", metadata: alpmData{ - "", + []string{""}, pkg.AlpmMetadata{ Package: "p", Version: "v", @@ -65,7 +65,7 @@ func Test_PackageURL(t *testing.T) { }, { metadata: alpmData{ - "", + []string{""}, pkg.AlpmMetadata{ Package: "python", Version: "3.10.0", @@ -80,7 +80,7 @@ func Test_PackageURL(t *testing.T) { }, { metadata: alpmData{ - "", + []string{""}, pkg.AlpmMetadata{ Package: "g plus plus", Version: "v84", @@ -96,7 +96,7 @@ func Test_PackageURL(t *testing.T) { { name: "add source information as qualifier", metadata: alpmData{ - "", + []string{""}, pkg.AlpmMetadata{ Package: "p", Version: "v", diff --git a/syft/pkg/cataloger/alpm/parse_alpm_db.go b/syft/pkg/cataloger/alpm/parse_alpm_db.go index cc10e0040e9..44e68fbfa24 100644 --- a/syft/pkg/cataloger/alpm/parse_alpm_db.go +++ b/syft/pkg/cataloger/alpm/parse_alpm_db.go @@ -32,7 +32,7 @@ var ( ) type alpmData struct { - License string `mapstructure:"license" json:"license"` + Licenses []string pkg.AlpmMetadata } @@ -131,7 +131,6 @@ func getFileReader(path string, resolver source.FileResolver) (io.Reader, error) } func parseDatabase(b *bufio.Scanner) (*alpmData, error) { - var entry pkg.AlpmMetadata var err error pkgFields := make(map[string]interface{}) for b.Scan() { @@ -184,26 +183,45 @@ func parseDatabase(b *bufio.Scanner) (*alpmData, error) { } } + return parsePkgFiles(pkgFields) +} + +func parsePkgFiles(pkgFields map[string]interface{}) (*alpmData, error) { + var ( + entry pkg.AlpmMetadata + licenses []string + ) + + if v, ok := pkgFields["license"]; ok { + // split the license string into a list of licenses new line + ls := strings.Split(v.(string), "\n") + for _, l := range ls { + // remove any leading/trailing whitespace + l = strings.TrimSpace(l) + // skip empty strings + if l == "" { + continue + } + // add the license to the list + licenses = append(licenses, l) + } + } + if err := mapstructure.Decode(pkgFields, &entry); err != nil { return nil, fmt.Errorf("unable to parse ALPM metadata: %w", err) } - if entry.Package == "" && len(entry.Files) == 0 && len(entry.Backup) == 0 { - return nil, nil - } if entry.Backup == nil { entry.Backup = make([]pkg.AlpmFileRecord, 0) } - var lic string - // TODO: this value can be a list of licenses separated by /n - if l, ok := pkgFields["license"]; ok { - lic = l.(string) + if entry.Package == "" && len(entry.Files) == 0 && len(entry.Backup) == 0 { + return nil, nil } return &alpmData{ - lic, - entry, + AlpmMetadata: entry, + Licenses: licenses, }, nil } From b25b2889b4d2673eb7abc5dea760cbd570e1d4d3 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Wed, 12 Apr 2023 10:47:12 -0400 Subject: [PATCH 11/99] fix: alpm license refactor Signed-off-by: Christopher Phillips --- syft/pkg/cataloger/alpm/package.go | 31 ++++++++++++---- syft/pkg/cataloger/alpm/package_test.go | 26 +++++++------- syft/pkg/cataloger/alpm/parse_alpm_db.go | 46 +++++++----------------- 3 files changed, 51 insertions(+), 52 deletions(-) diff --git a/syft/pkg/cataloger/alpm/package.go b/syft/pkg/cataloger/alpm/package.go index f06d761d2d7..c635e5691c8 100644 --- a/syft/pkg/cataloger/alpm/package.go +++ b/syft/pkg/cataloger/alpm/package.go @@ -1,13 +1,15 @@ package alpm import ( + "strings" + "github.com/anchore/packageurl-go" "github.com/anchore/syft/syft/linux" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/source" ) -func newPackage(m alpmData, release *linux.Release, locations ...source.Location) pkg.Package { +func newPackage(m *parsedData, release *linux.Release, locations ...source.Location) pkg.Package { // ALPM only passes a single location // We use this as the "declared" license location var licenseLocation source.Location @@ -15,9 +17,13 @@ func newPackage(m alpmData, release *linux.Release, locations ...source.Location licenseLocation = locations[0] } - var licenses []pkg.License - for _, l := range m.Licenses { - licenses = append(licenses, pkg.NewLicense(l, "", licenseLocation)) + // default to empty list; not nil field + licenses := make([]pkg.License, 0) + licenseCandidates := strings.Split(m.Licenses, "\n") + for _, l := range licenseCandidates { + if l != "" { + licenses = append(licenses, pkg.NewLicense(l, "", licenseLocation)) + } } p := pkg.Package{ @@ -28,13 +34,26 @@ func newPackage(m alpmData, release *linux.Release, locations ...source.Location Type: pkg.AlpmPkg, PURL: packageURL(m, release), MetadataType: pkg.AlpmMetadataType, - Metadata: m, + Metadata: pkg.AlpmMetadata{ + BasePackage: m.BasePackage, + Package: m.Package, + Version: m.Version, + Description: m.Description, + Architecture: m.Architecture, + Size: m.Size, + Packager: m.Packager, + URL: m.URL, + Validation: m.Validation, + Reason: m.Reason, + Files: m.Files, + Backup: m.Backup, + }, } p.SetID() return p } -func packageURL(m alpmData, distro *linux.Release) string { +func packageURL(m *parsedData, distro *linux.Release) string { if distro == nil || distro.ID != "arch" { // note: there is no namespace variation (like with debian ID_LIKE for ubuntu ID, for example) return "" diff --git a/syft/pkg/cataloger/alpm/package_test.go b/syft/pkg/cataloger/alpm/package_test.go index 813e18be6ee..cf0eeb3a40b 100644 --- a/syft/pkg/cataloger/alpm/package_test.go +++ b/syft/pkg/cataloger/alpm/package_test.go @@ -13,14 +13,14 @@ import ( func Test_PackageURL(t *testing.T) { tests := []struct { name string - metadata alpmData + metadata *parsedData distro linux.Release expected string }{ { name: "bad distro id", - metadata: alpmData{ - []string{""}, + metadata: &parsedData{ + "", pkg.AlpmMetadata{ Package: "p", Version: "v", @@ -35,8 +35,8 @@ func Test_PackageURL(t *testing.T) { }, { name: "gocase", - metadata: alpmData{ - []string{""}, + metadata: &parsedData{ + "", pkg.AlpmMetadata{ Package: "p", Version: "v", @@ -51,8 +51,8 @@ func Test_PackageURL(t *testing.T) { }, { name: "missing architecture", - metadata: alpmData{ - []string{""}, + metadata: &parsedData{ + "", pkg.AlpmMetadata{ Package: "p", Version: "v", @@ -64,8 +64,8 @@ func Test_PackageURL(t *testing.T) { expected: "pkg:alpm/arch/p@v?distro=arch", }, { - metadata: alpmData{ - []string{""}, + metadata: &parsedData{ + "", pkg.AlpmMetadata{ Package: "python", Version: "3.10.0", @@ -79,8 +79,8 @@ func Test_PackageURL(t *testing.T) { expected: "pkg:alpm/arch/python@3.10.0?arch=any&distro=arch-rolling", }, { - metadata: alpmData{ - []string{""}, + metadata: &parsedData{ + "", pkg.AlpmMetadata{ Package: "g plus plus", Version: "v84", @@ -95,8 +95,8 @@ func Test_PackageURL(t *testing.T) { }, { name: "add source information as qualifier", - metadata: alpmData{ - []string{""}, + metadata: &parsedData{ + "", pkg.AlpmMetadata{ Package: "p", Version: "v", diff --git a/syft/pkg/cataloger/alpm/parse_alpm_db.go b/syft/pkg/cataloger/alpm/parse_alpm_db.go index 44e68fbfa24..0e548240006 100644 --- a/syft/pkg/cataloger/alpm/parse_alpm_db.go +++ b/syft/pkg/cataloger/alpm/parse_alpm_db.go @@ -31,13 +31,13 @@ var ( } ) -type alpmData struct { - Licenses []string - pkg.AlpmMetadata +type parsedData struct { + Licenses string `mapstructure:"licenses"` + pkg.AlpmMetadata `mapstructure:",squash"` } func parseAlpmDB(resolver source.FileResolver, env *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { - metadata, err := parseAlpmDBEntry(reader) + parsedData, err := parseAlpmDBEntry(reader) if err != nil { return nil, nil, err } @@ -56,7 +56,7 @@ func parseAlpmDB(resolver source.FileResolver, env *generic.Environment, reader // replace the files found the pacman database with the files from the mtree These contain more metadata and // thus more useful. // TODO: probably want to use MTREE and PKGINFO here - metadata.Files = pkgFiles + parsedData.Files = pkgFiles // We only really do this to get any backup database entries from the files database files := filepath.Join(base, "files") @@ -68,19 +68,19 @@ func parseAlpmDB(resolver source.FileResolver, env *generic.Environment, reader if err != nil { return nil, nil, err } else if filesMetadata != nil { - metadata.Backup = filesMetadata.Backup + parsedData.Backup = filesMetadata.Backup } - if metadata.Package == "" { + if parsedData.Package == "" { return nil, nil, nil } return []pkg.Package{ - newPackage(*metadata, env.LinuxRelease, reader.Location), + newPackage(parsedData, env.LinuxRelease, reader.Location), }, nil, nil } -func parseAlpmDBEntry(reader io.Reader) (*alpmData, error) { +func parseAlpmDBEntry(reader io.Reader) (*parsedData, error) { scanner := newScanner(reader) metadata, err := parseDatabase(scanner) if err != nil { @@ -130,7 +130,7 @@ func getFileReader(path string, resolver source.FileResolver) (io.Reader, error) return dbContentReader, nil } -func parseDatabase(b *bufio.Scanner) (*alpmData, error) { +func parseDatabase(b *bufio.Scanner) (*parsedData, error) { var err error pkgFields := make(map[string]interface{}) for b.Scan() { @@ -186,27 +186,11 @@ func parseDatabase(b *bufio.Scanner) (*alpmData, error) { return parsePkgFiles(pkgFields) } -func parsePkgFiles(pkgFields map[string]interface{}) (*alpmData, error) { +func parsePkgFiles(pkgFields map[string]interface{}) (*parsedData, error) { var ( - entry pkg.AlpmMetadata - licenses []string + entry parsedData ) - if v, ok := pkgFields["license"]; ok { - // split the license string into a list of licenses new line - ls := strings.Split(v.(string), "\n") - for _, l := range ls { - // remove any leading/trailing whitespace - l = strings.TrimSpace(l) - // skip empty strings - if l == "" { - continue - } - // add the license to the list - licenses = append(licenses, l) - } - } - if err := mapstructure.Decode(pkgFields, &entry); err != nil { return nil, fmt.Errorf("unable to parse ALPM metadata: %w", err) } @@ -218,11 +202,7 @@ func parsePkgFiles(pkgFields map[string]interface{}) (*alpmData, error) { if entry.Package == "" && len(entry.Files) == 0 && len(entry.Backup) == 0 { return nil, nil } - - return &alpmData{ - AlpmMetadata: entry, - Licenses: licenses, - }, nil + return &entry, nil } func parseMtree(r io.Reader) ([]pkg.AlpmFileRecord, error) { From e499180751549aa70f63cd6f300b0b80d6a3f68e Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Wed, 12 Apr 2023 12:17:08 -0400 Subject: [PATCH 12/99] fix: update apkdb cataloger Signed-off-by: Christopher Phillips --- syft/pkg/cataloger/apkdb/package.go | 19 ++++---- syft/pkg/cataloger/apkdb/package_test.go | 24 ++++++---- syft/pkg/cataloger/apkdb/parse_apk_db.go | 20 ++++---- syft/pkg/cataloger/apkdb/parse_apk_db_test.go | 46 +++++++++++++++---- .../internal/pkgtest/test_generic_parser.go | 3 ++ syft/pkg/license.go | 1 + 6 files changed, 81 insertions(+), 32 deletions(-) diff --git a/syft/pkg/cataloger/apkdb/package.go b/syft/pkg/cataloger/apkdb/package.go index 017161f8465..47fd2e1e182 100644 --- a/syft/pkg/cataloger/apkdb/package.go +++ b/syft/pkg/cataloger/apkdb/package.go @@ -9,13 +9,16 @@ import ( "github.com/anchore/syft/syft/source" ) -func newPackage(d apkData, release *linux.Release, locations ...source.Location) pkg.Package { - // apkdb only passes a single location in its constructor - var licenseLocation source.Location - if len(locations) > 0 { - licenseLocation = locations[0] +func newPackage(d parsedData, release *linux.Release, locations ...source.Location) pkg.Package { + licenses := make([]pkg.License, 0) + if d.License != "" { + // apk packages can have multiple licenses separated by spaces + // ex: MIT BSD GPL2+ + licenseStrings := strings.Split(d.License, " ") + for _, l := range licenseStrings { + licenses = append(licenses, pkg.NewLicense(l, "", d.LicenseLocation)) + } } - licenses := []pkg.License{pkg.NewLicense(d.License, "", licenseLocation)} p := pkg.Package{ Name: d.Package, @@ -25,7 +28,7 @@ func newPackage(d apkData, release *linux.Release, locations ...source.Location) PURL: packageURL(d, release), Type: pkg.ApkPkg, MetadataType: pkg.ApkMetadataType, - Metadata: d, + Metadata: d.ApkMetadata, } p.SetID() @@ -34,7 +37,7 @@ func newPackage(d apkData, release *linux.Release, locations ...source.Location) } // packageURL returns the PURL for the specific Alpine package (see https://github.com/package-url/purl-spec) -func packageURL(m apkData, distro *linux.Release) string { +func packageURL(m parsedData, distro *linux.Release) string { if distro == nil { return "" } diff --git a/syft/pkg/cataloger/apkdb/package_test.go b/syft/pkg/cataloger/apkdb/package_test.go index bf45143ed59..b92bb5acf73 100644 --- a/syft/pkg/cataloger/apkdb/package_test.go +++ b/syft/pkg/cataloger/apkdb/package_test.go @@ -10,19 +10,21 @@ import ( "github.com/anchore/packageurl-go" "github.com/anchore/syft/syft/linux" "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/source" ) func Test_PackageURL(t *testing.T) { tests := []struct { name string - metadata apkData + metadata parsedData distro linux.Release expected string }{ { name: "non-alpine distro", - metadata: apkData{ + metadata: parsedData{ "", + source.Location{}, pkg.ApkMetadata{ Package: "p", Version: "v", @@ -37,8 +39,9 @@ func Test_PackageURL(t *testing.T) { }, { name: "gocase", - metadata: apkData{ + metadata: parsedData{ "", + source.Location{}, pkg.ApkMetadata{ Package: "p", Version: "v", @@ -53,8 +56,9 @@ func Test_PackageURL(t *testing.T) { }, { name: "missing architecture", - metadata: apkData{ + metadata: parsedData{ "", + source.Location{}, pkg.ApkMetadata{ Package: "p", Version: "v", @@ -68,8 +72,9 @@ func Test_PackageURL(t *testing.T) { }, // verify #351 { - metadata: apkData{ + metadata: parsedData{ "", + source.Location{}, pkg.ApkMetadata{ Package: "g++", Version: "v84", @@ -83,8 +88,9 @@ func Test_PackageURL(t *testing.T) { expected: "pkg:apk/alpine/g++@v84?arch=am86&distro=alpine-3.4.6", }, { - metadata: apkData{ + metadata: parsedData{ "", + source.Location{}, pkg.ApkMetadata{ Package: "g plus plus", Version: "v84", @@ -99,8 +105,9 @@ func Test_PackageURL(t *testing.T) { }, { name: "add source information as qualifier", - metadata: apkData{ + metadata: parsedData{ "", + source.Location{}, pkg.ApkMetadata{ Package: "p", Version: "v", @@ -116,8 +123,9 @@ func Test_PackageURL(t *testing.T) { }, { name: "wolfi distro", - metadata: apkData{ + metadata: parsedData{ "", + source.Location{}, pkg.ApkMetadata{ Package: "p", Version: "v", diff --git a/syft/pkg/cataloger/apkdb/parse_apk_db.go b/syft/pkg/cataloger/apkdb/parse_apk_db.go index 25e627c9773..664b273cfb4 100644 --- a/syft/pkg/cataloger/apkdb/parse_apk_db.go +++ b/syft/pkg/cataloger/apkdb/parse_apk_db.go @@ -26,8 +26,9 @@ var ( repoRegex = regexp.MustCompile(`(?m)^https://.*\.alpinelinux\.org/alpine/v([^/]+)/([a-zA-Z0-9_]+)$`) ) -type apkData struct { - License string `mapstructure:"L" json:"license"` +type parsedData struct { + License string `mapstructure:"L" json:"license"` + LicenseLocation source.Location pkg.ApkMetadata } @@ -38,15 +39,15 @@ type apkData struct { func parseApkDB(resolver source.FileResolver, env *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { scanner := bufio.NewScanner(reader) - var apks []apkData - var currentEntry apkData + var apks []parsedData + var currentEntry parsedData entryParsingInProgress := false fileParsingCtx := newApkFileParsingContext() // creating a dedicated append-like function here instead of using `append(...)` // below since there is nontrivial logic to be performed for each finalized apk // entry. - appendApk := func(p apkData) { + appendApk := func(p parsedData) { if files := fileParsingCtx.files; len(files) >= 1 { // attached accumulated files to current package p.Files = files @@ -73,7 +74,7 @@ func parseApkDB(resolver source.FileResolver, env *generic.Environment, reader s entryParsingInProgress = false // zero-out currentEntry for use by any future entry - currentEntry = apkData{} + currentEntry = parsedData{} continue } @@ -128,6 +129,9 @@ func parseApkDB(resolver source.FileResolver, env *generic.Environment, reader s pkgs := make([]pkg.Package, 0, len(apks)) for _, apk := range apks { + if apk.License != "" { + apk.LicenseLocation = reader.Location + } pkgs = append(pkgs, newPackage(apk, r, reader.Location)) } @@ -206,7 +210,7 @@ type apkField struct { } //nolint:funlen -func (f apkField) apply(p *apkData, ctx *apkFileParsingContext) { +func (f apkField) apply(p *parsedData, ctx *apkFileParsingContext) { switch f.name { // APKINDEX field parsing @@ -352,7 +356,7 @@ func parseListValue(value string) []string { return nil } -func nilFieldsToEmptySlice(p *apkData) { +func nilFieldsToEmptySlice(p *parsedData) { if p.Dependencies == nil { p.Dependencies = []string{} } diff --git a/syft/pkg/cataloger/apkdb/parse_apk_db_test.go b/syft/pkg/cataloger/apkdb/parse_apk_db_test.go index 2c0e6fb15fe..44ac21fb234 100644 --- a/syft/pkg/cataloger/apkdb/parse_apk_db_test.go +++ b/syft/pkg/cataloger/apkdb/parse_apk_db_test.go @@ -14,6 +14,7 @@ import ( "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/linux" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/generic" @@ -666,12 +667,23 @@ func TestSinglePackageDetails(t *testing.T) { func TestMultiplePackages(t *testing.T) { fixture := "test-fixtures/multiple" + fixtureLocation := source.Location{ + Coordinates: source.Coordinates{ + RealPath: fixture, + }, + } fixtureLocationSet := source.NewLocationSet(source.NewLocation(fixture)) expectedPkgs := []pkg.Package{ { - Name: "libc-utils", - Version: "0.7.2-r0", - Licenses: []pkg.License{}, + Name: "libc-utils", + Version: "0.7.2-r0", + Licenses: []pkg.License{ + { + Value: "BSD", + Type: license.Declared, + Location: fixtureLocation, + }, + }, Type: pkg.ApkPkg, PURL: "pkg:apk/alpine/libc-utils@0.7.2-r0?arch=x86_64&upstream=libc-dev&distro=alpine-3.12", Locations: fixtureLocationSet, @@ -694,11 +706,29 @@ func TestMultiplePackages(t *testing.T) { }, }, { - Name: "musl-utils", - Version: "1.1.24-r2", - Type: pkg.ApkPkg, - PURL: "pkg:apk/alpine/musl-utils@1.1.24-r2?arch=x86_64&upstream=musl&distro=alpine-3.12", - Locations: fixtureLocationSet, + Name: "musl-utils", + Version: "1.1.24-r2", + Type: pkg.ApkPkg, + PURL: "pkg:apk/alpine/musl-utils@1.1.24-r2?arch=x86_64&upstream=musl&distro=alpine-3.12", + Locations: fixtureLocationSet, + Licenses: []pkg.License{ + { + Value: "MIT", + SPDXExpression: "MIT", + Type: license.Declared, + Location: fixtureLocation, + }, + { + Value: "BSD", + Type: license.Declared, + Location: fixtureLocation, + }, + { + Value: "GPL2+", + Type: license.Declared, + Location: fixtureLocation, + }, + }, MetadataType: pkg.ApkMetadataType, Metadata: pkg.ApkMetadata{ Package: "musl-utils", diff --git a/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go b/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go index 99df368f45e..8ab7cfe6ce3 100644 --- a/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go +++ b/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go @@ -233,6 +233,9 @@ func (p *CatalogTester) assertPkgs(t *testing.T, pkgs []pkg.Package, relationshi return true }, ), + cmp.Comparer( + p.locationComparer, + ), ) { diff --git a/syft/pkg/license.go b/syft/pkg/license.go index e142c560358..2b865ac6b15 100644 --- a/syft/pkg/license.go +++ b/syft/pkg/license.go @@ -21,6 +21,7 @@ func NewLicense(value string, url string, location source.Location) License { // TODO: warn/trace error with package context? log.Trace("unable to parse license expression: %w", err) } + // TODO: how do we express other places where a license is declared // EX: we got this from the go module cache at path /x/y/z on disk return License{ From ff23f28c58004316e5a0ef7cb69a4d8a268549c8 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Wed, 12 Apr 2023 12:35:44 -0400 Subject: [PATCH 13/99] fix: deb cataloger license update Signed-off-by: Christopher Phillips --- syft/pkg/cataloger/deb/package.go | 3 +++ syft/pkg/cataloger/deb/parse_dpkg_db_test.go | 1 + 2 files changed, 4 insertions(+) diff --git a/syft/pkg/cataloger/deb/package.go b/syft/pkg/cataloger/deb/package.go index b512bc6bfb6..6c841eb81f6 100644 --- a/syft/pkg/cataloger/deb/package.go +++ b/syft/pkg/cataloger/deb/package.go @@ -22,9 +22,12 @@ const ( ) func newDpkgPackage(d pkg.DpkgMetadata, dbLocation source.Location, resolver source.FileResolver, release *linux.Release) pkg.Package { + // TODO: separate pr to license refactor, but explore extracting dpkg-specific license parsing into a separate function + licenses := make([]pkg.License, 0) p := pkg.Package{ Name: d.Package, Version: d.Version, + Licenses: licenses, Locations: source.NewLocationSet(dbLocation), PURL: packageURL(d, release), Type: pkg.DebPkg, diff --git a/syft/pkg/cataloger/deb/parse_dpkg_db_test.go b/syft/pkg/cataloger/deb/parse_dpkg_db_test.go index 0bae7c8962a..edfc65d477c 100644 --- a/syft/pkg/cataloger/deb/parse_dpkg_db_test.go +++ b/syft/pkg/cataloger/deb/parse_dpkg_db_test.go @@ -307,6 +307,7 @@ Installed-Size: 10kib Name: "apt", Type: "deb", PURL: "pkg:deb/debian/apt?distro=debian-10", + Licenses: []pkg.License{}, Locations: source.NewLocationSet(source.NewLocation("place")), MetadataType: "DpkgMetadata", Metadata: pkg.DpkgMetadata{ From f914dfa786e3d63de6a42ce3c6acd55404bc6815 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Wed, 12 Apr 2023 13:21:31 -0400 Subject: [PATCH 14/99] fix: update java cataloger with new license behavior Signed-off-by: Christopher Phillips --- .../internal/pkgtest/test_generic_parser.go | 3 +++ syft/pkg/cataloger/java/archive_parser.go | 9 ++++++++- syft/pkg/cataloger/java/archive_parser_test.go | 18 ++++++++++++++---- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go b/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go index 8ab7cfe6ce3..acccd7b18e3 100644 --- a/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go +++ b/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go @@ -299,6 +299,9 @@ func AssertPackagesEqual(t *testing.T, a, b pkg.Package) { return true }, ), + cmp.Comparer( + DefaultLocationComparer, + ), } if diff := cmp.Diff(a, b, opts...); diff != "" { diff --git a/syft/pkg/cataloger/java/archive_parser.go b/syft/pkg/cataloger/java/archive_parser.go index c8fe05de093..2e1eed5acf3 100644 --- a/syft/pkg/cataloger/java/archive_parser.go +++ b/syft/pkg/cataloger/java/archive_parser.go @@ -185,12 +185,19 @@ func (j *archiveParser) discoverMainPackage() (*pkg.Package, error) { } // TODO: update this to use the new license selection/validation logic - _ = selectLicense(manifest) + licenseCandidates := selectLicense(manifest) + + licenses := make([]pkg.License, 0) + for _, l := range licenseCandidates { + // we use j.location because we want to associate the license declaration with where we discovered the contents in the manifest + licenses = append(licenses, pkg.NewLicense(l, "", j.location)) + } return &pkg.Package{ Name: selectName(manifest, j.fileInfo), Version: selectVersion(manifest, j.fileInfo), Language: pkg.Java, + Licenses: licenses, Locations: source.NewLocationSet(j.location), Type: j.fileInfo.pkgType(), MetadataType: pkg.JavaMetadataType, diff --git a/syft/pkg/cataloger/java/archive_parser_test.go b/syft/pkg/cataloger/java/archive_parser_test.go index a64fbff2f68..fb055cba02d 100644 --- a/syft/pkg/cataloger/java/archive_parser_test.go +++ b/syft/pkg/cataloger/java/archive_parser_test.go @@ -96,10 +96,20 @@ func TestParseJar(t *testing.T) { }, expected: map[string]pkg.Package{ "example-jenkins-plugin": { - Name: "example-jenkins-plugin", - Version: "1.0-SNAPSHOT", - PURL: "pkg:maven/io.jenkins.plugins/example-jenkins-plugin@1.0-SNAPSHOT", - Licenses: []pkg.License{}, + Name: "example-jenkins-plugin", + Version: "1.0-SNAPSHOT", + PURL: "pkg:maven/io.jenkins.plugins/example-jenkins-plugin@1.0-SNAPSHOT", + Licenses: []pkg.License{ + { + Value: "MIT License", + Type: "declared", + Location: source.Location{ + Coordinates: source.Coordinates{ + RealPath: "test-fixtures/java-builds/packages/example-jenkins-plugin.hpi", + }, + }, + }, + }, Language: pkg.Java, Type: pkg.JenkinsPluginPkg, MetadataType: pkg.JavaMetadataType, From 681e01e441d38552e99751858ac972387ad1f39b Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Wed, 12 Apr 2023 13:57:24 -0400 Subject: [PATCH 15/99] fix: update javascript cataloger with license changes Signed-off-by: Christopher Phillips --- .../cataloger/javascript/cataloger_test.go | 47 ++++--- syft/pkg/cataloger/javascript/package.go | 130 ++++++++++-------- .../javascript/parse_package_json_test.go | 115 +++++++++++----- .../javascript/parse_package_lock_test.go | 8 -- 4 files changed, 189 insertions(+), 111 deletions(-) diff --git a/syft/pkg/cataloger/javascript/cataloger_test.go b/syft/pkg/cataloger/javascript/cataloger_test.go index b7383b1b37b..e3971060a46 100644 --- a/syft/pkg/cataloger/javascript/cataloger_test.go +++ b/syft/pkg/cataloger/javascript/cataloger_test.go @@ -3,6 +3,7 @@ package javascript import ( "testing" + "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" "github.com/anchore/syft/syft/source" @@ -12,14 +13,21 @@ func Test_JavascriptCataloger(t *testing.T) { locationSet := source.NewLocationSet(source.NewLocation("package-lock.json")) expectedPkgs := []pkg.Package{ { - Name: "@actions/core", - Version: "1.6.0", - FoundBy: "javascript-lock-cataloger", - PURL: "pkg:npm/%40actions/core@1.6.0", - Locations: locationSet, - Language: pkg.JavaScript, - Type: pkg.NpmPkg, - Licenses: []pkg.License{}, + Name: "@actions/core", + Version: "1.6.0", + FoundBy: "javascript-lock-cataloger", + PURL: "pkg:npm/%40actions/core@1.6.0", + Locations: locationSet, + Language: pkg.JavaScript, + Type: pkg.NpmPkg, + Licenses: []pkg.License{ + { + Value: "MIT", + SPDXExpression: "MIT", + Type: license.Declared, + Location: source.NewLocation("package-lock.json"), + }, + }, MetadataType: pkg.NpmPackageLockJSONMetadataType, Metadata: pkg.NpmPackageLockJSONMetadata{Resolved: "https://registry.npmjs.org/@actions/core/-/core-1.6.0.tgz", Integrity: "sha512-NB1UAZomZlCV/LmJqkLhNTqtKfFXJZAUPcfl/zqG7EfsQdeUJtaWO98SGbuQ3pydJ3fHl2CvI/51OKYlCYYcaw=="}, }, @@ -35,14 +43,21 @@ func Test_JavascriptCataloger(t *testing.T) { Metadata: pkg.NpmPackageLockJSONMetadata{Resolved: "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", Integrity: "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="}, }, { - Name: "cowsay", - Version: "1.4.0", - FoundBy: "javascript-lock-cataloger", - PURL: "pkg:npm/cowsay@1.4.0", - Locations: locationSet, - Language: pkg.JavaScript, - Type: pkg.NpmPkg, - Licenses: []pkg.License{}, + Name: "cowsay", + Version: "1.4.0", + FoundBy: "javascript-lock-cataloger", + PURL: "pkg:npm/cowsay@1.4.0", + Locations: locationSet, + Language: pkg.JavaScript, + Type: pkg.NpmPkg, + Licenses: []pkg.License{ + { + Value: "MIT", + SPDXExpression: "MIT", + Type: license.Declared, + Location: source.NewLocation("package-lock.json"), + }, + }, MetadataType: pkg.NpmPackageLockJSONMetadataType, Metadata: pkg.NpmPackageLockJSONMetadata{Resolved: "https://registry.npmjs.org/cowsay/-/cowsay-1.4.0.tgz", Integrity: "sha512-rdg5k5PsHFVJheO/pmE3aDg2rUDDTfPJau6yYkZYlHFktUz+UxbE+IgnUAEyyCyv4noL5ltxXD0gZzmHPCy/9g=="}, }, diff --git a/syft/pkg/cataloger/javascript/package.go b/syft/pkg/cataloger/javascript/package.go index 3972cb43e6a..7bdf938a0b8 100644 --- a/syft/pkg/cataloger/javascript/package.go +++ b/syft/pkg/cataloger/javascript/package.go @@ -1,6 +1,9 @@ package javascript import ( + "encoding/json" + "io" + "path" "strings" "github.com/anchore/packageurl-go" @@ -10,17 +13,33 @@ import ( ) func newPackageJSONPackage(u packageJSON, locations ...source.Location) pkg.Package { - _, err := u.licensesFromJSON() + var licenseLocation source.Location + if len(locations) > 0 { + // caller always passes the reader.Location of the package json where license information was found + // we use this when constructing the license object to show our work on where we lifted the declaration from + licenseLocation = locations[0] + } + licenseCandidates, err := u.licensesFromJSON() if err != nil { log.Warnf("unable to extract licenses from javascript package.json: %+v", err) } + licenses := make([]pkg.License, 0) + if len(licenseCandidates) != 0 { + for _, l := range licenseCandidates { + // we could extract the url from the json here, but we only want to include this information if it was used + // to determine the declared licnese - instead the package json location was used. + licenses = append(licenses, pkg.NewLicense(l, "", licenseLocation)) + } + } + p := pkg.Package{ Name: u.Name, Version: u.Version, PURL: packageURL(u.Name, u.Version), Locations: source.NewLocationSet(locations...), Language: pkg.JavaScript, + Licenses: licenses, Type: pkg.NpmPkg, MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ @@ -117,63 +136,66 @@ func newYarnLockPackage(resolver source.FileResolver, location source.Location, ) } -// TODO: update license extraction to use the new license finder -func finalizeLockPkg(_ source.FileResolver, _ source.Location, p pkg.Package) pkg.Package { +func finalizeLockPkg(resolver source.FileResolver, location source.Location, p pkg.Package) pkg.Package { + licenseCandidate := addLicenses(p.Name, resolver, location) + for _, l := range licenseCandidate { + p.Licenses = append(p.Licenses, pkg.NewLicense(l, "", location)) + } p.SetID() return p } -// func addLicenses(name string, resolver source.FileResolver, location source.Location) (allLicenses []string) { -// if resolver == nil { -// return allLicenses -// } -// -// dir := path.Dir(location.RealPath) -// pkgPath := []string{dir, "node_modules"} -// pkgPath = append(pkgPath, strings.Split(name, "/")...) -// pkgPath = append(pkgPath, "package.json") -// pkgFile := path.Join(pkgPath...) -// locations, err := resolver.FilesByPath(pkgFile) -// if err != nil { -// log.Debugf("an error occurred attempting to read: %s - %+v", pkgFile, err) -// return allLicenses -// } -// -// if len(locations) == 0 { -// return allLicenses -// } -// -// for _, l := range locations { -// contentReader, err := resolver.FileContentsByLocation(l) -// if err != nil { -// log.Debugf("error getting file content reader for %s: %v", pkgFile, err) -// return allLicenses -// } -// -// contents, err := io.ReadAll(contentReader) -// if err != nil { -// log.Debugf("error reading file contents for %s: %v", pkgFile, err) -// return allLicenses -// } -// -// var pkgJSON packageJSON -// err = json.Unmarshal(contents, &pkgJSON) -// if err != nil { -// log.Debugf("error parsing %s: %v", pkgFile, err) -// return allLicenses -// } -// -// licenses, err := pkgJSON.licensesFromJSON() -// if err != nil { -// log.Debugf("error getting licenses from %s: %v", pkgFile, err) -// return allLicenses -// } -// -// allLicenses = append(allLicenses, licenses...) -// } -// -// return allLicenses -//} +func addLicenses(name string, resolver source.FileResolver, location source.Location) (allLicenses []string) { + if resolver == nil { + return allLicenses + } + + dir := path.Dir(location.RealPath) + pkgPath := []string{dir, "node_modules"} + pkgPath = append(pkgPath, strings.Split(name, "/")...) + pkgPath = append(pkgPath, "package.json") + pkgFile := path.Join(pkgPath...) + locations, err := resolver.FilesByPath(pkgFile) + if err != nil { + log.Debugf("an error occurred attempting to read: %s - %+v", pkgFile, err) + return allLicenses + } + + if len(locations) == 0 { + return allLicenses + } + + for _, l := range locations { + contentReader, err := resolver.FileContentsByLocation(l) + if err != nil { + log.Debugf("error getting file content reader for %s: %v", pkgFile, err) + return allLicenses + } + + contents, err := io.ReadAll(contentReader) + if err != nil { + log.Debugf("error reading file contents for %s: %v", pkgFile, err) + return allLicenses + } + + var pkgJSON packageJSON + err = json.Unmarshal(contents, &pkgJSON) + if err != nil { + log.Debugf("error parsing %s: %v", pkgFile, err) + return allLicenses + } + + licenses, err := pkgJSON.licensesFromJSON() + if err != nil { + log.Debugf("error getting licenses from %s: %v", pkgFile, err) + return allLicenses + } + + allLicenses = append(allLicenses, licenses...) + } + + return allLicenses +} // packageURL returns the PURL for the specific NPM package (see https://github.com/package-url/purl-spec) func packageURL(name, version string) string { diff --git a/syft/pkg/cataloger/javascript/parse_package_json_test.go b/syft/pkg/cataloger/javascript/parse_package_json_test.go index ed0909c0d7f..e6239b77acc 100644 --- a/syft/pkg/cataloger/javascript/parse_package_json_test.go +++ b/syft/pkg/cataloger/javascript/parse_package_json_test.go @@ -5,6 +5,7 @@ import ( "github.com/stretchr/testify/assert" + "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" "github.com/anchore/syft/syft/source" @@ -18,12 +19,19 @@ func TestParsePackageJSON(t *testing.T) { { Fixture: "test-fixtures/pkg-json/package.json", ExpectedPkg: pkg.Package{ - Name: "npm", - Version: "6.14.6", - PURL: "pkg:npm/npm@6.14.6", - Type: pkg.NpmPkg, - Licenses: []pkg.License{}, - Language: pkg.JavaScript, + Name: "npm", + Version: "6.14.6", + PURL: "pkg:npm/npm@6.14.6", + Type: pkg.NpmPkg, + Language: pkg.JavaScript, + Licenses: []pkg.License{ + { + Value: "Artistic-2.0", + SPDXExpression: "Artistic-2.0", + Type: license.Declared, + Location: source.NewLocation("test-fixtures/pkg-json/package.json"), + }, + }, MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ Name: "npm", @@ -37,12 +45,19 @@ func TestParsePackageJSON(t *testing.T) { { Fixture: "test-fixtures/pkg-json/package-license-object.json", ExpectedPkg: pkg.Package{ - Name: "npm", - Version: "6.14.6", - PURL: "pkg:npm/npm@6.14.6", - Type: pkg.NpmPkg, - Licenses: []pkg.License{}, - Language: pkg.JavaScript, + Name: "npm", + Version: "6.14.6", + PURL: "pkg:npm/npm@6.14.6", + Type: pkg.NpmPkg, + Language: pkg.JavaScript, + Licenses: []pkg.License{ + { + Value: "ISC", + SPDXExpression: "ISC", + Type: license.Declared, + Location: source.NewLocation("test-fixtures/pkg-json/package-license-object.json"), + }, + }, MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ Name: "npm", @@ -56,11 +71,24 @@ func TestParsePackageJSON(t *testing.T) { { Fixture: "test-fixtures/pkg-json/package-license-objects.json", ExpectedPkg: pkg.Package{ - Name: "npm", - Version: "6.14.6", - PURL: "pkg:npm/npm@6.14.6", - Type: pkg.NpmPkg, - Licenses: []pkg.License{}, + Name: "npm", + Version: "6.14.6", + PURL: "pkg:npm/npm@6.14.6", + Type: pkg.NpmPkg, + Licenses: []pkg.License{ + { + Value: "MIT", + SPDXExpression: "MIT", + Type: license.Declared, + Location: source.NewLocation("test-fixtures/pkg-json/package-license-objects.json"), + }, + { + Value: "Apache-2.0", + SPDXExpression: "Apache-2.0", + Type: license.Declared, + Location: source.NewLocation("test-fixtures/pkg-json/package-license-objects.json"), + }, + }, Language: pkg.JavaScript, MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ @@ -79,7 +107,7 @@ func TestParsePackageJSON(t *testing.T) { Version: "6.14.6", PURL: "pkg:npm/npm@6.14.6", Type: pkg.NpmPkg, - Licenses: nil, + Licenses: []pkg.License{}, Language: pkg.JavaScript, MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ @@ -113,11 +141,18 @@ func TestParsePackageJSON(t *testing.T) { { Fixture: "test-fixtures/pkg-json/package-nested-author.json", ExpectedPkg: pkg.Package{ - Name: "npm", - Version: "6.14.6", - PURL: "pkg:npm/npm@6.14.6", - Type: pkg.NpmPkg, - Licenses: []pkg.License{}, + Name: "npm", + Version: "6.14.6", + PURL: "pkg:npm/npm@6.14.6", + Type: pkg.NpmPkg, + Licenses: []pkg.License{ + { + Value: "Artistic-2.0", + SPDXExpression: "Artistic-2.0", + Type: license.Declared, + Location: source.NewLocation("test-fixtures/pkg-json/package-nested-author.json"), + }, + }, Language: pkg.JavaScript, MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ @@ -132,11 +167,18 @@ func TestParsePackageJSON(t *testing.T) { { Fixture: "test-fixtures/pkg-json/package-repo-string.json", ExpectedPkg: pkg.Package{ - Name: "function-bind", - Version: "1.1.1", - PURL: "pkg:npm/function-bind@1.1.1", - Type: pkg.NpmPkg, - Licenses: []pkg.License{}, + Name: "function-bind", + Version: "1.1.1", + PURL: "pkg:npm/function-bind@1.1.1", + Type: pkg.NpmPkg, + Licenses: []pkg.License{ + { + Value: "MIT", + SPDXExpression: "MIT", + Type: license.Declared, + Location: source.NewLocation("test-fixtures/pkg-json/package-repo-string.json"), + }, + }, Language: pkg.JavaScript, MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ @@ -151,11 +193,18 @@ func TestParsePackageJSON(t *testing.T) { { Fixture: "test-fixtures/pkg-json/package-private.json", ExpectedPkg: pkg.Package{ - Name: "npm", - Version: "6.14.6", - PURL: "pkg:npm/npm@6.14.6", - Type: pkg.NpmPkg, - Licenses: []pkg.License{}, + Name: "npm", + Version: "6.14.6", + PURL: "pkg:npm/npm@6.14.6", + Type: pkg.NpmPkg, + Licenses: []pkg.License{ + { + Value: "Artistic-2.0", + SPDXExpression: "Artistic-2.0", + Type: license.Declared, + Location: source.NewLocation("test-fixtures/pkg-json/package-private.json"), + }, + }, Language: pkg.JavaScript, MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ diff --git a/syft/pkg/cataloger/javascript/parse_package_lock_test.go b/syft/pkg/cataloger/javascript/parse_package_lock_test.go index ba563218b50..c844f6bdd94 100644 --- a/syft/pkg/cataloger/javascript/parse_package_lock_test.go +++ b/syft/pkg/cataloger/javascript/parse_package_lock_test.go @@ -139,7 +139,6 @@ func TestParsePackageLockV2(t *testing.T) { PURL: "pkg:npm/%40types/prop-types@15.7.5", Language: pkg.JavaScript, Type: pkg.NpmPkg, - Licenses: []pkg.License{}, MetadataType: "NpmPackageLockJsonMetadata", Metadata: pkg.NpmPackageLockJSONMetadata{Resolved: "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", Integrity: "sha1-XxnSuFqY6VWANvajysyIGUIPBc8="}, }, @@ -149,7 +148,6 @@ func TestParsePackageLockV2(t *testing.T) { PURL: "pkg:npm/%40types/react@18.0.17", Language: pkg.JavaScript, Type: pkg.NpmPkg, - Licenses: []pkg.License{}, MetadataType: "NpmPackageLockJsonMetadata", Metadata: pkg.NpmPackageLockJSONMetadata{Resolved: "https://registry.npmjs.org/@types/react/-/react-18.0.17.tgz", Integrity: "sha1-RYPZwyLWfv5LOak10iPtzHBQzPQ="}, }, @@ -159,7 +157,6 @@ func TestParsePackageLockV2(t *testing.T) { PURL: "pkg:npm/%40types/scheduler@0.16.2", Language: pkg.JavaScript, Type: pkg.NpmPkg, - Licenses: []pkg.License{}, MetadataType: "NpmPackageLockJsonMetadata", Metadata: pkg.NpmPackageLockJSONMetadata{Resolved: "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", Integrity: "sha1-GmL4lSVyPd4kuhsBsJK/XfitTTk="}, }, @@ -169,7 +166,6 @@ func TestParsePackageLockV2(t *testing.T) { PURL: "pkg:npm/csstype@3.1.0", Language: pkg.JavaScript, Type: pkg.NpmPkg, - Licenses: []pkg.License{}, MetadataType: "NpmPackageLockJsonMetadata", Metadata: pkg.NpmPackageLockJSONMetadata{Resolved: "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz", Integrity: "sha1-TdysNxjXh8+d8NG30VAzklyPKfI="}, }, @@ -274,7 +270,6 @@ func TestParsePackageLockAlias(t *testing.T) { PURL: "pkg:npm/alias-check@1.0.0", Language: pkg.JavaScript, Type: pkg.NpmPkg, - Licenses: []pkg.License{}, MetadataType: "NpmPackageLockJsonMetadata", Metadata: pkg.NpmPackageLockJSONMetadata{}, } @@ -305,7 +300,6 @@ func TestParsePackageLockLicenseWithArray(t *testing.T) { { Name: "tmp", Version: "1.0.0", - Licenses: []pkg.License{}, Language: pkg.JavaScript, Type: pkg.NpmPkg, PURL: "pkg:npm/tmp@1.0.0", @@ -315,7 +309,6 @@ func TestParsePackageLockLicenseWithArray(t *testing.T) { { Name: "pause-stream", Version: "0.0.11", - Licenses: []pkg.License{}, Language: pkg.JavaScript, Type: pkg.NpmPkg, PURL: "pkg:npm/pause-stream@0.0.11", @@ -325,7 +318,6 @@ func TestParsePackageLockLicenseWithArray(t *testing.T) { { Name: "through", Version: "2.3.8", - Licenses: []pkg.License{}, Language: pkg.JavaScript, Type: pkg.NpmPkg, PURL: "pkg:npm/through@2.3.8", From 6fe8e93ed144e13b28ef58164c6b760b26893d3a Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Wed, 12 Apr 2023 14:45:51 -0400 Subject: [PATCH 16/99] feat: add php license detection Signed-off-by: Christopher Phillips --- syft/pkg/cataloger/php/package.go | 17 +++++++-- syft/pkg/cataloger/php/package_test.go | 29 +++++++++------ syft/pkg/cataloger/php/parse_composer_lock.go | 14 ++++++-- .../cataloger/php/parse_composer_lock_test.go | 36 +++++++++++++------ .../pkg/cataloger/php/parse_installed_json.go | 6 ++-- .../php/parse_installed_json_test.go | 31 +++++++++++----- syft/pkg/php_composer_json_metadata.go | 1 + 7 files changed, 98 insertions(+), 36 deletions(-) diff --git a/syft/pkg/cataloger/php/package.go b/syft/pkg/cataloger/php/package.go index ee3be7019f2..19838bedd0d 100644 --- a/syft/pkg/cataloger/php/package.go +++ b/syft/pkg/cataloger/php/package.go @@ -8,23 +8,34 @@ import ( "github.com/anchore/syft/syft/source" ) -func newComposerLockPackage(m pkg.PhpComposerJSONMetadata, location ...source.Location) pkg.Package { +func newComposerLockPackage(m parsedData, location ...source.Location) pkg.Package { + var licenseLocation source.Location + if len(location) > 0 { + // we want to use the composer lock location as the source for the license declaration + licenseLocation = location[0] + } + + licenses := make([]pkg.License, 0) + for _, l := range m.License { + licenses = append(licenses, pkg.NewLicense(l, "", licenseLocation)) + } p := pkg.Package{ Name: m.Name, Version: m.Version, Locations: source.NewLocationSet(location...), + Licenses: licenses, PURL: packageURL(m), Language: pkg.PHP, Type: pkg.PhpComposerPkg, MetadataType: pkg.PhpComposerJSONMetadataType, - Metadata: m, + Metadata: m.PhpComposerJSONMetadata, } p.SetID() return p } -func packageURL(m pkg.PhpComposerJSONMetadata) string { +func packageURL(m parsedData) string { var name, vendor string fields := strings.Split(m.Name, "/") switch len(fields) { diff --git a/syft/pkg/cataloger/php/package_test.go b/syft/pkg/cataloger/php/package_test.go index 9a32df1ff99..310f4100dcc 100644 --- a/syft/pkg/cataloger/php/package_test.go +++ b/syft/pkg/cataloger/php/package_test.go @@ -11,30 +11,39 @@ import ( func Test_packageURL(t *testing.T) { tests := []struct { name string - metadata pkg.PhpComposerJSONMetadata + metadata parsedData expected string }{ { name: "with extractable vendor", - metadata: pkg.PhpComposerJSONMetadata{ - Name: "ven/name", - Version: "1.0.1", + metadata: parsedData{ + []string{}, + pkg.PhpComposerJSONMetadata{ + Version: "1.0.1", + Name: "ven/name", + }, }, expected: "pkg:composer/ven/name@1.0.1", }, { name: "name with slashes (invalid)", - metadata: pkg.PhpComposerJSONMetadata{ - Name: "ven/name/component", - Version: "1.0.1", + metadata: parsedData{ + []string{}, + pkg.PhpComposerJSONMetadata{ + Name: "ven/name/component", + Version: "1.0.1", + }, }, expected: "pkg:composer/ven/name-component@1.0.1", }, { name: "unknown vendor", - metadata: pkg.PhpComposerJSONMetadata{ - Name: "name", - Version: "1.0.1", + metadata: parsedData{ + []string{}, + pkg.PhpComposerJSONMetadata{ + Name: "name", + Version: "1.0.1", + }, }, expected: "pkg:composer/name@1.0.1", }, diff --git a/syft/pkg/cataloger/php/parse_composer_lock.go b/syft/pkg/cataloger/php/parse_composer_lock.go index e8d258c7207..2609fe497be 100644 --- a/syft/pkg/cataloger/php/parse_composer_lock.go +++ b/syft/pkg/cataloger/php/parse_composer_lock.go @@ -14,9 +14,14 @@ import ( var _ generic.Parser = parseComposerLock +type parsedData struct { + License []string `json:"license"` + pkg.PhpComposerJSONMetadata +} + type composerLock struct { - Packages []pkg.PhpComposerJSONMetadata `json:"packages"` - PackageDev []pkg.PhpComposerJSONMetadata `json:"packages-dev"` + Packages []parsedData `json:"packages"` + PackageDev []parsedData `json:"packages-dev"` } // parseComposerLock is a parser function for Composer.lock contents, returning "Default" php packages discovered. @@ -34,6 +39,11 @@ func parseComposerLock(_ source.FileResolver, _ *generic.Environment, reader sou for _, m := range lock.Packages { pkgs = append(pkgs, newComposerLockPackage(m, reader.Location)) } + + // TODO: did we omit this on purpose? + //for _, m := range lock.PackageDev { + // pkgs = append(pkgs, newComposerLockPackage(m, reader.Location)) + //} } return pkgs, nil, nil diff --git a/syft/pkg/cataloger/php/parse_composer_lock_test.go b/syft/pkg/cataloger/php/parse_composer_lock_test.go index 8d33ca71c6a..5c91e488e7a 100644 --- a/syft/pkg/cataloger/php/parse_composer_lock_test.go +++ b/syft/pkg/cataloger/php/parse_composer_lock_test.go @@ -4,22 +4,30 @@ import ( "testing" "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" "github.com/anchore/syft/syft/source" ) -// TODO: add license parsing onto the package func TestParseComposerFileLock(t *testing.T) { var expectedRelationships []artifact.Relationship fixture := "test-fixtures/composer.lock" locations := source.NewLocationSet(source.NewLocation(fixture)) expectedPkgs := []pkg.Package{ { - Name: "adoy/fastcgi-client", - Version: "1.0.2", - PURL: "pkg:composer/adoy/fastcgi-client@1.0.2", - Locations: locations, + Name: "adoy/fastcgi-client", + Version: "1.0.2", + PURL: "pkg:composer/adoy/fastcgi-client@1.0.2", + Locations: locations, + Licenses: []pkg.License{ + { + Value: "MIT", + SPDXExpression: "MIT", + Type: license.Declared, + Location: source.NewLocation(fixture), + }, + }, Language: pkg.PHP, Type: pkg.PhpComposerPkg, MetadataType: pkg.PhpComposerJSONMetadataType, @@ -53,11 +61,19 @@ func TestParseComposerFileLock(t *testing.T) { }, }, { - Name: "alcaeus/mongo-php-adapter", - Version: "1.1.11", - Locations: locations, - PURL: "pkg:composer/alcaeus/mongo-php-adapter@1.1.11", - Language: pkg.PHP, + Name: "alcaeus/mongo-php-adapter", + Version: "1.1.11", + Locations: locations, + PURL: "pkg:composer/alcaeus/mongo-php-adapter@1.1.11", + Language: pkg.PHP, + Licenses: []pkg.License{ + { + Value: "MIT", + SPDXExpression: "MIT", + Type: license.Declared, + Location: source.NewLocation(fixture), + }, + }, Type: pkg.PhpComposerPkg, MetadataType: pkg.PhpComposerJSONMetadataType, Metadata: pkg.PhpComposerJSONMetadata{ diff --git a/syft/pkg/cataloger/php/parse_installed_json.go b/syft/pkg/cataloger/php/parse_installed_json.go index b5cd1b86a88..ba6f9f04090 100644 --- a/syft/pkg/cataloger/php/parse_installed_json.go +++ b/syft/pkg/cataloger/php/parse_installed_json.go @@ -16,19 +16,19 @@ var _ generic.Parser = parseComposerLock // Note: composer version 2 introduced a new structure for the installed.json file, so we support both type installedJSONComposerV2 struct { - Packages []pkg.PhpComposerJSONMetadata `json:"packages"` + Packages []parsedData `json:"packages"` } func (w *installedJSONComposerV2) UnmarshalJSON(data []byte) error { type compv2 struct { - Packages []pkg.PhpComposerJSONMetadata `json:"packages"` + Packages []parsedData `json:"packages"` } compv2er := new(compv2) err := json.Unmarshal(data, &compv2er) if err != nil { // If we had an err or, we may be dealing with a composer v.1 installed.json // which should be all arrays - var packages []pkg.PhpComposerJSONMetadata + var packages []parsedData err := json.Unmarshal(data, &packages) if err != nil { return err diff --git a/syft/pkg/cataloger/php/parse_installed_json_test.go b/syft/pkg/cataloger/php/parse_installed_json_test.go index d1a8b3977f5..384b7e6f022 100644 --- a/syft/pkg/cataloger/php/parse_installed_json_test.go +++ b/syft/pkg/cataloger/php/parse_installed_json_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" "github.com/anchore/syft/syft/source" @@ -24,7 +25,13 @@ func TestParseInstalledJsonComposerV1(t *testing.T) { Language: pkg.PHP, Type: pkg.PhpComposerPkg, MetadataType: pkg.PhpComposerJSONMetadataType, - Licenses: []pkg.License{}, + Licenses: []pkg.License{ + { + Value: "MIT", + SPDXExpression: "MIT", + Type: license.Declared, + }, + }, Metadata: pkg.PhpComposerJSONMetadata{ Name: "asm89/stack-cors", Version: "1.3.0", @@ -66,12 +73,18 @@ func TestParseInstalledJsonComposerV1(t *testing.T) { }, }, { - Name: "behat/mink", - Version: "v1.8.1", - PURL: "pkg:composer/behat/mink@v1.8.1", - Language: pkg.PHP, - Type: pkg.PhpComposerPkg, - Licenses: []pkg.License{}, + Name: "behat/mink", + Version: "v1.8.1", + PURL: "pkg:composer/behat/mink@v1.8.1", + Language: pkg.PHP, + Type: pkg.PhpComposerPkg, + Licenses: []pkg.License{ + { + Value: "MIT", + SPDXExpression: "MIT", + Type: license.Declared, + }, + }, MetadataType: pkg.PhpComposerJSONMetadataType, Metadata: pkg.PhpComposerJSONMetadata{ Name: "behat/mink", @@ -129,9 +142,11 @@ func TestParseInstalledJsonComposerV1(t *testing.T) { locations := source.NewLocationSet(source.NewLocation(fixture)) for i := range expectedPkgs { expectedPkgs[i].Locations = locations + for k := range expectedPkgs[i].Licenses { + expectedPkgs[i].Licenses[k].Location = source.NewLocation(fixture) + } } pkgtest.TestFileParser(t, fixture, parseInstalledJSON, expectedPkgs, expectedRelationships) }) } - } diff --git a/syft/pkg/php_composer_json_metadata.go b/syft/pkg/php_composer_json_metadata.go index ab1b85a3a91..4bc7528c111 100644 --- a/syft/pkg/php_composer_json_metadata.go +++ b/syft/pkg/php_composer_json_metadata.go @@ -10,6 +10,7 @@ type PhpComposerJSONMetadata struct { Provide map[string]string `json:"provide,omitempty"` RequireDev map[string]string `json:"require-dev,omitempty"` Suggest map[string]string `json:"suggest,omitempty"` + License []string `json:"license,omitempty"` Type string `json:"type,omitempty"` NotificationURL string `json:"notification-url,omitempty"` Bin []string `json:"bin,omitempty"` From e7626f6ee5919f8121f795c9255e0952a8e2194d Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Wed, 12 Apr 2023 15:03:24 -0400 Subject: [PATCH 17/99] feat: update portage licenses with new structure Signed-off-by: Christopher Phillips --- syft/pkg/cataloger/portage/cataloger_test.go | 42 +++++++++++- .../portage/parse_portage_contents.go | 64 ++++++++++--------- 2 files changed, 74 insertions(+), 32 deletions(-) diff --git a/syft/pkg/cataloger/portage/cataloger_test.go b/syft/pkg/cataloger/portage/cataloger_test.go index 711dcafdd4a..118310558df 100644 --- a/syft/pkg/cataloger/portage/cataloger_test.go +++ b/syft/pkg/cataloger/portage/cataloger_test.go @@ -5,13 +5,14 @@ import ( "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" "github.com/anchore/syft/syft/source" ) func TestPortageCataloger(t *testing.T) { - + expectedLicenseLocation := source.NewLocation("var/db/pkg/app-containers/skopeo-1.5.1/LICENSE") expectedPkgs := []pkg.Package{ { Name: "app-containers/skopeo", @@ -20,10 +21,45 @@ func TestPortageCataloger(t *testing.T) { PURL: "pkg:ebuild/app-containers/skopeo@1.5.1", Locations: source.NewLocationSet( source.NewLocation("var/db/pkg/app-containers/skopeo-1.5.1/CONTENTS"), - source.NewLocation("var/db/pkg/app-containers/skopeo-1.5.1/LICENSE"), source.NewLocation("var/db/pkg/app-containers/skopeo-1.5.1/SIZE"), + expectedLicenseLocation, ), - Licenses: []pkg.License{}, + Licenses: []pkg.License{ + { + Value: "Apache-2.0", + SPDXExpression: "Apache-2.0", + Type: license.Declared, + Location: expectedLicenseLocation, + }, + { + Value: "BSD", + Type: license.Declared, + Location: expectedLicenseLocation, + }, + { + Value: "BSD-2", + Type: license.Declared, + Location: expectedLicenseLocation, + }, + { + Value: "CC-BY-SA-4.0", + SPDXExpression: "CC-BY-SA-4.0", + Type: license.Declared, + Location: expectedLicenseLocation, + }, + { + Value: "ISC", + SPDXExpression: "ISC", + Type: license.Declared, + Location: expectedLicenseLocation, + }, + { + Value: "MIT", + SPDXExpression: "MIT", + Type: license.Declared, + Location: expectedLicenseLocation, + }, + }, Type: pkg.PortagePkg, MetadataType: pkg.PortageMetadataType, Metadata: pkg.PortageMetadata{ diff --git a/syft/pkg/cataloger/portage/parse_portage_contents.go b/syft/pkg/cataloger/portage/parse_portage_contents.go index d78a68b5b0d..4f3b0d438a4 100644 --- a/syft/pkg/cataloger/portage/parse_portage_contents.go +++ b/syft/pkg/cataloger/portage/parse_portage_contents.go @@ -9,6 +9,7 @@ import ( "strconv" "strings" + "github.com/anchore/syft/internal" "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/file" @@ -47,7 +48,7 @@ func parsePortageContents(resolver source.FileResolver, _ *generic.Environment, }, } // TODO: update this to use latest license parsing and validation tooling - // addLicenses(resolver, reader.Location, &p) + addLicenses(resolver, reader.Location, &p) addSize(resolver, reader.Location, &p) addFiles(resolver, reader.Location, &p) @@ -89,34 +90,39 @@ func addFiles(resolver source.FileResolver, dbLocation source.Location, p *pkg.P p.Locations.Add(dbLocation) } -// func addLicenses(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) { -// parentPath := filepath.Dir(dbLocation.RealPath) -// -// location := resolver.RelativeFileByPath(dbLocation, path.Join(parentPath, "LICENSE")) -// -// if location == nil { -// return -// } -// -// licenseReader, err := resolver.FileContentsByLocation(*location) -// if err != nil { -// log.WithFields("path", dbLocation.RealPath).Warnf("failed to fetch portage LICENSE: %+v", err) -// return -// } -// -// findings := internal.NewStringSet() -// scanner := bufio.NewScanner(licenseReader) -// scanner.Split(bufio.ScanWords) -// for scanner.Scan() { -// token := scanner.Text() -// if token != "||" && token != "(" && token != ")" { -// findings.Add(token) -// } -// } -// _ = findings.ToSlice() -// // sort.Strings(licenses) -// p.Locations.Add(*location) -//} +func addLicenses(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) { + parentPath := filepath.Dir(dbLocation.RealPath) + + location := resolver.RelativeFileByPath(dbLocation, path.Join(parentPath, "LICENSE")) + + if location == nil { + return + } + + licenseReader, err := resolver.FileContentsByLocation(*location) + if err != nil { + log.WithFields("path", dbLocation.RealPath).Warnf("failed to fetch portage LICENSE: %+v", err) + return + } + + findings := internal.NewStringSet() + scanner := bufio.NewScanner(licenseReader) + scanner.Split(bufio.ScanWords) + for scanner.Scan() { + token := scanner.Text() + if token != "||" && token != "(" && token != ")" { + findings.Add(token) + } + } + licenseCandidates := findings.ToSlice() + + licenses := make([]pkg.License, 0) + for _, licenseCandidate := range licenseCandidates { + licenses = append(licenses, pkg.NewLicense(licenseCandidate, "", *location)) + } + p.Licenses = licenses + p.Locations.Add(*location) +} func addSize(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) { parentPath := filepath.Dir(dbLocation.RealPath) From 644d4c8718163c7c803b15ba3e95cabf70a4278c Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Wed, 12 Apr 2023 16:18:04 -0400 Subject: [PATCH 18/99] feat: python updates for license restructure Signed-off-by: Christopher Phillips --- syft/pkg/cataloger/python/cataloger_test.go | 46 +++++++++++--- syft/pkg/cataloger/python/package.go | 12 ++-- syft/pkg/cataloger/python/parse_wheel_egg.go | 2 +- .../python/parse_wheel_egg_metadata.go | 26 +++++--- .../python/parse_wheel_egg_metadata_test.go | 63 ++++++++++++------- 5 files changed, 105 insertions(+), 44 deletions(-) diff --git a/syft/pkg/cataloger/python/cataloger_test.go b/syft/pkg/cataloger/python/cataloger_test.go index 68d7ae51e5b..6c9410cfb08 100644 --- a/syft/pkg/cataloger/python/cataloger_test.go +++ b/syft/pkg/cataloger/python/cataloger_test.go @@ -5,6 +5,7 @@ import ( "github.com/stretchr/testify/require" + "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" "github.com/anchore/syft/syft/source" @@ -24,6 +25,7 @@ func Test_PackageCataloger(t *testing.T) { PURL: "pkg:pypi/no-version", Type: pkg.PythonPkg, Language: pkg.Python, + Licenses: []pkg.License{}, FoundBy: "python-package-cataloger", MetadataType: pkg.PythonPackageMetadataType, Metadata: pkg.PythonPackageMetadata{ @@ -45,7 +47,13 @@ func Test_PackageCataloger(t *testing.T) { PURL: "pkg:pypi/requests@2.22.0", Type: pkg.PythonPkg, Language: pkg.Python, - Licenses: []pkg.License{}, + Licenses: []pkg.License{ + { + Value: "Apache 2.0", + Type: license.Declared, + Location: source.NewLocation("test-fixtures/egg-info/PKG-INFO"), + }, + }, //Licenses: []string{"Apache 2.0"}, FoundBy: "python-package-cataloger", MetadataType: pkg.PythonPackageMetadataType, @@ -82,8 +90,13 @@ func Test_PackageCataloger(t *testing.T) { PURL: "pkg:pypi/Pygments@2.6.1?vcs_url=git+https://github.com/python-test/test.git%40aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", Type: pkg.PythonPkg, Language: pkg.Python, - Licenses: []pkg.License{}, - //Licenses: []string{"BSD License"}, + Licenses: []pkg.License{ + { + Value: "BSD License", + Type: license.Declared, + Location: source.NewLocation("test-fixtures/dist-info/METADATA"), + }, + }, FoundBy: "python-package-cataloger", MetadataType: pkg.PythonPackageMetadataType, Metadata: pkg.PythonPackageMetadata{ @@ -119,8 +132,13 @@ func Test_PackageCataloger(t *testing.T) { PURL: "pkg:pypi/Pygments@2.6.1", Type: pkg.PythonPkg, Language: pkg.Python, - Licenses: []pkg.License{}, - //Licenses: []string{"BSD License"}, + Licenses: []pkg.License{ + { + Value: "BSD License", + Type: license.Declared, + Location: source.NewLocation("test-fixtures/malformed-record/dist-info/METADATA"), + }, + }, FoundBy: "python-package-cataloger", MetadataType: pkg.PythonPackageMetadataType, Metadata: pkg.PythonPackageMetadata{ @@ -150,8 +168,13 @@ func Test_PackageCataloger(t *testing.T) { PURL: "pkg:pypi/Pygments@2.6.1", Type: pkg.PythonPkg, Language: pkg.Python, - Licenses: []pkg.License{}, - //Licenses: []string{"BSD License"}, + Licenses: []pkg.License{ + { + Value: "BSD License", + Type: license.Declared, + Location: source.NewLocation("test-fixtures/partial.dist-info/METADATA"), + }, + }, FoundBy: "python-package-cataloger", MetadataType: pkg.PythonPackageMetadataType, Metadata: pkg.PythonPackageMetadata{ @@ -173,8 +196,13 @@ func Test_PackageCataloger(t *testing.T) { PURL: "pkg:pypi/requests@2.22.0", Type: pkg.PythonPkg, Language: pkg.Python, - Licenses: []pkg.License{}, - //Licenses: []string{"Apache 2.0"}, + Licenses: []pkg.License{ + { + Value: "Apache 2.0", + Type: license.Declared, + Location: source.NewLocation("test-fixtures/test.egg-info"), + }, + }, FoundBy: "python-package-cataloger", MetadataType: pkg.PythonPackageMetadataType, Metadata: pkg.PythonPackageMetadata{ diff --git a/syft/pkg/cataloger/python/package.go b/syft/pkg/cataloger/python/package.go index a12bcdb5453..358c64591ba 100644 --- a/syft/pkg/cataloger/python/package.go +++ b/syft/pkg/cataloger/python/package.go @@ -40,17 +40,21 @@ func newPackageForIndexWithMetadata(name, version string, metadata pkg.PythonPip return p } -func newPackageForPackage(m pkg.PythonPackageMetadata, sources ...source.Location) pkg.Package { - // TODO: lift licenses out of metadata as part of construction +func newPackageForPackage(m parsedData, sources ...source.Location) pkg.Package { + licenses := make([]pkg.License, 0) + if m.Licenses != "" { + licenses = append(licenses, pkg.NewLicense(m.Licenses, "", m.LicenseLocation)) + } p := pkg.Package{ Name: m.Name, Version: m.Version, - PURL: packageURL(m.Name, m.Version, &m), + PURL: packageURL(m.Name, m.Version, &m.PythonPackageMetadata), Locations: source.NewLocationSet(sources...), Language: pkg.Python, + Licenses: licenses, Type: pkg.PythonPkg, MetadataType: pkg.PythonPackageMetadataType, - Metadata: m, + Metadata: m.PythonPackageMetadata, } p.SetID() diff --git a/syft/pkg/cataloger/python/parse_wheel_egg.go b/syft/pkg/cataloger/python/parse_wheel_egg.go index 1ccf4664b16..bffea16450f 100644 --- a/syft/pkg/cataloger/python/parse_wheel_egg.go +++ b/syft/pkg/cataloger/python/parse_wheel_egg.go @@ -160,7 +160,7 @@ func fetchDirectURLData(resolver source.FileResolver, metadataLocation source.Lo } // assembleEggOrWheelMetadata discovers and accumulates python package metadata from multiple file sources and returns a single metadata object as well as a list of files where the metadata was derived from. -func assembleEggOrWheelMetadata(resolver source.FileResolver, metadataLocation source.Location) (*pkg.PythonPackageMetadata, []source.Location, error) { +func assembleEggOrWheelMetadata(resolver source.FileResolver, metadataLocation source.Location) (*parsedData, []source.Location, error) { var sources = []source.Location{metadataLocation} metadataContents, err := resolver.FileContentsByLocation(metadataLocation) diff --git a/syft/pkg/cataloger/python/parse_wheel_egg_metadata.go b/syft/pkg/cataloger/python/parse_wheel_egg_metadata.go index ab97a06a94f..2caf1c51f2f 100644 --- a/syft/pkg/cataloger/python/parse_wheel_egg_metadata.go +++ b/syft/pkg/cataloger/python/parse_wheel_egg_metadata.go @@ -12,11 +12,18 @@ import ( "github.com/anchore/syft/internal/file" "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/source" ) +type parsedData struct { + Licenses string `mapstructure:"License"` + LicenseLocation source.Location + pkg.PythonPackageMetadata `mapstructure:",squash"` +} + // parseWheelOrEggMetadata takes a Python Egg or Wheel (which share the same format and values for our purposes), // returning all Python packages listed. -func parseWheelOrEggMetadata(path string, reader io.Reader) (pkg.PythonPackageMetadata, error) { +func parseWheelOrEggMetadata(path string, reader io.Reader) (parsedData, error) { fields := make(map[string]string) var key string @@ -43,7 +50,7 @@ func parseWheelOrEggMetadata(path string, reader io.Reader) (pkg.PythonPackageMe // a field-body continuation updatedValue, err := handleFieldBodyContinuation(key, line, fields) if err != nil { - return pkg.PythonPackageMetadata{}, err + return parsedData{}, err } fields[key] = updatedValue @@ -62,19 +69,22 @@ func parseWheelOrEggMetadata(path string, reader io.Reader) (pkg.PythonPackageMe } if err := scanner.Err(); err != nil { - return pkg.PythonPackageMetadata{}, fmt.Errorf("failed to parse python wheel/egg: %w", err) + return parsedData{}, fmt.Errorf("failed to parse python wheel/egg: %w", err) } - var metadata pkg.PythonPackageMetadata - if err := mapstructure.Decode(fields, &metadata); err != nil { - return pkg.PythonPackageMetadata{}, fmt.Errorf("unable to parse APK metadata: %w", err) + var parsedData parsedData + if err := mapstructure.Decode(fields, &parsedData); err != nil { + return parsedData, fmt.Errorf("unable to parse APK metadata: %w", err) } // add additional metadata not stored in the egg/wheel metadata file - metadata.SitePackagesRootPath = determineSitePackagesRootPath(path) + parsedData.SitePackagesRootPath = determineSitePackagesRootPath(path) + if parsedData.Licenses != "" { + parsedData.LicenseLocation = source.NewLocation(path) + } - return metadata, nil + return parsedData, nil } // isEggRegularFile determines if the specified path is the regular file variant diff --git a/syft/pkg/cataloger/python/parse_wheel_egg_metadata_test.go b/syft/pkg/cataloger/python/parse_wheel_egg_metadata_test.go index 7159962b827..14894d5afdf 100644 --- a/syft/pkg/cataloger/python/parse_wheel_egg_metadata_test.go +++ b/syft/pkg/cataloger/python/parse_wheel_egg_metadata_test.go @@ -7,35 +7,50 @@ import ( "github.com/go-test/deep" "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/source" ) func TestParseWheelEggMetadata(t *testing.T) { tests := []struct { Fixture string - ExpectedMetadata pkg.PythonPackageMetadata + ExpectedMetadata parsedData }{ { Fixture: "test-fixtures/egg-info/PKG-INFO", - ExpectedMetadata: pkg.PythonPackageMetadata{ - Name: "requests", - Version: "2.22.0", - License: "Apache 2.0", - Platform: "UNKNOWN", - Author: "Kenneth Reitz", - AuthorEmail: "me@kennethreitz.org", - SitePackagesRootPath: "test-fixtures", + ExpectedMetadata: parsedData{ + "Apache 2.0", + source.Location{ + Coordinates: source.Coordinates{ + RealPath: "test-fixtures/egg-info/PKG-INFO", + }, + }, + pkg.PythonPackageMetadata{ + Name: "requests", + Version: "2.22.0", + Platform: "UNKNOWN", + Author: "Kenneth Reitz", + AuthorEmail: "me@kennethreitz.org", + SitePackagesRootPath: "test-fixtures", + }, }, }, { Fixture: "test-fixtures/dist-info/METADATA", - ExpectedMetadata: pkg.PythonPackageMetadata{ - Name: "Pygments", - Version: "2.6.1", - License: "BSD License", - Platform: "any", - Author: "Georg Brandl", - AuthorEmail: "georg@python.org", - SitePackagesRootPath: "test-fixtures", + ExpectedMetadata: parsedData{ + "BSD License", + source.Location{ + Coordinates: source.Coordinates{ + RealPath: "test-fixtures/dist-info/METADATA", + }, + }, + pkg.PythonPackageMetadata{ + Name: "Pygments", + Version: "2.6.1", + Platform: "any", + Author: "Georg Brandl", + AuthorEmail: "georg@python.org", + SitePackagesRootPath: "test-fixtures", + }, }, }, } @@ -122,14 +137,18 @@ func TestDetermineSitePackagesRootPath(t *testing.T) { func TestParseWheelEggMetadataInvalid(t *testing.T) { tests := []struct { Fixture string - ExpectedMetadata pkg.PythonPackageMetadata + ExpectedMetadata parsedData }{ { Fixture: "test-fixtures/egg-info/PKG-INFO-INVALID", - ExpectedMetadata: pkg.PythonPackageMetadata{ - Name: "mxnet", - Version: "1.8.0", - SitePackagesRootPath: "test-fixtures", + ExpectedMetadata: parsedData{ + "", + source.Location{}, + pkg.PythonPackageMetadata{ + Name: "mxnet", + Version: "1.8.0", + SitePackagesRootPath: "test-fixtures", + }, }, }, } From 6a3f6f5cee3c272389384c0cb498571f77b43968 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Thu, 13 Apr 2023 13:23:33 -0400 Subject: [PATCH 19/99] chore: update license package to use new Validate lists Signed-off-by: Christopher Phillips --- syft/license/license.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/syft/license/license.go b/syft/license/license.go index 9de546265d3..8f1b1273676 100644 --- a/syft/license/license.go +++ b/syft/license/license.go @@ -1,7 +1,11 @@ // package license provides common methods for working with SPDX license data package license -import "github.com/github/go-spdx/v2/spdxexp" +import ( + "fmt" + + "github.com/github/go-spdx/v2/spdxexp" +) type Type string @@ -17,12 +21,10 @@ type Evidence struct { } func ParseExpression(expression string) (string, error) { - node, err := spdxexp.Parse(expression) - if err != nil { - return "", err + valid, _ := spdxexp.ValidateLicenses([]string{expression}) + if !valid { + return "", fmt.Errorf("failed to validate spdx expression: %s", expression) } - if node == nil { - return "", nil - } - return *node.ReconstructedLicenseString(), nil + + return expression, nil } From 678fa67f4037a915694844263ef47e54a9046d2f Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Mon, 17 Apr 2023 12:53:15 -0400 Subject: [PATCH 20/99] chore: merge conflicts update to take new changes Signed-off-by: Christopher Phillips --- internal/licenses/parser.go | 28 ++++--- syft/formats/syftjson/to_format_model.go | 6 +- syft/pkg/cataloger/golang/licenses.go | 16 ++-- syft/pkg/cataloger/java/archive_parser.go | 8 +- .../javascript/parse_package_json_test.go | 81 +++++++++---------- syft/pkg/cataloger/kernel/package.go | 10 ++- syft/pkg/cataloger/php/parse_composer_lock.go | 2 +- syft/pkg/cataloger/python/parse_wheel_egg.go | 26 +++--- syft/pkg/npm_package_json_metadata.go | 14 ++-- 9 files changed, 96 insertions(+), 95 deletions(-) diff --git a/internal/licenses/parser.go b/internal/licenses/parser.go index cd20bf0b616..35f5c09210c 100644 --- a/internal/licenses/parser.go +++ b/internal/licenses/parser.go @@ -4,7 +4,10 @@ import ( "io" "github.com/google/licensecheck" - "golang.org/x/exp/slices" + + "github.com/anchore/syft/syft/license" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/source" ) const ( @@ -13,21 +16,28 @@ const ( ) // Parse scans the contents of a license file to attempt to determine the type of license it is -func Parse(reader io.Reader) (licenses []string, err error) { +func Parse(reader io.Reader, l source.Location) (licenses []pkg.License, err error) { + licenses = make([]pkg.License, 0) contents, err := io.ReadAll(reader) if err != nil { return nil, err } cov := licensecheck.Scan(contents) - - if cov.Percent < float64(coverageThreshold) { - licenses = append(licenses, unknownLicenseType) + if cov.Percent < coverageThreshold { + // unknown or no licenses here? + return licenses, nil } + for _, m := range cov.Match { - if slices.Contains(licenses, m.ID) { - continue + // TODO: spdx ID validation here? + l := pkg.License{ + SPDXExpression: m.ID, + Type: license.Concluded, + Location: l, } - licenses = append(licenses, m.ID) + + licenses = append(licenses, l) } - return + + return licenses, nil } diff --git a/syft/formats/syftjson/to_format_model.go b/syft/formats/syftjson/to_format_model.go index 0caa1f9da00..d30ea5d3c30 100644 --- a/syft/formats/syftjson/to_format_model.go +++ b/syft/formats/syftjson/to_format_model.go @@ -204,12 +204,8 @@ func toPackageModel(p pkg.Package) model.Package { Version: p.Version, Type: p.Type, FoundBy: p.FoundBy, -<<<<<<< HEAD - Locations: coordinates, -======= Locations: p.Locations.ToSlice(), - Licenses: licenses, ->>>>>>> main + Licenses: p.Licenses, Language: p.Language, CPEs: cpes, PURL: p.PURL, diff --git a/syft/pkg/cataloger/golang/licenses.go b/syft/pkg/cataloger/golang/licenses.go index bfffb9f7f31..e85ad7ec7f0 100644 --- a/syft/pkg/cataloger/golang/licenses.go +++ b/syft/pkg/cataloger/golang/licenses.go @@ -22,6 +22,7 @@ import ( "github.com/anchore/syft/internal/licenses" "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/event" + "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/source" ) @@ -74,7 +75,7 @@ func modCacheResolver(modCacheDir string) source.WritableFileResolver { return r } -func (c *goLicenses) getLicenses(resolver source.FileResolver, moduleName, moduleVersion string) (licenses []string, err error) { +func (c *goLicenses) getLicenses(resolver source.FileResolver, moduleName, moduleVersion string) (licenses []pkg.License, err error) { licenses, err = findLicenses(resolver, fmt.Sprintf(`**/go/pkg/mod/%s@%s/*`, processCaps(moduleName), moduleVersion), ) @@ -93,7 +94,7 @@ func (c *goLicenses) getLicenses(resolver source.FileResolver, moduleName, modul return requireCollection(licenses), err } -func (c *goLicenses) getLicensesFromLocal(moduleName, moduleVersion string) ([]string, error) { +func (c *goLicenses) getLicensesFromLocal(moduleName, moduleVersion string) ([]pkg.License, error) { if !c.opts.searchLocalModCacheLicenses { return nil, nil } @@ -103,7 +104,7 @@ func (c *goLicenses) getLicensesFromLocal(moduleName, moduleVersion string) ([]s return findLicenses(c.localModCacheResolver, moduleSearchGlob(moduleName, moduleVersion)) } -func (c *goLicenses) getLicensesFromRemote(moduleName, moduleVersion string) ([]string, error) { +func (c *goLicenses) getLicensesFromRemote(moduleName, moduleVersion string) ([]pkg.License, error) { if !c.opts.searchRemoteLicenses { return nil, nil } @@ -148,14 +149,15 @@ func moduleSearchGlob(moduleName, moduleVersion string) string { return fmt.Sprintf("%s/*", moduleDir(moduleName, moduleVersion)) } -func requireCollection(licenses []string) []string { +func requireCollection(licenses []pkg.License) []pkg.License { if licenses == nil { - return []string{} + return make([]pkg.License, 0) } return licenses } -func findLicenses(resolver source.FileResolver, globMatch string) (out []string, err error) { +func findLicenses(resolver source.FileResolver, globMatch string) (out []pkg.License, err error) { + out = make([]pkg.License, 0) if resolver == nil { return } @@ -172,7 +174,7 @@ func findLicenses(resolver source.FileResolver, globMatch string) (out []string, if err != nil { return nil, err } - parsed, err := licenses.Parse(contents) + parsed, err := licenses.Parse(contents, l) if err != nil { return nil, err } diff --git a/syft/pkg/cataloger/java/archive_parser.go b/syft/pkg/cataloger/java/archive_parser.go index 9f36b5ca41f..7b12ace769e 100644 --- a/syft/pkg/cataloger/java/archive_parser.go +++ b/syft/pkg/cataloger/java/archive_parser.go @@ -195,10 +195,10 @@ func (j *archiveParser) discoverMainPackage() (*pkg.Package, error) { } return &pkg.Package{ - Name: selectName(manifest, j.fileInfo), - Version: selectVersion(manifest, j.fileInfo), - Language: pkg.Java, - Licenses: licenses, + Name: selectName(manifest, j.fileInfo), + Version: selectVersion(manifest, j.fileInfo), + Language: pkg.Java, + Licenses: licenses, Locations: source.NewLocationSet( j.location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), ), diff --git a/syft/pkg/cataloger/javascript/parse_package_json_test.go b/syft/pkg/cataloger/javascript/parse_package_json_test.go index 2497a323139..508672d8d5d 100644 --- a/syft/pkg/cataloger/javascript/parse_package_json_test.go +++ b/syft/pkg/cataloger/javascript/parse_package_json_test.go @@ -34,11 +34,11 @@ func TestParsePackageJSON(t *testing.T) { }, MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ - Name: "npm", - Version: "6.14.6", - Author: "Isaac Z. Schlueter (http://blog.izs.me)", - Homepage: "https://docs.npmjs.com/", - URL: "https://github.com/npm/cli", + Name: "npm", + Version: "6.14.6", + Author: "Isaac Z. Schlueter (http://blog.izs.me)", + Homepage: "https://docs.npmjs.com/", + URL: "https://github.com/npm/cli", Description: "a package manager for JavaScript", }, }, @@ -61,21 +61,12 @@ func TestParsePackageJSON(t *testing.T) { }, MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ -<<<<<<< HEAD - Name: "npm", - Version: "6.14.6", - Author: "Isaac Z. Schlueter (http://blog.izs.me)", - Homepage: "https://docs.npmjs.com/", - URL: "https://github.com/npm/cli", -======= Name: "npm", Version: "6.14.6", Author: "Isaac Z. Schlueter (http://blog.izs.me)", Homepage: "https://docs.npmjs.com/", URL: "https://github.com/npm/cli", - Licenses: []string{"ISC"}, Description: "a package manager for JavaScript", ->>>>>>> main }, }, }, @@ -103,11 +94,11 @@ func TestParsePackageJSON(t *testing.T) { Language: pkg.JavaScript, MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ - Name: "npm", - Version: "6.14.6", - Author: "Isaac Z. Schlueter (http://blog.izs.me)", - Homepage: "https://docs.npmjs.com/", - URL: "https://github.com/npm/cli", + Name: "npm", + Version: "6.14.6", + Author: "Isaac Z. Schlueter (http://blog.izs.me)", + Homepage: "https://docs.npmjs.com/", + URL: "https://github.com/npm/cli", Description: "a package manager for JavaScript", }, }, @@ -123,11 +114,11 @@ func TestParsePackageJSON(t *testing.T) { Language: pkg.JavaScript, MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ - Name: "npm", - Version: "6.14.6", - Author: "Isaac Z. Schlueter (http://blog.izs.me)", - Homepage: "https://docs.npmjs.com/", - URL: "https://github.com/npm/cli", + Name: "npm", + Version: "6.14.6", + Author: "Isaac Z. Schlueter (http://blog.izs.me)", + Homepage: "https://docs.npmjs.com/", + URL: "https://github.com/npm/cli", Description: "a package manager for JavaScript", }, }, @@ -143,11 +134,11 @@ func TestParsePackageJSON(t *testing.T) { Language: pkg.JavaScript, MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ - Name: "npm", - Version: "6.14.6", - Author: "Isaac Z. Schlueter (http://blog.izs.me)", - Homepage: "https://docs.npmjs.com/", - URL: "https://github.com/npm/cli", + Name: "npm", + Version: "6.14.6", + Author: "Isaac Z. Schlueter (http://blog.izs.me)", + Homepage: "https://docs.npmjs.com/", + URL: "https://github.com/npm/cli", Description: "a package manager for JavaScript", }, }, @@ -170,11 +161,11 @@ func TestParsePackageJSON(t *testing.T) { Language: pkg.JavaScript, MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ - Name: "npm", - Version: "6.14.6", - Author: "Isaac Z. Schlueter (http://blog.izs.me)", - Homepage: "https://docs.npmjs.com/", - URL: "https://github.com/npm/cli", + Name: "npm", + Version: "6.14.6", + Author: "Isaac Z. Schlueter (http://blog.izs.me)", + Homepage: "https://docs.npmjs.com/", + URL: "https://github.com/npm/cli", Description: "a package manager for JavaScript", }, }, @@ -197,11 +188,11 @@ func TestParsePackageJSON(t *testing.T) { Language: pkg.JavaScript, MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ - Name: "function-bind", - Version: "1.1.1", - Author: "Raynos ", - Homepage: "https://github.com/Raynos/function-bind", - URL: "git://github.com/Raynos/function-bind.git", + Name: "function-bind", + Version: "1.1.1", + Author: "Raynos ", + Homepage: "https://github.com/Raynos/function-bind", + URL: "git://github.com/Raynos/function-bind.git", Description: "Implementation of Function.prototype.bind", }, }, @@ -224,12 +215,12 @@ func TestParsePackageJSON(t *testing.T) { Language: pkg.JavaScript, MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ - Name: "npm", - Version: "6.14.6", - Author: "Isaac Z. Schlueter (http://blog.izs.me)", - Homepage: "https://docs.npmjs.com/", - URL: "https://github.com/npm/cli", - Private: true, + Name: "npm", + Version: "6.14.6", + Author: "Isaac Z. Schlueter (http://blog.izs.me)", + Homepage: "https://docs.npmjs.com/", + URL: "https://github.com/npm/cli", + Private: true, Description: "a package manager for JavaScript", }, }, diff --git a/syft/pkg/cataloger/kernel/package.go b/syft/pkg/cataloger/kernel/package.go index 69fab21b308..85198320b71 100644 --- a/syft/pkg/cataloger/kernel/package.go +++ b/syft/pkg/cataloger/kernel/package.go @@ -4,6 +4,7 @@ import ( "strings" "github.com/anchore/packageurl-go" + "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/source" ) @@ -27,11 +28,12 @@ func newLinuxKernelPackage(metadata pkg.LinuxKernelMetadata, locations ...source } func newLinuxKernelModulePackage(metadata pkg.LinuxKernelModuleMetadata, locations ...source.Location) pkg.Package { - var licenses []string + licenses := make([]pkg.License, 0) if metadata.License != "" { - licenses = []string{metadata.License} - } else { - licenses = []string{} + licenses = append(licenses, pkg.License{ + Value: metadata.License, + Type: license.Declared, + }) } p := pkg.Package{ diff --git a/syft/pkg/cataloger/php/parse_composer_lock.go b/syft/pkg/cataloger/php/parse_composer_lock.go index 29900671c86..248b7519eb8 100644 --- a/syft/pkg/cataloger/php/parse_composer_lock.go +++ b/syft/pkg/cataloger/php/parse_composer_lock.go @@ -47,7 +47,7 @@ func parseComposerLock(_ source.FileResolver, _ *generic.Environment, reader sou } // TODO: did we omit this on purpose? - //for _, m := range lock.PackageDev { + // for _, m := range lock.PackageDev { // pkgs = append(pkgs, newComposerLockPackage(m, reader.Location)) //} } diff --git a/syft/pkg/cataloger/python/parse_wheel_egg.go b/syft/pkg/cataloger/python/parse_wheel_egg.go index 7299e150c22..60756bf7601 100644 --- a/syft/pkg/cataloger/python/parse_wheel_egg.go +++ b/syft/pkg/cataloger/python/parse_wheel_egg.go @@ -17,21 +17,21 @@ import ( // parseWheelOrEgg takes the primary metadata file reference and returns the python package it represents. func parseWheelOrEgg(resolver source.FileResolver, _ *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { - metadata, sources, err := assembleEggOrWheelMetadata(resolver, reader.Location) + parsedData, sources, err := assembleEggOrWheelMetadata(resolver, reader.Location) if err != nil { return nil, nil, err } - if metadata == nil { + if parsedData == nil { return nil, nil, nil } // This can happen for Python 2.7 where it is reported from an egg-info, but Python is // the actual runtime, it isn't a "package". The special-casing here allows to skip it - if metadata.Name == "Python" { + if parsedData.Name == "Python" { return nil, nil, nil } - pkgs := []pkg.Package{newPackageForPackage(*metadata, sources...)} + pkgs := []pkg.Package{newPackageForPackage(*parsedData, sources...)} return pkgs, nil, nil } @@ -160,7 +160,7 @@ func fetchDirectURLData(resolver source.FileResolver, metadataLocation source.Lo } // assembleEggOrWheelMetadata discovers and accumulates python package metadata from multiple file sources and returns a single metadata object as well as a list of files where the metadata was derived from. -func assembleEggOrWheelMetadata(resolver source.FileResolver, metadataLocation source.Location) (*pkg.PythonPackageMetadata, []source.Location, error) { +func assembleEggOrWheelMetadata(resolver source.FileResolver, metadataLocation source.Location) (*parsedData, []source.Location, error) { var sources = []source.Location{ metadataLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), } @@ -171,12 +171,12 @@ func assembleEggOrWheelMetadata(resolver source.FileResolver, metadataLocation s } defer internal.CloseAndLogError(metadataContents, metadataLocation.VirtualPath) - metadata, err := parseWheelOrEggMetadata(metadataLocation.RealPath, metadataContents) + parsedData, err := parseWheelOrEggMetadata(metadataLocation.RealPath, metadataContents) if err != nil { return nil, nil, err } - if metadata.Name == "" { + if parsedData.Name == "" { return nil, nil, nil } @@ -186,14 +186,14 @@ func assembleEggOrWheelMetadata(resolver source.FileResolver, metadataLocation s return nil, nil, err } if len(r) == 0 { - r, s, err = fetchInstalledFiles(resolver, metadataLocation, metadata.SitePackagesRootPath) + r, s, err = fetchInstalledFiles(resolver, metadataLocation, parsedData.SitePackagesRootPath) if err != nil { return nil, nil, err } } sources = append(sources, s...) - metadata.Files = r + parsedData.Files = r // attach any top-level package names found for the given wheel/egg installation p, s, err := fetchTopLevelPackages(resolver, metadataLocation) @@ -201,15 +201,15 @@ func assembleEggOrWheelMetadata(resolver source.FileResolver, metadataLocation s return nil, nil, err } sources = append(sources, s...) - metadata.TopLevelPackages = p + parsedData.TopLevelPackages = p // attach any direct-url package data found for the given wheel/egg installation d, s, err := fetchDirectURLData(resolver, metadataLocation) if err != nil { return nil, nil, err } - sources = append(sources, s...) - metadata.DirectURLOrigin = d - return &metadata, sources, nil + sources = append(sources, s...) + parsedData.DirectURLOrigin = d + return &parsedData, sources, nil } diff --git a/syft/pkg/npm_package_json_metadata.go b/syft/pkg/npm_package_json_metadata.go index 404dff78d26..a17f4035f2d 100644 --- a/syft/pkg/npm_package_json_metadata.go +++ b/syft/pkg/npm_package_json_metadata.go @@ -2,11 +2,11 @@ package pkg // NpmPackageJSONMetadata holds parsing information for a javascript package.json file type NpmPackageJSONMetadata struct { - Name string `mapstructure:"name" json:"name"` - Version string `mapstructure:"version" json:"version"` - Author string `mapstructure:"author" json:"author"` - Homepage string `mapstructure:"homepage" json:"homepage"` - Description string `mapstructure:"description" json:"description"` - URL string `mapstructure:"url" json:"url"` - Private bool `mapstructure:"private" json:"private"` + Name string `mapstructure:"name" json:"name"` + Version string `mapstructure:"version" json:"version"` + Author string `mapstructure:"author" json:"author"` + Homepage string `mapstructure:"homepage" json:"homepage"` + Description string `mapstructure:"description" json:"description"` + URL string `mapstructure:"url" json:"url"` + Private bool `mapstructure:"private" json:"private"` } From 3d6755c5eabaca19863442586012fc85b06fa784 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Mon, 17 Apr 2023 12:55:34 -0400 Subject: [PATCH 21/99] chore: update schema with new breaking changes Signed-off-by: Christopher Phillips --- internal/constants.go | 2 +- schema/json/schema-9.0.0.json | 1770 +++++++++++++++++++++++++++++++++ 2 files changed, 1771 insertions(+), 1 deletion(-) create mode 100644 schema/json/schema-9.0.0.json diff --git a/internal/constants.go b/internal/constants.go index b7be2824039..bf520ab9068 100644 --- a/internal/constants.go +++ b/internal/constants.go @@ -6,5 +6,5 @@ const ( // JSONSchemaVersion is the current schema version output by the JSON encoder // This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment. - JSONSchemaVersion = "8.0.0" + JSONSchemaVersion = "9.0.0" ) diff --git a/schema/json/schema-9.0.0.json b/schema/json/schema-9.0.0.json new file mode 100644 index 00000000000..ce6916f1934 --- /dev/null +++ b/schema/json/schema-9.0.0.json @@ -0,0 +1,1770 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/anchore/syft/syft/formats/syftjson/model/document", + "$ref": "#/$defs/Document", + "$defs": { + "AlpmFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "type": { + "type": "string" + }, + "uid": { + "type": "string" + }, + "gid": { + "type": "string" + }, + "time": { + "type": "string", + "format": "date-time" + }, + "size": { + "type": "string" + }, + "link": { + "type": "string" + }, + "digest": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object" + }, + "AlpmMetadata": { + "properties": { + "basepackage": { + "type": "string" + }, + "package": { + "type": "string" + }, + "version": { + "type": "string" + }, + "description": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "packager": { + "type": "string" + }, + "url": { + "type": "string" + }, + "validation": { + "type": "string" + }, + "reason": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/AlpmFileRecord" + }, + "type": "array" + }, + "backup": { + "items": { + "$ref": "#/$defs/AlpmFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "basepackage", + "package", + "version", + "description", + "architecture", + "size", + "packager", + "url", + "validation", + "reason", + "files", + "backup" + ] + }, + "ApkFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "ownerUid": { + "type": "string" + }, + "ownerGid": { + "type": "string" + }, + "permissions": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "ApkMetadata": { + "properties": { + "package": { + "type": "string" + }, + "originPackage": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "version": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "url": { + "type": "string" + }, + "description": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "installedSize": { + "type": "integer" + }, + "pullDependencies": { + "items": { + "type": "string" + }, + "type": "array" + }, + "provides": { + "items": { + "type": "string" + }, + "type": "array" + }, + "pullChecksum": { + "type": "string" + }, + "gitCommitOfApkPort": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/ApkFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "originPackage", + "maintainer", + "version", + "architecture", + "url", + "description", + "size", + "installedSize", + "pullDependencies", + "provides", + "pullChecksum", + "gitCommitOfApkPort", + "files" + ] + }, + "BinaryMetadata": { + "properties": { + "matches": { + "items": { + "$ref": "#/$defs/ClassifierMatch" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "matches" + ] + }, + "CargoPackageMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "type": "string" + }, + "checksum": { + "type": "string" + }, + "dependencies": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "checksum", + "dependencies" + ] + }, + "ClassifierMatch": { + "properties": { + "classifier": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Location" + } + }, + "type": "object", + "required": [ + "classifier", + "location" + ] + }, + "CocoapodsMetadata": { + "properties": { + "checksum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "checksum" + ] + }, + "ConanLockMetadata": { + "properties": { + "ref": { + "type": "string" + }, + "package_id": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "requires": { + "type": "string" + }, + "build_requires": { + "type": "string" + }, + "py_requires": { + "type": "string" + }, + "options": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "path": { + "type": "string" + }, + "context": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "ConanMetadata": { + "properties": { + "ref": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "Coordinates": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "DartPubMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "hosted_url": { + "type": "string" + }, + "vcs_url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Descriptor": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "configuration": true + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Digest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algorithm", + "value" + ] + }, + "Document": { + "properties": { + "artifacts": { + "items": { + "$ref": "#/$defs/Package" + }, + "type": "array" + }, + "artifactRelationships": { + "items": { + "$ref": "#/$defs/Relationship" + }, + "type": "array" + }, + "files": { + "items": { + "$ref": "#/$defs/File" + }, + "type": "array" + }, + "secrets": { + "items": { + "$ref": "#/$defs/Secrets" + }, + "type": "array" + }, + "source": { + "$ref": "#/$defs/Source" + }, + "distro": { + "$ref": "#/$defs/LinuxRelease" + }, + "descriptor": { + "$ref": "#/$defs/Descriptor" + }, + "schema": { + "$ref": "#/$defs/Schema" + } + }, + "type": "object", + "required": [ + "artifacts", + "artifactRelationships", + "source", + "distro", + "descriptor", + "schema" + ] + }, + "DotnetDepsMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "path": { + "type": "string" + }, + "sha512": { + "type": "string" + }, + "hashPath": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "path", + "sha512", + "hashPath" + ] + }, + "DpkgFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "isConfigFile": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "path", + "isConfigFile" + ] + }, + "DpkgMetadata": { + "properties": { + "package": { + "type": "string" + }, + "source": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "installedSize": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/DpkgFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "source", + "version", + "sourceVersion", + "architecture", + "maintainer", + "installedSize", + "files" + ] + }, + "File": { + "properties": { + "id": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Coordinates" + }, + "metadata": { + "$ref": "#/$defs/FileMetadataEntry" + }, + "contents": { + "type": "string" + }, + "digests": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "id", + "location" + ] + }, + "FileMetadataEntry": { + "properties": { + "mode": { + "type": "integer" + }, + "type": { + "type": "string" + }, + "linkDestination": { + "type": "string" + }, + "userID": { + "type": "integer" + }, + "groupID": { + "type": "integer" + }, + "mimeType": { + "type": "string" + }, + "size": { + "type": "integer" + } + }, + "type": "object", + "required": [ + "mode", + "type", + "userID", + "groupID", + "mimeType", + "size" + ] + }, + "GemMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "type": "string" + }, + "type": "array" + }, + "homepage": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "GolangBinMetadata": { + "properties": { + "goBuildSettings": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "goCompiledVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "h1Digest": { + "type": "string" + }, + "mainModule": { + "type": "string" + } + }, + "type": "object", + "required": [ + "goCompiledVersion", + "architecture" + ] + }, + "GolangModMetadata": { + "properties": { + "h1Digest": { + "type": "string" + } + }, + "type": "object" + }, + "HackageMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "snapshotURL": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "IDLikes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "JavaManifest": { + "properties": { + "main": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "namedSections": { + "patternProperties": { + ".*": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "JavaMetadata": { + "properties": { + "virtualPath": { + "type": "string" + }, + "manifest": { + "$ref": "#/$defs/JavaManifest" + }, + "pomProperties": { + "$ref": "#/$defs/PomProperties" + }, + "pomProject": { + "$ref": "#/$defs/PomProject" + }, + "digest": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "virtualPath" + ] + }, + "KbPackageMetadata": { + "properties": { + "product_id": { + "type": "string" + }, + "kb": { + "type": "string" + } + }, + "type": "object", + "required": [ + "product_id", + "kb" + ] + }, + "License": { + "properties": { + "value": { + "type": "string" + }, + "spdx-expression": { + "type": "string" + }, + "type": { + "type": "string" + }, + "url": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Location" + } + }, + "type": "object", + "required": [ + "value", + "spdx-expression", + "type", + "url", + "location" + ] + }, + "LinuxKernelMetadata": { + "properties": { + "name": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "version": { + "type": "string" + }, + "extendedVersion": { + "type": "string" + }, + "buildTime": { + "type": "string" + }, + "author": { + "type": "string" + }, + "format": { + "type": "string" + }, + "rwRootFS": { + "type": "boolean" + }, + "swapDevice": { + "type": "integer" + }, + "rootDevice": { + "type": "integer" + }, + "videoMode": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "architecture", + "version" + ] + }, + "LinuxKernelModuleMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "path": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "license": { + "type": "string" + }, + "kernelVersion": { + "type": "string" + }, + "versionMagic": { + "type": "string" + }, + "parameters": { + "patternProperties": { + ".*": { + "$ref": "#/$defs/LinuxKernelModuleParameter" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "LinuxKernelModuleParameter": { + "properties": { + "type": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "type": "object" + }, + "LinuxRelease": { + "properties": { + "prettyName": { + "type": "string" + }, + "name": { + "type": "string" + }, + "id": { + "type": "string" + }, + "idLike": { + "$ref": "#/$defs/IDLikes" + }, + "version": { + "type": "string" + }, + "versionID": { + "type": "string" + }, + "versionCodename": { + "type": "string" + }, + "buildID": { + "type": "string" + }, + "imageID": { + "type": "string" + }, + "imageVersion": { + "type": "string" + }, + "variant": { + "type": "string" + }, + "variantID": { + "type": "string" + }, + "homeURL": { + "type": "string" + }, + "supportURL": { + "type": "string" + }, + "bugReportURL": { + "type": "string" + }, + "privacyPolicyURL": { + "type": "string" + }, + "cpeName": { + "type": "string" + }, + "supportEnd": { + "type": "string" + } + }, + "type": "object" + }, + "Location": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + }, + "annotations": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "MixLockMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "pkgHashExt": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "pkgHash", + "pkgHashExt" + ] + }, + "NixStoreMetadata": { + "properties": { + "outputHash": { + "type": "string" + }, + "output": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "outputHash", + "files" + ] + }, + "NpmPackageJSONMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "author": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + }, + "private": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "homepage", + "description", + "url", + "private" + ] + }, + "NpmPackageLockJSONMetadata": { + "properties": { + "resolved": { + "type": "string" + }, + "integrity": { + "type": "string" + } + }, + "type": "object", + "required": [ + "resolved", + "integrity" + ] + }, + "Package": { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "type": { + "type": "string" + }, + "foundBy": { + "type": "string" + }, + "locations": { + "items": { + "$ref": "#/$defs/Location" + }, + "type": "array" + }, + "licenses": { + "items": { + "$ref": "#/$defs/License" + }, + "type": "array" + }, + "language": { + "type": "string" + }, + "cpes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "purl": { + "type": "string" + }, + "metadataType": { + "type": "string" + }, + "metadata": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "#/$defs/AlpmMetadata" + }, + { + "$ref": "#/$defs/ApkMetadata" + }, + { + "$ref": "#/$defs/BinaryMetadata" + }, + { + "$ref": "#/$defs/CargoPackageMetadata" + }, + { + "$ref": "#/$defs/CocoapodsMetadata" + }, + { + "$ref": "#/$defs/ConanLockMetadata" + }, + { + "$ref": "#/$defs/ConanMetadata" + }, + { + "$ref": "#/$defs/DartPubMetadata" + }, + { + "$ref": "#/$defs/DotnetDepsMetadata" + }, + { + "$ref": "#/$defs/DpkgMetadata" + }, + { + "$ref": "#/$defs/GemMetadata" + }, + { + "$ref": "#/$defs/GolangBinMetadata" + }, + { + "$ref": "#/$defs/GolangModMetadata" + }, + { + "$ref": "#/$defs/HackageMetadata" + }, + { + "$ref": "#/$defs/JavaMetadata" + }, + { + "$ref": "#/$defs/KbPackageMetadata" + }, + { + "$ref": "#/$defs/LinuxKernelMetadata" + }, + { + "$ref": "#/$defs/LinuxKernelModuleMetadata" + }, + { + "$ref": "#/$defs/MixLockMetadata" + }, + { + "$ref": "#/$defs/NixStoreMetadata" + }, + { + "$ref": "#/$defs/NpmPackageJSONMetadata" + }, + { + "$ref": "#/$defs/NpmPackageLockJSONMetadata" + }, + { + "$ref": "#/$defs/PhpComposerJSONMetadata" + }, + { + "$ref": "#/$defs/PortageMetadata" + }, + { + "$ref": "#/$defs/PythonPackageMetadata" + }, + { + "$ref": "#/$defs/PythonPipfileLockMetadata" + }, + { + "$ref": "#/$defs/RebarLockMetadata" + }, + { + "$ref": "#/$defs/RpmMetadata" + } + ] + } + }, + "type": "object", + "required": [ + "id", + "name", + "version", + "type", + "foundBy", + "locations", + "licenses", + "language", + "cpes", + "purl" + ] + }, + "PhpComposerAuthors": { + "properties": { + "name": { + "type": "string" + }, + "email": { + "type": "string" + }, + "homepage": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name" + ] + }, + "PhpComposerExternalReference": { + "properties": { + "type": { + "type": "string" + }, + "url": { + "type": "string" + }, + "reference": { + "type": "string" + }, + "shasum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "type", + "url", + "reference" + ] + }, + "PhpComposerJSONMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "dist": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "require": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "provide": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "require-dev": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "suggest": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "license": { + "items": { + "type": "string" + }, + "type": "array" + }, + "type": { + "type": "string" + }, + "notification-url": { + "type": "string" + }, + "bin": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "$ref": "#/$defs/PhpComposerAuthors" + }, + "type": "array" + }, + "description": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "keywords": { + "items": { + "type": "string" + }, + "type": "array" + }, + "time": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "dist" + ] + }, + "PomParent": { + "properties": { + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "type": "object", + "required": [ + "groupId", + "artifactId", + "version" + ] + }, + "PomProject": { + "properties": { + "path": { + "type": "string" + }, + "parent": { + "$ref": "#/$defs/PomParent" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "groupId", + "artifactId", + "version", + "name" + ] + }, + "PomProperties": { + "properties": { + "path": { + "type": "string" + }, + "name": { + "type": "string" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "extraFields": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path", + "name", + "groupId", + "artifactId", + "version" + ] + }, + "PortageFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "PortageMetadata": { + "properties": { + "installedSize": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/PortageFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "installedSize", + "files" + ] + }, + "PythonDirectURLOriginInfo": { + "properties": { + "url": { + "type": "string" + }, + "commitId": { + "type": "string" + }, + "vcs": { + "type": "string" + } + }, + "type": "object", + "required": [ + "url" + ] + }, + "PythonFileDigest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algorithm", + "value" + ] + }, + "PythonFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/PythonFileDigest" + }, + "size": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "PythonPackageMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "author": { + "type": "string" + }, + "authorEmail": { + "type": "string" + }, + "platform": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/PythonFileRecord" + }, + "type": "array" + }, + "sitePackagesRootPath": { + "type": "string" + }, + "topLevelPackages": { + "items": { + "type": "string" + }, + "type": "array" + }, + "directUrlOrigin": { + "$ref": "#/$defs/PythonDirectURLOriginInfo" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "authorEmail", + "platform", + "sitePackagesRootPath" + ] + }, + "PythonPipfileLockMetadata": { + "properties": { + "hashes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "index": { + "type": "string" + } + }, + "type": "object", + "required": [ + "hashes", + "index" + ] + }, + "RebarLockMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "pkgHashExt": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "pkgHash", + "pkgHashExt" + ] + }, + "Relationship": { + "properties": { + "parent": { + "type": "string" + }, + "child": { + "type": "string" + }, + "type": { + "type": "string" + }, + "metadata": true + }, + "type": "object", + "required": [ + "parent", + "child", + "type" + ] + }, + "RpmMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "epoch": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "architecture": { + "type": "string" + }, + "release": { + "type": "string" + }, + "sourceRpm": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "vendor": { + "type": "string" + }, + "modularityLabel": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/RpmdbFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "epoch", + "architecture", + "release", + "sourceRpm", + "size", + "vendor", + "modularityLabel", + "files" + ] + }, + "RpmdbFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "mode": { + "type": "integer" + }, + "size": { + "type": "integer" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "userName": { + "type": "string" + }, + "groupName": { + "type": "string" + }, + "flags": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "mode", + "size", + "digest", + "userName", + "groupName", + "flags" + ] + }, + "Schema": { + "properties": { + "version": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "version", + "url" + ] + }, + "SearchResult": { + "properties": { + "classification": { + "type": "string" + }, + "lineNumber": { + "type": "integer" + }, + "lineOffset": { + "type": "integer" + }, + "seekPosition": { + "type": "integer" + }, + "length": { + "type": "integer" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "classification", + "lineNumber", + "lineOffset", + "seekPosition", + "length" + ] + }, + "Secrets": { + "properties": { + "location": { + "$ref": "#/$defs/Coordinates" + }, + "secrets": { + "items": { + "$ref": "#/$defs/SearchResult" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "location", + "secrets" + ] + }, + "Source": { + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string" + }, + "target": true + }, + "type": "object", + "required": [ + "id", + "type", + "target" + ] + } + } +} From 0d4cc6fe9e8c138b7583bfbb2f6d6ef656a4d18b Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Mon, 17 Apr 2023 12:58:11 -0400 Subject: [PATCH 22/99] chore: remove double schema bump Signed-off-by: Christopher Phillips --- internal/constants.go | 2 +- schema/json/schema-8.0.0.json | 176 +++- schema/json/schema-9.0.0.json | 1770 --------------------------------- 3 files changed, 169 insertions(+), 1779 deletions(-) delete mode 100644 schema/json/schema-9.0.0.json diff --git a/internal/constants.go b/internal/constants.go index bf520ab9068..b7be2824039 100644 --- a/internal/constants.go +++ b/internal/constants.go @@ -6,5 +6,5 @@ const ( // JSONSchemaVersion is the current schema version output by the JSON encoder // This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment. - JSONSchemaVersion = "9.0.0" + JSONSchemaVersion = "8.0.0" ) diff --git a/schema/json/schema-8.0.0.json b/schema/json/schema-8.0.0.json index aaae4eee4d9..ce6916f1934 100644 --- a/schema/json/schema-8.0.0.json +++ b/schema/json/schema-8.0.0.json @@ -561,6 +561,9 @@ }, "mimeType": { "type": "string" + }, + "size": { + "type": "integer" } }, "type": "object", @@ -569,7 +572,8 @@ "type", "userID", "groupID", - "mimeType" + "mimeType", + "size" ] }, "GemMetadata": { @@ -733,6 +737,127 @@ "kb" ] }, + "License": { + "properties": { + "value": { + "type": "string" + }, + "spdx-expression": { + "type": "string" + }, + "type": { + "type": "string" + }, + "url": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Location" + } + }, + "type": "object", + "required": [ + "value", + "spdx-expression", + "type", + "url", + "location" + ] + }, + "LinuxKernelMetadata": { + "properties": { + "name": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "version": { + "type": "string" + }, + "extendedVersion": { + "type": "string" + }, + "buildTime": { + "type": "string" + }, + "author": { + "type": "string" + }, + "format": { + "type": "string" + }, + "rwRootFS": { + "type": "boolean" + }, + "swapDevice": { + "type": "integer" + }, + "rootDevice": { + "type": "integer" + }, + "videoMode": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "architecture", + "version" + ] + }, + "LinuxKernelModuleMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "path": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "license": { + "type": "string" + }, + "kernelVersion": { + "type": "string" + }, + "versionMagic": { + "type": "string" + }, + "parameters": { + "patternProperties": { + ".*": { + "$ref": "#/$defs/LinuxKernelModuleParameter" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "LinuxKernelModuleParameter": { + "properties": { + "type": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "type": "object" + }, "LinuxRelease": { "properties": { "prettyName": { @@ -800,8 +925,13 @@ "layerID": { "type": "string" }, - "virtualPath": { - "type": "string" + "annotations": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" } }, "type": "object", @@ -832,12 +962,12 @@ "pkgHashExt" ] }, - "NpmPackageJSONMetadata": { + "NixStoreMetadata": { "properties": { - "name": { + "outputHash": { "type": "string" }, - "version": { + "output": { "type": "string" }, "files": { @@ -845,6 +975,21 @@ "type": "string" }, "type": "array" + } + }, + "type": "object", + "required": [ + "outputHash", + "files" + ] + }, + "NpmPackageJSONMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" }, "author": { "type": "string" @@ -907,13 +1052,13 @@ }, "locations": { "items": { - "$ref": "#/$defs/Coordinates" + "$ref": "#/$defs/Location" }, "type": "array" }, "licenses": { "items": { - "type": "string" + "$ref": "#/$defs/License" }, "type": "array" }, @@ -985,9 +1130,18 @@ { "$ref": "#/$defs/KbPackageMetadata" }, + { + "$ref": "#/$defs/LinuxKernelMetadata" + }, + { + "$ref": "#/$defs/LinuxKernelModuleMetadata" + }, { "$ref": "#/$defs/MixLockMetadata" }, + { + "$ref": "#/$defs/NixStoreMetadata" + }, { "$ref": "#/$defs/NpmPackageJSONMetadata" }, @@ -1114,6 +1268,12 @@ }, "type": "object" }, + "license": { + "items": { + "type": "string" + }, + "type": "array" + }, "type": { "type": "string" }, diff --git a/schema/json/schema-9.0.0.json b/schema/json/schema-9.0.0.json deleted file mode 100644 index ce6916f1934..00000000000 --- a/schema/json/schema-9.0.0.json +++ /dev/null @@ -1,1770 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/anchore/syft/syft/formats/syftjson/model/document", - "$ref": "#/$defs/Document", - "$defs": { - "AlpmFileRecord": { - "properties": { - "path": { - "type": "string" - }, - "type": { - "type": "string" - }, - "uid": { - "type": "string" - }, - "gid": { - "type": "string" - }, - "time": { - "type": "string", - "format": "date-time" - }, - "size": { - "type": "string" - }, - "link": { - "type": "string" - }, - "digest": { - "items": { - "$ref": "#/$defs/Digest" - }, - "type": "array" - } - }, - "type": "object" - }, - "AlpmMetadata": { - "properties": { - "basepackage": { - "type": "string" - }, - "package": { - "type": "string" - }, - "version": { - "type": "string" - }, - "description": { - "type": "string" - }, - "architecture": { - "type": "string" - }, - "size": { - "type": "integer" - }, - "packager": { - "type": "string" - }, - "url": { - "type": "string" - }, - "validation": { - "type": "string" - }, - "reason": { - "type": "integer" - }, - "files": { - "items": { - "$ref": "#/$defs/AlpmFileRecord" - }, - "type": "array" - }, - "backup": { - "items": { - "$ref": "#/$defs/AlpmFileRecord" - }, - "type": "array" - } - }, - "type": "object", - "required": [ - "basepackage", - "package", - "version", - "description", - "architecture", - "size", - "packager", - "url", - "validation", - "reason", - "files", - "backup" - ] - }, - "ApkFileRecord": { - "properties": { - "path": { - "type": "string" - }, - "ownerUid": { - "type": "string" - }, - "ownerGid": { - "type": "string" - }, - "permissions": { - "type": "string" - }, - "digest": { - "$ref": "#/$defs/Digest" - } - }, - "type": "object", - "required": [ - "path" - ] - }, - "ApkMetadata": { - "properties": { - "package": { - "type": "string" - }, - "originPackage": { - "type": "string" - }, - "maintainer": { - "type": "string" - }, - "version": { - "type": "string" - }, - "architecture": { - "type": "string" - }, - "url": { - "type": "string" - }, - "description": { - "type": "string" - }, - "size": { - "type": "integer" - }, - "installedSize": { - "type": "integer" - }, - "pullDependencies": { - "items": { - "type": "string" - }, - "type": "array" - }, - "provides": { - "items": { - "type": "string" - }, - "type": "array" - }, - "pullChecksum": { - "type": "string" - }, - "gitCommitOfApkPort": { - "type": "string" - }, - "files": { - "items": { - "$ref": "#/$defs/ApkFileRecord" - }, - "type": "array" - } - }, - "type": "object", - "required": [ - "package", - "originPackage", - "maintainer", - "version", - "architecture", - "url", - "description", - "size", - "installedSize", - "pullDependencies", - "provides", - "pullChecksum", - "gitCommitOfApkPort", - "files" - ] - }, - "BinaryMetadata": { - "properties": { - "matches": { - "items": { - "$ref": "#/$defs/ClassifierMatch" - }, - "type": "array" - } - }, - "type": "object", - "required": [ - "matches" - ] - }, - "CargoPackageMetadata": { - "properties": { - "name": { - "type": "string" - }, - "version": { - "type": "string" - }, - "source": { - "type": "string" - }, - "checksum": { - "type": "string" - }, - "dependencies": { - "items": { - "type": "string" - }, - "type": "array" - } - }, - "type": "object", - "required": [ - "name", - "version", - "source", - "checksum", - "dependencies" - ] - }, - "ClassifierMatch": { - "properties": { - "classifier": { - "type": "string" - }, - "location": { - "$ref": "#/$defs/Location" - } - }, - "type": "object", - "required": [ - "classifier", - "location" - ] - }, - "CocoapodsMetadata": { - "properties": { - "checksum": { - "type": "string" - } - }, - "type": "object", - "required": [ - "checksum" - ] - }, - "ConanLockMetadata": { - "properties": { - "ref": { - "type": "string" - }, - "package_id": { - "type": "string" - }, - "prev": { - "type": "string" - }, - "requires": { - "type": "string" - }, - "build_requires": { - "type": "string" - }, - "py_requires": { - "type": "string" - }, - "options": { - "patternProperties": { - ".*": { - "type": "string" - } - }, - "type": "object" - }, - "path": { - "type": "string" - }, - "context": { - "type": "string" - } - }, - "type": "object", - "required": [ - "ref" - ] - }, - "ConanMetadata": { - "properties": { - "ref": { - "type": "string" - } - }, - "type": "object", - "required": [ - "ref" - ] - }, - "Coordinates": { - "properties": { - "path": { - "type": "string" - }, - "layerID": { - "type": "string" - } - }, - "type": "object", - "required": [ - "path" - ] - }, - "DartPubMetadata": { - "properties": { - "name": { - "type": "string" - }, - "version": { - "type": "string" - }, - "hosted_url": { - "type": "string" - }, - "vcs_url": { - "type": "string" - } - }, - "type": "object", - "required": [ - "name", - "version" - ] - }, - "Descriptor": { - "properties": { - "name": { - "type": "string" - }, - "version": { - "type": "string" - }, - "configuration": true - }, - "type": "object", - "required": [ - "name", - "version" - ] - }, - "Digest": { - "properties": { - "algorithm": { - "type": "string" - }, - "value": { - "type": "string" - } - }, - "type": "object", - "required": [ - "algorithm", - "value" - ] - }, - "Document": { - "properties": { - "artifacts": { - "items": { - "$ref": "#/$defs/Package" - }, - "type": "array" - }, - "artifactRelationships": { - "items": { - "$ref": "#/$defs/Relationship" - }, - "type": "array" - }, - "files": { - "items": { - "$ref": "#/$defs/File" - }, - "type": "array" - }, - "secrets": { - "items": { - "$ref": "#/$defs/Secrets" - }, - "type": "array" - }, - "source": { - "$ref": "#/$defs/Source" - }, - "distro": { - "$ref": "#/$defs/LinuxRelease" - }, - "descriptor": { - "$ref": "#/$defs/Descriptor" - }, - "schema": { - "$ref": "#/$defs/Schema" - } - }, - "type": "object", - "required": [ - "artifacts", - "artifactRelationships", - "source", - "distro", - "descriptor", - "schema" - ] - }, - "DotnetDepsMetadata": { - "properties": { - "name": { - "type": "string" - }, - "version": { - "type": "string" - }, - "path": { - "type": "string" - }, - "sha512": { - "type": "string" - }, - "hashPath": { - "type": "string" - } - }, - "type": "object", - "required": [ - "name", - "version", - "path", - "sha512", - "hashPath" - ] - }, - "DpkgFileRecord": { - "properties": { - "path": { - "type": "string" - }, - "digest": { - "$ref": "#/$defs/Digest" - }, - "isConfigFile": { - "type": "boolean" - } - }, - "type": "object", - "required": [ - "path", - "isConfigFile" - ] - }, - "DpkgMetadata": { - "properties": { - "package": { - "type": "string" - }, - "source": { - "type": "string" - }, - "version": { - "type": "string" - }, - "sourceVersion": { - "type": "string" - }, - "architecture": { - "type": "string" - }, - "maintainer": { - "type": "string" - }, - "installedSize": { - "type": "integer" - }, - "files": { - "items": { - "$ref": "#/$defs/DpkgFileRecord" - }, - "type": "array" - } - }, - "type": "object", - "required": [ - "package", - "source", - "version", - "sourceVersion", - "architecture", - "maintainer", - "installedSize", - "files" - ] - }, - "File": { - "properties": { - "id": { - "type": "string" - }, - "location": { - "$ref": "#/$defs/Coordinates" - }, - "metadata": { - "$ref": "#/$defs/FileMetadataEntry" - }, - "contents": { - "type": "string" - }, - "digests": { - "items": { - "$ref": "#/$defs/Digest" - }, - "type": "array" - } - }, - "type": "object", - "required": [ - "id", - "location" - ] - }, - "FileMetadataEntry": { - "properties": { - "mode": { - "type": "integer" - }, - "type": { - "type": "string" - }, - "linkDestination": { - "type": "string" - }, - "userID": { - "type": "integer" - }, - "groupID": { - "type": "integer" - }, - "mimeType": { - "type": "string" - }, - "size": { - "type": "integer" - } - }, - "type": "object", - "required": [ - "mode", - "type", - "userID", - "groupID", - "mimeType", - "size" - ] - }, - "GemMetadata": { - "properties": { - "name": { - "type": "string" - }, - "version": { - "type": "string" - }, - "files": { - "items": { - "type": "string" - }, - "type": "array" - }, - "authors": { - "items": { - "type": "string" - }, - "type": "array" - }, - "homepage": { - "type": "string" - } - }, - "type": "object", - "required": [ - "name", - "version" - ] - }, - "GolangBinMetadata": { - "properties": { - "goBuildSettings": { - "patternProperties": { - ".*": { - "type": "string" - } - }, - "type": "object" - }, - "goCompiledVersion": { - "type": "string" - }, - "architecture": { - "type": "string" - }, - "h1Digest": { - "type": "string" - }, - "mainModule": { - "type": "string" - } - }, - "type": "object", - "required": [ - "goCompiledVersion", - "architecture" - ] - }, - "GolangModMetadata": { - "properties": { - "h1Digest": { - "type": "string" - } - }, - "type": "object" - }, - "HackageMetadata": { - "properties": { - "name": { - "type": "string" - }, - "version": { - "type": "string" - }, - "pkgHash": { - "type": "string" - }, - "snapshotURL": { - "type": "string" - } - }, - "type": "object", - "required": [ - "name", - "version" - ] - }, - "IDLikes": { - "items": { - "type": "string" - }, - "type": "array" - }, - "JavaManifest": { - "properties": { - "main": { - "patternProperties": { - ".*": { - "type": "string" - } - }, - "type": "object" - }, - "namedSections": { - "patternProperties": { - ".*": { - "patternProperties": { - ".*": { - "type": "string" - } - }, - "type": "object" - } - }, - "type": "object" - } - }, - "type": "object" - }, - "JavaMetadata": { - "properties": { - "virtualPath": { - "type": "string" - }, - "manifest": { - "$ref": "#/$defs/JavaManifest" - }, - "pomProperties": { - "$ref": "#/$defs/PomProperties" - }, - "pomProject": { - "$ref": "#/$defs/PomProject" - }, - "digest": { - "items": { - "$ref": "#/$defs/Digest" - }, - "type": "array" - } - }, - "type": "object", - "required": [ - "virtualPath" - ] - }, - "KbPackageMetadata": { - "properties": { - "product_id": { - "type": "string" - }, - "kb": { - "type": "string" - } - }, - "type": "object", - "required": [ - "product_id", - "kb" - ] - }, - "License": { - "properties": { - "value": { - "type": "string" - }, - "spdx-expression": { - "type": "string" - }, - "type": { - "type": "string" - }, - "url": { - "type": "string" - }, - "location": { - "$ref": "#/$defs/Location" - } - }, - "type": "object", - "required": [ - "value", - "spdx-expression", - "type", - "url", - "location" - ] - }, - "LinuxKernelMetadata": { - "properties": { - "name": { - "type": "string" - }, - "architecture": { - "type": "string" - }, - "version": { - "type": "string" - }, - "extendedVersion": { - "type": "string" - }, - "buildTime": { - "type": "string" - }, - "author": { - "type": "string" - }, - "format": { - "type": "string" - }, - "rwRootFS": { - "type": "boolean" - }, - "swapDevice": { - "type": "integer" - }, - "rootDevice": { - "type": "integer" - }, - "videoMode": { - "type": "string" - } - }, - "type": "object", - "required": [ - "name", - "architecture", - "version" - ] - }, - "LinuxKernelModuleMetadata": { - "properties": { - "name": { - "type": "string" - }, - "version": { - "type": "string" - }, - "sourceVersion": { - "type": "string" - }, - "path": { - "type": "string" - }, - "description": { - "type": "string" - }, - "author": { - "type": "string" - }, - "license": { - "type": "string" - }, - "kernelVersion": { - "type": "string" - }, - "versionMagic": { - "type": "string" - }, - "parameters": { - "patternProperties": { - ".*": { - "$ref": "#/$defs/LinuxKernelModuleParameter" - } - }, - "type": "object" - } - }, - "type": "object" - }, - "LinuxKernelModuleParameter": { - "properties": { - "type": { - "type": "string" - }, - "description": { - "type": "string" - } - }, - "type": "object" - }, - "LinuxRelease": { - "properties": { - "prettyName": { - "type": "string" - }, - "name": { - "type": "string" - }, - "id": { - "type": "string" - }, - "idLike": { - "$ref": "#/$defs/IDLikes" - }, - "version": { - "type": "string" - }, - "versionID": { - "type": "string" - }, - "versionCodename": { - "type": "string" - }, - "buildID": { - "type": "string" - }, - "imageID": { - "type": "string" - }, - "imageVersion": { - "type": "string" - }, - "variant": { - "type": "string" - }, - "variantID": { - "type": "string" - }, - "homeURL": { - "type": "string" - }, - "supportURL": { - "type": "string" - }, - "bugReportURL": { - "type": "string" - }, - "privacyPolicyURL": { - "type": "string" - }, - "cpeName": { - "type": "string" - }, - "supportEnd": { - "type": "string" - } - }, - "type": "object" - }, - "Location": { - "properties": { - "path": { - "type": "string" - }, - "layerID": { - "type": "string" - }, - "annotations": { - "patternProperties": { - ".*": { - "type": "string" - } - }, - "type": "object" - } - }, - "type": "object", - "required": [ - "path" - ] - }, - "MixLockMetadata": { - "properties": { - "name": { - "type": "string" - }, - "version": { - "type": "string" - }, - "pkgHash": { - "type": "string" - }, - "pkgHashExt": { - "type": "string" - } - }, - "type": "object", - "required": [ - "name", - "version", - "pkgHash", - "pkgHashExt" - ] - }, - "NixStoreMetadata": { - "properties": { - "outputHash": { - "type": "string" - }, - "output": { - "type": "string" - }, - "files": { - "items": { - "type": "string" - }, - "type": "array" - } - }, - "type": "object", - "required": [ - "outputHash", - "files" - ] - }, - "NpmPackageJSONMetadata": { - "properties": { - "name": { - "type": "string" - }, - "version": { - "type": "string" - }, - "author": { - "type": "string" - }, - "homepage": { - "type": "string" - }, - "description": { - "type": "string" - }, - "url": { - "type": "string" - }, - "private": { - "type": "boolean" - } - }, - "type": "object", - "required": [ - "name", - "version", - "author", - "homepage", - "description", - "url", - "private" - ] - }, - "NpmPackageLockJSONMetadata": { - "properties": { - "resolved": { - "type": "string" - }, - "integrity": { - "type": "string" - } - }, - "type": "object", - "required": [ - "resolved", - "integrity" - ] - }, - "Package": { - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "version": { - "type": "string" - }, - "type": { - "type": "string" - }, - "foundBy": { - "type": "string" - }, - "locations": { - "items": { - "$ref": "#/$defs/Location" - }, - "type": "array" - }, - "licenses": { - "items": { - "$ref": "#/$defs/License" - }, - "type": "array" - }, - "language": { - "type": "string" - }, - "cpes": { - "items": { - "type": "string" - }, - "type": "array" - }, - "purl": { - "type": "string" - }, - "metadataType": { - "type": "string" - }, - "metadata": { - "anyOf": [ - { - "type": "null" - }, - { - "$ref": "#/$defs/AlpmMetadata" - }, - { - "$ref": "#/$defs/ApkMetadata" - }, - { - "$ref": "#/$defs/BinaryMetadata" - }, - { - "$ref": "#/$defs/CargoPackageMetadata" - }, - { - "$ref": "#/$defs/CocoapodsMetadata" - }, - { - "$ref": "#/$defs/ConanLockMetadata" - }, - { - "$ref": "#/$defs/ConanMetadata" - }, - { - "$ref": "#/$defs/DartPubMetadata" - }, - { - "$ref": "#/$defs/DotnetDepsMetadata" - }, - { - "$ref": "#/$defs/DpkgMetadata" - }, - { - "$ref": "#/$defs/GemMetadata" - }, - { - "$ref": "#/$defs/GolangBinMetadata" - }, - { - "$ref": "#/$defs/GolangModMetadata" - }, - { - "$ref": "#/$defs/HackageMetadata" - }, - { - "$ref": "#/$defs/JavaMetadata" - }, - { - "$ref": "#/$defs/KbPackageMetadata" - }, - { - "$ref": "#/$defs/LinuxKernelMetadata" - }, - { - "$ref": "#/$defs/LinuxKernelModuleMetadata" - }, - { - "$ref": "#/$defs/MixLockMetadata" - }, - { - "$ref": "#/$defs/NixStoreMetadata" - }, - { - "$ref": "#/$defs/NpmPackageJSONMetadata" - }, - { - "$ref": "#/$defs/NpmPackageLockJSONMetadata" - }, - { - "$ref": "#/$defs/PhpComposerJSONMetadata" - }, - { - "$ref": "#/$defs/PortageMetadata" - }, - { - "$ref": "#/$defs/PythonPackageMetadata" - }, - { - "$ref": "#/$defs/PythonPipfileLockMetadata" - }, - { - "$ref": "#/$defs/RebarLockMetadata" - }, - { - "$ref": "#/$defs/RpmMetadata" - } - ] - } - }, - "type": "object", - "required": [ - "id", - "name", - "version", - "type", - "foundBy", - "locations", - "licenses", - "language", - "cpes", - "purl" - ] - }, - "PhpComposerAuthors": { - "properties": { - "name": { - "type": "string" - }, - "email": { - "type": "string" - }, - "homepage": { - "type": "string" - } - }, - "type": "object", - "required": [ - "name" - ] - }, - "PhpComposerExternalReference": { - "properties": { - "type": { - "type": "string" - }, - "url": { - "type": "string" - }, - "reference": { - "type": "string" - }, - "shasum": { - "type": "string" - } - }, - "type": "object", - "required": [ - "type", - "url", - "reference" - ] - }, - "PhpComposerJSONMetadata": { - "properties": { - "name": { - "type": "string" - }, - "version": { - "type": "string" - }, - "source": { - "$ref": "#/$defs/PhpComposerExternalReference" - }, - "dist": { - "$ref": "#/$defs/PhpComposerExternalReference" - }, - "require": { - "patternProperties": { - ".*": { - "type": "string" - } - }, - "type": "object" - }, - "provide": { - "patternProperties": { - ".*": { - "type": "string" - } - }, - "type": "object" - }, - "require-dev": { - "patternProperties": { - ".*": { - "type": "string" - } - }, - "type": "object" - }, - "suggest": { - "patternProperties": { - ".*": { - "type": "string" - } - }, - "type": "object" - }, - "license": { - "items": { - "type": "string" - }, - "type": "array" - }, - "type": { - "type": "string" - }, - "notification-url": { - "type": "string" - }, - "bin": { - "items": { - "type": "string" - }, - "type": "array" - }, - "authors": { - "items": { - "$ref": "#/$defs/PhpComposerAuthors" - }, - "type": "array" - }, - "description": { - "type": "string" - }, - "homepage": { - "type": "string" - }, - "keywords": { - "items": { - "type": "string" - }, - "type": "array" - }, - "time": { - "type": "string" - } - }, - "type": "object", - "required": [ - "name", - "version", - "source", - "dist" - ] - }, - "PomParent": { - "properties": { - "groupId": { - "type": "string" - }, - "artifactId": { - "type": "string" - }, - "version": { - "type": "string" - } - }, - "type": "object", - "required": [ - "groupId", - "artifactId", - "version" - ] - }, - "PomProject": { - "properties": { - "path": { - "type": "string" - }, - "parent": { - "$ref": "#/$defs/PomParent" - }, - "groupId": { - "type": "string" - }, - "artifactId": { - "type": "string" - }, - "version": { - "type": "string" - }, - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "url": { - "type": "string" - } - }, - "type": "object", - "required": [ - "path", - "groupId", - "artifactId", - "version", - "name" - ] - }, - "PomProperties": { - "properties": { - "path": { - "type": "string" - }, - "name": { - "type": "string" - }, - "groupId": { - "type": "string" - }, - "artifactId": { - "type": "string" - }, - "version": { - "type": "string" - }, - "extraFields": { - "patternProperties": { - ".*": { - "type": "string" - } - }, - "type": "object" - } - }, - "type": "object", - "required": [ - "path", - "name", - "groupId", - "artifactId", - "version" - ] - }, - "PortageFileRecord": { - "properties": { - "path": { - "type": "string" - }, - "digest": { - "$ref": "#/$defs/Digest" - } - }, - "type": "object", - "required": [ - "path" - ] - }, - "PortageMetadata": { - "properties": { - "installedSize": { - "type": "integer" - }, - "files": { - "items": { - "$ref": "#/$defs/PortageFileRecord" - }, - "type": "array" - } - }, - "type": "object", - "required": [ - "installedSize", - "files" - ] - }, - "PythonDirectURLOriginInfo": { - "properties": { - "url": { - "type": "string" - }, - "commitId": { - "type": "string" - }, - "vcs": { - "type": "string" - } - }, - "type": "object", - "required": [ - "url" - ] - }, - "PythonFileDigest": { - "properties": { - "algorithm": { - "type": "string" - }, - "value": { - "type": "string" - } - }, - "type": "object", - "required": [ - "algorithm", - "value" - ] - }, - "PythonFileRecord": { - "properties": { - "path": { - "type": "string" - }, - "digest": { - "$ref": "#/$defs/PythonFileDigest" - }, - "size": { - "type": "string" - } - }, - "type": "object", - "required": [ - "path" - ] - }, - "PythonPackageMetadata": { - "properties": { - "name": { - "type": "string" - }, - "version": { - "type": "string" - }, - "author": { - "type": "string" - }, - "authorEmail": { - "type": "string" - }, - "platform": { - "type": "string" - }, - "files": { - "items": { - "$ref": "#/$defs/PythonFileRecord" - }, - "type": "array" - }, - "sitePackagesRootPath": { - "type": "string" - }, - "topLevelPackages": { - "items": { - "type": "string" - }, - "type": "array" - }, - "directUrlOrigin": { - "$ref": "#/$defs/PythonDirectURLOriginInfo" - } - }, - "type": "object", - "required": [ - "name", - "version", - "author", - "authorEmail", - "platform", - "sitePackagesRootPath" - ] - }, - "PythonPipfileLockMetadata": { - "properties": { - "hashes": { - "items": { - "type": "string" - }, - "type": "array" - }, - "index": { - "type": "string" - } - }, - "type": "object", - "required": [ - "hashes", - "index" - ] - }, - "RebarLockMetadata": { - "properties": { - "name": { - "type": "string" - }, - "version": { - "type": "string" - }, - "pkgHash": { - "type": "string" - }, - "pkgHashExt": { - "type": "string" - } - }, - "type": "object", - "required": [ - "name", - "version", - "pkgHash", - "pkgHashExt" - ] - }, - "Relationship": { - "properties": { - "parent": { - "type": "string" - }, - "child": { - "type": "string" - }, - "type": { - "type": "string" - }, - "metadata": true - }, - "type": "object", - "required": [ - "parent", - "child", - "type" - ] - }, - "RpmMetadata": { - "properties": { - "name": { - "type": "string" - }, - "version": { - "type": "string" - }, - "epoch": { - "oneOf": [ - { - "type": "integer" - }, - { - "type": "null" - } - ] - }, - "architecture": { - "type": "string" - }, - "release": { - "type": "string" - }, - "sourceRpm": { - "type": "string" - }, - "size": { - "type": "integer" - }, - "vendor": { - "type": "string" - }, - "modularityLabel": { - "type": "string" - }, - "files": { - "items": { - "$ref": "#/$defs/RpmdbFileRecord" - }, - "type": "array" - } - }, - "type": "object", - "required": [ - "name", - "version", - "epoch", - "architecture", - "release", - "sourceRpm", - "size", - "vendor", - "modularityLabel", - "files" - ] - }, - "RpmdbFileRecord": { - "properties": { - "path": { - "type": "string" - }, - "mode": { - "type": "integer" - }, - "size": { - "type": "integer" - }, - "digest": { - "$ref": "#/$defs/Digest" - }, - "userName": { - "type": "string" - }, - "groupName": { - "type": "string" - }, - "flags": { - "type": "string" - } - }, - "type": "object", - "required": [ - "path", - "mode", - "size", - "digest", - "userName", - "groupName", - "flags" - ] - }, - "Schema": { - "properties": { - "version": { - "type": "string" - }, - "url": { - "type": "string" - } - }, - "type": "object", - "required": [ - "version", - "url" - ] - }, - "SearchResult": { - "properties": { - "classification": { - "type": "string" - }, - "lineNumber": { - "type": "integer" - }, - "lineOffset": { - "type": "integer" - }, - "seekPosition": { - "type": "integer" - }, - "length": { - "type": "integer" - }, - "value": { - "type": "string" - } - }, - "type": "object", - "required": [ - "classification", - "lineNumber", - "lineOffset", - "seekPosition", - "length" - ] - }, - "Secrets": { - "properties": { - "location": { - "$ref": "#/$defs/Coordinates" - }, - "secrets": { - "items": { - "$ref": "#/$defs/SearchResult" - }, - "type": "array" - } - }, - "type": "object", - "required": [ - "location", - "secrets" - ] - }, - "Source": { - "properties": { - "id": { - "type": "string" - }, - "type": { - "type": "string" - }, - "target": true - }, - "type": "object", - "required": [ - "id", - "type", - "target" - ] - } - } -} From 7763daa82b6bf8ee53e3b269bc5c2aa6e8d96557 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Wed, 19 Apr 2023 15:23:05 -0400 Subject: [PATCH 23/99] wip: wip Signed-off-by: Christopher Phillips --- .../common/cyclonedxhelpers/licenses_test.go | 40 ++++++++--------- .../common/spdxhelpers/to_format_model.go | 8 ++-- .../spdxhelpers/to_format_model_test.go | 30 ++++++++----- syft/formats/syftjson/model/package_test.go | 4 +- syft/pkg/cataloger/javascript/package.go | 13 +++--- syft/pkg/cataloger/rpm/package.go | 43 +++++++++++-------- syft/pkg/cataloger/rpm/parse_rpm_db.go | 2 +- 7 files changed, 75 insertions(+), 65 deletions(-) diff --git a/syft/formats/common/cyclonedxhelpers/licenses_test.go b/syft/formats/common/cyclonedxhelpers/licenses_test.go index 65d9ece7d3b..e07a1f15bf2 100644 --- a/syft/formats/common/cyclonedxhelpers/licenses_test.go +++ b/syft/formats/common/cyclonedxhelpers/licenses_test.go @@ -25,14 +25,17 @@ func Test_encodeLicense(t *testing.T) { input: pkg.Package{ Licenses: []pkg.License{}, }, - expected: &cyclonedx.Licenses{ - {License: &cyclonedx.License{Name: "made-up"}}, - }, + expected: nil, }, { name: "with SPDX license", input: pkg.Package{ - Licenses: []pkg.License{}, + Licenses: []pkg.License{ + { + Value: "mit", + SPDXExpression: "MIT", + }, + }, }, expected: &cyclonedx.Licenses{ {License: &cyclonedx.License{ID: "MIT"}}, @@ -41,31 +44,22 @@ func Test_encodeLicense(t *testing.T) { { name: "with SPDX license expression", input: pkg.Package{ - Licenses: []pkg.License{}, + Licenses: []pkg.License{ + { + Value: "mit", + SPDXExpression: "MIT", + }, + { + Value: "gpl-3.0-only", + SPDXExpression: "GPL-3.0-only", + }, + }, }, expected: &cyclonedx.Licenses{ {License: &cyclonedx.License{ID: "MIT"}}, {License: &cyclonedx.License{ID: "GPL-3.0-only"}}, }, }, - { - name: "cap insensitive", - input: pkg.Package{ - Licenses: []pkg.License{}, - }, - expected: &cyclonedx.Licenses{ - {License: &cyclonedx.License{ID: "GPL-3.0-only"}}, - }, - }, - { - name: "debian to spdx conversion", - input: pkg.Package{ - Licenses: []pkg.License{}, - }, - expected: &cyclonedx.Licenses{ - {License: &cyclonedx.License{ID: "GPL-2.0-only"}}, - }, - }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { diff --git a/syft/formats/common/spdxhelpers/to_format_model.go b/syft/formats/common/spdxhelpers/to_format_model.go index e24f3263be2..e04e22548c1 100644 --- a/syft/formats/common/spdxhelpers/to_format_model.go +++ b/syft/formats/common/spdxhelpers/to_format_model.go @@ -501,7 +501,7 @@ func toFileTypes(metadata *source.FileMetadata) (ty []string) { return ty } -// TODO: other licenses are for licenses from the pkg.Package that do not have an SPDXExpression +// other licenses are for licenses from the pkg.Package that do not have an SPDXExpression // field. The spdxexpression field is only filled given a validated Value field. func toOtherLicenses(catalog *pkg.Catalog) []*spdx.OtherLicense { licenses := map[string]bool{} @@ -518,12 +518,12 @@ func toOtherLicenses(catalog *pkg.Catalog) []*spdx.OtherLicense { sorted := maps.Keys(licenses) slices.Sort(sorted) for _, license := range sorted { - // separate the actual ID from the prefix + // separate the found value from the prefix + // this only contains licenses that are not found on the SPDX License List name := strings.TrimPrefix(license, spdxlicense.LicenseRefPrefix) result = append(result, &spdx.OtherLicense{ LicenseIdentifier: SanitizeElementID(license), - LicenseName: name, - ExtractedText: NONE, // we probably should have some extracted text here, but this is good enough for now + ExtractedText: name, }) } return result diff --git a/syft/formats/common/spdxhelpers/to_format_model_test.go b/syft/formats/common/spdxhelpers/to_format_model_test.go index 42a13577034..d1128cf49b3 100644 --- a/syft/formats/common/spdxhelpers/to_format_model_test.go +++ b/syft/formats/common/spdxhelpers/to_format_model_test.go @@ -454,31 +454,39 @@ func Test_OtherLicenses(t *testing.T) { { name: "single licenseRef", pkg: pkg.Package{ - Licenses: []pkg.License{}, + Licenses: []pkg.License{ + { + Value: "foobar", // Only testing licenses not in spdx list + }, + }, }, expected: []*spdx.OtherLicense{ { - LicenseIdentifier: "LicenseRef-un-known", - LicenseName: "un known", - ExtractedText: NONE, + LicenseIdentifier: "LicenseRef-foobar", + ExtractedText: "foobar", }, }, }, { name: "multiple licenseRef", pkg: pkg.Package{ - Licenses: []pkg.License{}, + Licenses: []pkg.License{ + { + Value: "internal made up license name", + }, + { + Value: "new apple license 2.0", + }, + }, }, expected: []*spdx.OtherLicense{ { - LicenseIdentifier: "LicenseRef-not-known--s", - LicenseName: "not known %s", - ExtractedText: NONE, + LicenseIdentifier: "LicenseRef-internal-made-up-license-name", + ExtractedText: "internal made up license name", }, { - LicenseIdentifier: "LicenseRef-un-known", - LicenseName: "un known", - ExtractedText: NONE, + LicenseIdentifier: "LicenseRef-new-apple-license-2.0", + ExtractedText: "new apple license 2.0", }, }, }, diff --git a/syft/formats/syftjson/model/package_test.go b/syft/formats/syftjson/model/package_test.go index 2aa2cda0515..3c5bdb93b76 100644 --- a/syft/formats/syftjson/model/package_test.go +++ b/syft/formats/syftjson/model/package_test.go @@ -11,6 +11,7 @@ import ( "github.com/anchore/syft/syft/pkg" ) +// TODO: add new license struct to this test func TestUnmarshalPackageGolang(t *testing.T) { tests := []struct { name string @@ -152,9 +153,6 @@ func Test_unpackMetadata(t *testing.T) { "layerID": "sha256:74ddd0ec08fa43d09f32636ba91a0a3053b02cb4627c35051aff89f853606b59" } ], - "licenses": [ - "GPLv2+" - ], "language": "", "cpes": [ "cpe:2.3:a:centos:acl:2.2.53-1.el8:*:*:*:*:*:*:*", diff --git a/syft/pkg/cataloger/javascript/package.go b/syft/pkg/cataloger/javascript/package.go index 445686e1d1e..a48ba4f7524 100644 --- a/syft/pkg/cataloger/javascript/package.go +++ b/syft/pkg/cataloger/javascript/package.go @@ -44,12 +44,13 @@ func newPackageJSONPackage(u packageJSON, locations ...source.Location) pkg.Pack Type: pkg.NpmPkg, MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ - Name: u.Name, - Version: u.Version, - Author: u.Author.AuthorString(), - Homepage: u.Homepage, - URL: u.Repository.URL, - Private: u.Private, + Name: u.Name, + Version: u.Version, + Description: u.Description, + Author: u.Author.AuthorString(), + Homepage: u.Homepage, + URL: u.Repository.URL, + Private: u.Private, }, } diff --git a/syft/pkg/cataloger/rpm/package.go b/syft/pkg/cataloger/rpm/package.go index 958a8f92ed1..9deaab2f675 100644 --- a/syft/pkg/cataloger/rpm/package.go +++ b/syft/pkg/cataloger/rpm/package.go @@ -13,34 +13,43 @@ import ( "github.com/anchore/syft/syft/source" ) -func newPackage(location source.Location, metadata pkg.RpmMetadata, distro *linux.Release) pkg.Package { +func newPackage(location source.Location, pd parsedData, distro *linux.Release) pkg.Package { p := pkg.Package{ - Name: metadata.Name, - Version: toELVersion(metadata), - PURL: packageURL(metadata, distro), + Name: pd.Name, + Version: toELVersion(pd.RpmMetadata), + PURL: packageURL(pd.RpmMetadata), Locations: source.NewLocationSet(location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)), Type: pkg.RpmPkg, MetadataType: pkg.RpmMetadataType, - Metadata: metadata, + Metadata: pd.RpmMetadata, } p.SetID() return p } -func newMetadataFromEntry(entry rpmdb.PackageInfo, files []pkg.RpmdbFileRecord) pkg.RpmMetadata { +type parsedData struct { + Licenses []pkg.License + pkg.RpmMetadata +} + +func newParsedDatafromEntry(entry rpmdb.PackageInfo, files []pkg.RpmdbFileRecord) parsedData { // TODO: use entry to populate the pkg.RpmMetadata struct in package constructor - return pkg.RpmMetadata{ - Name: entry.Name, - Version: entry.Version, - Epoch: entry.Epoch, - Arch: entry.Arch, - Release: entry.Release, - SourceRpm: entry.SourceRpm, - Vendor: entry.Vendor, - Size: entry.Size, - ModularityLabel: entry.Modularitylabel, - Files: files, + license := pkg.NewLicense(entry.License, "", nil) + return parsedData{ + Licenses: []pkg.License{license}, + RpmMetadata: pkg.RpmMetadata{ + Name: entry.Name, + Version: entry.Version, + Epoch: entry.Epoch, + Arch: entry.Arch, + Release: entry.Release, + SourceRpm: entry.SourceRpm, + Vendor: entry.Vendor, + Size: entry.Size, + ModularityLabel: entry.Modularitylabel, + Files: files, + }, } } diff --git a/syft/pkg/cataloger/rpm/parse_rpm_db.go b/syft/pkg/cataloger/rpm/parse_rpm_db.go index 58e96e9cbff..faafa77040f 100644 --- a/syft/pkg/cataloger/rpm/parse_rpm_db.go +++ b/syft/pkg/cataloger/rpm/parse_rpm_db.go @@ -60,7 +60,7 @@ func parseRpmDB(resolver source.FileResolver, env *generic.Environment, reader s p := newPackage( reader.Location, - newMetadataFromEntry(*entry, extractRpmdbFileRecords(resolver, *entry)), + newParsedDatafromEntry(*entry, extractRpmdbFileRecords(resolver, *entry)), distro, ) From 5624420e0857d2cda0d695daf145c2315bbf3e3e Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Thu, 20 Apr 2023 12:59:31 -0400 Subject: [PATCH 24/99] wip: wip Signed-off-by: Christopher Phillips --- syft/formats/syftjson/decoder.go | 1 + syft/pkg/cataloger/rpm/package.go | 30 ++++----- syft/pkg/cataloger/rpm/parse_rpm.go | 33 +++++----- syft/pkg/cataloger/rpm/parse_rpm_db.go | 2 +- syft/pkg/cataloger/rpm/parse_rpm_db_test.go | 27 ++++++-- syft/pkg/cataloger/rpm/parse_rpm_test.go | 27 ++++++-- syft/pkg/cataloger/ruby/package.go | 7 ++- syft/pkg/cataloger/ruby/parse_gemspec.go | 4 +- syft/pkg/cataloger/ruby/parse_gemspec_test.go | 20 ++++-- syft/pkg/cataloger/sbom/cataloger_test.go | 2 +- .../alpine/syft-json/sbom.syft.json | 62 +++++-------------- 11 files changed, 119 insertions(+), 96 deletions(-) diff --git a/syft/formats/syftjson/decoder.go b/syft/formats/syftjson/decoder.go index b6286bd97c6..949f1e88a93 100644 --- a/syft/formats/syftjson/decoder.go +++ b/syft/formats/syftjson/decoder.go @@ -15,6 +15,7 @@ func decoder(reader io.Reader) (*sbom.SBOM, error) { var doc model.Document err := dec.Decode(&doc) if err != nil { + fmt.Println(err) return nil, fmt.Errorf("unable to decode syft-json: %w", err) } diff --git a/syft/pkg/cataloger/rpm/package.go b/syft/pkg/cataloger/rpm/package.go index 9deaab2f675..29ed77806c2 100644 --- a/syft/pkg/cataloger/rpm/package.go +++ b/syft/pkg/cataloger/rpm/package.go @@ -17,7 +17,8 @@ func newPackage(location source.Location, pd parsedData, distro *linux.Release) p := pkg.Package{ Name: pd.Name, Version: toELVersion(pd.RpmMetadata), - PURL: packageURL(pd.RpmMetadata), + Licenses: pd.Licenses, + PURL: packageURL(pd.RpmMetadata, distro), Locations: source.NewLocationSet(location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)), Type: pkg.RpmPkg, MetadataType: pkg.RpmMetadataType, @@ -33,9 +34,9 @@ type parsedData struct { pkg.RpmMetadata } -func newParsedDatafromEntry(entry rpmdb.PackageInfo, files []pkg.RpmdbFileRecord) parsedData { +func newParsedDatafromEntry(licenseLocation source.Location, entry rpmdb.PackageInfo, files []pkg.RpmdbFileRecord) parsedData { // TODO: use entry to populate the pkg.RpmMetadata struct in package constructor - license := pkg.NewLicense(entry.License, "", nil) + license := pkg.NewLicense(entry.License, "", licenseLocation) return parsedData{ Licenses: []pkg.License{license}, RpmMetadata: pkg.RpmMetadata{ @@ -53,7 +54,7 @@ func newParsedDatafromEntry(entry rpmdb.PackageInfo, files []pkg.RpmdbFileRecord } } -func newMetadataFromManifestLine(entry string) (*pkg.RpmMetadata, error) { +func newMetadataFromManifestLine(entry string) (*parsedData, error) { parts := strings.Split(entry, "\t") if len(parts) < 10 { return nil, fmt.Errorf("unexpected number of fields in line: %s", entry) @@ -79,16 +80,17 @@ func newMetadataFromManifestLine(entry string) (*pkg.RpmMetadata, error) { if err == nil { size = converted } - - return &pkg.RpmMetadata{ - Name: parts[0], - Version: version, - Epoch: epoch, - Arch: parts[7], - Release: release, - SourceRpm: parts[9], - Vendor: parts[4], - Size: size, + return &parsedData{ + RpmMetadata: pkg.RpmMetadata{ + Name: parts[0], + Version: version, + Epoch: epoch, + Arch: parts[7], + Release: release, + SourceRpm: parts[9], + Vendor: parts[4], + Size: size, + }, }, nil } diff --git a/syft/pkg/cataloger/rpm/parse_rpm.go b/syft/pkg/cataloger/rpm/parse_rpm.go index 60b4657945c..6d10d5118c9 100644 --- a/syft/pkg/cataloger/rpm/parse_rpm.go +++ b/syft/pkg/cataloger/rpm/parse_rpm.go @@ -26,29 +26,34 @@ func parseRpm(_ source.FileResolver, _ *generic.Environment, reader source.Locat return nil, nil, err } - _, _ = rpm.Header.GetStrings(rpmutils.LICENSE) + cl, _ := rpm.Header.GetStrings(rpmutils.LICENSE) sourceRpm, _ := rpm.Header.GetString(rpmutils.SOURCERPM) vendor, _ := rpm.Header.GetString(rpmutils.VENDOR) digestAlgorithm := getDigestAlgorithm(rpm.Header) size, _ := rpm.Header.InstalledSize() files, _ := rpm.Header.GetFiles() - // TODO: UPDATE THIS TO USE LATEST LICENSE PARSING AND VALIDATION TOOLING - // License: strings.Join(licenses, " AND "), // TODO: AND conjunction is not necessarily correct, but we don't have a way to represent multiple licenses yet + licenses := []pkg.License{} + for _, l := range cl { + licenses = append(licenses, pkg.NewLicense(l, "", reader.Location)) + } - metadata := pkg.RpmMetadata{ - Name: nevra.Name, - Version: nevra.Version, - Epoch: parseEpoch(nevra.Epoch), - Arch: nevra.Arch, - Release: nevra.Release, - SourceRpm: sourceRpm, - Vendor: vendor, - Size: int(size), - Files: mapFiles(files, digestAlgorithm), + pd := parsedData{ + licenses, + pkg.RpmMetadata{ + Name: nevra.Name, + Version: nevra.Version, + Epoch: parseEpoch(nevra.Epoch), + Arch: nevra.Arch, + Release: nevra.Release, + SourceRpm: sourceRpm, + Vendor: vendor, + Size: int(size), + Files: mapFiles(files, digestAlgorithm), + }, } - return []pkg.Package{newPackage(reader.Location, metadata, nil)}, nil, nil + return []pkg.Package{newPackage(reader.Location, pd, nil)}, nil, nil } func getDigestAlgorithm(header *rpmutils.RpmHeader) string { diff --git a/syft/pkg/cataloger/rpm/parse_rpm_db.go b/syft/pkg/cataloger/rpm/parse_rpm_db.go index faafa77040f..503f2caf72f 100644 --- a/syft/pkg/cataloger/rpm/parse_rpm_db.go +++ b/syft/pkg/cataloger/rpm/parse_rpm_db.go @@ -60,7 +60,7 @@ func parseRpmDB(resolver source.FileResolver, env *generic.Environment, reader s p := newPackage( reader.Location, - newParsedDatafromEntry(*entry, extractRpmdbFileRecords(resolver, *entry)), + newParsedDatafromEntry(reader.Location, *entry, extractRpmdbFileRecords(resolver, *entry)), distro, ) diff --git a/syft/pkg/cataloger/rpm/parse_rpm_db_test.go b/syft/pkg/cataloger/rpm/parse_rpm_db_test.go index 4d71b9846b6..bbe0e4d0c7e 100644 --- a/syft/pkg/cataloger/rpm/parse_rpm_db_test.go +++ b/syft/pkg/cataloger/rpm/parse_rpm_db_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" "github.com/anchore/syft/syft/source" @@ -80,6 +81,7 @@ func (r *rpmdbTestFileResolverMock) FilesByMIMEType(...string) ([]source.Locatio } func TestParseRpmDB(t *testing.T) { + packagesLocation := source.NewLocation("test-fixtures/Packages") tests := []struct { fixture string expected []pkg.Package @@ -94,10 +96,18 @@ func TestParseRpmDB(t *testing.T) { Name: "dive", Version: "0.9.2-1", PURL: "pkg:rpm/dive@0.9.2-1?arch=x86_64&upstream=dive-0.9.2-1.src.rpm", - Locations: source.NewLocationSet(source.NewLocation("test-fixtures/Packages")), + Locations: source.NewLocationSet(packagesLocation), Type: pkg.RpmPkg, MetadataType: pkg.RpmMetadataType, - Licenses: []pkg.License{}, + Licenses: []pkg.License{ + { + Value: "MIT", + SPDXExpression: "MIT", + Type: license.Declared, + URL: "", + Location: packagesLocation, + }, + }, Metadata: pkg.RpmMetadata{ Name: "dive", Epoch: nil, @@ -121,10 +131,18 @@ func TestParseRpmDB(t *testing.T) { Name: "dive", Version: "0.9.2-1", PURL: "pkg:rpm/dive@0.9.2-1?arch=x86_64&upstream=dive-0.9.2-1.src.rpm", - Locations: source.NewLocationSet(source.NewLocation("test-fixtures/Packages")), + Locations: source.NewLocationSet(packagesLocation), Type: pkg.RpmPkg, MetadataType: pkg.RpmMetadataType, - Licenses: []pkg.License{}, + Licenses: []pkg.License{ + { + Value: "MIT", + SPDXExpression: "MIT", + Type: license.Declared, + URL: "", + Location: packagesLocation, + }, + }, Metadata: pkg.RpmMetadata{ Name: "dive", Epoch: nil, @@ -161,7 +179,6 @@ func TestParseRpmDB(t *testing.T) { TestParser(t, parseRpmDB) }) } - } func TestToElVersion(t *testing.T) { diff --git a/syft/pkg/cataloger/rpm/parse_rpm_test.go b/syft/pkg/cataloger/rpm/parse_rpm_test.go index 84c8988c8d3..90a395d2093 100644 --- a/syft/pkg/cataloger/rpm/parse_rpm_test.go +++ b/syft/pkg/cataloger/rpm/parse_rpm_test.go @@ -4,12 +4,15 @@ import ( "testing" "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" "github.com/anchore/syft/syft/source" ) func TestParseRpmFiles(t *testing.T) { + abcRpmLocation := source.NewLocation("abc-1.01-9.hg20160905.el7.x86_64.rpm") + zorkRpmLocation := source.NewLocation("zork-1.0.3-1.el7.x86_64.rpm") tests := []struct { fixture string expected []pkg.Package @@ -21,11 +24,19 @@ func TestParseRpmFiles(t *testing.T) { Name: "abc", Version: "0:1.01-9.hg20160905.el7", PURL: "pkg:rpm/abc@1.01-9.hg20160905.el7?arch=x86_64&epoch=0&upstream=abc-1.01-9.hg20160905.el7.src.rpm", - Locations: source.NewLocationSet(source.NewLocation("abc-1.01-9.hg20160905.el7.x86_64.rpm")), + Locations: source.NewLocationSet(abcRpmLocation), FoundBy: "rpm-file-cataloger", Type: pkg.RpmPkg, MetadataType: pkg.RpmMetadataType, - Licenses: []pkg.License{}, + Licenses: []pkg.License{ + { + Value: "MIT", + SPDXExpression: "MIT", + Type: license.Declared, + URL: "", + Location: abcRpmLocation, + }, + }, Metadata: pkg.RpmMetadata{ Name: "abc", Epoch: intRef(0), @@ -48,11 +59,19 @@ func TestParseRpmFiles(t *testing.T) { Name: "zork", Version: "0:1.0.3-1.el7", PURL: "pkg:rpm/zork@1.0.3-1.el7?arch=x86_64&epoch=0&upstream=zork-1.0.3-1.el7.src.rpm", - Locations: source.NewLocationSet(source.NewLocation("zork-1.0.3-1.el7.x86_64.rpm")), + Locations: source.NewLocationSet(zorkRpmLocation), FoundBy: "rpm-file-cataloger", Type: pkg.RpmPkg, MetadataType: pkg.RpmMetadataType, - Licenses: []pkg.License{}, + Licenses: []pkg.License{ + { + Value: "Public Domain", + SPDXExpression: "", + Type: license.Declared, + URL: "", + Location: zorkRpmLocation, + }, + }, Metadata: pkg.RpmMetadata{ Name: "zork", Epoch: intRef(0), diff --git a/syft/pkg/cataloger/ruby/package.go b/syft/pkg/cataloger/ruby/package.go index 17ecf57c2f0..7088ad9586b 100644 --- a/syft/pkg/cataloger/ruby/package.go +++ b/syft/pkg/cataloger/ruby/package.go @@ -22,15 +22,20 @@ func newGemfileLockPackage(name, version string, locations ...source.Location) p } func newGemspecPackage(m gemData, locations ...source.Location) pkg.Package { + licenses := make([]pkg.License, 0) + for _, l := range m.Licenses { + licenses = append(licenses, pkg.NewLicense(l, "", locations[0])) + } p := pkg.Package{ Name: m.Name, Version: m.Version, Locations: source.NewLocationSet(locations...), + Licenses: licenses, PURL: packageURL(m.Name, m.Version), Language: pkg.Ruby, Type: pkg.GemPkg, MetadataType: pkg.GemMetadataType, - Metadata: m, + Metadata: m.GemMetadata, } p.SetID() diff --git a/syft/pkg/cataloger/ruby/parse_gemspec.go b/syft/pkg/cataloger/ruby/parse_gemspec.go index 773123ca49e..74ee3489518 100644 --- a/syft/pkg/cataloger/ruby/parse_gemspec.go +++ b/syft/pkg/cataloger/ruby/parse_gemspec.go @@ -21,8 +21,8 @@ var _ generic.Parser = parseGemFileLockEntries type postProcessor func(string) []string type gemData struct { - Licenses []string `mapstructure:"licenses" json:"licenses,omitempty"` - pkg.GemMetadata + Licenses []string `mapstructure:"licenses" json:"licenses,omitempty"` + pkg.GemMetadata `mapstructure:",squash" json:",inline"` } // match example: Al\u003Ex ---> 003E diff --git a/syft/pkg/cataloger/ruby/parse_gemspec_test.go b/syft/pkg/cataloger/ruby/parse_gemspec_test.go index 327fbea9368..ac86b6b6739 100644 --- a/syft/pkg/cataloger/ruby/parse_gemspec_test.go +++ b/syft/pkg/cataloger/ruby/parse_gemspec_test.go @@ -3,6 +3,7 @@ package ruby import ( "testing" + "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" "github.com/anchore/syft/syft/source" @@ -14,12 +15,19 @@ func TestParseGemspec(t *testing.T) { locations := source.NewLocationSet(source.NewLocation(fixture)) var expectedPkg = pkg.Package{ - Name: "bundler", - Version: "2.1.4", - PURL: "pkg:gem/bundler@2.1.4", - Locations: locations, - Type: pkg.GemPkg, - Licenses: []pkg.License{}, + Name: "bundler", + Version: "2.1.4", + PURL: "pkg:gem/bundler@2.1.4", + Locations: locations, + Type: pkg.GemPkg, + Licenses: []pkg.License{ + { + Value: "MIT", + SPDXExpression: "MIT", + Location: source.NewLocation(fixture), + Type: license.Declared, + }, + }, Language: pkg.Ruby, MetadataType: pkg.GemMetadataType, Metadata: pkg.GemMetadata{ diff --git a/syft/pkg/cataloger/sbom/cataloger_test.go b/syft/pkg/cataloger/sbom/cataloger_test.go index 5e770f3e4d4..2cec27edbe0 100644 --- a/syft/pkg/cataloger/sbom/cataloger_test.go +++ b/syft/pkg/cataloger/sbom/cataloger_test.go @@ -32,7 +32,7 @@ func must(c cpe.CPE, e error) cpe.CPE { } func Test_parseSBOM(t *testing.T) { - + // TODO: update fixtures to use new license format expectedPkgs := []pkg.Package{ { Name: "alpine-baselayout", diff --git a/syft/pkg/cataloger/sbom/test-fixtures/alpine/syft-json/sbom.syft.json b/syft/pkg/cataloger/sbom/test-fixtures/alpine/syft-json/sbom.syft.json index c9e83d12045..d424f9a53f0 100644 --- a/syft/pkg/cataloger/sbom/test-fixtures/alpine/syft-json/sbom.syft.json +++ b/syft/pkg/cataloger/sbom/test-fixtures/alpine/syft-json/sbom.syft.json @@ -12,9 +12,7 @@ "layerID": "sha256:e5e13b0c77cbb769548077189c3da2f0a764ceca06af49d8d558e759f5c232bd" } ], - "licenses": [ - "GPL-2.0-only" - ], + "licenses": [], "language": "", "cpes": [ "cpe:2.3:a:alpine-baselayout:alpine-baselayout:3.2.0-r23:*:*:*:*:*:*:*", @@ -395,9 +393,7 @@ "layerID": "sha256:e5e13b0c77cbb769548077189c3da2f0a764ceca06af49d8d558e759f5c232bd" } ], - "licenses": [ - "GPL-2.0-only" - ], + "licenses": [], "language": "", "cpes": [ "cpe:2.3:a:alpine-baselayout-data:alpine-baselayout-data:3.2.0-r23:*:*:*:*:*:*:*", @@ -558,9 +554,7 @@ "layerID": "sha256:e5e13b0c77cbb769548077189c3da2f0a764ceca06af49d8d558e759f5c232bd" } ], - "licenses": [ - "MIT" - ], + "licenses": [], "language": "", "cpes": [ "cpe:2.3:a:alpine-keys:alpine-keys:2.4-r1:*:*:*:*:*:*:*", @@ -995,9 +989,7 @@ "layerID": "sha256:e5e13b0c77cbb769548077189c3da2f0a764ceca06af49d8d558e759f5c232bd" } ], - "licenses": [ - "GPL-2.0-only" - ], + "licenses": [], "language": "", "cpes": [ "cpe:2.3:a:apk-tools:apk-tools:2.12.9-r3:*:*:*:*:*:*:*", @@ -1103,9 +1095,7 @@ "layerID": "sha256:e5e13b0c77cbb769548077189c3da2f0a764ceca06af49d8d558e759f5c232bd" } ], - "licenses": [ - "GPL-2.0-only" - ], + "licenses": [], "language": "", "cpes": [ "cpe:2.3:a:busybox:busybox:1.35.0-r17:*:*:*:*:*:*:*" @@ -1276,11 +1266,7 @@ "layerID": "sha256:e5e13b0c77cbb769548077189c3da2f0a764ceca06af49d8d558e759f5c232bd" } ], - "licenses": [ - "MPL-2.0", - "AND", - "MIT" - ], + "licenses": [], "language": "", "cpes": [ "cpe:2.3:a:ca-certificates-bundle:ca-certificates-bundle:20220614-r0:*:*:*:*:*:*:*", @@ -1355,11 +1341,7 @@ "layerID": "sha256:e5e13b0c77cbb769548077189c3da2f0a764ceca06af49d8d558e759f5c232bd" } ], - "licenses": [ - "BSD-2-Clause", - "AND", - "BSD-3-Clause" - ], + "licenses": [], "language": "", "cpes": [ "cpe:2.3:a:libc-utils:libc-utils:0.7.2-r3:*:*:*:*:*:*:*", @@ -1403,9 +1385,7 @@ "layerID": "sha256:e5e13b0c77cbb769548077189c3da2f0a764ceca06af49d8d558e759f5c232bd" } ], - "licenses": [ - "OpenSSL" - ], + "licenses": [], "language": "", "cpes": [ "cpe:2.3:a:libcrypto1.1:libcrypto1.1:1.1.1s-r0:*:*:*:*:*:*:*" @@ -1582,9 +1562,7 @@ "layerID": "sha256:e5e13b0c77cbb769548077189c3da2f0a764ceca06af49d8d558e759f5c232bd" } ], - "licenses": [ - "OpenSSL" - ], + "licenses": [], "language": "", "cpes": [ "cpe:2.3:a:libssl1.1:libssl1.1:1.1.1s-r0:*:*:*:*:*:*:*" @@ -1656,9 +1634,7 @@ "layerID": "sha256:e5e13b0c77cbb769548077189c3da2f0a764ceca06af49d8d558e759f5c232bd" } ], - "licenses": [ - "MIT" - ], + "licenses": [], "language": "", "cpes": [ "cpe:2.3:a:musl:musl:1.2.3-r1:*:*:*:*:*:*:*" @@ -1721,11 +1697,7 @@ "layerID": "sha256:e5e13b0c77cbb769548077189c3da2f0a764ceca06af49d8d558e759f5c232bd" } ], - "licenses": [ - "MIT", - "BSD", - "GPL2+" - ], + "licenses": [], "language": "", "cpes": [ "cpe:2.3:a:musl-utils:musl-utils:1.2.3-r1:*:*:*:*:*:*:*", @@ -1836,9 +1808,7 @@ "layerID": "sha256:e5e13b0c77cbb769548077189c3da2f0a764ceca06af49d8d558e759f5c232bd" } ], - "licenses": [ - "GPL-2.0-only" - ], + "licenses": [], "language": "", "cpes": [ "cpe:2.3:a:scanelf:scanelf:1.3.4-r0:*:*:*:*:*:*:*" @@ -1896,9 +1866,7 @@ "layerID": "sha256:e5e13b0c77cbb769548077189c3da2f0a764ceca06af49d8d558e759f5c232bd" } ], - "licenses": [ - "GPL-2.0-only" - ], + "licenses": [], "language": "", "cpes": [ "cpe:2.3:a:ssl-client:ssl-client:1.35.0-r17:*:*:*:*:*:*:*", @@ -1963,9 +1931,7 @@ "layerID": "sha256:e5e13b0c77cbb769548077189c3da2f0a764ceca06af49d8d558e759f5c232bd" } ], - "licenses": [ - "Zlib" - ], + "licenses": [], "language": "", "cpes": [ "cpe:2.3:a:zlib:zlib:1.2.12-r3:*:*:*:*:*:*:*" From ce27877770a7d435580bae7884fd23a5084e794e Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Mon, 24 Apr 2023 10:44:14 -0400 Subject: [PATCH 25/99] chore: update fixtures with latest output: NOT FINAL Signed-off-by: Christopher Phillips --- .../TestSPDXJSONDirectoryEncoder.golden | 12 ++---- .../snapshot/TestSPDXJSONImageEncoder.golden | 12 ++---- .../snapshot/TestSPDXRelationshipOrder.golden | 24 +++++------- .../stereoscope-fixture-image-simple.golden | Bin 15360 -> 15360 bytes .../snapshot/TestSPDXJSONSPDXIDs.golden | 12 ++---- .../snapshot/TestSPDXRelationshipOrder.golden | 24 +++++------- .../TestSPDXTagValueDirectoryEncoder.golden | 12 ++---- .../TestSPDXTagValueImageEncoder.golden | 12 ++---- .../stereoscope-fixture-image-simple.golden | Bin 15360 -> 15360 bytes .../snapshot/TestDirectoryEncoder.golden | 19 +++++++--- .../TestEncodeFullJSONDocument.golden | 13 +++---- .../snapshot/TestImageEncoder.golden | 35 +++++++++++------- .../stereoscope-fixture-image-simple.golden | Bin 15360 -> 15360 bytes 13 files changed, 78 insertions(+), 97 deletions(-) diff --git a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONDirectoryEncoder.golden b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONDirectoryEncoder.golden index 4ef14120dfe..14ad20c9cd4 100644 --- a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONDirectoryEncoder.golden +++ b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONDirectoryEncoder.golden @@ -3,24 +3,22 @@ "dataLicense": "CC0-1.0", "SPDXID": "SPDXRef-DOCUMENT", "name": "/some/path", - "documentNamespace": "https://anchore.com/syft/dir/some/path-1fe34646-a616-48c7-974b-3d1e27d406e3", + "documentNamespace": "https://anchore.com/syft/dir/some/path-df64dcf1-8330-4b6f-826d-2d6b0f40a1a1", "creationInfo": { - "licenseListVersion": "3.19", + "licenseListVersion": "3.20", "creators": [ "Organization: Anchore, Inc", "Tool: syft-v0.42.0-bogus" ], - "created": "2023-01-20T21:41:03Z" + "created": "2023-04-24T14:38:59Z" }, "packages": [ { "name": "package-1", - "SPDXID": "SPDXRef-Package-python-package-1-1b1d0be59ac59d2c", + "SPDXID": "SPDXRef-Package-python-package-1-ed5945af814b9a95", "versionInfo": "1.0.1", "downloadLocation": "NOASSERTION", "sourceInfo": "acquired package info from installed python package manifest file: /some/path/pkg1", - "licenseConcluded": "MIT", - "licenseDeclared": "MIT", "copyrightText": "NOASSERTION", "externalRefs": [ { @@ -41,8 +39,6 @@ "versionInfo": "2.0.1", "downloadLocation": "NOASSERTION", "sourceInfo": "acquired package info from DPKG DB: /some/path/pkg1", - "licenseConcluded": "NONE", - "licenseDeclared": "NONE", "copyrightText": "NOASSERTION", "externalRefs": [ { diff --git a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONImageEncoder.golden b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONImageEncoder.golden index 8a5214f293f..ed19788c88d 100644 --- a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONImageEncoder.golden +++ b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONImageEncoder.golden @@ -3,24 +3,22 @@ "dataLicense": "CC0-1.0", "SPDXID": "SPDXRef-DOCUMENT", "name": "user-image-input", - "documentNamespace": "https://anchore.com/syft/image/user-image-input-33759ac3-6006-4f2c-bdc4-f40b9287a7f0", + "documentNamespace": "https://anchore.com/syft/image/user-image-input-75fc6304-ce2d-4c7b-bf76-fa7395294fef", "creationInfo": { - "licenseListVersion": "3.19", + "licenseListVersion": "3.20", "creators": [ "Organization: Anchore, Inc", "Tool: syft-v0.42.0-bogus" ], - "created": "2023-01-20T21:41:03Z" + "created": "2023-04-24T14:39:32Z" }, "packages": [ { "name": "package-1", - "SPDXID": "SPDXRef-Package-python-package-1-66ba429119b8bec6", + "SPDXID": "SPDXRef-Package-python-package-1-ed77c5cb0567a1a6", "versionInfo": "1.0.1", "downloadLocation": "NOASSERTION", "sourceInfo": "acquired package info from installed python package manifest file: /somefile-1.txt", - "licenseConcluded": "MIT", - "licenseDeclared": "MIT", "copyrightText": "NOASSERTION", "externalRefs": [ { @@ -41,8 +39,6 @@ "versionInfo": "2.0.1", "downloadLocation": "NOASSERTION", "sourceInfo": "acquired package info from DPKG DB: /somefile-2.txt", - "licenseConcluded": "NONE", - "licenseDeclared": "NONE", "copyrightText": "NOASSERTION", "externalRefs": [ { diff --git a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden index 9e8e1453cc1..1aa7a41d0fd 100644 --- a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden +++ b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden @@ -3,24 +3,22 @@ "dataLicense": "CC0-1.0", "SPDXID": "SPDXRef-DOCUMENT", "name": "user-image-input", - "documentNamespace": "https://anchore.com/syft/image/user-image-input-ce98f51f-b483-4e93-9a15-5a8a16d35de6", + "documentNamespace": "https://anchore.com/syft/image/user-image-input-a1463ff3-8a7e-4ad9-b326-2cfbde38e82f", "creationInfo": { - "licenseListVersion": "3.19", + "licenseListVersion": "3.20", "creators": [ "Organization: Anchore, Inc", "Tool: syft-v0.42.0-bogus" ], - "created": "2023-01-20T21:41:03Z" + "created": "2023-04-24T14:39:59Z" }, "packages": [ { "name": "package-1", - "SPDXID": "SPDXRef-Package-python-package-1-66ba429119b8bec6", + "SPDXID": "SPDXRef-Package-python-package-1-ed77c5cb0567a1a6", "versionInfo": "1.0.1", "downloadLocation": "NOASSERTION", "sourceInfo": "acquired package info from installed python package manifest file: /somefile-1.txt", - "licenseConcluded": "MIT", - "licenseDeclared": "MIT", "copyrightText": "NOASSERTION", "externalRefs": [ { @@ -41,8 +39,6 @@ "versionInfo": "2.0.1", "downloadLocation": "NOASSERTION", "sourceInfo": "acquired package info from DPKG DB: /somefile-2.txt", - "licenseConcluded": "NONE", - "licenseDeclared": "NONE", "copyrightText": "NOASSERTION", "externalRefs": [ { @@ -152,32 +148,32 @@ ], "relationships": [ { - "spdxElementId": "SPDXRef-Package-python-package-1-66ba429119b8bec6", + "spdxElementId": "SPDXRef-Package-python-package-1-ed77c5cb0567a1a6", "relatedSpdxElement": "SPDXRef-5265a4dde3edbf7c", "relationshipType": "CONTAINS" }, { - "spdxElementId": "SPDXRef-Package-python-package-1-66ba429119b8bec6", + "spdxElementId": "SPDXRef-Package-python-package-1-ed77c5cb0567a1a6", "relatedSpdxElement": "SPDXRef-839d99ee67d9d174", "relationshipType": "CONTAINS" }, { - "spdxElementId": "SPDXRef-Package-python-package-1-66ba429119b8bec6", + "spdxElementId": "SPDXRef-Package-python-package-1-ed77c5cb0567a1a6", "relatedSpdxElement": "SPDXRef-9c2f7510199b17f6", "relationshipType": "CONTAINS" }, { - "spdxElementId": "SPDXRef-Package-python-package-1-66ba429119b8bec6", + "spdxElementId": "SPDXRef-Package-python-package-1-ed77c5cb0567a1a6", "relatedSpdxElement": "SPDXRef-c641caa71518099f", "relationshipType": "CONTAINS" }, { - "spdxElementId": "SPDXRef-Package-python-package-1-66ba429119b8bec6", + "spdxElementId": "SPDXRef-Package-python-package-1-ed77c5cb0567a1a6", "relatedSpdxElement": "SPDXRef-c6f5b29dca12661f", "relationshipType": "CONTAINS" }, { - "spdxElementId": "SPDXRef-Package-python-package-1-66ba429119b8bec6", + "spdxElementId": "SPDXRef-Package-python-package-1-ed77c5cb0567a1a6", "relatedSpdxElement": "SPDXRef-f9e49132a4b96ccd", "relationshipType": "CONTAINS" }, diff --git a/syft/formats/spdxjson/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden b/syft/formats/spdxjson/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden index b9ddf6e33390ab196d24a941bdda1dc969b30a0e..f4aa1e7bb74e9d8c2cf36a69cd7a6d910a9a7087 100644 GIT binary patch literal 15360 zcmeHOTW{Mo6wdR0g{Qs7Ht&=S>>+Ccv_P>8ZPoz`f`H;B(PB%6Bo_$`|Mwlqj$^w^ zv_!U(q7)36*F*9p$TkutO) zGMZRoBVzjp%@0BhNzM<@{Mr1kQV%d=1aU4!RSrUg9&~uK==LQhMX7S=Qd>!zgKH~q zPxWzI{eL<6^!dZ-?@x6XVT1%@n|i;G19Egj|CkGW&}prcr#f|?_oM5-w-=P}(f0pu z41w16|4)&n4P$Dsf6U3|`i~hV-u@e4-AeW}NK7yY+D?PuFc_=crX|SYM54%0$w{n2 zA$c5QCv6l*1`9{9V-`8eonl!GO+a+Yy<@!;(r!4pXVHvrP9}* zOOvF*z^P$j4})b|Hi5xxCfXdTE)vhmyJh%vY}0i)b?h|N(1}I)lk{saNGHSLVeoDQ zW19y*jMUYlqhGS&WMqr`oWHXCCP}Z3lSOY&(lWmrXGsO}yzXiG+awt-MSWDU9WJWR zYGmF%9_PUyM`!PU?N5rlKg_fm_KP@42P^Sq^dX~8nnxiRT)YI(KFo~gBC2wIos?FW zle|)y8X3VrOa_<-!z7*D2D7agd{D0oFwgZ>em_ZItqO5x~tfH0O`|0@9QFgVwJr+SOR+q?LAhfR6W z9s!SlN5CWSh7f3bSlf~RmlEmyzx$Xbt@Xbf|1TBX@BjCqf9(Cg`+)h)w$a-D+xvf! zAb9ip55chh@8!$H5B~jU;s0$r4Jt?M(r1e!iMj~G?+l?q{qvsMr#!W<=s;|%_#OHG zF!SGk_VUhH`v352`v1f?{yzr%55NBJehQS=NztjCfSqeqEZu;+Z`Zfr5%36j1Uv$- zi-4k9V52c*hB%62hPmd55@sb288OmDSP;mHR2VT4T8<+cQi@{g3{|ez{``jt+dTgv z#+iTr?_*|6gGWdW4{Vy6x3VRk#u}%kqF}61SSr%W@)X&~{DGZIndQL2IiEIFLsDSM zIj@hQ^A;{+s2Lm`AJ@B|ZoU2O^w+n;onJa}MX_G{j?ClF>~2I(3k`lv+s^j zvk#-)VjClsCe9Y+|5?Xh+H4d5gLAsV1xGdi!}C91iuUvB!P@z5!J7=WvKIjB|76G6 znYx;kQrQ`x@|-v75Nb*#yq21SE~R=bAQM!E%t`E2$!Eoco+AMpry L1Uv%Y4FdlHH$Ie? literal 15360 zcmeHOZExE)5ccQ&3QzkQ+a$#|8Q6!eDbNDNQnXnItSAb;CR%ODkmMpkkpI4u>^QNz zR!d|nDMrD7#kV8%9PfNQJ&u_WL{sC4wG4YAF?W_&oG8wCEIH;@I^mcSRx68*XAI#) zS|o9zEfbSL`-Koig0TTwUAy0P?Eur5Au0rp;{hTFBRm+gcG2B?EXzt2u%xb%b|0>* zyq?C#UG@L|#oKqUPJaL1azUGjQ@X47n=&95@8}=nSPX`pb@IJM-L(DB^^eEBpnQXN z_6o6%e<^IHFq!`dK~9-=x|&9qjbfp`(yV%`5m_9dET%h~KG zdOn9|Tiw2ztF!e&-{-UC+?6*if9A#KG&?&^*Qb25t zwjO@nAoKL`_z=B$cKY(y$+9dav)riJq||9Py;I&4ZyTDbRS|BZj}HNK0z2crh^oSz zrA6l z|6xLR&wm8LYrt^+>tS15Mc*Jb+_GsJ-pZbM8e5!}nu4)KVW~+gn^R;f^9LfADldS8 z^Wmyx7?D6RgT~UWm<1Z+>K5ARYHgoA;7t*|kzq624`JM{_M$DZvKrLZwZg1@1)1d9wZf_kg5$BsxsiS)4r zwt4jN`iCRb^kB35oMNuB)Vs3!FMj-?^S1Fn7z57#?bYxRh5KIPz;`}h^CqLc z!~$UbZ`~q4Rc9-oQn|wArO6j=*SvY_orI``@723)J?{d9eROsbfp-RaCZEXdN|lHqt#X%&)JXrUh-m P843Y`fIvXthe6;Ux}S+Y diff --git a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXJSONSPDXIDs.golden b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXJSONSPDXIDs.golden index c017916c28f..c6933cc54c3 100644 --- a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXJSONSPDXIDs.golden +++ b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXJSONSPDXIDs.golden @@ -2,11 +2,11 @@ SPDXVersion: SPDX-2.3 DataLicense: CC0-1.0 SPDXID: SPDXRef-DOCUMENT DocumentName: foobar/baz -DocumentNamespace: https://anchore.com/syft/dir/foobar/baz-478e410d-7fad-472c-b4e9-a4068ef28160 -LicenseListVersion: 3.19 +DocumentNamespace: https://anchore.com/syft/dir/foobar/baz-33139e6b-26d4-415e-97ee-6b1a733e849f +LicenseListVersion: 3.20 Creator: Organization: Anchore, Inc Creator: Tool: syft-v0.42.0-bogus -Created: 2022-12-21T03:39:05Z +Created: 2023-04-24T14:40:55Z ##### Package: @at-sign @@ -15,8 +15,6 @@ SPDXID: SPDXRef-Package---at-sign-3732f7a5679bdec4 PackageDownloadLocation: NOASSERTION FilesAnalyzed: false PackageSourceInfo: acquired package info from the following paths: -PackageLicenseConcluded: NONE -PackageLicenseDeclared: NONE PackageCopyrightText: NOASSERTION ##### Package: some/slashes @@ -26,8 +24,6 @@ SPDXID: SPDXRef-Package--some-slashes-1345166d4801153b PackageDownloadLocation: NOASSERTION FilesAnalyzed: false PackageSourceInfo: acquired package info from the following paths: -PackageLicenseConcluded: NONE -PackageLicenseDeclared: NONE PackageCopyrightText: NOASSERTION ##### Package: under_scores @@ -37,8 +33,6 @@ SPDXID: SPDXRef-Package--under-scores-290d5c77210978c1 PackageDownloadLocation: NOASSERTION FilesAnalyzed: false PackageSourceInfo: acquired package info from the following paths: -PackageLicenseConcluded: NONE -PackageLicenseDeclared: NONE PackageCopyrightText: NOASSERTION ##### Relationships diff --git a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden index 94cd399de23..063a97ca427 100644 --- a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden +++ b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden @@ -2,11 +2,11 @@ SPDXVersion: SPDX-2.3 DataLicense: CC0-1.0 SPDXID: SPDXRef-DOCUMENT DocumentName: user-image-input -DocumentNamespace: https://anchore.com/syft/image/user-image-input-73433e8c-364f-42b6-b5b7-9a4da8799868 -LicenseListVersion: 3.19 +DocumentNamespace: https://anchore.com/syft/image/user-image-input-d8df09c1-4a0f-4da2-a74b-d9f0c24d5ee6 +LicenseListVersion: 3.20 Creator: Organization: Anchore, Inc Creator: Tool: syft-v0.42.0-bogus -Created: 2022-12-21T03:39:05Z +Created: 2023-04-24T14:42:14Z ##### Unpackaged files @@ -54,8 +54,6 @@ PackageVersion: 2.0.1 PackageDownloadLocation: NOASSERTION FilesAnalyzed: false PackageSourceInfo: acquired package info from DPKG DB: /somefile-2.txt -PackageLicenseConcluded: NONE -PackageLicenseDeclared: NONE PackageCopyrightText: NOASSERTION ExternalRef: SECURITY cpe23Type cpe:2.3:*:some:package:2:*:*:*:*:*:*:* ExternalRef: PACKAGE-MANAGER purl pkg:deb/debian/package-2@2.0.1 @@ -63,24 +61,22 @@ ExternalRef: PACKAGE-MANAGER purl pkg:deb/debian/package-2@2.0.1 ##### Package: package-1 PackageName: package-1 -SPDXID: SPDXRef-Package-python-package-1-66ba429119b8bec6 +SPDXID: SPDXRef-Package-python-package-1-ed77c5cb0567a1a6 PackageVersion: 1.0.1 PackageDownloadLocation: NOASSERTION FilesAnalyzed: false PackageSourceInfo: acquired package info from installed python package manifest file: /somefile-1.txt -PackageLicenseConcluded: MIT -PackageLicenseDeclared: MIT PackageCopyrightText: NOASSERTION ExternalRef: SECURITY cpe23Type cpe:2.3:*:some:package:1:*:*:*:*:*:*:* ExternalRef: PACKAGE-MANAGER purl a-purl-1 ##### Relationships -Relationship: SPDXRef-Package-python-package-1-66ba429119b8bec6 CONTAINS SPDXRef-5265a4dde3edbf7c -Relationship: SPDXRef-Package-python-package-1-66ba429119b8bec6 CONTAINS SPDXRef-839d99ee67d9d174 -Relationship: SPDXRef-Package-python-package-1-66ba429119b8bec6 CONTAINS SPDXRef-9c2f7510199b17f6 -Relationship: SPDXRef-Package-python-package-1-66ba429119b8bec6 CONTAINS SPDXRef-c641caa71518099f -Relationship: SPDXRef-Package-python-package-1-66ba429119b8bec6 CONTAINS SPDXRef-c6f5b29dca12661f -Relationship: SPDXRef-Package-python-package-1-66ba429119b8bec6 CONTAINS SPDXRef-f9e49132a4b96ccd +Relationship: SPDXRef-Package-python-package-1-ed77c5cb0567a1a6 CONTAINS SPDXRef-5265a4dde3edbf7c +Relationship: SPDXRef-Package-python-package-1-ed77c5cb0567a1a6 CONTAINS SPDXRef-839d99ee67d9d174 +Relationship: SPDXRef-Package-python-package-1-ed77c5cb0567a1a6 CONTAINS SPDXRef-9c2f7510199b17f6 +Relationship: SPDXRef-Package-python-package-1-ed77c5cb0567a1a6 CONTAINS SPDXRef-c641caa71518099f +Relationship: SPDXRef-Package-python-package-1-ed77c5cb0567a1a6 CONTAINS SPDXRef-c6f5b29dca12661f +Relationship: SPDXRef-Package-python-package-1-ed77c5cb0567a1a6 CONTAINS SPDXRef-f9e49132a4b96ccd Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-DOCUMENT diff --git a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueDirectoryEncoder.golden b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueDirectoryEncoder.golden index 7bd71f05f05..c14c15cf23c 100644 --- a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueDirectoryEncoder.golden +++ b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueDirectoryEncoder.golden @@ -2,11 +2,11 @@ SPDXVersion: SPDX-2.3 DataLicense: CC0-1.0 SPDXID: SPDXRef-DOCUMENT DocumentName: /some/path -DocumentNamespace: https://anchore.com/syft/dir/some/path-1d303762-46d2-47b5-9c81-defa91387275 -LicenseListVersion: 3.19 +DocumentNamespace: https://anchore.com/syft/dir/some/path-ee9cc47e-9791-41ab-bec4-eeb1d73f6355 +LicenseListVersion: 3.20 Creator: Organization: Anchore, Inc Creator: Tool: syft-v0.42.0-bogus -Created: 2022-12-21T03:39:05Z +Created: 2023-04-24T14:40:45Z ##### Package: package-2 @@ -16,8 +16,6 @@ PackageVersion: 2.0.1 PackageDownloadLocation: NOASSERTION FilesAnalyzed: false PackageSourceInfo: acquired package info from DPKG DB: /some/path/pkg1 -PackageLicenseConcluded: NONE -PackageLicenseDeclared: NONE PackageCopyrightText: NOASSERTION ExternalRef: SECURITY cpe23Type cpe:2.3:*:some:package:2:*:*:*:*:*:*:* ExternalRef: PACKAGE-MANAGER purl pkg:deb/debian/package-2@2.0.1 @@ -25,13 +23,11 @@ ExternalRef: PACKAGE-MANAGER purl pkg:deb/debian/package-2@2.0.1 ##### Package: package-1 PackageName: package-1 -SPDXID: SPDXRef-Package-python-package-1-1b1d0be59ac59d2c +SPDXID: SPDXRef-Package-python-package-1-ed5945af814b9a95 PackageVersion: 1.0.1 PackageDownloadLocation: NOASSERTION FilesAnalyzed: false PackageSourceInfo: acquired package info from installed python package manifest file: /some/path/pkg1 -PackageLicenseConcluded: MIT -PackageLicenseDeclared: MIT PackageCopyrightText: NOASSERTION ExternalRef: SECURITY cpe23Type cpe:2.3:*:some:package:2:*:*:*:*:*:*:* ExternalRef: PACKAGE-MANAGER purl a-purl-2 diff --git a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueImageEncoder.golden b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueImageEncoder.golden index df1cb1467d3..acc27ddfdd4 100644 --- a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueImageEncoder.golden +++ b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueImageEncoder.golden @@ -2,11 +2,11 @@ SPDXVersion: SPDX-2.3 DataLicense: CC0-1.0 SPDXID: SPDXRef-DOCUMENT DocumentName: user-image-input -DocumentNamespace: https://anchore.com/syft/image/user-image-input-559af225-63af-4bc0-94fb-bce94913bcfa -LicenseListVersion: 3.19 +DocumentNamespace: https://anchore.com/syft/image/user-image-input-5b84390e-76e7-4849-8da1-7fae75cb9a37 +LicenseListVersion: 3.20 Creator: Organization: Anchore, Inc Creator: Tool: syft-v0.42.0-bogus -Created: 2022-12-21T03:39:05Z +Created: 2023-04-24T14:40:50Z ##### Package: package-2 @@ -16,8 +16,6 @@ PackageVersion: 2.0.1 PackageDownloadLocation: NOASSERTION FilesAnalyzed: false PackageSourceInfo: acquired package info from DPKG DB: /somefile-2.txt -PackageLicenseConcluded: NONE -PackageLicenseDeclared: NONE PackageCopyrightText: NOASSERTION ExternalRef: SECURITY cpe23Type cpe:2.3:*:some:package:2:*:*:*:*:*:*:* ExternalRef: PACKAGE-MANAGER purl pkg:deb/debian/package-2@2.0.1 @@ -25,13 +23,11 @@ ExternalRef: PACKAGE-MANAGER purl pkg:deb/debian/package-2@2.0.1 ##### Package: package-1 PackageName: package-1 -SPDXID: SPDXRef-Package-python-package-1-66ba429119b8bec6 +SPDXID: SPDXRef-Package-python-package-1-ed77c5cb0567a1a6 PackageVersion: 1.0.1 PackageDownloadLocation: NOASSERTION FilesAnalyzed: false PackageSourceInfo: acquired package info from installed python package manifest file: /somefile-1.txt -PackageLicenseConcluded: MIT -PackageLicenseDeclared: MIT PackageCopyrightText: NOASSERTION ExternalRef: SECURITY cpe23Type cpe:2.3:*:some:package:1:*:*:*:*:*:*:* ExternalRef: PACKAGE-MANAGER purl a-purl-1 diff --git a/syft/formats/spdxtagvalue/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden b/syft/formats/spdxtagvalue/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden index b9ddf6e33390ab196d24a941bdda1dc969b30a0e..f4aa1e7bb74e9d8c2cf36a69cd7a6d910a9a7087 100644 GIT binary patch literal 15360 zcmeHOTW{Mo6wdR0g{Qs7Ht&=S>>+Ccv_P>8ZPoz`f`H;B(PB%6Bo_$`|Mwlqj$^w^ zv_!U(q7)36*F*9p$TkutO) zGMZRoBVzjp%@0BhNzM<@{Mr1kQV%d=1aU4!RSrUg9&~uK==LQhMX7S=Qd>!zgKH~q zPxWzI{eL<6^!dZ-?@x6XVT1%@n|i;G19Egj|CkGW&}prcr#f|?_oM5-w-=P}(f0pu z41w16|4)&n4P$Dsf6U3|`i~hV-u@e4-AeW}NK7yY+D?PuFc_=crX|SYM54%0$w{n2 zA$c5QCv6l*1`9{9V-`8eonl!GO+a+Yy<@!;(r!4pXVHvrP9}* zOOvF*z^P$j4})b|Hi5xxCfXdTE)vhmyJh%vY}0i)b?h|N(1}I)lk{saNGHSLVeoDQ zW19y*jMUYlqhGS&WMqr`oWHXCCP}Z3lSOY&(lWmrXGsO}yzXiG+awt-MSWDU9WJWR zYGmF%9_PUyM`!PU?N5rlKg_fm_KP@42P^Sq^dX~8nnxiRT)YI(KFo~gBC2wIos?FW zle|)y8X3VrOa_<-!z7*D2D7agd{D0oFwgZ>em_ZItqO5x~tfH0O`|0@9QFgVwJr+SOR+q?LAhfR6W z9s!SlN5CWSh7f3bSlf~RmlEmyzx$Xbt@Xbf|1TBX@BjCqf9(Cg`+)h)w$a-D+xvf! zAb9ip55chh@8!$H5B~jU;s0$r4Jt?M(r1e!iMj~G?+l?q{qvsMr#!W<=s;|%_#OHG zF!SGk_VUhH`v352`v1f?{yzr%55NBJehQS=NztjCfSqeqEZu;+Z`Zfr5%36j1Uv$- zi-4k9V52c*hB%62hPmd55@sb288OmDSP;mHR2VT4T8<+cQi@{g3{|ez{``jt+dTgv z#+iTr?_*|6gGWdW4{Vy6x3VRk#u}%kqF}61SSr%W@)X&~{DGZIndQL2IiEIFLsDSM zIj@hQ^A;{+s2Lm`AJ@B|ZoU2O^w+n;onJa}MX_G{j?ClF>~2I(3k`lv+s^j zvk#-)VjClsCe9Y+|5?Xh+H4d5gLAsV1xGdi!}C91iuUvB!P@z5!J7=WvKIjB|76G6 znYx;kQrQ`x@|-v75Nb*#yq21SE~R=bAQM!E%t`E2$!Eoco+AMpry L1Uv%Y4FdlHH$Ie? literal 15360 zcmeHOZExE)5ccQ&3QzkQ+a$#|8Q6!eDbNDNQnXnItSAb;CR%ODkmMpkkpI4u>^QNz zR!d|nDMrD7#kV8%9PfNQJ&u_WL{sC4wG4YAF?W_&oG8wCEIH;@I^mcSRx68*XAI#) zS|o9zEfbSL`-Koig0TTwUAy0P?Eur5Au0rp;{hTFBRm+gcG2B?EXzt2u%xb%b|0>* zyq?C#UG@L|#oKqUPJaL1azUGjQ@X47n=&95@8}=nSPX`pb@IJM-L(DB^^eEBpnQXN z_6o6%e<^IHFq!`dK~9-=x|&9qjbfp`(yV%`5m_9dET%h~KG zdOn9|Tiw2ztF!e&-{-UC+?6*if9A#KG&?&^*Qb25t zwjO@nAoKL`_z=B$cKY(y$+9dav)riJq||9Py;I&4ZyTDbRS|BZj}HNK0z2crh^oSz zrA6l z|6xLR&wm8LYrt^+>tS15Mc*Jb+_GsJ-pZbM8e5!}nu4)KVW~+gn^R;f^9LfADldS8 z^Wmyx7?D6RgT~UWm<1Z+>K5ARYHgoA;7t*|kzq624`JM{_M$DZvKrLZwZg1@1)1d9wZf_kg5$BsxsiS)4r zwt4jN`iCRb^kB35oMNuB)Vs3!FMj-?^S1Fn7z57#?bYxRh5KIPz;`}h^CqLc z!~$UbZ`~q4Rc9-oQn|wArO6j=*SvY_orI``@723)J?{d9eROsbfp-RaCZEXdN|lHqt#X%&)JXrUh-m P843Y`fIvXthe6;Ux}S+Y diff --git a/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden b/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden index 3288035324b..719d73afea3 100644 --- a/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden +++ b/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden @@ -1,7 +1,7 @@ { "artifacts": [ { - "id": "1b1d0be59ac59d2c", + "id": "ed5945af814b9a95", "name": "package-1", "version": "1.0.1", "type": "python", @@ -12,7 +12,15 @@ } ], "licenses": [ - "MIT" + { + "value": "MIT", + "spdx-expression": "MIT", + "type": "declared", + "url": "", + "location": { + "path": "" + } + } ], "language": "python", "cpes": [ @@ -23,7 +31,6 @@ "metadata": { "name": "package-1", "version": "1.0.1", - "license": "", "author": "", "authorEmail": "", "platform": "", @@ -46,7 +53,7 @@ "path": "/some/path/pkg1" } ], - "licenses": [], + "licenses": null, "language": "", "cpes": [ "cpe:2.3:*:some:package:2:*:*:*:*:*:*:*" @@ -89,7 +96,7 @@ } }, "schema": { - "version": "7.1.4", - "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-7.1.4.json" + "version": "8.0.0", + "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-8.0.0.json" } } diff --git a/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden b/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden index 322e8cb0fde..2a4ef36b525 100644 --- a/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden +++ b/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden @@ -1,7 +1,7 @@ { "artifacts": [ { - "id": "304a5a8e5958a49d", + "id": "eec39d3202dce6f3", "name": "package-1", "version": "1.0.1", "type": "python", @@ -11,9 +11,7 @@ "path": "/a/place/a" } ], - "licenses": [ - "MIT" - ], + "licenses": [], "language": "python", "cpes": [ "cpe:2.3:*:some:package:1:*:*:*:*:*:*:*" @@ -23,7 +21,6 @@ "metadata": { "name": "package-1", "version": "1.0.1", - "license": "", "author": "", "authorEmail": "", "platform": "", @@ -41,7 +38,7 @@ "path": "/b/place/b" } ], - "licenses": [], + "licenses": null, "language": "", "cpes": [ "cpe:2.3:*:some:package:2:*:*:*:*:*:*:*" @@ -189,7 +186,7 @@ } }, "schema": { - "version": "7.1.4", - "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-7.1.4.json" + "version": "8.0.0", + "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-8.0.0.json" } } diff --git a/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden b/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden index eda15743f5f..38b77e76340 100644 --- a/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden +++ b/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden @@ -1,7 +1,7 @@ { "artifacts": [ { - "id": "66ba429119b8bec6", + "id": "ed77c5cb0567a1a6", "name": "package-1", "version": "1.0.1", "type": "python", @@ -9,11 +9,19 @@ "locations": [ { "path": "/somefile-1.txt", - "layerID": "sha256:fb6beecb75b39f4bb813dbf177e501edd5ddb3e69bb45cedeb78c676ee1b7a59" + "layerID": "sha256:ab62016f9bec7286af65604081564cadeeb364a48faca2346c3f5a5a1f5ef777" } ], "licenses": [ - "MIT" + { + "value": "MIT", + "spdx-expression": "MIT", + "type": "declared", + "url": "", + "location": { + "path": "" + } + } ], "language": "python", "cpes": [ @@ -24,7 +32,6 @@ "metadata": { "name": "package-1", "version": "1.0.1", - "license": "", "author": "", "authorEmail": "", "platform": "", @@ -40,10 +47,10 @@ "locations": [ { "path": "/somefile-2.txt", - "layerID": "sha256:319b588ce64253a87b533c8ed01cf0025e0eac98e7b516e12532957e1244fdec" + "layerID": "sha256:f1803845b6747d94d6e4ecce2331457e5f1c4fb97de5216f392a76f4582f63b2" } ], - "licenses": [], + "licenses": null, "language": "", "cpes": [ "cpe:2.3:*:some:package:2:*:*:*:*:*:*:*" @@ -64,11 +71,11 @@ ], "artifactRelationships": [], "source": { - "id": "1a678f111c8ddc66fd82687bb024e0dd6af61314404937a80e810c0cf317b796", + "id": "c8ac88bbaf3d1c036f6a1d601c3d52bafbf05571c97d68322e7cb3a7ecaa304f", "type": "image", "target": { "userInput": "user-image-input", - "imageID": "sha256:3c51b06feb0cda8ee62d0e3755ef2a8496a6b71f8a55b245f07f31c4bb813d31", + "imageID": "sha256:a3c61dc134d2f31b415c50324e75842d7f91622f39a89468e51938330b3fd3af", "manifestDigest": "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368", "mediaType": "application/vnd.docker.distribution.manifest.v2+json", "tags": [ @@ -78,17 +85,17 @@ "layers": [ { "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", - "digest": "sha256:fb6beecb75b39f4bb813dbf177e501edd5ddb3e69bb45cedeb78c676ee1b7a59", + "digest": "sha256:ab62016f9bec7286af65604081564cadeeb364a48faca2346c3f5a5a1f5ef777", "size": 22 }, { "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", - "digest": "sha256:319b588ce64253a87b533c8ed01cf0025e0eac98e7b516e12532957e1244fdec", + "digest": "sha256:f1803845b6747d94d6e4ecce2331457e5f1c4fb97de5216f392a76f4582f63b2", "size": 16 } ], - "manifest": "eyJzY2hlbWFWZXJzaW9uIjoyLCJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmRpc3RyaWJ1dGlvbi5tYW5pZmVzdC52Mitqc29uIiwiY29uZmlnIjp7Im1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuY29udGFpbmVyLmltYWdlLnYxK2pzb24iLCJzaXplIjo2NzMsImRpZ2VzdCI6InNoYTI1NjozYzUxYjA2ZmViMGNkYThlZTYyZDBlMzc1NWVmMmE4NDk2YTZiNzFmOGE1NWIyNDVmMDdmMzFjNGJiODEzZDMxIn0sImxheWVycyI6W3sibWVkaWFUeXBlIjoiYXBwbGljYXRpb24vdm5kLmRvY2tlci5pbWFnZS5yb290ZnMuZGlmZi50YXIuZ3ppcCIsInNpemUiOjIwNDgsImRpZ2VzdCI6InNoYTI1NjpmYjZiZWVjYjc1YjM5ZjRiYjgxM2RiZjE3N2U1MDFlZGQ1ZGRiM2U2OWJiNDVjZWRlYjc4YzY3NmVlMWI3YTU5In0seyJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmltYWdlLnJvb3Rmcy5kaWZmLnRhci5nemlwIiwic2l6ZSI6MjA0OCwiZGlnZXN0Ijoic2hhMjU2OjMxOWI1ODhjZTY0MjUzYTg3YjUzM2M4ZWQwMWNmMDAyNWUwZWFjOThlN2I1MTZlMTI1MzI5NTdlMTI0NGZkZWMifV19", - "config": "eyJhcmNoaXRlY3R1cmUiOiJhbWQ2NCIsImNvbmZpZyI6eyJFbnYiOlsiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iXSwiV29ya2luZ0RpciI6Ii8iLCJPbkJ1aWxkIjpudWxsfSwiY3JlYXRlZCI6IjIwMjItMDgtMDFUMjA6MDk6MjIuNTA5NDIxNzEyWiIsImhpc3RvcnkiOlt7ImNyZWF0ZWQiOiIyMDIyLTA4LTAxVDIwOjA5OjIyLjQ4Nzg5NTUxOVoiLCJjcmVhdGVkX2J5IjoiQUREIGZpbGUtMS50eHQgL3NvbWVmaWxlLTEudHh0ICMgYnVpbGRraXQiLCJjb21tZW50IjoiYnVpbGRraXQuZG9ja2VyZmlsZS52MCJ9LHsiY3JlYXRlZCI6IjIwMjItMDgtMDFUMjA6MDk6MjIuNTA5NDIxNzEyWiIsImNyZWF0ZWRfYnkiOiJBREQgZmlsZS0yLnR4dCAvc29tZWZpbGUtMi50eHQgIyBidWlsZGtpdCIsImNvbW1lbnQiOiJidWlsZGtpdC5kb2NrZXJmaWxlLnYwIn1dLCJvcyI6ImxpbnV4Iiwicm9vdGZzIjp7InR5cGUiOiJsYXllcnMiLCJkaWZmX2lkcyI6WyJzaGEyNTY6ZmI2YmVlY2I3NWIzOWY0YmI4MTNkYmYxNzdlNTAxZWRkNWRkYjNlNjliYjQ1Y2VkZWI3OGM2NzZlZTFiN2E1OSIsInNoYTI1NjozMTliNTg4Y2U2NDI1M2E4N2I1MzNjOGVkMDFjZjAwMjVlMGVhYzk4ZTdiNTE2ZTEyNTMyOTU3ZTEyNDRmZGVjIl19fQ==", + "manifest": "eyJzY2hlbWFWZXJzaW9uIjoyLCJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmRpc3RyaWJ1dGlvbi5tYW5pZmVzdC52Mitqc29uIiwiY29uZmlnIjp7Im1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuY29udGFpbmVyLmltYWdlLnYxK2pzb24iLCJzaXplIjo2NzMsImRpZ2VzdCI6InNoYTI1NjphM2M2MWRjMTM0ZDJmMzFiNDE1YzUwMzI0ZTc1ODQyZDdmOTE2MjJmMzlhODk0NjhlNTE5MzgzMzBiM2ZkM2FmIn0sImxheWVycyI6W3sibWVkaWFUeXBlIjoiYXBwbGljYXRpb24vdm5kLmRvY2tlci5pbWFnZS5yb290ZnMuZGlmZi50YXIuZ3ppcCIsInNpemUiOjIwNDgsImRpZ2VzdCI6InNoYTI1NjphYjYyMDE2ZjliZWM3Mjg2YWY2NTYwNDA4MTU2NGNhZGVlYjM2NGE0OGZhY2EyMzQ2YzNmNWE1YTFmNWVmNzc3In0seyJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmltYWdlLnJvb3Rmcy5kaWZmLnRhci5nemlwIiwic2l6ZSI6MjA0OCwiZGlnZXN0Ijoic2hhMjU2OmYxODAzODQ1YjY3NDdkOTRkNmU0ZWNjZTIzMzE0NTdlNWYxYzRmYjk3ZGU1MjE2ZjM5MmE3NmY0NTgyZjYzYjIifV19", + "config": "eyJhcmNoaXRlY3R1cmUiOiJhbWQ2NCIsImNvbmZpZyI6eyJFbnYiOlsiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iXSwiV29ya2luZ0RpciI6Ii8iLCJPbkJ1aWxkIjpudWxsfSwiY3JlYXRlZCI6IjIwMjMtMDQtMjFUMTk6MTA6MzcuNjUxODMxMjM0WiIsImhpc3RvcnkiOlt7ImNyZWF0ZWQiOiIyMDIzLTA0LTIxVDE5OjEwOjM3LjYwNzYxMzU1NVoiLCJjcmVhdGVkX2J5IjoiQUREIGZpbGUtMS50eHQgL3NvbWVmaWxlLTEudHh0ICMgYnVpbGRraXQiLCJjb21tZW50IjoiYnVpbGRraXQuZG9ja2VyZmlsZS52MCJ9LHsiY3JlYXRlZCI6IjIwMjMtMDQtMjFUMTk6MTA6MzcuNjUxODMxMjM0WiIsImNyZWF0ZWRfYnkiOiJBREQgZmlsZS0yLnR4dCAvc29tZWZpbGUtMi50eHQgIyBidWlsZGtpdCIsImNvbW1lbnQiOiJidWlsZGtpdC5kb2NrZXJmaWxlLnYwIn1dLCJvcyI6ImxpbnV4Iiwicm9vdGZzIjp7InR5cGUiOiJsYXllcnMiLCJkaWZmX2lkcyI6WyJzaGEyNTY6YWI2MjAxNmY5YmVjNzI4NmFmNjU2MDQwODE1NjRjYWRlZWIzNjRhNDhmYWNhMjM0NmMzZjVhNWExZjVlZjc3NyIsInNoYTI1NjpmMTgwMzg0NWI2NzQ3ZDk0ZDZlNGVjY2UyMzMxNDU3ZTVmMWM0ZmI5N2RlNTIxNmYzOTJhNzZmNDU4MmY2M2IyIl19fQ==", "repoDigests": [], "architecture": "", "os": "" @@ -112,7 +119,7 @@ } }, "schema": { - "version": "7.1.4", - "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-7.1.4.json" + "version": "8.0.0", + "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-8.0.0.json" } } diff --git a/syft/formats/syftjson/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden b/syft/formats/syftjson/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden index 11a1958c8935029370bb6a48ad18899403776949..f4aa1e7bb74e9d8c2cf36a69cd7a6d910a9a7087 100644 GIT binary patch literal 15360 zcmeHOTW{Mo6wdR0g{Qs7Ht&=S>>+Ccv_P>8ZPoz`f`H;B(PB%6Bo_$`|Mwlqj$^w^ zv_!U(q7)36*F*9p$TkutO) zGMZRoBVzjp%@0BhNzM<@{Mr1kQV%d=1aU4!RSrUg9&~uK==LQhMX7S=Qd>!zgKH~q zPxWzI{eL<6^!dZ-?@x6XVT1%@n|i;G19Egj|CkGW&}prcr#f|?_oM5-w-=P}(f0pu z41w16|4)&n4P$Dsf6U3|`i~hV-u@e4-AeW}NK7yY+D?PuFc_=crX|SYM54%0$w{n2 zA$c5QCv6l*1`9{9V-`8eonl!GO+a+Yy<@!;(r!4pXVHvrP9}* zOOvF*z^P$j4})b|Hi5xxCfXdTE)vhmyJh%vY}0i)b?h|N(1}I)lk{saNGHSLVeoDQ zW19y*jMUYlqhGS&WMqr`oWHXCCP}Z3lSOY&(lWmrXGsO}yzXiG+awt-MSWDU9WJWR zYGmF%9_PUyM`!PU?N5rlKg_fm_KP@42P^Sq^dX~8nnxiRT)YI(KFo~gBC2wIos?FW zle|)y8X3VrOa_<-!z7*D2D7agd{D0oFwgZ>em_ZItqO5x~tfH0O`|0@9QFgVwJr+SOR+q?LAhfR6W z9s!SlN5CWSh7f3bSlf~RmlEmyzx$Xbt@Xbf|1TBX@BjCqf9(Cg`+)h)w$a-D+xvf! zAb9ip55chh@8!$H5B~jU;s0$r4Jt?M(r1e!iMj~G?+l?q{qvsMr#!W<=s;|%_#OHG zF!SGk_VUhH`v352`v1f?{yzr%55NBJehQS=NztjCfSqeqEZu;+Z`Zfr5%36j1Uv$- zi-4k9V52c*hB%62hPmd55@sb288OmDSP;mHR2VT4T8<+cQi@{g3{|ez{``jt+dTgv z#+iTr?_*|6gGWdW4{Vy6x3VRk#u}%kqF}61SSr%W@)X&~{DGZIndQL2IiEIFLsDSM zIj@hQ^A;{+s2Lm`AJ@B|ZoU2O^w+n;onJa}MX_G{j?ClF>~2I(3k`lv+s^j zvk#-)VjClsCe9Y+|5?Xh+H4d5gLAsV1xGdi!}C91iuUvB!P@z5!J7=WvKIjB|76G6 znYx;kQrQ`x@|-v75Nb*#yq21SE~R=bAQM!E%t`E2$!Eoco+AMpry L1Uv%Y4FdlHH$Ie? literal 15360 zcmeHOZExE)5ccQ&3Qzl**nFpCU>~}sKnoO0(Pka6qA2)sOtjjPA<0F9Apdm%%?PJlu^wTR3f}9JyT$INc2+m0LY2Bo z+kCjL@($GhDlwJ2$Qy9Ytb$~en2A>+IZ9kvg#0`iT_ul-cgg5g^6bIcA**Z}Z~(B(HF&Ar5m|{4vYNJ{c9W@%S2tt-v&Z z<%AK&j!Ak<=oup;l8qP}NRkOgHD&)`*NePv%IXqzyV}xCq&m%{q*>c-Il>!@J(C9~ zC;gC*;g}AZ_f3CTmlIei|JJvyc5m}$Zjs3ZiUxaqxH<5pdka}WC)5Q(m;?gGWhr0tP6t%jdrI6?%$4P|rP|4?pM&h_=5YAJNDFF!+e z=O#b-`rq0AFCRUB@#OT+yZo?a|3~-pb^kM>h>ZLHT{hiS9UXLze0HTul=BrV(0Tsv zApVzZWBq4Da24nOAzp{8ByWT5uCD>cd{a{`qhswgO%cpt?3XzyKzi#9rAlcoiC`%g zUKpNQNsP1tFy!MF(O;Tr$E;^4>mDcoaY|$I0;)H+h~;rvH-(wBfi4O=DJR(Aw)|HO zE&9Nk2Tfz##f!!xI2i>qu3_a~Im1wY^Cnc-b$RowK?Je9KAl3bE@xgkEgUMbY%|AE zEzCJe9!{2s=O}qHG3QG~UzX$91TYj|G+M(-sO_VosV=8w-a?Q?i#70clsq*S#>?(4 zl)6E1->^M)ge&iF&=!2&*P@?*$}3RoyMrrTC;o7bBnK^r^2y(p_)mNk|Fby_6#1V| zT6A|6H-yKWXV{(GRY_a?k3>Mb{}ZMtG5}Qf2&I}M{yRuy`jg|oIoZYf?JkPDcm3 z?))!NY%~7@`QP~c=OFLQlK<`c8X%c=%h}RQ6HMD^GdBokZnS^%#EZz(ejsi)nA)#M zAa-^9-uOQf`JY2905|;KJOOGyBW~ya)JOaurVB>?Z$Iu>liFYw-8=)Xe_`qy8gQ*i zEJO=L3q%V<3;ci&PqIn?_abld!Ax4Gf-TyUKK3XcKB_mIfGV=884#+q6MM_z9S3#2P@x!egFUf From 3b01aa0a6b9ccd5ecd17c969ae6f0d6042d22747 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Mon, 24 Apr 2023 11:04:27 -0400 Subject: [PATCH 26/99] test: update unit tests with latest merged code Signed-off-by: Christopher Phillips --- syft/pkg/cataloger/apkdb/parse_apk_db_test.go | 16 ++---- syft/pkg/cataloger/golang/licenses_test.go | 56 ++++++++++++++----- .../cataloger/golang/parse_go_binary_test.go | 2 +- .../pkg/cataloger/golang/parse_go_mod_test.go | 4 +- .../pkg/cataloger/java/archive_parser_test.go | 10 +--- syft/pkg/cataloger/kernel/cataloger_test.go | 8 ++- .../python/parse_wheel_egg_metadata_test.go | 12 +--- 7 files changed, 62 insertions(+), 46 deletions(-) diff --git a/syft/pkg/cataloger/apkdb/parse_apk_db_test.go b/syft/pkg/cataloger/apkdb/parse_apk_db_test.go index 44ac21fb234..d2802249a50 100644 --- a/syft/pkg/cataloger/apkdb/parse_apk_db_test.go +++ b/syft/pkg/cataloger/apkdb/parse_apk_db_test.go @@ -667,12 +667,8 @@ func TestSinglePackageDetails(t *testing.T) { func TestMultiplePackages(t *testing.T) { fixture := "test-fixtures/multiple" - fixtureLocation := source.Location{ - Coordinates: source.Coordinates{ - RealPath: fixture, - }, - } - fixtureLocationSet := source.NewLocationSet(source.NewLocation(fixture)) + location := source.NewLocation(fixture) + fixtureLocationSet := source.NewLocationSet(location) expectedPkgs := []pkg.Package{ { Name: "libc-utils", @@ -681,7 +677,7 @@ func TestMultiplePackages(t *testing.T) { { Value: "BSD", Type: license.Declared, - Location: fixtureLocation, + Location: location, }, }, Type: pkg.ApkPkg, @@ -716,17 +712,17 @@ func TestMultiplePackages(t *testing.T) { Value: "MIT", SPDXExpression: "MIT", Type: license.Declared, - Location: fixtureLocation, + Location: location, }, { Value: "BSD", Type: license.Declared, - Location: fixtureLocation, + Location: location, }, { Value: "GPL2+", Type: license.Declared, - Location: fixtureLocation, + Location: location, }, }, MetadataType: pkg.ApkMetadataType, diff --git a/syft/pkg/cataloger/golang/licenses_test.go b/syft/pkg/cataloger/golang/licenses_test.go index fa361004a04..81b5644b2bd 100644 --- a/syft/pkg/cataloger/golang/licenses_test.go +++ b/syft/pkg/cataloger/golang/licenses_test.go @@ -13,24 +13,39 @@ import ( "github.com/stretchr/testify/require" + "github.com/anchore/syft/syft/license" + "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/source" ) func Test_LocalLicenseSearch(t *testing.T) { + // TODO: should locationsByGlob return non default constructor values? + loc1 := source.NewLocation("github.com/someorg/somename@v0.3.2/LICENSE") + loc1.Annotations = nil + loc2 := source.NewLocation("github.com/!cap!o!r!g/!cap!project@v4.111.5/LICENSE.txt") + loc2.Annotations = nil tests := []struct { name string version string - expected string + expected pkg.License }{ { - name: "github.com/someorg/somename", - version: "v0.3.2", - expected: "Apache-2.0", + name: "github.com/someorg/somename", + version: "v0.3.2", + expected: pkg.License{ + SPDXExpression: "Apache-2.0", + Type: license.Concluded, + Location: loc1, + }, }, { - name: "github.com/CapORG/CapProject", - version: "v4.111.5", - expected: "MIT", + name: "github.com/CapORG/CapProject", + version: "v4.111.5", + expected: pkg.License{ + SPDXExpression: "MIT", + Type: license.Concluded, + Location: loc2, + }, }, } @@ -54,6 +69,11 @@ func Test_LocalLicenseSearch(t *testing.T) { } func Test_RemoteProxyLicenseSearch(t *testing.T) { + // TODO: should locationsByGlob return non default constructor values? + loc1 := source.NewLocation("github.com/someorg/somename@v0.3.2/LICENSE") + loc1.Annotations = nil + loc2 := source.NewLocation("github.com/!cap!o!r!g/!cap!project@v4.111.5/LICENSE.txt") + loc2.Annotations = nil server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { buf := &bytes.Buffer{} uri := strings.TrimPrefix(strings.TrimSuffix(r.RequestURI, ".zip"), "/") @@ -94,17 +114,25 @@ func Test_RemoteProxyLicenseSearch(t *testing.T) { tests := []struct { name string version string - expected string + expected pkg.License }{ { - name: "github.com/someorg/somename", - version: "v0.3.2", - expected: "Apache-2.0", + name: "github.com/someorg/somename", + version: "v0.3.2", + expected: pkg.License{ + SPDXExpression: "Apache-2.0", + Type: license.Concluded, + Location: loc1, + }, }, { - name: "github.com/CapORG/CapProject", - version: "v4.111.5", - expected: "MIT", + name: "github.com/CapORG/CapProject", + version: "v4.111.5", + expected: pkg.License{ + SPDXExpression: "MIT", + Type: license.Concluded, + Location: loc2, + }, }, } diff --git a/syft/pkg/cataloger/golang/parse_go_binary_test.go b/syft/pkg/cataloger/golang/parse_go_binary_test.go index 4fe87c2ecc2..0ae0b57359b 100644 --- a/syft/pkg/cataloger/golang/parse_go_binary_test.go +++ b/syft/pkg/cataloger/golang/parse_go_binary_test.go @@ -498,7 +498,7 @@ func TestBuildGoPkgInfo(t *testing.T) { for i := range test.expected { p := &test.expected[i] if p.Licenses == nil { - p.Licenses = []string{} + p.Licenses = []pkg.License{} } p.SetID() } diff --git a/syft/pkg/cataloger/golang/parse_go_mod_test.go b/syft/pkg/cataloger/golang/parse_go_mod_test.go index 531ee5ef736..56ab179eb6e 100644 --- a/syft/pkg/cataloger/golang/parse_go_mod_test.go +++ b/syft/pkg/cataloger/golang/parse_go_mod_test.go @@ -91,7 +91,7 @@ func TestParseGoMod(t *testing.T) { for i := range test.expected { p := &test.expected[i] if p.Licenses == nil { - p.Licenses = []string{} + p.Licenses = []pkg.License{} } } c := goModCataloger{} @@ -157,7 +157,7 @@ func Test_GoSumHashes(t *testing.T) { for i := range test.expected { p := &test.expected[i] if p.Licenses == nil { - p.Licenses = []string{} + p.Licenses = []pkg.License{} } } pkgtest.NewCatalogTester(). diff --git a/syft/pkg/cataloger/java/archive_parser_test.go b/syft/pkg/cataloger/java/archive_parser_test.go index 83ab355fb10..fb23f78e0ec 100644 --- a/syft/pkg/cataloger/java/archive_parser_test.go +++ b/syft/pkg/cataloger/java/archive_parser_test.go @@ -101,13 +101,9 @@ func TestParseJar(t *testing.T) { PURL: "pkg:maven/io.jenkins.plugins/example-jenkins-plugin@1.0-SNAPSHOT", Licenses: []pkg.License{ { - Value: "MIT License", - Type: "declared", - Location: source.Location{ - Coordinates: source.Coordinates{ - RealPath: "test-fixtures/java-builds/packages/example-jenkins-plugin.hpi", - }, - }, + Value: "MIT License", + Type: "declared", + Location: source.NewLocation("test-fixtures/java-builds/packages/example-jenkins-plugin.hpi"), }, }, Language: pkg.Java, diff --git a/syft/pkg/cataloger/kernel/cataloger_test.go b/syft/pkg/cataloger/kernel/cataloger_test.go index ed5e6b083c9..96e63a54cf6 100644 --- a/syft/pkg/cataloger/kernel/cataloger_test.go +++ b/syft/pkg/cataloger/kernel/cataloger_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" "github.com/anchore/syft/syft/source" @@ -47,8 +48,11 @@ func Test_KernelCataloger(t *testing.T) { "/lib/modules/6.0.7-301.fc37.x86_64/kernel/drivers/tty/ttynull.ko", ), ), - Licenses: []string{ - "GPL v2", + Licenses: []pkg.License{ + { + Value: "GPL v2", + Type: license.Declared, + }, }, Type: pkg.LinuxKernelModulePkg, PURL: "pkg:generic/ttynull", diff --git a/syft/pkg/cataloger/python/parse_wheel_egg_metadata_test.go b/syft/pkg/cataloger/python/parse_wheel_egg_metadata_test.go index 14894d5afdf..cb776b66937 100644 --- a/syft/pkg/cataloger/python/parse_wheel_egg_metadata_test.go +++ b/syft/pkg/cataloger/python/parse_wheel_egg_metadata_test.go @@ -19,11 +19,7 @@ func TestParseWheelEggMetadata(t *testing.T) { Fixture: "test-fixtures/egg-info/PKG-INFO", ExpectedMetadata: parsedData{ "Apache 2.0", - source.Location{ - Coordinates: source.Coordinates{ - RealPath: "test-fixtures/egg-info/PKG-INFO", - }, - }, + source.NewLocation("test-fixtures/egg-info/PKG-INFO"), pkg.PythonPackageMetadata{ Name: "requests", Version: "2.22.0", @@ -38,11 +34,7 @@ func TestParseWheelEggMetadata(t *testing.T) { Fixture: "test-fixtures/dist-info/METADATA", ExpectedMetadata: parsedData{ "BSD License", - source.Location{ - Coordinates: source.Coordinates{ - RealPath: "test-fixtures/dist-info/METADATA", - }, - }, + source.NewLocation("test-fixtures/dist-info/METADATA"), pkg.PythonPackageMetadata{ Name: "Pygments", Version: "2.6.1", From 195ee038b7389f45a156908888d69416f20ad3b9 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Mon, 24 Apr 2023 11:18:47 -0400 Subject: [PATCH 27/99] chore: go mod tidy Signed-off-by: Christopher Phillips --- go.mod | 20 +++++-------------- go.sum | 62 +++++++++++++--------------------------------------------- 2 files changed, 19 insertions(+), 63 deletions(-) diff --git a/go.mod b/go.mod index dbcb56d3dad..c4b62f8c63c 100644 --- a/go.mod +++ b/go.mod @@ -49,7 +49,7 @@ require ( ) require ( - github.com/CycloneDX/cyclonedx-go v0.7.1-0.20221222100750-41a1ac565cce + github.com/CycloneDX/cyclonedx-go v0.7.1 github.com/Masterminds/sprig/v3 v3.2.3 github.com/anchore/go-logger v0.0.0-20220728155337-03b66a5207d8 github.com/anchore/stereoscope v0.0.0-20230412183729-8602f1afc574 @@ -61,7 +61,7 @@ require ( github.com/google/go-containerregistry v0.14.0 github.com/google/licensecheck v0.3.1 github.com/invopop/jsonschema v0.7.0 - github.com/knqyf263/go-rpmdb v0.0.0-20221030135625-4082a22221ce + github.com/knqyf263/go-rpmdb v0.0.0-20230301153543-ba94b245509b github.com/opencontainers/go-digest v1.0.0 github.com/sassoftware/go-rpmutils v0.2.0 github.com/vbatts/go-mtree v0.5.3 @@ -103,14 +103,13 @@ require ( github.com/imdario/mergo v0.3.13 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect - github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect github.com/klauspost/compress v1.16.4 // indirect github.com/klauspost/pgzip v1.2.5 // indirect github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.16 // indirect + github.com/mattn/go-isatty v0.0.17 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect @@ -121,7 +120,6 @@ require ( github.com/pierrec/lz4/v4 v4.1.15 // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/rogpeppe/go-internal v1.8.0 // indirect github.com/shopspring/decimal v1.2.0 // indirect @@ -150,16 +148,8 @@ require ( google.golang.org/protobuf v1.30.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect - lukechampine.com/uint128 v1.1.1 // indirect - modernc.org/cc/v3 v3.36.0 // indirect - modernc.org/ccgo/v3 v3.16.6 // indirect - modernc.org/libc v1.16.7 // indirect - modernc.org/mathutil v1.4.1 // indirect - modernc.org/memory v1.1.1 // indirect - modernc.org/opt v0.1.1 // indirect - modernc.org/sqlite v1.17.3 // indirect - modernc.org/strutil v1.1.1 // indirect - modernc.org/token v1.0.0 // indirect + modernc.org/libc v1.22.4 // indirect + modernc.org/sqlite v1.21.2 // indirect ) require ( diff --git a/go.sum b/go.sum index 9f934be8cec..45c8ff0f565 100644 --- a/go.sum +++ b/go.sum @@ -54,8 +54,8 @@ github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg6 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/CycloneDX/cyclonedx-go v0.7.1-0.20221222100750-41a1ac565cce h1:o5r3msApzvtE5LhcMkxWaKernD/PK0HpMccu7ywBj5Q= -github.com/CycloneDX/cyclonedx-go v0.7.1-0.20221222100750-41a1ac565cce/go.mod h1:XURd0m8zvnLE5aIRqg6JOVRl7qZ/pWBtuFa9EHjQwFc= +github.com/CycloneDX/cyclonedx-go v0.7.1 h1:5w1SxjGm9MTMNTuRbEPyw21ObdbaagTWF/KfF0qHTRE= +github.com/CycloneDX/cyclonedx-go v0.7.1/go.mod h1:N/nrdWQI2SIjaACyyDs/u7+ddCkyl/zkNs8xFsHF2Ps= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= @@ -176,7 +176,6 @@ github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY= github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= @@ -209,6 +208,7 @@ github.com/gabriel-vasile/mimetype v1.4.0/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmx github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/github/go-spdx/v2 v2.1.2 h1:p+Tv0yMgcuO0/vnMe9Qh4tmUgYhI6AsLVlakZ/Sx+DM= github.com/github/go-spdx/v2 v2.1.2/go.mod h1:hMCrsFgT0QnCwn7G8gxy/MxMpy67WgZrwFeISTn0o6w= +github.com/glebarez/go-sqlite v1.20.3 h1:89BkqGOXR9oRmG58ZrzgoY/Fhy5x0M+/WV48U5zVrZ4= github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4= github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= @@ -391,8 +391,6 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1 github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= @@ -405,8 +403,8 @@ github.com/klauspost/compress v1.16.4/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQs github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= -github.com/knqyf263/go-rpmdb v0.0.0-20221030135625-4082a22221ce h1:/w0hAcauo/FBVaBvNMQdPZgKjTu5Ip3jvGIM1+VUE7o= -github.com/knqyf263/go-rpmdb v0.0.0-20221030135625-4082a22221ce/go.mod h1:zp6SMcRd0GB+uwNJjr+DkrNZdQZ4er2HMO6KyD0vIGU= +github.com/knqyf263/go-rpmdb v0.0.0-20230301153543-ba94b245509b h1:boYyvL3tbUuKcMN029mpCl7oYYJ7yIXujLj+fiW4Alc= +github.com/knqyf263/go-rpmdb v0.0.0-20230301153543-ba94b245509b/go.mod h1:9LQcoMCMQ9vrF7HcDtXfvqGO4+ddxFQ8+YF/0CVGDww= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= @@ -440,13 +438,12 @@ github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcME github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-sqlite3 v1.14.12 h1:TJ1bhYJPV44phC+IMu1u2K/i5RriLTPe+yc68XDJ1Z0= -github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= @@ -528,8 +525,7 @@ github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8b github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= -github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= @@ -966,7 +962,6 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= @@ -1175,41 +1170,12 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -lukechampine.com/uint128 v1.1.1 h1:pnxCASz787iMf+02ssImqk6OLt+Z5QHMoZyUXR4z6JU= -lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -modernc.org/cc/v3 v3.36.0 h1:0kmRkTmqNidmu3c7BNDSdVHCxXCkWLmWmCIVX4LUboo= -modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= -modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc= -modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw= -modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= -modernc.org/ccgo/v3 v3.16.6 h1:3l18poV+iUemQ98O3X5OMr97LOqlzis+ytivU4NqGhA= -modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= -modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk= -modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= -modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM= -modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= -modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= -modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A= -modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU= -modernc.org/libc v1.16.7 h1:qzQtHhsZNpVPpeCu+aMIQldXeV1P0vRhSqCL0nOIJOA= -modernc.org/libc v1.16.7/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= -modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8= -modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/memory v1.1.1 h1:bDOL0DIDLQv7bWhP3gMvIrnoFw+Eo6F7a2QK9HPDiFU= -modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= -modernc.org/opt v0.1.1 h1:/0RX92k9vwVeDXj+Xn23DKp2VJubL7k8qNffND6qn3A= -modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/sqlite v1.17.3 h1:iE+coC5g17LtByDYDWKpR6m2Z9022YrSh3bumwOnIrI= -modernc.org/sqlite v1.17.3/go.mod h1:10hPVYar9C0kfXuTWGz8s0XtB8uAGymUy51ZzStYe3k= -modernc.org/strutil v1.1.1 h1:xv+J1BXY3Opl2ALrBwyfEikFAj8pmqcpnfmuwUwcozs= -modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= -modernc.org/tcl v1.13.1 h1:npxzTwFTZYM8ghWicVIX1cRWzj7Nd8i6AqqX2p+IYao= -modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw= -modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk= -modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -modernc.org/z v1.5.1 h1:RTNHdsrOpeoSeOF4FbzTo8gBYByaJ5xT7NgZ9ZqRiJM= -modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8= +modernc.org/libc v1.22.4 h1:wymSbZb0AlrjdAVX3cjreCHTPCpPARbQXNz6BHPzdwQ= +modernc.org/libc v1.22.4/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY= +modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ= +modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds= +modernc.org/sqlite v1.21.2 h1:ixuUG0QS413Vfzyx6FWx6PYTmHaOegTY+hjzhn7L+a0= +modernc.org/sqlite v1.21.2/go.mod h1:cxbLkB5WS32DnQqeH4h4o1B0eMr8W/y8/RGuxQ3JsC0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= From cb9f23bb943ae9eb26c4e6dcdb2a3717334d8e5f Mon Sep 17 00:00:00 2001 From: Christopher Angelo Phillips <32073428+spiffcs@users.noreply.github.com> Date: Mon, 24 Apr 2023 11:46:33 -0400 Subject: [PATCH 28/99] Update syft/pkg/cataloger/alpm/package.go Co-authored-by: Alex Goodman Signed-off-by: Christopher Phillips --- syft/pkg/cataloger/alpm/package.go | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/syft/pkg/cataloger/alpm/package.go b/syft/pkg/cataloger/alpm/package.go index c635e5691c8..6e5bc58e124 100644 --- a/syft/pkg/cataloger/alpm/package.go +++ b/syft/pkg/cataloger/alpm/package.go @@ -34,20 +34,7 @@ func newPackage(m *parsedData, release *linux.Release, locations ...source.Locat Type: pkg.AlpmPkg, PURL: packageURL(m, release), MetadataType: pkg.AlpmMetadataType, - Metadata: pkg.AlpmMetadata{ - BasePackage: m.BasePackage, - Package: m.Package, - Version: m.Version, - Description: m.Description, - Architecture: m.Architecture, - Size: m.Size, - Packager: m.Packager, - URL: m.URL, - Validation: m.Validation, - Reason: m.Reason, - Files: m.Files, - Backup: m.Backup, - }, + Metadata: m.AlpmMetadata, } p.SetID() return p From 247b5059e1fd125ae3bf9a0a5046504869bce76b Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Mon, 24 Apr 2023 11:56:15 -0400 Subject: [PATCH 29/99] chore: remove old wip comments/debug Signed-off-by: Christopher Phillips --- syft/formats/syftjson/decoder.go | 1 - syft/license/license.go | 2 ++ syft/pkg/cataloger/alpm/package.go | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/syft/formats/syftjson/decoder.go b/syft/formats/syftjson/decoder.go index 949f1e88a93..b6286bd97c6 100644 --- a/syft/formats/syftjson/decoder.go +++ b/syft/formats/syftjson/decoder.go @@ -15,7 +15,6 @@ func decoder(reader io.Reader) (*sbom.SBOM, error) { var doc model.Document err := dec.Decode(&doc) if err != nil { - fmt.Println(err) return nil, fmt.Errorf("unable to decode syft-json: %w", err) } diff --git a/syft/license/license.go b/syft/license/license.go index 8f1b1273676..8cd8d2f7be3 100644 --- a/syft/license/license.go +++ b/syft/license/license.go @@ -21,6 +21,8 @@ type Evidence struct { } func ParseExpression(expression string) (string, error) { + // returns true if all licenses are valid + // ignored variable is any invalid expressions valid, _ := spdxexp.ValidateLicenses([]string{expression}) if !valid { return "", fmt.Errorf("failed to validate spdx expression: %s", expression) diff --git a/syft/pkg/cataloger/alpm/package.go b/syft/pkg/cataloger/alpm/package.go index 6e5bc58e124..7cd08a62bb8 100644 --- a/syft/pkg/cataloger/alpm/package.go +++ b/syft/pkg/cataloger/alpm/package.go @@ -34,7 +34,7 @@ func newPackage(m *parsedData, release *linux.Release, locations ...source.Locat Type: pkg.AlpmPkg, PURL: packageURL(m, release), MetadataType: pkg.AlpmMetadataType, - Metadata: m.AlpmMetadata, + Metadata: m.AlpmMetadata, } p.SetID() return p From 1d8eac853ce014156b59d67d1cad936f67c7c59d Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Tue, 25 Apr 2023 11:05:50 -0400 Subject: [PATCH 30/99] chore: update cyclonedx decodeLicenses Signed-off-by: Christopher Phillips --- .../common/cyclonedxhelpers/licenses.go | 28 ++++-- .../common/cyclonedxhelpers/licenses_test.go | 92 ++++++++++++++++++- syft/pkg/license.go | 14 ++- 3 files changed, 115 insertions(+), 19 deletions(-) diff --git a/syft/formats/common/cyclonedxhelpers/licenses.go b/syft/formats/common/cyclonedxhelpers/licenses.go index fa41ed20935..f92f9fb40f5 100644 --- a/syft/formats/common/cyclonedxhelpers/licenses.go +++ b/syft/formats/common/cyclonedxhelpers/licenses.go @@ -4,9 +4,9 @@ import ( "github.com/CycloneDX/cyclonedx-go" "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/source" ) -// TODO: update this to only return valid cyclonedx expression types // This should be a function that just surfaces licenses already validated in the package struct func encodeLicenses(p pkg.Package) *cyclonedx.Licenses { lc := cyclonedx.Licenses{} @@ -16,6 +16,7 @@ func encodeLicenses(p pkg.Package) *cyclonedx.Licenses { License: &cyclonedx.License{ ID: l.SPDXExpression, }, + Expression: l.SPDXExpression, }) } else { // not found so append the licenseName as is @@ -33,11 +34,22 @@ func encodeLicenses(p pkg.Package) *cyclonedx.Licenses { return nil } -func decodeLicenses(_ *cyclonedx.Component) []pkg.License { - // if c.Licenses != nil { - // for range *c.Licenses { - // // TODO: switch on if it's a license or expression - // } - //} - return nil +func decodeLicenses(c *cyclonedx.Component) []pkg.License { + licenses := make([]pkg.License, 0) + if c.Licenses != nil { + for _, l := range *c.Licenses { + licenseValue := l.License.ID + if l.Expression != "" { + licenseValue = l.Expression + } + + if licenseValue == "" { + licenseValue = l.License.Name + } + + var licenseLocation *source.Location + licenses = append(licenses, pkg.NewLicense(licenseValue, l.License.URL, licenseLocation)) + } + } + return licenses } diff --git a/syft/formats/common/cyclonedxhelpers/licenses_test.go b/syft/formats/common/cyclonedxhelpers/licenses_test.go index e07a1f15bf2..737d964384b 100644 --- a/syft/formats/common/cyclonedxhelpers/licenses_test.go +++ b/syft/formats/common/cyclonedxhelpers/licenses_test.go @@ -6,6 +6,7 @@ import ( "github.com/CycloneDX/cyclonedx-go" "github.com/stretchr/testify/assert" + "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/pkg" ) @@ -38,7 +39,10 @@ func Test_encodeLicense(t *testing.T) { }, }, expected: &cyclonedx.Licenses{ - {License: &cyclonedx.License{ID: "MIT"}}, + { + License: &cyclonedx.License{ID: "MIT"}, + Expression: "MIT", + }, }, }, { @@ -56,8 +60,14 @@ func Test_encodeLicense(t *testing.T) { }, }, expected: &cyclonedx.Licenses{ - {License: &cyclonedx.License{ID: "MIT"}}, - {License: &cyclonedx.License{ID: "GPL-3.0-only"}}, + { + License: &cyclonedx.License{ID: "MIT"}, + Expression: "MIT", + }, + { + License: &cyclonedx.License{ID: "GPL-3.0-only"}, + Expression: "GPL-3.0-only", + }, }, }, } @@ -67,3 +77,79 @@ func Test_encodeLicense(t *testing.T) { }) } } + +func TestDecodeLicenses(t *testing.T) { + tests := []struct { + name string + input *cyclonedx.Component + expected []pkg.License + }{ + { + name: "no licenses", + input: &cyclonedx.Component{}, + expected: []pkg.License{}, + }, + { + name: "no SPDX license ID or expression", + input: &cyclonedx.Component{ + Licenses: &cyclonedx.Licenses{ + { + License: &cyclonedx.License{ + Name: "RandomLicense", + }, + }, + }, + }, + expected: []pkg.License{ + { + Value: "RandomLicense", + // CycloneDX specification doesn't give a field for determining the license type + Type: license.Declared, + }, + }, + }, + { + name: "with SPDX license ID", + input: &cyclonedx.Component{ + Licenses: &cyclonedx.Licenses{ + { + License: &cyclonedx.License{ + ID: "MIT", + }, + Expression: "MIT", + }, + }, + }, + expected: []pkg.License{ + { + Value: "MIT", + SPDXExpression: "MIT", + Type: license.Declared, + }, + }, + }, + { + name: "with complex SPDX license expression", + input: &cyclonedx.Component{ + Licenses: &cyclonedx.Licenses{ + { + License: &cyclonedx.License{}, + Expression: "MIT AND GPL-3.0-only WITH Classpath-exception-2.0", + }, + }, + }, + expected: []pkg.License{ + { + Value: "MIT AND GPL-3.0-only WITH Classpath-exception-2.0", + SPDXExpression: "MIT AND GPL-3.0-only WITH Classpath-exception-2.0", + Type: license.Declared, + }, + }, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + assert.Equal(t, test.expected, decodeLicenses(test.input)) + }) + } +} diff --git a/syft/pkg/license.go b/syft/pkg/license.go index 831c45a0606..36fd4ddc34e 100644 --- a/syft/pkg/license.go +++ b/syft/pkg/license.go @@ -7,18 +7,16 @@ import ( ) type License struct { - Value string `json:"value"` - SPDXExpression string `json:"spdx-expression"` - Type license.Type `json:"type"` - URL string `json:"url"` // external sources - Location source.Location `json:"location"` // on disk declaration + Value string `json:"value"` + SPDXExpression string `json:"spdx-expression"` + Type license.Type `json:"type"` + URL string `json:"url"` // external sources + Location *source.Location `json:"location"` // on disk declaration } -func NewLicense(value string, url string, location source.Location) License { - // TODO: check license package for validation enhancements +func NewLicense(value string, url string, location *source.Location) License { spdxExpression, err := license.ParseExpression(value) if err != nil { - // TODO: warn/trace error with package context? log.Trace("unable to parse license expression: %w", err) } From 7caaf1cbdda0b692c6318edf373ec526bfd4d86c Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Tue, 25 Apr 2023 11:27:51 -0400 Subject: [PATCH 31/99] chore: update cyclonedx decode to handle nil values Signed-off-by: Christopher Phillips --- .../common/cyclonedxhelpers/licenses.go | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/syft/formats/common/cyclonedxhelpers/licenses.go b/syft/formats/common/cyclonedxhelpers/licenses.go index f92f9fb40f5..472fcf392dd 100644 --- a/syft/formats/common/cyclonedxhelpers/licenses.go +++ b/syft/formats/common/cyclonedxhelpers/licenses.go @@ -36,20 +36,24 @@ func encodeLicenses(p pkg.Package) *cyclonedx.Licenses { func decodeLicenses(c *cyclonedx.Component) []pkg.License { licenses := make([]pkg.License, 0) - if c.Licenses != nil { - for _, l := range *c.Licenses { - licenseValue := l.License.ID - if l.Expression != "" { - licenseValue = l.Expression - } + if c != nil { + if c.Licenses != nil { + for _, l := range *c.Licenses { + // priority: Expression -> ID -> Name + licenseValue := l.Expression + if l.License != nil && licenseValue == "" { + licenseValue = l.License.ID + } - if licenseValue == "" { - licenseValue = l.License.Name - } + if l.License != nil && licenseValue == "" { + licenseValue = l.License.Name + } - var licenseLocation *source.Location - licenses = append(licenses, pkg.NewLicense(licenseValue, l.License.URL, licenseLocation)) + var licenseLocation *source.Location + licenses = append(licenses, pkg.NewLicense(licenseValue, l.License.URL, licenseLocation)) + } } } + return licenses } From dfc68cdc5cf7184f88c3c2c57b0fd9a57a989c99 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Tue, 25 Apr 2023 12:55:59 -0400 Subject: [PATCH 32/99] feat: add spdx license encoding from syft model Signed-off-by: Christopher Phillips --- syft/formats/common/spdxhelpers/license.go | 54 +++++--- .../common/spdxhelpers/license_test.go | 118 ++++++++++++++++++ .../common/spdxhelpers/to_format_model.go | 28 ++++- 3 files changed, 183 insertions(+), 17 deletions(-) create mode 100644 syft/formats/common/spdxhelpers/license_test.go diff --git a/syft/formats/common/spdxhelpers/license.go b/syft/formats/common/spdxhelpers/license.go index 85dd4575018..1a3ede7d727 100644 --- a/syft/formats/common/spdxhelpers/license.go +++ b/syft/formats/common/spdxhelpers/license.go @@ -4,10 +4,11 @@ import ( "strings" "github.com/anchore/syft/internal/spdxlicense" + "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/pkg" ) -func License(p pkg.Package) string { +func License(p pkg.Package) (concluded, declared string) { // source: https://spdx.github.io/spdx-spec/3-package-information/#313-concluded-license // The options to populate this field are limited to: // A valid SPDX License Expression as defined in Appendix IV; @@ -18,34 +19,59 @@ func License(p pkg.Package) string { // (iii) the SPDX file creator has intentionally provided no information (no meaning should be implied by doing so). if len(p.Licenses) == 0 { - return NONE + return NOASSERTION, NOASSERTION } - // take all licenses and assume an AND expression; for information about license expressions see https://spdx.github.io/spdx-spec/appendix-IV-SPDX-license-expressions/ - parsedLicenses := parseLicenses(p.Licenses) + // 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) - for i, v := range parsedLicenses { + for i, v := range pc { if strings.HasPrefix(v, spdxlicense.LicenseRefPrefix) { - parsedLicenses[i] = SanitizeElementID(v) + pc[i] = SanitizeElementID(v) } } - if len(parsedLicenses) == 0 { - return NOASSERTION + for i, v := range pd { + if strings.HasPrefix(v, spdxlicense.LicenseRefPrefix) { + pd[i] = SanitizeElementID(v) + } + } + + if len(pc) != 0 { + concluded = strings.Join(pc, " AND ") + } else { + concluded = NOASSERTION } - return strings.Join(parsedLicenses, " AND ") + if len(pd) != 0 { + declared = strings.Join(pd, " AND ") + } else { + declared = NOASSERTION + } + + return concluded, declared } -func parseLicenses(raw []pkg.License) (parsedLicenses []string) { +func parseLicenses(raw []pkg.License) (concluded, declared []string) { + c := make([]string, 0) + d := make([]string, 0) for _, l := range raw { + var candidate string if l.SPDXExpression != "" { - parsedLicenses = append(parsedLicenses, l.SPDXExpression) + candidate = l.SPDXExpression } else { // we did not find a valid SPDX license ID so treat as separate license - otherLicense := spdxlicense.LicenseRefPrefix + l.Value - parsedLicenses = append(parsedLicenses, otherLicense) + candidate = spdxlicense.LicenseRefPrefix + l.Value + } + + switch l.Type { + case license.Concluded: + c = append(c, candidate) + case license.Declared: + d = append(d, candidate) } } - return + return c, d } diff --git a/syft/formats/common/spdxhelpers/license_test.go b/syft/formats/common/spdxhelpers/license_test.go new file mode 100644 index 00000000000..f0cf3b102d6 --- /dev/null +++ b/syft/formats/common/spdxhelpers/license_test.go @@ -0,0 +1,118 @@ +package spdxhelpers + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/anchore/syft/syft/license" + "github.com/anchore/syft/syft/pkg" +) + +func Test_License(t *testing.T) { + tests := []struct { + name string + input pkg.Package + expected struct { + concluded string + declared string + } + }{ + { + name: "no licenses", + input: pkg.Package{}, + expected: struct{ concluded, declared string }{ + concluded: "NOASSERTION", + declared: "NOASSERTION", + }, + }, + { + name: "no SPDX licenses", + input: pkg.Package{ + Licenses: []pkg.License{ + { + Value: "made-up", + Type: license.Declared, + }, + }, + }, + expected: struct{ concluded, declared string }{ + concluded: "NOASSERTION", + declared: "LicenseRef-made-up", + }, + }, + { + name: "with SPDX license", + input: pkg.Package{ + Licenses: []pkg.License{ + { + SPDXExpression: "MIT", + Type: license.Declared, + }, + }, + }, + expected: struct { + concluded string + declared string + }{ + concluded: "NOASSERTION", + declared: "MIT", + }, + }, + { + name: "with SPDX license expression", + input: pkg.Package{ + Licenses: []pkg.License{ + { + SPDXExpression: "MIT", + Type: license.Declared, + }, + { + SPDXExpression: "GPL-3.0-only", + Type: license.Declared, + }, + }, + }, + expected: struct { + concluded string + declared string + }{ + concluded: "NOASSERTION", + declared: "MIT AND GPL-3.0-only", + }, + }, + { + name: "includes valid LicenseRef-", + input: pkg.Package{ + Licenses: []pkg.License{ + { + Value: "one thing first", + Type: license.Declared, + }, + { + Value: "two things/#$^second", + Type: license.Declared, + }, + { + Value: "MIT", + Type: license.Declared, + }, + }, + }, + expected: struct { + concluded string + declared string + }{ + concluded: "NOASSERTION", + declared: "LicenseRef-one-thing-first AND LicenseRef-two-things----second AND LicenseRef-MIT", + }, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + c, d := License(test.input) + assert.Equal(t, test.expected.concluded, c) + assert.Equal(t, test.expected.declared, d) + }) + } +} diff --git a/syft/formats/common/spdxhelpers/to_format_model.go b/syft/formats/common/spdxhelpers/to_format_model.go index e04e22548c1..79018c2b9b8 100644 --- a/syft/formats/common/spdxhelpers/to_format_model.go +++ b/syft/formats/common/spdxhelpers/to_format_model.go @@ -152,8 +152,8 @@ func toPackages(catalog *pkg.Catalog, sbom sbom.SBOM) (results []*spdx.Package) // 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. - // TODO: package licenses are validated on construction at the cataloger level // extract these correctly to the spdx license format + concluded, declared := License(p) // two ways to get filesAnalyzed == true: // 1. syft has generated a sha1 digest for the package itself - usually in the java cataloger @@ -253,7 +253,23 @@ func toPackages(catalog *pkg.Catalog, sbom sbom.SBOM) (results []*spdx.Package) // Cardinality: optional, one PackageSourceInfo: SourceInfo(p), - // TODO 7.13 ----- 7.16 + // 7.13: Concluded License: SPDX License Expression, "NONE" or "NOASSERTION" + // Cardinality: mandatory, one + // Purpose: Contain the license the SPDX file creator has concluded as governing the + // package or alternative values, if the governing license cannot be determined. + PackageLicenseConcluded: concluded, + + // 7.14: All Licenses Info from Files: SPDX License Expression, "NONE" or "NOASSERTION" + // Cardinality: mandatory, one or many if filesAnalyzed is true / omitted; + // zero (must be omitted) if filesAnalyzed is false + PackageLicenseInfoFromFiles: nil, + + // 7.15: Declared License: SPDX License Expression, "NONE" or "NOASSERTION" + // Cardinality: mandatory, one + // Purpose: List the licenses that have been declared by the authors of the package. + // Any license information that does not originate from the package authors, e.g. license + // information from a third party repository, should not be included in this field. + PackageLicenseDeclared: declared, // 7.16: Comments on License // Cardinality: optional, one @@ -506,7 +522,13 @@ func toFileTypes(metadata *source.FileMetadata) (ty []string) { func toOtherLicenses(catalog *pkg.Catalog) []*spdx.OtherLicense { licenses := map[string]bool{} for _, p := range catalog.Sorted() { - for _, license := range parseLicenses(p.Licenses) { + d, c := parseLicenses(p.Licenses) + for _, license := range d { + if strings.HasPrefix(license, spdxlicense.LicenseRefPrefix) { + licenses[license] = true + } + } + for _, license := range c { if strings.HasPrefix(license, spdxlicense.LicenseRefPrefix) { licenses[license] = true } From db9f4463b214afc2234086b955d20b8f4029b48d Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Tue, 25 Apr 2023 13:05:24 -0400 Subject: [PATCH 33/99] chore: update test to account for paren join Signed-off-by: Christopher Phillips --- syft/formats/common/spdxhelpers/license.go | 23 ++++++++++++++++ .../common/spdxhelpers/license_test.go | 26 +++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/syft/formats/common/spdxhelpers/license.go b/syft/formats/common/spdxhelpers/license.go index 1a3ede7d727..63e4893861c 100644 --- a/syft/formats/common/spdxhelpers/license.go +++ b/syft/formats/common/spdxhelpers/license.go @@ -39,6 +39,9 @@ func License(p pkg.Package) (concluded, declared string) { } } + concluded = joinLicenses(pc) + declared = joinLicenses(pd) + if len(pc) != 0 { concluded = strings.Join(pc, " AND ") } else { @@ -54,6 +57,26 @@ func License(p pkg.Package) (concluded, declared string) { return concluded, declared } +func joinLicenses(licenses []string) string { + if len(licenses) == 0 { + return NOASSERTION + } + + for i, v := range licenses { + // check if license does not start or end with parens + if !strings.HasPrefix(v, "(") && !strings.HasSuffix(v, ")") { + // if license contains AND, OR, or WITH, then wrap in parens + if strings.Contains(v, " AND ") || + strings.Contains(v, " OR ") || + strings.Contains(v, " WITH ") { + licenses[i] = "(" + v + ")" + } + } + } + + return strings.Join(licenses, " AND ") +} + func parseLicenses(raw []pkg.License) (concluded, declared []string) { c := make([]string, 0) d := make([]string, 0) diff --git a/syft/formats/common/spdxhelpers/license_test.go b/syft/formats/common/spdxhelpers/license_test.go index f0cf3b102d6..0980da74e33 100644 --- a/syft/formats/common/spdxhelpers/license_test.go +++ b/syft/formats/common/spdxhelpers/license_test.go @@ -107,6 +107,32 @@ func Test_License(t *testing.T) { declared: "LicenseRef-one-thing-first AND LicenseRef-two-things----second AND LicenseRef-MIT", }, }, + { + name: "join parentheses correctly", + input: pkg.Package{ + Licenses: []pkg.License{ + { + Value: "one thing first", + Type: license.Declared, + }, + { + SPDXExpression: "(MIT AND GPL-3.0-only)", + Type: license.Declared, + }, + { + SPDXExpression: "MIT OR APACHE-2.0", + Type: license.Declared, + }, + }, + }, + expected: struct { + concluded string + declared string + }{ + concluded: "NOASSERTION", + declared: "LicenseRef-one-thing-first AND (MIT AND GPL-3.0-only) AND (MIT OR APACHE-2.0)", + }, + }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { From 90a5369ef2a4b60b9435670949bd0d119d886867 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Tue, 25 Apr 2023 13:16:40 -0400 Subject: [PATCH 34/99] chore: add license types to format model tests Signed-off-by: Christopher Phillips --- syft/formats/common/spdxhelpers/to_format_model_test.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/syft/formats/common/spdxhelpers/to_format_model_test.go b/syft/formats/common/spdxhelpers/to_format_model_test.go index d1128cf49b3..6ba10084ab7 100644 --- a/syft/formats/common/spdxhelpers/to_format_model_test.go +++ b/syft/formats/common/spdxhelpers/to_format_model_test.go @@ -10,6 +10,7 @@ import ( "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/sbom" "github.com/anchore/syft/syft/source" @@ -456,7 +457,8 @@ func Test_OtherLicenses(t *testing.T) { pkg: pkg.Package{ Licenses: []pkg.License{ { - Value: "foobar", // Only testing licenses not in spdx list + Value: "foobar", + Type: license.Declared, // Only testing licenses not in spdx list }, }, }, @@ -473,9 +475,11 @@ func Test_OtherLicenses(t *testing.T) { Licenses: []pkg.License{ { Value: "internal made up license name", + Type: license.Declared, // Only testing licenses not in spdx list }, { Value: "new apple license 2.0", + Type: license.Declared, // Only testing licenses not in spdx list }, }, }, From 1dacbddc053ae2953722f0694ca6632c19020ed7 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Tue, 25 Apr 2023 13:27:13 -0400 Subject: [PATCH 35/99] chore: update to_syft_model to parse new SPDX license format Signed-off-by: Christopher Phillips --- .../common/spdxhelpers/to_syft_model.go | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/syft/formats/common/spdxhelpers/to_syft_model.go b/syft/formats/common/spdxhelpers/to_syft_model.go index a861df2a0ed..74ea20b22d1 100644 --- a/syft/formats/common/spdxhelpers/to_syft_model.go +++ b/syft/formats/common/spdxhelpers/to_syft_model.go @@ -276,11 +276,10 @@ func toSyftPackage(p *spdx.Package) *pkg.Package { info := extractPkgInfo(p) metadataType, metadata := extractMetadata(p, info) sP := pkg.Package{ - Type: info.typ, - Name: p.PackageName, - Version: p.PackageVersion, - // TODO: this should be updated to account for concluded and declared - // Licenses: parseLicense(p.PackageLicenseDeclared), + Type: info.typ, + Name: p.PackageName, + Version: p.PackageVersion, + Licenses: parseSPDXLicenses(p), CPEs: extractCPEs(p), PURL: info.purl.String(), Language: info.lang, @@ -293,6 +292,23 @@ func toSyftPackage(p *spdx.Package) *pkg.Package { return &sP } +func parseSPDXLicenses(p *spdx.Package) []pkg.License { + licenses := make([]pkg.License, 0) + + // concluded + if p.PackageLicenseConcluded != NOASSERTION && p.PackageLicenseConcluded != NONE { + licenses = append(licenses, pkg.NewLicense(p.PackageLicenseConcluded, "", nil)) + } + + // declared + if p.PackageLicenseDeclared != NOASSERTION && p.PackageLicenseDeclared != NONE { + licenses = append(licenses, pkg.NewLicense(p.PackageLicenseDeclared, "", nil)) + } + + // TODO: do we need other licenses? These are at the document level + return licenses +} + //nolint:funlen func extractMetadata(p *spdx.Package, info pkgInfo) (pkg.MetadataType, interface{}) { arch := info.qualifierValue(pkg.PURLQualifierArch) From 3a6bb77d9ffd99cd9ceabdc0b97cb4418f1f4304 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Tue, 25 Apr 2023 13:50:30 -0400 Subject: [PATCH 36/99] chore: refactor joinLicenses Signed-off-by: Christopher Phillips --- syft/formats/common/spdxhelpers/license.go | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/syft/formats/common/spdxhelpers/license.go b/syft/formats/common/spdxhelpers/license.go index 63e4893861c..4e5fa1c1cfe 100644 --- a/syft/formats/common/spdxhelpers/license.go +++ b/syft/formats/common/spdxhelpers/license.go @@ -39,22 +39,7 @@ func License(p pkg.Package) (concluded, declared string) { } } - concluded = joinLicenses(pc) - declared = joinLicenses(pd) - - if len(pc) != 0 { - concluded = strings.Join(pc, " AND ") - } else { - concluded = NOASSERTION - } - - if len(pd) != 0 { - declared = strings.Join(pd, " AND ") - } else { - declared = NOASSERTION - } - - return concluded, declared + return joinLicenses(pc), joinLicenses(pd) } func joinLicenses(licenses []string) string { From 6cfd42a7d1684f650c998fe231428c9970fd424e Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Tue, 25 Apr 2023 13:56:49 -0400 Subject: [PATCH 37/99] chore: update license encoder to use new license struct Signed-off-by: Christopher Phillips --- syft/formats/syftjson/encoder_test.go | 9 ++++++++- .../snapshot/TestDirectoryEncoder.golden | 5 +---- .../snapshot/TestEncodeFullJSONDocument.golden | 11 +++++++++-- .../test-fixtures/snapshot/TestImageEncoder.golden | 5 +---- syft/pkg/license.go | 2 +- 5 files changed, 20 insertions(+), 12 deletions(-) diff --git a/syft/formats/syftjson/encoder_test.go b/syft/formats/syftjson/encoder_test.go index 67558bd732b..e049f376b06 100644 --- a/syft/formats/syftjson/encoder_test.go +++ b/syft/formats/syftjson/encoder_test.go @@ -10,6 +10,7 @@ import ( "github.com/anchore/syft/syft/cpe" "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/formats/internal/testutils" + "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/linux" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/sbom" @@ -61,7 +62,13 @@ func TestEncodeFullJSONDocument(t *testing.T) { FoundBy: "the-cataloger-1", Language: pkg.Python, MetadataType: pkg.PythonPackageMetadataType, - Licenses: []pkg.License{}, + Licenses: []pkg.License{ + { + SPDXExpression: "MIT", + Value: "MIT", + Type: license.Declared, + }, + }, Metadata: pkg.PythonPackageMetadata{ Name: "package-1", Version: "1.0.1", diff --git a/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden b/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden index 719d73afea3..7e570a96976 100644 --- a/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden +++ b/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden @@ -16,10 +16,7 @@ "value": "MIT", "spdx-expression": "MIT", "type": "declared", - "url": "", - "location": { - "path": "" - } + "url": "" } ], "language": "python", diff --git a/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden b/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden index 2a4ef36b525..cf0040eb125 100644 --- a/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden +++ b/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden @@ -1,7 +1,7 @@ { "artifacts": [ { - "id": "eec39d3202dce6f3", + "id": "424914a52c6f83b8", "name": "package-1", "version": "1.0.1", "type": "python", @@ -11,7 +11,14 @@ "path": "/a/place/a" } ], - "licenses": [], + "licenses": [ + { + "value": "MIT", + "spdx-expression": "MIT", + "type": "declared", + "url": "" + } + ], "language": "python", "cpes": [ "cpe:2.3:*:some:package:1:*:*:*:*:*:*:*" diff --git a/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden b/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden index 38b77e76340..0027f1e3791 100644 --- a/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden +++ b/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden @@ -17,10 +17,7 @@ "value": "MIT", "spdx-expression": "MIT", "type": "declared", - "url": "", - "location": { - "path": "" - } + "url": "" } ], "language": "python", diff --git a/syft/pkg/license.go b/syft/pkg/license.go index 36fd4ddc34e..d3308c92ca5 100644 --- a/syft/pkg/license.go +++ b/syft/pkg/license.go @@ -11,7 +11,7 @@ type License struct { SPDXExpression string `json:"spdx-expression"` Type license.Type `json:"type"` URL string `json:"url"` // external sources - Location *source.Location `json:"location"` // on disk declaration + Location *source.Location `json:"location,omitempty"` // on disk declaration } func NewLicense(value string, url string, location *source.Location) License { From 2164a431651289a7d6b8a5705c1d78ad6051e4f6 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Tue, 25 Apr 2023 14:01:05 -0400 Subject: [PATCH 38/99] chore: add back check for nil array in format_model Signed-off-by: Christopher Phillips --- syft/formats/syftjson/to_format_model.go | 13 +++++++------ syft/pkg/license.go | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/syft/formats/syftjson/to_format_model.go b/syft/formats/syftjson/to_format_model.go index d30ea5d3c30..c8af616189e 100644 --- a/syft/formats/syftjson/to_format_model.go +++ b/syft/formats/syftjson/to_format_model.go @@ -191,11 +191,12 @@ func toPackageModel(p pkg.Package) model.Package { cpes[i] = cpe.String(c) } - // TODO: update user facing package to use latest license model - // var licenses = make([]string, 0) - // if p.Licenses != nil { - // licenses = p.Licenses - //} + // we want to make sure all catalogers are + // initializing the array; this is a good choke point for this check + var licenses = make([]pkg.License, 0) + if p.Licenses != nil { + licenses = p.Licenses + } return model.Package{ PackageBasicData: model.PackageBasicData{ @@ -205,7 +206,7 @@ func toPackageModel(p pkg.Package) model.Package { Type: p.Type, FoundBy: p.FoundBy, Locations: p.Locations.ToSlice(), - Licenses: p.Licenses, + Licenses: licenses, Language: p.Language, CPEs: cpes, PURL: p.PURL, diff --git a/syft/pkg/license.go b/syft/pkg/license.go index d3308c92ca5..d99e5503a4f 100644 --- a/syft/pkg/license.go +++ b/syft/pkg/license.go @@ -10,7 +10,7 @@ type License struct { Value string `json:"value"` SPDXExpression string `json:"spdx-expression"` Type license.Type `json:"type"` - URL string `json:"url"` // external sources + URL string `json:"url"` // external sources Location *source.Location `json:"location,omitempty"` // on disk declaration } From 206546eb8f581121f5699c4c7766f643ee83846f Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Tue, 25 Apr 2023 14:20:10 -0400 Subject: [PATCH 39/99] chore: remove old location code from rebase Signed-off-by: Christopher Phillips --- syft/formats/syftjson/to_syft_model.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/syft/formats/syftjson/to_syft_model.go b/syft/formats/syftjson/to_syft_model.go index a3f77be2f45..a4d1accf42c 100644 --- a/syft/formats/syftjson/to_syft_model.go +++ b/syft/formats/syftjson/to_syft_model.go @@ -276,11 +276,6 @@ func toSyftPackage(p model.Package, idAliases map[string]string) pkg.Package { cpes = append(cpes, value) } - var locations = make([]source.Location, len(p.Locations)) - for i, c := range p.Locations { - locations[i] = source.NewLocationFromCoordinates(c.Coordinates) - } - out := pkg.Package{ Name: p.Name, Version: p.Version, From 4a328bc695f140572cee626bce636cbc7f6235f6 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Tue, 25 Apr 2023 15:51:33 -0400 Subject: [PATCH 40/99] chore: move evidence over into file.License Signed-off-by: Christopher Phillips --- syft/file/license.go | 8 +++++++- syft/license/license.go | 6 ------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/syft/file/license.go b/syft/file/license.go index 96a766d8abd..525141cbdc3 100644 --- a/syft/file/license.go +++ b/syft/file/license.go @@ -9,7 +9,13 @@ type License struct { Value string SPDXExpression string Type license.Type - LicenseEvidence *license.Evidence // evidence from license classifier + LicenseEvidence *LicenseEvidence // evidence from license classifier +} + +type LicenseEvidence struct { + Confidence int + Offset int + Extent int } func NewLicense(value string) License { diff --git a/syft/license/license.go b/syft/license/license.go index 8cd8d2f7be3..7f971aa5fa7 100644 --- a/syft/license/license.go +++ b/syft/license/license.go @@ -14,12 +14,6 @@ const ( Concluded Type = "concluded" ) -type Evidence struct { - Confidence int - Offset int - Extent int -} - func ParseExpression(expression string) (string, error) { // returns true if all licenses are valid // ignored variable is any invalid expressions From a9349bb9c44042fc50658f81931facdb00537827 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Tue, 25 Apr 2023 15:56:50 -0400 Subject: [PATCH 41/99] chore: remove old java todo - update pointer declaration Signed-off-by: Christopher Phillips --- syft/pkg/cataloger/java/archive_parser.go | 3 +-- syft/pkg/package_test.go | 7 +++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/syft/pkg/cataloger/java/archive_parser.go b/syft/pkg/cataloger/java/archive_parser.go index 7b12ace769e..9eab1df85d6 100644 --- a/syft/pkg/cataloger/java/archive_parser.go +++ b/syft/pkg/cataloger/java/archive_parser.go @@ -185,13 +185,12 @@ func (j *archiveParser) discoverMainPackage() (*pkg.Package, error) { log.Warnf("failed to create digest for file=%q: %+v", j.archivePath, err) } - // TODO: update this to use the new license selection/validation logic licenseCandidates := selectLicense(manifest) licenses := make([]pkg.License, 0) for _, l := range licenseCandidates { // we use j.location because we want to associate the license declaration with where we discovered the contents in the manifest - licenses = append(licenses, pkg.NewLicense(l, "", j.location)) + licenses = append(licenses, pkg.NewLicense(l, "", &j.location)) } return &pkg.Package{ diff --git a/syft/pkg/package_test.go b/syft/pkg/package_test.go index aa0d5e3cc6b..8645f397e03 100644 --- a/syft/pkg/package_test.go +++ b/syft/pkg/package_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/require" "github.com/anchore/syft/syft/cpe" + "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/source" ) @@ -27,6 +28,12 @@ func TestIDUniqueness(t *testing.T) { Locations: source.NewLocationSet( originalLocation, ), + Licenses: []License{ + { + SPDXExpression: "MIT AND GPL-2.0-only", + Type: license.Declared, + }, + }, Language: "math", Type: PythonPkg, CPEs: []cpe.CPE{ From d8ccdcc594f26bfe69a13809eed986d12f6a6b6c Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 26 Apr 2023 10:09:12 -0400 Subject: [PATCH 42/99] fix: adjust catalogers to conform with work for license structure updates *fix alpm cataloger tests * add without-annotations helper * split new license constructor into multiple constructors * adjust apk cataloger with updated location helper * adjust alpm cataloger with updated location helper * fix apkdb tests to use pkgtest helper * fix deb test * use location constructor within unindexed dir resolver * use license constructor within parser * fix golang license tests * fix java license tests * fix javascript license tests * fix kernel license tests * fix php license tests * add constructor for multiple licenses * fix portage license tests * fix python license tests * fix rpm license tests * fix ruby license tests * lint fixes * require encode-decode cycle assertions to prevent panic * allow for license without source * fix test panic + java test failures Signed-off-by: Alex Goodman wagoodman@users.noreply.github.com Signed-off-by: Christopher Phillips --- internal/licenses/parser.go | 9 +- .../common/cyclonedxhelpers/licenses.go | 37 +- .../common/spdxhelpers/to_syft_model.go | 8 +- syft/pkg/cataloger/alpm/cataloger_test.go | 27 +- syft/pkg/cataloger/alpm/package.go | 21 +- syft/pkg/cataloger/alpm/parse_alpm_db.go | 12 +- syft/pkg/cataloger/apkdb/package.go | 16 +- syft/pkg/cataloger/apkdb/package_test.go | 36 +- syft/pkg/cataloger/apkdb/parse_apk_db.go | 8 +- syft/pkg/cataloger/apkdb/parse_apk_db_test.go | 1089 +++++++++-------- syft/pkg/cataloger/deb/cataloger_test.go | 27 +- syft/pkg/cataloger/deb/package.go | 6 +- syft/pkg/cataloger/golang/licenses_test.go | 30 +- syft/pkg/cataloger/java/archive_parser.go | 14 +- .../pkg/cataloger/java/archive_parser_test.go | 8 +- .../pkg/cataloger/java/parse_java_manifest.go | 2 +- .../cataloger/javascript/cataloger_test.go | 15 +- syft/pkg/cataloger/javascript/package.go | 27 +- .../javascript/parse_package_json_test.go | 52 +- .../javascript/parse_package_lock.go | 66 +- .../javascript/parse_package_lock_test.go | 116 +- syft/pkg/cataloger/kernel/cataloger_test.go | 11 +- syft/pkg/cataloger/kernel/package.go | 19 +- .../kernel/parse_linux_kernel_file.go | 2 +- .../kernel/parse_linux_kernel_module_file.go | 2 +- syft/pkg/cataloger/php/package.go | 16 +- .../cataloger/php/parse_composer_lock_test.go | 15 +- .../php/parse_installed_json_test.go | 3 +- syft/pkg/cataloger/portage/cataloger_test.go | 38 +- .../portage/parse_portage_contents.go | 8 +- syft/pkg/cataloger/python/cataloger_test.go | 33 +- syft/pkg/cataloger/python/package.go | 7 +- syft/pkg/cataloger/python/parse_wheel_egg.go | 22 +- .../python/parse_wheel_egg_metadata.go | 14 +- syft/pkg/cataloger/rpm/package.go | 10 +- syft/pkg/cataloger/rpm/parse_rpm.go | 11 +- syft/pkg/cataloger/rpm/parse_rpm_db.go | 2 +- syft/pkg/cataloger/rpm/parse_rpm_db_test.go | 17 +- syft/pkg/cataloger/rpm/parse_rpm_test.go | 17 +- syft/pkg/cataloger/ruby/package.go | 10 +- syft/pkg/cataloger/ruby/parse_gemspec.go | 5 +- syft/pkg/cataloger/ruby/parse_gemspec_test.go | 8 +- syft/pkg/license.go | 36 +- syft/source/location.go | 6 + syft/source/unindexed_directory_resolver.go | 18 +- test/integration/encode_decode_cycle_test.go | 8 +- 46 files changed, 931 insertions(+), 1033 deletions(-) diff --git a/internal/licenses/parser.go b/internal/licenses/parser.go index 35f5c09210c..65b495d1b6e 100644 --- a/internal/licenses/parser.go +++ b/internal/licenses/parser.go @@ -30,13 +30,10 @@ func Parse(reader io.Reader, l source.Location) (licenses []pkg.License, err err for _, m := range cov.Match { // TODO: spdx ID validation here? - l := pkg.License{ - SPDXExpression: m.ID, - Type: license.Concluded, - Location: l, - } + lic := pkg.NewLicenseFromLocation(m.ID, l) + lic.Type = license.Concluded - licenses = append(licenses, l) + licenses = append(licenses, lic) } return licenses, nil diff --git a/syft/formats/common/cyclonedxhelpers/licenses.go b/syft/formats/common/cyclonedxhelpers/licenses.go index 472fcf392dd..1098c91aeb6 100644 --- a/syft/formats/common/cyclonedxhelpers/licenses.go +++ b/syft/formats/common/cyclonedxhelpers/licenses.go @@ -4,7 +4,6 @@ import ( "github.com/CycloneDX/cyclonedx-go" "github.com/anchore/syft/syft/pkg" - "github.com/anchore/syft/syft/source" ) // This should be a function that just surfaces licenses already validated in the package struct @@ -36,23 +35,29 @@ func encodeLicenses(p pkg.Package) *cyclonedx.Licenses { func decodeLicenses(c *cyclonedx.Component) []pkg.License { licenses := make([]pkg.License, 0) - if c != nil { - if c.Licenses != nil { - for _, l := range *c.Licenses { - // priority: Expression -> ID -> Name - licenseValue := l.Expression - if l.License != nil && licenseValue == "" { - licenseValue = l.License.ID - } - - if l.License != nil && licenseValue == "" { - licenseValue = l.License.Name - } - - var licenseLocation *source.Location - licenses = append(licenses, pkg.NewLicense(licenseValue, l.License.URL, licenseLocation)) + if c == nil || c.Licenses == nil { + return licenses + } + + for _, l := range *c.Licenses { + var url string + // priority: Expression -> ID -> Name + licenseValue := l.Expression + if l.License != nil { + url = l.License.URL + switch { + case l.License.ID != "": + licenseValue = l.License.ID + case l.License.Name != "": + licenseValue = l.License.Name } } + + if licenseValue == "" { + continue + } + + licenses = append(licenses, pkg.NewLicenseFromURL(licenseValue, url)) } return licenses diff --git a/syft/formats/common/spdxhelpers/to_syft_model.go b/syft/formats/common/spdxhelpers/to_syft_model.go index 74ea20b22d1..c48d4b1fd57 100644 --- a/syft/formats/common/spdxhelpers/to_syft_model.go +++ b/syft/formats/common/spdxhelpers/to_syft_model.go @@ -6,6 +6,8 @@ import ( "strconv" "strings" + "github.com/anchore/syft/syft/license" + "github.com/spdx/tools-golang/spdx" "github.com/anchore/packageurl-go" @@ -297,12 +299,14 @@ func parseSPDXLicenses(p *spdx.Package) []pkg.License { // concluded if p.PackageLicenseConcluded != NOASSERTION && p.PackageLicenseConcluded != NONE { - licenses = append(licenses, pkg.NewLicense(p.PackageLicenseConcluded, "", nil)) + l := pkg.NewLicense(p.PackageLicenseConcluded) + l.Type = license.Concluded + licenses = append(licenses, l) } // declared if p.PackageLicenseDeclared != NOASSERTION && p.PackageLicenseDeclared != NONE { - licenses = append(licenses, pkg.NewLicense(p.PackageLicenseDeclared, "", nil)) + licenses = append(licenses, pkg.NewLicense(p.PackageLicenseDeclared)) } // TODO: do we need other licenses? These are at the document level diff --git a/syft/pkg/cataloger/alpm/cataloger_test.go b/syft/pkg/cataloger/alpm/cataloger_test.go index f505da66c77..77ed11909ce 100644 --- a/syft/pkg/cataloger/alpm/cataloger_test.go +++ b/syft/pkg/cataloger/alpm/cataloger_test.go @@ -7,20 +7,35 @@ import ( "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" "github.com/anchore/syft/syft/source" ) func TestAlpmCataloger(t *testing.T) { + dbLocation := source.NewLocation("var/lib/pacman/local/gmp-6.2.1-2/desc") expectedPkgs := []pkg.Package{ { - Name: "gmp", - Version: "6.2.1-2", - Type: pkg.AlpmPkg, - FoundBy: "alpmdb-cataloger", - Licenses: []pkg.License{}, - Locations: source.NewLocationSet(source.NewLocation("var/lib/pacman/local/gmp-6.2.1-2/desc")), + Name: "gmp", + Version: "6.2.1-2", + Type: pkg.AlpmPkg, + FoundBy: "alpmdb-cataloger", + Licenses: []pkg.License{ + { + Value: "LGPL3", + SPDXExpression: "", + Type: license.Declared, + Location: &dbLocation, + }, + { + Value: "GPL", + SPDXExpression: "", + Type: license.Declared, + Location: &dbLocation, + }, + }, + Locations: source.NewLocationSet(dbLocation), CPEs: nil, PURL: "", MetadataType: "AlpmMetadata", diff --git a/syft/pkg/cataloger/alpm/package.go b/syft/pkg/cataloger/alpm/package.go index 7cd08a62bb8..3e6ee3f9941 100644 --- a/syft/pkg/cataloger/alpm/package.go +++ b/syft/pkg/cataloger/alpm/package.go @@ -9,34 +9,21 @@ import ( "github.com/anchore/syft/syft/source" ) -func newPackage(m *parsedData, release *linux.Release, locations ...source.Location) pkg.Package { - // ALPM only passes a single location - // We use this as the "declared" license location - var licenseLocation source.Location - if len(locations) > 0 { - licenseLocation = locations[0] - } - - // default to empty list; not nil field - licenses := make([]pkg.License, 0) +func newPackage(m *parsedData, release *linux.Release, dbLocation source.Location) pkg.Package { licenseCandidates := strings.Split(m.Licenses, "\n") - for _, l := range licenseCandidates { - if l != "" { - licenses = append(licenses, pkg.NewLicense(l, "", licenseLocation)) - } - } p := pkg.Package{ Name: m.Package, Version: m.Version, - Locations: source.NewLocationSet(locations...), - Licenses: licenses, + Locations: source.NewLocationSet(dbLocation), + Licenses: pkg.NewLicensesFromLocation(dbLocation.WithoutAnnotations(), licenseCandidates...), Type: pkg.AlpmPkg, PURL: packageURL(m, release), MetadataType: pkg.AlpmMetadataType, Metadata: m.AlpmMetadata, } p.SetID() + return p } diff --git a/syft/pkg/cataloger/alpm/parse_alpm_db.go b/syft/pkg/cataloger/alpm/parse_alpm_db.go index acdcee9ec5b..88f4769b778 100644 --- a/syft/pkg/cataloger/alpm/parse_alpm_db.go +++ b/syft/pkg/cataloger/alpm/parse_alpm_db.go @@ -32,12 +32,12 @@ var ( ) type parsedData struct { - Licenses string `mapstructure:"licenses"` + Licenses string `mapstructure:"license"` pkg.AlpmMetadata `mapstructure:",squash"` } func parseAlpmDB(resolver source.FileResolver, env *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { - parsedData, err := parseAlpmDBEntry(reader) + data, err := parseAlpmDBEntry(reader) if err != nil { return nil, nil, err } @@ -56,7 +56,7 @@ func parseAlpmDB(resolver source.FileResolver, env *generic.Environment, reader // replace the files found the pacman database with the files from the mtree These contain more metadata and // thus more useful. // TODO: probably want to use MTREE and PKGINFO here - parsedData.Files = pkgFiles + data.Files = pkgFiles // We only really do this to get any backup database entries from the files database files := filepath.Join(base, "files") @@ -68,16 +68,16 @@ func parseAlpmDB(resolver source.FileResolver, env *generic.Environment, reader if err != nil { return nil, nil, err } else if filesMetadata != nil { - parsedData.Backup = filesMetadata.Backup + data.Backup = filesMetadata.Backup } - if parsedData.Package == "" { + if data.Package == "" { return nil, nil, nil } return []pkg.Package{ newPackage( - parsedData, + data, env.LinuxRelease, reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), ), diff --git a/syft/pkg/cataloger/apkdb/package.go b/syft/pkg/cataloger/apkdb/package.go index 47fd2e1e182..5c0496ce19a 100644 --- a/syft/pkg/cataloger/apkdb/package.go +++ b/syft/pkg/cataloger/apkdb/package.go @@ -9,22 +9,14 @@ import ( "github.com/anchore/syft/syft/source" ) -func newPackage(d parsedData, release *linux.Release, locations ...source.Location) pkg.Package { - licenses := make([]pkg.License, 0) - if d.License != "" { - // apk packages can have multiple licenses separated by spaces - // ex: MIT BSD GPL2+ - licenseStrings := strings.Split(d.License, " ") - for _, l := range licenseStrings { - licenses = append(licenses, pkg.NewLicense(l, "", d.LicenseLocation)) - } - } +func newPackage(d parsedData, release *linux.Release, dbLocation source.Location) pkg.Package { + licenseStrings := strings.Split(d.License, " ") p := pkg.Package{ Name: d.Package, Version: d.Version, - Locations: source.NewLocationSet(locations...), - Licenses: licenses, + Locations: source.NewLocationSet(dbLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)), + Licenses: pkg.NewLicensesFromLocation(dbLocation, licenseStrings...), PURL: packageURL(d, release), Type: pkg.ApkPkg, MetadataType: pkg.ApkMetadataType, diff --git a/syft/pkg/cataloger/apkdb/package_test.go b/syft/pkg/cataloger/apkdb/package_test.go index b92bb5acf73..6b94b3f70f5 100644 --- a/syft/pkg/cataloger/apkdb/package_test.go +++ b/syft/pkg/cataloger/apkdb/package_test.go @@ -10,7 +10,6 @@ import ( "github.com/anchore/packageurl-go" "github.com/anchore/syft/syft/linux" "github.com/anchore/syft/syft/pkg" - "github.com/anchore/syft/syft/source" ) func Test_PackageURL(t *testing.T) { @@ -23,9 +22,8 @@ func Test_PackageURL(t *testing.T) { { name: "non-alpine distro", metadata: parsedData{ - "", - source.Location{}, - pkg.ApkMetadata{ + License: "", + ApkMetadata: pkg.ApkMetadata{ Package: "p", Version: "v", Architecture: "a", @@ -40,9 +38,8 @@ func Test_PackageURL(t *testing.T) { { name: "gocase", metadata: parsedData{ - "", - source.Location{}, - pkg.ApkMetadata{ + License: "", + ApkMetadata: pkg.ApkMetadata{ Package: "p", Version: "v", Architecture: "a", @@ -57,9 +54,8 @@ func Test_PackageURL(t *testing.T) { { name: "missing architecture", metadata: parsedData{ - "", - source.Location{}, - pkg.ApkMetadata{ + License: "", + ApkMetadata: pkg.ApkMetadata{ Package: "p", Version: "v", }, @@ -73,9 +69,8 @@ func Test_PackageURL(t *testing.T) { // verify #351 { metadata: parsedData{ - "", - source.Location{}, - pkg.ApkMetadata{ + License: "", + ApkMetadata: pkg.ApkMetadata{ Package: "g++", Version: "v84", Architecture: "am86", @@ -89,9 +84,8 @@ func Test_PackageURL(t *testing.T) { }, { metadata: parsedData{ - "", - source.Location{}, - pkg.ApkMetadata{ + License: "", + ApkMetadata: pkg.ApkMetadata{ Package: "g plus plus", Version: "v84", Architecture: "am86", @@ -106,9 +100,8 @@ func Test_PackageURL(t *testing.T) { { name: "add source information as qualifier", metadata: parsedData{ - "", - source.Location{}, - pkg.ApkMetadata{ + License: "", + ApkMetadata: pkg.ApkMetadata{ Package: "p", Version: "v", Architecture: "a", @@ -124,9 +117,8 @@ func Test_PackageURL(t *testing.T) { { name: "wolfi distro", metadata: parsedData{ - "", - source.Location{}, - pkg.ApkMetadata{ + License: "", + ApkMetadata: pkg.ApkMetadata{ Package: "p", Version: "v", Architecture: "a", diff --git a/syft/pkg/cataloger/apkdb/parse_apk_db.go b/syft/pkg/cataloger/apkdb/parse_apk_db.go index 8af69fb10d0..748ed7d5852 100644 --- a/syft/pkg/cataloger/apkdb/parse_apk_db.go +++ b/syft/pkg/cataloger/apkdb/parse_apk_db.go @@ -27,8 +27,7 @@ var ( ) type parsedData struct { - License string `mapstructure:"L" json:"license"` - LicenseLocation source.Location + License string `mapstructure:"L" json:"license"` pkg.ApkMetadata } @@ -129,10 +128,7 @@ func parseApkDB(resolver source.FileResolver, env *generic.Environment, reader s pkgs := make([]pkg.Package, 0, len(apks)) for _, apk := range apks { - if apk.License != "" { - apk.LicenseLocation = reader.Location - } - pkgs = append(pkgs, newPackage(apk, r, reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation))) + pkgs = append(pkgs, newPackage(apk, r, reader.Location)) } return pkgs, discoverPackageDependencies(pkgs), nil diff --git a/syft/pkg/cataloger/apkdb/parse_apk_db_test.go b/syft/pkg/cataloger/apkdb/parse_apk_db_test.go index d2802249a50..e7615640243 100644 --- a/syft/pkg/cataloger/apkdb/parse_apk_db_test.go +++ b/syft/pkg/cataloger/apkdb/parse_apk_db_test.go @@ -81,82 +81,103 @@ func TestExtraFileAttributes(t *testing.T) { func TestSinglePackageDetails(t *testing.T) { tests := []struct { fixture string - expected pkg.ApkMetadata + expected pkg.Package }{ { fixture: "test-fixtures/single", - expected: pkg.ApkMetadata{ - Package: "musl-utils", - OriginPackage: "musl", - Version: "1.1.24-r2", - Description: "the musl c library (libc) implementation", - Maintainer: "Timo Teräs ", - Architecture: "x86_64", - URL: "https://musl.libc.org/", - Size: 37944, - InstalledSize: 151552, - Dependencies: []string{"scanelf", "so:libc.musl-x86_64.so.1"}, - Provides: []string{"cmd:getconf", "cmd:getent", "cmd:iconv", "cmd:ldconfig", "cmd:ldd"}, - Checksum: "Q1bTtF5526tETKfL+lnigzIDvm+2o=", - GitCommit: "4024cc3b29ad4c65544ad068b8f59172b5494306", - Files: []pkg.ApkFileRecord{ + expected: pkg.Package{ + Name: "musl-utils", + Version: "1.1.24-r2", + Licenses: []pkg.License{ { - Path: "/sbin", - }, - { - Path: "/sbin/ldconfig", - OwnerUID: "0", - OwnerGID: "0", - Permissions: "755", - Digest: &file.Digest{ - Algorithm: "'Q1'+base64(sha1)", - Value: "Q1Kja2+POZKxEkUOZqwSjC6kmaED4=", - }, + Value: "MIT", + SPDXExpression: "MIT", + Type: license.Declared, }, { - Path: "/usr", + Value: "BSD", + Type: license.Declared, }, { - Path: "/usr/bin", + Value: "GPL2+", + Type: license.Declared, }, - { - Path: "/usr/bin/iconv", - OwnerUID: "0", - OwnerGID: "0", - Permissions: "755", - Digest: &file.Digest{ - Algorithm: "'Q1'+base64(sha1)", - Value: "Q1CVmFbdY+Hv6/jAHl1gec2Kbx1EY=", + }, + Type: pkg.ApkPkg, + MetadataType: pkg.ApkMetadataType, + Metadata: pkg.ApkMetadata{ + Package: "musl-utils", + OriginPackage: "musl", + Version: "1.1.24-r2", + Description: "the musl c library (libc) implementation", + Maintainer: "Timo Teräs ", + Architecture: "x86_64", + URL: "https://musl.libc.org/", + Size: 37944, + InstalledSize: 151552, + Dependencies: []string{"scanelf", "so:libc.musl-x86_64.so.1"}, + Provides: []string{"cmd:getconf", "cmd:getent", "cmd:iconv", "cmd:ldconfig", "cmd:ldd"}, + Checksum: "Q1bTtF5526tETKfL+lnigzIDvm+2o=", + GitCommit: "4024cc3b29ad4c65544ad068b8f59172b5494306", + Files: []pkg.ApkFileRecord{ + { + Path: "/sbin", }, - }, - { - Path: "/usr/bin/ldd", - OwnerUID: "0", - OwnerGID: "0", - Permissions: "755", - Digest: &file.Digest{ - Algorithm: "'Q1'+base64(sha1)", - Value: "Q1yFAhGggmL7ERgbIA7KQxyTzf3ks=", + { + Path: "/sbin/ldconfig", + OwnerUID: "0", + OwnerGID: "0", + Permissions: "755", + Digest: &file.Digest{ + Algorithm: "'Q1'+base64(sha1)", + Value: "Q1Kja2+POZKxEkUOZqwSjC6kmaED4=", + }, }, - }, - { - Path: "/usr/bin/getconf", - OwnerUID: "0", - OwnerGID: "0", - Permissions: "755", - Digest: &file.Digest{ - Algorithm: "'Q1'+base64(sha1)", - Value: "Q1dAdYK8M/INibRQF5B3Rw7cmNDDA=", + { + Path: "/usr", }, - }, - { - Path: "/usr/bin/getent", - OwnerUID: "0", - OwnerGID: "0", - Permissions: "755", - Digest: &file.Digest{ - Algorithm: "'Q1'+base64(sha1)", - Value: "Q1eR2Dz/WylabgbWMTkd2+hGmEya4=", + { + Path: "/usr/bin", + }, + { + Path: "/usr/bin/iconv", + OwnerUID: "0", + OwnerGID: "0", + Permissions: "755", + Digest: &file.Digest{ + Algorithm: "'Q1'+base64(sha1)", + Value: "Q1CVmFbdY+Hv6/jAHl1gec2Kbx1EY=", + }, + }, + { + Path: "/usr/bin/ldd", + OwnerUID: "0", + OwnerGID: "0", + Permissions: "755", + Digest: &file.Digest{ + Algorithm: "'Q1'+base64(sha1)", + Value: "Q1yFAhGggmL7ERgbIA7KQxyTzf3ks=", + }, + }, + { + Path: "/usr/bin/getconf", + OwnerUID: "0", + OwnerGID: "0", + Permissions: "755", + Digest: &file.Digest{ + Algorithm: "'Q1'+base64(sha1)", + Value: "Q1dAdYK8M/INibRQF5B3Rw7cmNDDA=", + }, + }, + { + Path: "/usr/bin/getent", + OwnerUID: "0", + OwnerGID: "0", + Permissions: "755", + Digest: &file.Digest{ + Algorithm: "'Q1'+base64(sha1)", + Value: "Q1eR2Dz/WylabgbWMTkd2+hGmEya4=", + }, }, }, }, @@ -164,485 +185,512 @@ func TestSinglePackageDetails(t *testing.T) { }, { fixture: "test-fixtures/empty-deps-and-provides", - expected: pkg.ApkMetadata{ - Package: "alpine-baselayout-data", - OriginPackage: "alpine-baselayout", - Version: "3.4.0-r0", - Description: "Alpine base dir structure and init scripts", - Maintainer: "Natanael Copa ", - Architecture: "x86_64", - URL: "https://git.alpinelinux.org/cgit/aports/tree/main/alpine-baselayout", - Size: 11664, - InstalledSize: 77824, - Dependencies: []string{}, - Provides: []string{}, - Checksum: "Q15ffjKT28lB7iSXjzpI/eDdYRCwM=", - GitCommit: "bd965a7ebf7fd8f07d7a0cc0d7375bf3e4eb9b24", - Files: []pkg.ApkFileRecord{ - {Path: "/etc"}, - {Path: "/etc/fstab"}, - {Path: "/etc/group"}, - {Path: "/etc/hostname"}, - {Path: "/etc/hosts"}, - {Path: "/etc/inittab"}, - {Path: "/etc/modules"}, - {Path: "/etc/mtab", OwnerUID: "0", OwnerGID: "0", Permissions: "0777"}, - {Path: "/etc/nsswitch.conf"}, - {Path: "/etc/passwd"}, - {Path: "/etc/profile"}, - {Path: "/etc/protocols"}, - {Path: "/etc/services"}, - {Path: "/etc/shadow", OwnerUID: "0", OwnerGID: "148", Permissions: "0640"}, - {Path: "/etc/shells"}, - {Path: "/etc/sysctl.conf"}, + expected: pkg.Package{ + Name: "alpine-baselayout-data", + Version: "3.4.0-r0", + Licenses: []pkg.License{ + { + Value: "GPL-2.0-only", + SPDXExpression: "GPL-2.0-only", + Type: license.Declared, + }, + }, + Type: pkg.ApkPkg, + MetadataType: pkg.ApkMetadataType, + Metadata: pkg.ApkMetadata{ + Package: "alpine-baselayout-data", + OriginPackage: "alpine-baselayout", + Version: "3.4.0-r0", + Description: "Alpine base dir structure and init scripts", + Maintainer: "Natanael Copa ", + Architecture: "x86_64", + URL: "https://git.alpinelinux.org/cgit/aports/tree/main/alpine-baselayout", + Size: 11664, + InstalledSize: 77824, + Dependencies: []string{}, + Provides: []string{}, + Checksum: "Q15ffjKT28lB7iSXjzpI/eDdYRCwM=", + GitCommit: "bd965a7ebf7fd8f07d7a0cc0d7375bf3e4eb9b24", + Files: []pkg.ApkFileRecord{ + {Path: "/etc"}, + {Path: "/etc/fstab"}, + {Path: "/etc/group"}, + {Path: "/etc/hostname"}, + {Path: "/etc/hosts"}, + {Path: "/etc/inittab"}, + {Path: "/etc/modules"}, + {Path: "/etc/mtab", OwnerUID: "0", OwnerGID: "0", Permissions: "0777"}, + {Path: "/etc/nsswitch.conf"}, + {Path: "/etc/passwd"}, + {Path: "/etc/profile"}, + {Path: "/etc/protocols"}, + {Path: "/etc/services"}, + {Path: "/etc/shadow", OwnerUID: "0", OwnerGID: "148", Permissions: "0640"}, + {Path: "/etc/shells"}, + {Path: "/etc/sysctl.conf"}, + }, }, }, }, { fixture: "test-fixtures/base", - expected: pkg.ApkMetadata{ - Package: "alpine-baselayout", - OriginPackage: "alpine-baselayout", - Version: "3.2.0-r6", - Description: "Alpine base dir structure and init scripts", - Maintainer: "Natanael Copa ", - Architecture: "x86_64", - URL: "https://git.alpinelinux.org/cgit/aports/tree/main/alpine-baselayout", - Size: 19917, - InstalledSize: 409600, - Dependencies: []string{"/bin/sh", "so:libc.musl-x86_64.so.1"}, - Provides: []string{"cmd:mkmntdirs"}, - Checksum: "Q1myMNfd7u5v5UTgNHeq1e31qTjZU=", - GitCommit: "e1c51734fa96fa4bac92e9f14a474324c67916fc", - Files: []pkg.ApkFileRecord{ - { - Path: "/dev", - }, - { - Path: "/dev/pts", - }, - { - Path: "/dev/shm", - }, + expected: pkg.Package{ + Name: "alpine-baselayout", + Version: "3.2.0-r6", + Licenses: []pkg.License{ { - Path: "/etc", + Value: "GPL-2.0-only", + SPDXExpression: "GPL-2.0-only", + Type: license.Declared, }, - { - Path: "/etc/fstab", - Digest: &file.Digest{ - Algorithm: "'Q1'+base64(sha1)", - Value: "Q11Q7hNe8QpDS531guqCdrXBzoA/o=", + }, + Type: pkg.ApkPkg, + PURL: "", + MetadataType: pkg.ApkMetadataType, + Metadata: pkg.ApkMetadata{ + Package: "alpine-baselayout", + OriginPackage: "alpine-baselayout", + Version: "3.2.0-r6", + Description: "Alpine base dir structure and init scripts", + Maintainer: "Natanael Copa ", + Architecture: "x86_64", + URL: "https://git.alpinelinux.org/cgit/aports/tree/main/alpine-baselayout", + Size: 19917, + InstalledSize: 409600, + Dependencies: []string{"/bin/sh", "so:libc.musl-x86_64.so.1"}, + Provides: []string{"cmd:mkmntdirs"}, + Checksum: "Q1myMNfd7u5v5UTgNHeq1e31qTjZU=", + GitCommit: "e1c51734fa96fa4bac92e9f14a474324c67916fc", + Files: []pkg.ApkFileRecord{ + { + Path: "/dev", }, - }, - { - Path: "/etc/group", - Digest: &file.Digest{ - Algorithm: "'Q1'+base64(sha1)", - Value: "Q1oJ16xWudgKOrXIEquEDzlF2Lsm4=", + { + Path: "/dev/pts", }, - }, - { - Path: "/etc/hostname", - Digest: &file.Digest{ - Algorithm: "'Q1'+base64(sha1)", - Value: "Q16nVwYVXP/tChvUPdukVD2ifXOmc=", + { + Path: "/dev/shm", }, - }, - { - Path: "/etc/hosts", - Digest: &file.Digest{ - Algorithm: "'Q1'+base64(sha1)", - Value: "Q1BD6zJKZTRWyqGnPi4tSfd3krsMU=", + { + Path: "/etc", }, - }, - { - Path: "/etc/inittab", - Digest: &file.Digest{ - Algorithm: "'Q1'+base64(sha1)", - Value: "Q1TsthbhW7QzWRe1E/NKwTOuD4pHc=", + { + Path: "/etc/fstab", + Digest: &file.Digest{ + Algorithm: "'Q1'+base64(sha1)", + Value: "Q11Q7hNe8QpDS531guqCdrXBzoA/o=", + }, }, - }, - { - Path: "/etc/modules", - Digest: &file.Digest{ - Algorithm: "'Q1'+base64(sha1)", - Value: "Q1toogjUipHGcMgECgPJX64SwUT1M=", + { + Path: "/etc/group", + Digest: &file.Digest{ + Algorithm: "'Q1'+base64(sha1)", + Value: "Q1oJ16xWudgKOrXIEquEDzlF2Lsm4=", + }, }, - }, - { - Path: "/etc/motd", - Digest: &file.Digest{ - Algorithm: "'Q1'+base64(sha1)", - Value: "Q1XmduVVNURHQ27TvYp1Lr5TMtFcA=", + { + Path: "/etc/hostname", + Digest: &file.Digest{ + Algorithm: "'Q1'+base64(sha1)", + Value: "Q16nVwYVXP/tChvUPdukVD2ifXOmc=", + }, }, - }, - { - Path: "/etc/mtab", - OwnerUID: "0", - OwnerGID: "0", - Permissions: "777", - Digest: &file.Digest{ - Algorithm: "'Q1'+base64(sha1)", - Value: "Q1kiljhXXH1LlQroHsEJIkPZg2eiw=", + { + Path: "/etc/hosts", + Digest: &file.Digest{ + Algorithm: "'Q1'+base64(sha1)", + Value: "Q1BD6zJKZTRWyqGnPi4tSfd3krsMU=", + }, }, - }, - { - Path: "/etc/passwd", - Digest: &file.Digest{ - Algorithm: "'Q1'+base64(sha1)", - Value: "Q1TchuuLUfur0izvfZQZxgN/LJhB8=", + { + Path: "/etc/inittab", + Digest: &file.Digest{ + Algorithm: "'Q1'+base64(sha1)", + Value: "Q1TsthbhW7QzWRe1E/NKwTOuD4pHc=", + }, }, - }, - { - Path: "/etc/profile", - Digest: &file.Digest{ - Algorithm: "'Q1'+base64(sha1)", - Value: "Q1KpFb8kl5LvwXWlY3e58FNsjrI34=", + { + Path: "/etc/modules", + Digest: &file.Digest{ + Algorithm: "'Q1'+base64(sha1)", + Value: "Q1toogjUipHGcMgECgPJX64SwUT1M=", + }, }, - }, - { - Path: "/etc/protocols", - Digest: &file.Digest{ - Algorithm: "'Q1'+base64(sha1)", - Value: "Q13FqXUnvuOpMDrH/6rehxuYAEE34=", + { + Path: "/etc/motd", + Digest: &file.Digest{ + Algorithm: "'Q1'+base64(sha1)", + Value: "Q1XmduVVNURHQ27TvYp1Lr5TMtFcA=", + }, }, - }, - { - Path: "/etc/services", - Digest: &file.Digest{ - Algorithm: "'Q1'+base64(sha1)", - Value: "Q1C6HJNgQvLWqt5VY+n7MZJ1rsDuY=", + { + Path: "/etc/mtab", + OwnerUID: "0", + OwnerGID: "0", + Permissions: "777", + Digest: &file.Digest{ + Algorithm: "'Q1'+base64(sha1)", + Value: "Q1kiljhXXH1LlQroHsEJIkPZg2eiw=", + }, }, - }, - { - Path: "/etc/shadow", - OwnerUID: "0", - OwnerGID: "42", - Permissions: "640", - Digest: &file.Digest{ - Algorithm: "'Q1'+base64(sha1)", - Value: "Q1ltrPIAW2zHeDiajsex2Bdmq3uqA=", + { + Path: "/etc/passwd", + Digest: &file.Digest{ + Algorithm: "'Q1'+base64(sha1)", + Value: "Q1TchuuLUfur0izvfZQZxgN/LJhB8=", + }, }, - }, - { - Path: "/etc/shells", - Digest: &file.Digest{ - Algorithm: "'Q1'+base64(sha1)", - Value: "Q1ojm2YdpCJ6B/apGDaZ/Sdb2xJkA=", + { + Path: "/etc/profile", + Digest: &file.Digest{ + Algorithm: "'Q1'+base64(sha1)", + Value: "Q1KpFb8kl5LvwXWlY3e58FNsjrI34=", + }, }, - }, - { - Path: "/etc/sysctl.conf", - Digest: &file.Digest{ - Algorithm: "'Q1'+base64(sha1)", - Value: "Q14upz3tfnNxZkIEsUhWn7Xoiw96g=", + { + Path: "/etc/protocols", + Digest: &file.Digest{ + Algorithm: "'Q1'+base64(sha1)", + Value: "Q13FqXUnvuOpMDrH/6rehxuYAEE34=", + }, }, - }, - { - Path: "/etc/apk", - }, - { - Path: "/etc/conf.d", - }, - { - Path: "/etc/crontabs", - }, - { - Path: "/etc/crontabs/root", - OwnerUID: "0", - OwnerGID: "0", - Permissions: "600", - Digest: &file.Digest{ - Algorithm: "'Q1'+base64(sha1)", - Value: "Q1vfk1apUWI4yLJGhhNRd0kJixfvY=", + { + Path: "/etc/services", + Digest: &file.Digest{ + Algorithm: "'Q1'+base64(sha1)", + Value: "Q1C6HJNgQvLWqt5VY+n7MZJ1rsDuY=", + }, }, - }, - { - Path: "/etc/init.d", - }, - { - Path: "/etc/modprobe.d", - }, - { - Path: "/etc/modprobe.d/aliases.conf", - Digest: &file.Digest{ - Algorithm: "'Q1'+base64(sha1)", - Value: "Q1WUbh6TBYNVK7e4Y+uUvLs/7viqk=", + { + Path: "/etc/shadow", + OwnerUID: "0", + OwnerGID: "42", + Permissions: "640", + Digest: &file.Digest{ + Algorithm: "'Q1'+base64(sha1)", + Value: "Q1ltrPIAW2zHeDiajsex2Bdmq3uqA=", + }, }, - }, - { - Path: "/etc/modprobe.d/blacklist.conf", - Digest: &file.Digest{ - Algorithm: "'Q1'+base64(sha1)", - Value: "Q1xxYGU6S6TLQvb7ervPrWWwAWqMg=", + { + Path: "/etc/shells", + Digest: &file.Digest{ + Algorithm: "'Q1'+base64(sha1)", + Value: "Q1ojm2YdpCJ6B/apGDaZ/Sdb2xJkA=", + }, }, - }, - { - Path: "/etc/modprobe.d/i386.conf", - Digest: &file.Digest{ - Algorithm: "'Q1'+base64(sha1)", - Value: "Q1pnay/njn6ol9cCssL7KiZZ8etlc=", + { + Path: "/etc/sysctl.conf", + Digest: &file.Digest{ + Algorithm: "'Q1'+base64(sha1)", + Value: "Q14upz3tfnNxZkIEsUhWn7Xoiw96g=", + }, }, - }, - { - Path: "/etc/modprobe.d/kms.conf", - Digest: &file.Digest{ - Algorithm: "'Q1'+base64(sha1)", - Value: "Q1ynbLn3GYDpvajba/ldp1niayeog=", + { + Path: "/etc/apk", }, - }, - { - Path: "/etc/modules-load.d", - }, - { - Path: "/etc/network", - }, - { - Path: "/etc/network/if-down.d", - }, - { - Path: "/etc/network/if-post-down.d", - }, - { - Path: "/etc/network/if-pre-up.d", - }, - { - Path: "/etc/network/if-up.d", - }, - { - Path: "/etc/opt", - }, - { - Path: "/etc/periodic", - }, - { - Path: "/etc/periodic/15min", - }, - { - Path: "/etc/periodic/daily", - }, - { - Path: "/etc/periodic/hourly", - }, - { - Path: "/etc/periodic/monthly", - }, - { - Path: "/etc/periodic/weekly", - }, - { - Path: "/etc/profile.d", - }, - { - Path: "/etc/profile.d/color_prompt", - Digest: &file.Digest{ - Algorithm: "'Q1'+base64(sha1)", - Value: "Q10wL23GuSCVfumMRgakabUI6EsSk=", + { + Path: "/etc/conf.d", }, - }, - { - Path: "/etc/profile.d/locale", - Digest: &file.Digest{ - Algorithm: "'Q1'+base64(sha1)", - Value: "Q1R4bIEpnKxxOSrlnZy9AoawqZ5DU=", + { + Path: "/etc/crontabs", }, - }, - { - Path: "/etc/sysctl.d", - }, - { - Path: "/home", - }, - { - Path: "/lib", - }, - { - Path: "/lib/firmware", - }, - { - Path: "/lib/mdev", - }, - { - Path: "/lib/modules-load.d", - }, - { - Path: "/lib/sysctl.d", - }, - { - Path: "/lib/sysctl.d/00-alpine.conf", - Digest: &file.Digest{ - Algorithm: "'Q1'+base64(sha1)", - Value: "Q1HpElzW1xEgmKfERtTy7oommnq6c=", + { + Path: "/etc/crontabs/root", + OwnerUID: "0", + OwnerGID: "0", + Permissions: "600", + Digest: &file.Digest{ + Algorithm: "'Q1'+base64(sha1)", + Value: "Q1vfk1apUWI4yLJGhhNRd0kJixfvY=", + }, }, - }, - { - Path: "/media", - }, - { - Path: "/media/cdrom", - }, - { - Path: "/media/floppy", - }, - { - Path: "/media/usb", - }, - { - Path: "/mnt", - }, - { - Path: "/opt", - }, - { - Path: "/proc", - }, - { - Path: "/root", - OwnerUID: "0", - OwnerGID: "0", - Permissions: "700", - }, - { - Path: "/run", - }, - { - Path: "/sbin", - }, - { - Path: "/sbin/mkmntdirs", - OwnerUID: "0", - OwnerGID: "0", - Permissions: "755", - Digest: &file.Digest{ - Algorithm: "'Q1'+base64(sha1)", - Value: "Q1YeuSmC7iDbEWrusPzA/zUQF6YSg=", + { + Path: "/etc/init.d", }, - }, - { - Path: "/srv", - }, - { - Path: "/sys", - }, - { - Path: "/tmp", - OwnerUID: "0", - OwnerGID: "0", - Permissions: "1777", - }, - { - Path: "/usr", - }, - { - Path: "/usr/lib", - }, - { - Path: "/usr/lib/modules-load.d", - }, - { - Path: "/usr/local", - }, - { - Path: "/usr/local/bin", - }, - { - Path: "/usr/local/lib", - }, - { - Path: "/usr/local/share", - }, - { - Path: "/usr/sbin", - }, - { - Path: "/usr/share", - }, - { - Path: "/usr/share/man", - }, - { - Path: "/usr/share/misc", - }, - { - Path: "/var", - }, - { - Path: "/var/run", - OwnerUID: "0", - OwnerGID: "0", - Permissions: "777", - Digest: &file.Digest{ - Algorithm: "'Q1'+base64(sha1)", - Value: "Q11/SNZz/8cK2dSKK+cJpVrZIuF4Q=", + { + Path: "/etc/modprobe.d", }, - }, - { - Path: "/var/cache", - }, - { - Path: "/var/cache/misc", - }, - { - Path: "/var/empty", - OwnerUID: "0", - OwnerGID: "0", - Permissions: "555", - }, - { - Path: "/var/lib", - }, - { - Path: "/var/lib/misc", - }, - { - Path: "/var/local", - }, - { - Path: "/var/lock", - }, - { - Path: "/var/lock/subsys", - }, - { - Path: "/var/log", - }, - { - Path: "/var/mail", - }, - { - Path: "/var/opt", - }, - { - Path: "/var/spool", - }, - { - Path: "/var/spool/mail", - OwnerUID: "0", - OwnerGID: "0", - Permissions: "777", - Digest: &file.Digest{ - Algorithm: "'Q1'+base64(sha1)", - Value: "Q1dzbdazYZA2nTzSIG3YyNw7d4Juc=", + { + Path: "/etc/modprobe.d/aliases.conf", + Digest: &file.Digest{ + Algorithm: "'Q1'+base64(sha1)", + Value: "Q1WUbh6TBYNVK7e4Y+uUvLs/7viqk=", + }, }, - }, - { - Path: "/var/spool/cron", - }, - { - Path: "/var/spool/cron/crontabs", - OwnerUID: "0", - OwnerGID: "0", - Permissions: "777", - Digest: &file.Digest{ - Algorithm: "'Q1'+base64(sha1)", - Value: "Q1OFZt+ZMp7j0Gny0rqSKuWJyqYmA=", + { + Path: "/etc/modprobe.d/blacklist.conf", + Digest: &file.Digest{ + Algorithm: "'Q1'+base64(sha1)", + Value: "Q1xxYGU6S6TLQvb7ervPrWWwAWqMg=", + }, + }, + { + Path: "/etc/modprobe.d/i386.conf", + Digest: &file.Digest{ + Algorithm: "'Q1'+base64(sha1)", + Value: "Q1pnay/njn6ol9cCssL7KiZZ8etlc=", + }, + }, + { + Path: "/etc/modprobe.d/kms.conf", + Digest: &file.Digest{ + Algorithm: "'Q1'+base64(sha1)", + Value: "Q1ynbLn3GYDpvajba/ldp1niayeog=", + }, + }, + { + Path: "/etc/modules-load.d", + }, + { + Path: "/etc/network", + }, + { + Path: "/etc/network/if-down.d", + }, + { + Path: "/etc/network/if-post-down.d", + }, + { + Path: "/etc/network/if-pre-up.d", + }, + { + Path: "/etc/network/if-up.d", + }, + { + Path: "/etc/opt", + }, + { + Path: "/etc/periodic", + }, + { + Path: "/etc/periodic/15min", + }, + { + Path: "/etc/periodic/daily", + }, + { + Path: "/etc/periodic/hourly", + }, + { + Path: "/etc/periodic/monthly", + }, + { + Path: "/etc/periodic/weekly", + }, + { + Path: "/etc/profile.d", + }, + { + Path: "/etc/profile.d/color_prompt", + Digest: &file.Digest{ + Algorithm: "'Q1'+base64(sha1)", + Value: "Q10wL23GuSCVfumMRgakabUI6EsSk=", + }, + }, + { + Path: "/etc/profile.d/locale", + Digest: &file.Digest{ + Algorithm: "'Q1'+base64(sha1)", + Value: "Q1R4bIEpnKxxOSrlnZy9AoawqZ5DU=", + }, + }, + { + Path: "/etc/sysctl.d", + }, + { + Path: "/home", + }, + { + Path: "/lib", + }, + { + Path: "/lib/firmware", + }, + { + Path: "/lib/mdev", + }, + { + Path: "/lib/modules-load.d", + }, + { + Path: "/lib/sysctl.d", + }, + { + Path: "/lib/sysctl.d/00-alpine.conf", + Digest: &file.Digest{ + Algorithm: "'Q1'+base64(sha1)", + Value: "Q1HpElzW1xEgmKfERtTy7oommnq6c=", + }, + }, + { + Path: "/media", + }, + { + Path: "/media/cdrom", + }, + { + Path: "/media/floppy", + }, + { + Path: "/media/usb", + }, + { + Path: "/mnt", + }, + { + Path: "/opt", + }, + { + Path: "/proc", + }, + { + Path: "/root", + OwnerUID: "0", + OwnerGID: "0", + Permissions: "700", + }, + { + Path: "/run", + }, + { + Path: "/sbin", + }, + { + Path: "/sbin/mkmntdirs", + OwnerUID: "0", + OwnerGID: "0", + Permissions: "755", + Digest: &file.Digest{ + Algorithm: "'Q1'+base64(sha1)", + Value: "Q1YeuSmC7iDbEWrusPzA/zUQF6YSg=", + }, + }, + { + Path: "/srv", + }, + { + Path: "/sys", + }, + { + Path: "/tmp", + OwnerUID: "0", + OwnerGID: "0", + Permissions: "1777", + }, + { + Path: "/usr", + }, + { + Path: "/usr/lib", + }, + { + Path: "/usr/lib/modules-load.d", + }, + { + Path: "/usr/local", + }, + { + Path: "/usr/local/bin", + }, + { + Path: "/usr/local/lib", + }, + { + Path: "/usr/local/share", + }, + { + Path: "/usr/sbin", + }, + { + Path: "/usr/share", + }, + { + Path: "/usr/share/man", + }, + { + Path: "/usr/share/misc", + }, + { + Path: "/var", + }, + { + Path: "/var/run", + OwnerUID: "0", + OwnerGID: "0", + Permissions: "777", + Digest: &file.Digest{ + Algorithm: "'Q1'+base64(sha1)", + Value: "Q11/SNZz/8cK2dSKK+cJpVrZIuF4Q=", + }, + }, + { + Path: "/var/cache", + }, + { + Path: "/var/cache/misc", + }, + { + Path: "/var/empty", + OwnerUID: "0", + OwnerGID: "0", + Permissions: "555", + }, + { + Path: "/var/lib", + }, + { + Path: "/var/lib/misc", + }, + { + Path: "/var/local", + }, + { + Path: "/var/lock", + }, + { + Path: "/var/lock/subsys", + }, + { + Path: "/var/log", + }, + { + Path: "/var/mail", + }, + { + Path: "/var/opt", + }, + { + Path: "/var/spool", + }, + { + Path: "/var/spool/mail", + OwnerUID: "0", + OwnerGID: "0", + Permissions: "777", + Digest: &file.Digest{ + Algorithm: "'Q1'+base64(sha1)", + Value: "Q1dzbdazYZA2nTzSIG3YyNw7d4Juc=", + }, + }, + { + Path: "/var/spool/cron", + }, + { + Path: "/var/spool/cron/crontabs", + OwnerUID: "0", + OwnerGID: "0", + Permissions: "777", + Digest: &file.Digest{ + Algorithm: "'Q1'+base64(sha1)", + Value: "Q1OFZt+ZMp7j0Gny0rqSKuWJyqYmA=", + }, + }, + { + Path: "/var/tmp", + OwnerUID: "0", + OwnerGID: "0", + Permissions: "1777", }, - }, - { - Path: "/var/tmp", - OwnerUID: "0", - OwnerGID: "0", - Permissions: "1777", }, }, }, @@ -651,16 +699,15 @@ func TestSinglePackageDetails(t *testing.T) { for _, test := range tests { t.Run(test.fixture, func(t *testing.T) { - lrc := newLocationReadCloser(t, test.fixture) - pkgs, _, err := parseApkDB(nil, new(generic.Environment), lrc) - require.NoError(t, err) - require.Len(t, pkgs, 1) - metadata := pkgs[0].Metadata.(pkg.ApkMetadata) - - if diff := cmp.Diff(test.expected, metadata); diff != "" { - t.Errorf("Entry mismatch (-want +got):\n%s", diff) + fixtureLocation := source.NewLocation(test.fixture) + test.expected.Locations = source.NewLocationSet(fixtureLocation) + for i := range test.expected.Licenses { + test.expected.Licenses[i].Location = &fixtureLocation } + + pkgtest.TestFileParser(t, test.fixture, parseApkDB, []pkg.Package{test.expected}, nil) + }) } } @@ -677,7 +724,7 @@ func TestMultiplePackages(t *testing.T) { { Value: "BSD", Type: license.Declared, - Location: location, + Location: &location, }, }, Type: pkg.ApkPkg, @@ -712,17 +759,17 @@ func TestMultiplePackages(t *testing.T) { Value: "MIT", SPDXExpression: "MIT", Type: license.Declared, - Location: location, + Location: &location, }, { Value: "BSD", Type: license.Declared, - Location: location, + Location: &location, }, { Value: "GPL2+", Type: license.Declared, - Location: location, + Location: &location, }, }, MetadataType: pkg.ApkMetadataType, diff --git a/syft/pkg/cataloger/deb/cataloger_test.go b/syft/pkg/cataloger/deb/cataloger_test.go index 958db023cd7..674f12c5aa1 100644 --- a/syft/pkg/cataloger/deb/cataloger_test.go +++ b/syft/pkg/cataloger/deb/cataloger_test.go @@ -4,18 +4,37 @@ import ( "testing" "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" "github.com/anchore/syft/syft/source" ) func TestDpkgCataloger(t *testing.T) { + licenseLocation := source.NewVirtualLocation("/usr/share/doc/libpam-runtime/copyright", "/usr/share/doc/libpam-runtime/copyright") expected := []pkg.Package{ { - Name: "libpam-runtime", - Version: "1.1.8-3.6", - FoundBy: "dpkgdb-cataloger", - Licenses: []pkg.License{}, + Name: "libpam-runtime", + Version: "1.1.8-3.6", + FoundBy: "dpkgdb-cataloger", + Licenses: []pkg.License{ + { + Value: "GPL-1", + Location: &licenseLocation, + Type: license.Declared, + }, + { + Value: "GPL-2", + Location: &licenseLocation, + Type: license.Declared, + }, + { + Value: "LGPL-2.1", + SPDXExpression: "LGPL-2.1", + Location: &licenseLocation, + Type: license.Declared, + }, + }, Locations: source.NewLocationSet( source.NewVirtualLocation("/var/lib/dpkg/status", "/var/lib/dpkg/status"), source.NewVirtualLocation("/var/lib/dpkg/info/libpam-runtime.md5sums", "/var/lib/dpkg/info/libpam-runtime.md5sums"), diff --git a/syft/pkg/cataloger/deb/package.go b/syft/pkg/cataloger/deb/package.go index 3dda1fcf8e7..2e6ea875f90 100644 --- a/syft/pkg/cataloger/deb/package.go +++ b/syft/pkg/cataloger/deb/package.go @@ -96,8 +96,10 @@ func addLicenses(resolver source.FileResolver, dbLocation source.Location, p *pk if copyrightReader != nil && copyrightLocation != nil { defer internal.CloseAndLogError(copyrightReader, copyrightLocation.VirtualPath) // attach the licenses - // TODO: update this to use new license model/validation - _ = parseLicensesFromCopyright(copyrightReader) + licenseStrs := parseLicensesFromCopyright(copyrightReader) + for _, licenseStr := range licenseStrs { + p.Licenses = append(p.Licenses, pkg.NewLicenseFromLocation(licenseStr, copyrightLocation.WithoutAnnotations())) + } // keep a record of the file where this was discovered p.Locations.Add(*copyrightLocation) } diff --git a/syft/pkg/cataloger/golang/licenses_test.go b/syft/pkg/cataloger/golang/licenses_test.go index 81b5644b2bd..186afa0468b 100644 --- a/syft/pkg/cataloger/golang/licenses_test.go +++ b/syft/pkg/cataloger/golang/licenses_test.go @@ -19,11 +19,9 @@ import ( ) func Test_LocalLicenseSearch(t *testing.T) { - // TODO: should locationsByGlob return non default constructor values? loc1 := source.NewLocation("github.com/someorg/somename@v0.3.2/LICENSE") - loc1.Annotations = nil loc2 := source.NewLocation("github.com/!cap!o!r!g/!cap!project@v4.111.5/LICENSE.txt") - loc2.Annotations = nil + tests := []struct { name string version string @@ -33,18 +31,20 @@ func Test_LocalLicenseSearch(t *testing.T) { name: "github.com/someorg/somename", version: "v0.3.2", expected: pkg.License{ + Value: "Apache-2.0", SPDXExpression: "Apache-2.0", Type: license.Concluded, - Location: loc1, + Location: &loc1, }, }, { name: "github.com/CapORG/CapProject", version: "v4.111.5", expected: pkg.License{ + Value: "MIT", SPDXExpression: "MIT", Type: license.Concluded, - Location: loc2, + Location: &loc2, }, }, } @@ -54,10 +54,12 @@ func Test_LocalLicenseSearch(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - l := newGoLicenses(GoCatalogerOpts{ - searchLocalModCacheLicenses: true, - localModCacheDir: path.Join(wd, "test-fixtures", "licenses", "pkg", "mod"), - }) + l := newGoLicenses( + GoCatalogerOpts{ + searchLocalModCacheLicenses: true, + localModCacheDir: path.Join(wd, "test-fixtures", "licenses", "pkg", "mod"), + }, + ) licenses, err := l.getLicenses(source.EmptyResolver{}, test.name, test.version) require.NoError(t, err) @@ -69,11 +71,9 @@ func Test_LocalLicenseSearch(t *testing.T) { } func Test_RemoteProxyLicenseSearch(t *testing.T) { - // TODO: should locationsByGlob return non default constructor values? loc1 := source.NewLocation("github.com/someorg/somename@v0.3.2/LICENSE") - loc1.Annotations = nil loc2 := source.NewLocation("github.com/!cap!o!r!g/!cap!project@v4.111.5/LICENSE.txt") - loc2.Annotations = nil + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { buf := &bytes.Buffer{} uri := strings.TrimPrefix(strings.TrimSuffix(r.RequestURI, ".zip"), "/") @@ -120,18 +120,20 @@ func Test_RemoteProxyLicenseSearch(t *testing.T) { name: "github.com/someorg/somename", version: "v0.3.2", expected: pkg.License{ + Value: "Apache-2.0", SPDXExpression: "Apache-2.0", Type: license.Concluded, - Location: loc1, + Location: &loc1, }, }, { name: "github.com/CapORG/CapProject", version: "v4.111.5", expected: pkg.License{ + Value: "MIT", SPDXExpression: "MIT", Type: license.Concluded, - Location: loc2, + Location: &loc2, }, }, } diff --git a/syft/pkg/cataloger/java/archive_parser.go b/syft/pkg/cataloger/java/archive_parser.go index 9eab1df85d6..2536d304688 100644 --- a/syft/pkg/cataloger/java/archive_parser.go +++ b/syft/pkg/cataloger/java/archive_parser.go @@ -185,19 +185,15 @@ func (j *archiveParser) discoverMainPackage() (*pkg.Package, error) { log.Warnf("failed to create digest for file=%q: %+v", j.archivePath, err) } - licenseCandidates := selectLicense(manifest) - - licenses := make([]pkg.License, 0) - for _, l := range licenseCandidates { - // we use j.location because we want to associate the license declaration with where we discovered the contents in the manifest - licenses = append(licenses, pkg.NewLicense(l, "", &j.location)) - } - return &pkg.Package{ Name: selectName(manifest, j.fileInfo), Version: selectVersion(manifest, j.fileInfo), Language: pkg.Java, - Licenses: licenses, + Licenses: pkg.NewLicensesFromLocation( + // we use j.location because we want to associate the license declaration with where we discovered the contents in the manifest + j.location, + selectLicenses(manifest)..., + ), Locations: source.NewLocationSet( j.location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), ), diff --git a/syft/pkg/cataloger/java/archive_parser_test.go b/syft/pkg/cataloger/java/archive_parser_test.go index fb23f78e0ec..09fd7adf95c 100644 --- a/syft/pkg/cataloger/java/archive_parser_test.go +++ b/syft/pkg/cataloger/java/archive_parser_test.go @@ -100,11 +100,7 @@ func TestParseJar(t *testing.T) { Version: "1.0-SNAPSHOT", PURL: "pkg:maven/io.jenkins.plugins/example-jenkins-plugin@1.0-SNAPSHOT", Licenses: []pkg.License{ - { - Value: "MIT License", - Type: "declared", - Location: source.NewLocation("test-fixtures/java-builds/packages/example-jenkins-plugin.hpi"), - }, + pkg.NewLicenseFromLocation("MIT License", source.NewLocation("test-fixtures/java-builds/packages/example-jenkins-plugin.hpi")), }, Language: pkg.Java, Type: pkg.JenkinsPluginPkg, @@ -156,7 +152,6 @@ func TestParseJar(t *testing.T) { Name: "example-java-app-gradle", Version: "0.1.0", PURL: "pkg:maven/example-java-app-gradle/example-java-app-gradle@0.1.0", - Licenses: []pkg.License{}, Language: pkg.Java, Type: pkg.JavaPkg, MetadataType: pkg.JavaMetadataType, @@ -211,7 +206,6 @@ func TestParseJar(t *testing.T) { Name: "example-java-app-maven", Version: "0.1.0", PURL: "pkg:maven/org.anchore/example-java-app-maven@0.1.0", - Licenses: []pkg.License{}, Language: pkg.Java, Type: pkg.JavaPkg, MetadataType: pkg.JavaMetadataType, diff --git a/syft/pkg/cataloger/java/parse_java_manifest.go b/syft/pkg/cataloger/java/parse_java_manifest.go index 134aa22976c..f615da40603 100644 --- a/syft/pkg/cataloger/java/parse_java_manifest.go +++ b/syft/pkg/cataloger/java/parse_java_manifest.go @@ -157,7 +157,7 @@ func selectVersion(manifest *pkg.JavaManifest, filenameObj archiveFilename) stri return "" } -func selectLicense(manifest *pkg.JavaManifest) []string { +func selectLicenses(manifest *pkg.JavaManifest) []string { result := []string{} if manifest == nil { return result diff --git a/syft/pkg/cataloger/javascript/cataloger_test.go b/syft/pkg/cataloger/javascript/cataloger_test.go index e3971060a46..e800ae3295b 100644 --- a/syft/pkg/cataloger/javascript/cataloger_test.go +++ b/syft/pkg/cataloger/javascript/cataloger_test.go @@ -3,7 +3,6 @@ package javascript import ( "testing" - "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" "github.com/anchore/syft/syft/source" @@ -21,12 +20,7 @@ func Test_JavascriptCataloger(t *testing.T) { Language: pkg.JavaScript, Type: pkg.NpmPkg, Licenses: []pkg.License{ - { - Value: "MIT", - SPDXExpression: "MIT", - Type: license.Declared, - Location: source.NewLocation("package-lock.json"), - }, + pkg.NewLicenseFromLocation("MIT", source.NewLocation("package-lock.json")), }, MetadataType: pkg.NpmPackageLockJSONMetadataType, Metadata: pkg.NpmPackageLockJSONMetadata{Resolved: "https://registry.npmjs.org/@actions/core/-/core-1.6.0.tgz", Integrity: "sha512-NB1UAZomZlCV/LmJqkLhNTqtKfFXJZAUPcfl/zqG7EfsQdeUJtaWO98SGbuQ3pydJ3fHl2CvI/51OKYlCYYcaw=="}, @@ -51,12 +45,7 @@ func Test_JavascriptCataloger(t *testing.T) { Language: pkg.JavaScript, Type: pkg.NpmPkg, Licenses: []pkg.License{ - { - Value: "MIT", - SPDXExpression: "MIT", - Type: license.Declared, - Location: source.NewLocation("package-lock.json"), - }, + pkg.NewLicenseFromLocation("MIT", source.NewLocation("package-lock.json")), }, MetadataType: pkg.NpmPackageLockJSONMetadataType, Metadata: pkg.NpmPackageLockJSONMetadata{Resolved: "https://registry.npmjs.org/cowsay/-/cowsay-1.4.0.tgz", Integrity: "sha512-rdg5k5PsHFVJheO/pmE3aDg2rUDDTfPJau6yYkZYlHFktUz+UxbE+IgnUAEyyCyv4noL5ltxXD0gZzmHPCy/9g=="}, diff --git a/syft/pkg/cataloger/javascript/package.go b/syft/pkg/cataloger/javascript/package.go index a48ba4f7524..1c1ae9db24c 100644 --- a/syft/pkg/cataloger/javascript/package.go +++ b/syft/pkg/cataloger/javascript/package.go @@ -12,35 +12,19 @@ import ( "github.com/anchore/syft/syft/source" ) -func newPackageJSONPackage(u packageJSON, locations ...source.Location) pkg.Package { - var licenseLocation source.Location - if len(locations) > 0 { - // caller always passes the reader.Location of the package json where license information was found - // we use this when constructing the license object to show our work on where we lifted the declaration from - licenseLocation = locations[0] - } - +func newPackageJSONPackage(u packageJSON, indexLocation source.Location) pkg.Package { licenseCandidates, err := u.licensesFromJSON() if err != nil { log.Warnf("unable to extract licenses from javascript package.json: %+v", err) } - licenses := make([]pkg.License, 0) - if len(licenseCandidates) != 0 { - for _, l := range licenseCandidates { - // we could extract the url from the json here, but we only want to include this information if it was used - // to determine the declared licnese - instead the package json location was used. - licenses = append(licenses, pkg.NewLicense(l, "", licenseLocation)) - } - } - p := pkg.Package{ Name: u.Name, Version: u.Version, PURL: packageURL(u.Name, u.Version), - Locations: source.NewLocationSet(locations...), + Locations: source.NewLocationSet(indexLocation), Language: pkg.JavaScript, - Licenses: licenses, + Licenses: pkg.NewLicensesFromLocation(indexLocation, licenseCandidates...), Type: pkg.NpmPkg, MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ @@ -99,6 +83,7 @@ func newPackageLockV2Package(resolver source.FileResolver, location source.Locat Name: name, Version: u.Version, Locations: source.NewLocationSet(location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)), + Licenses: pkg.NewLicensesFromLocation(location, u.License...), PURL: packageURL(name, u.Version), Language: pkg.JavaScript, Type: pkg.NpmPkg, @@ -140,9 +125,7 @@ func newYarnLockPackage(resolver source.FileResolver, location source.Location, func finalizeLockPkg(resolver source.FileResolver, location source.Location, p pkg.Package) pkg.Package { licenseCandidate := addLicenses(p.Name, resolver, location) - for _, l := range licenseCandidate { - p.Licenses = append(p.Licenses, pkg.NewLicense(l, "", location)) - } + p.Licenses = append(p.Licenses, pkg.NewLicensesFromLocation(location, licenseCandidate...)...) p.SetID() return p } diff --git a/syft/pkg/cataloger/javascript/parse_package_json_test.go b/syft/pkg/cataloger/javascript/parse_package_json_test.go index 508672d8d5d..dbeb44bc382 100644 --- a/syft/pkg/cataloger/javascript/parse_package_json_test.go +++ b/syft/pkg/cataloger/javascript/parse_package_json_test.go @@ -5,7 +5,6 @@ import ( "github.com/stretchr/testify/assert" - "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" "github.com/anchore/syft/syft/source" @@ -25,12 +24,7 @@ func TestParsePackageJSON(t *testing.T) { Type: pkg.NpmPkg, Language: pkg.JavaScript, Licenses: []pkg.License{ - { - Value: "Artistic-2.0", - SPDXExpression: "Artistic-2.0", - Type: license.Declared, - Location: source.NewLocation("test-fixtures/pkg-json/package.json"), - }, + pkg.NewLicenseFromLocation("Artistic-2.0", source.NewLocation("test-fixtures/pkg-json/package.json")), }, MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ @@ -52,12 +46,7 @@ func TestParsePackageJSON(t *testing.T) { Type: pkg.NpmPkg, Language: pkg.JavaScript, Licenses: []pkg.License{ - { - Value: "ISC", - SPDXExpression: "ISC", - Type: license.Declared, - Location: source.NewLocation("test-fixtures/pkg-json/package-license-object.json"), - }, + pkg.NewLicenseFromLocation("ISC", source.NewLocation("test-fixtures/pkg-json/package-license-object.json")), }, MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ @@ -78,18 +67,8 @@ func TestParsePackageJSON(t *testing.T) { PURL: "pkg:npm/npm@6.14.6", Type: pkg.NpmPkg, Licenses: []pkg.License{ - { - Value: "MIT", - SPDXExpression: "MIT", - Type: license.Declared, - Location: source.NewLocation("test-fixtures/pkg-json/package-license-objects.json"), - }, - { - Value: "Apache-2.0", - SPDXExpression: "Apache-2.0", - Type: license.Declared, - Location: source.NewLocation("test-fixtures/pkg-json/package-license-objects.json"), - }, + pkg.NewLicenseFromLocation("MIT", source.NewLocation("test-fixtures/pkg-json/package-license-objects.json")), + pkg.NewLicenseFromLocation("Apache-2.0", source.NewLocation("test-fixtures/pkg-json/package-license-objects.json")), }, Language: pkg.JavaScript, MetadataType: pkg.NpmPackageJSONMetadataType, @@ -110,7 +89,6 @@ func TestParsePackageJSON(t *testing.T) { Version: "6.14.6", PURL: "pkg:npm/npm@6.14.6", Type: pkg.NpmPkg, - Licenses: []pkg.License{}, Language: pkg.JavaScript, MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ @@ -130,7 +108,6 @@ func TestParsePackageJSON(t *testing.T) { Version: "6.14.6", PURL: "pkg:npm/npm@6.14.6", Type: pkg.NpmPkg, - Licenses: []pkg.License{}, Language: pkg.JavaScript, MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ @@ -151,12 +128,7 @@ func TestParsePackageJSON(t *testing.T) { PURL: "pkg:npm/npm@6.14.6", Type: pkg.NpmPkg, Licenses: []pkg.License{ - { - Value: "Artistic-2.0", - SPDXExpression: "Artistic-2.0", - Type: license.Declared, - Location: source.NewLocation("test-fixtures/pkg-json/package-nested-author.json"), - }, + pkg.NewLicenseFromLocation("Artistic-2.0", source.NewLocation("test-fixtures/pkg-json/package-nested-author.json")), }, Language: pkg.JavaScript, MetadataType: pkg.NpmPackageJSONMetadataType, @@ -178,12 +150,7 @@ func TestParsePackageJSON(t *testing.T) { PURL: "pkg:npm/function-bind@1.1.1", Type: pkg.NpmPkg, Licenses: []pkg.License{ - { - Value: "MIT", - SPDXExpression: "MIT", - Type: license.Declared, - Location: source.NewLocation("test-fixtures/pkg-json/package-repo-string.json"), - }, + pkg.NewLicenseFromLocation("MIT", source.NewLocation("test-fixtures/pkg-json/package-repo-string.json")), }, Language: pkg.JavaScript, MetadataType: pkg.NpmPackageJSONMetadataType, @@ -205,12 +172,7 @@ func TestParsePackageJSON(t *testing.T) { PURL: "pkg:npm/npm@6.14.6", Type: pkg.NpmPkg, Licenses: []pkg.License{ - { - Value: "Artistic-2.0", - SPDXExpression: "Artistic-2.0", - Type: license.Declared, - Location: source.NewLocation("test-fixtures/pkg-json/package-private.json"), - }, + pkg.NewLicenseFromLocation("Artistic-2.0", source.NewLocation("test-fixtures/pkg-json/package-private.json")), }, Language: pkg.JavaScript, MetadataType: pkg.NpmPackageJSONMetadataType, diff --git a/syft/pkg/cataloger/javascript/parse_package_lock.go b/syft/pkg/cataloger/javascript/parse_package_lock.go index d0a75f1acff..7ca2669b38a 100644 --- a/syft/pkg/cataloger/javascript/parse_package_lock.go +++ b/syft/pkg/cataloger/javascript/parse_package_lock.go @@ -25,39 +25,6 @@ type packageLock struct { Packages map[string]lockPackage } -// packageLockLicense -type packageLockLicense []string - -func (licenses *packageLockLicense) UnmarshalJSON(data []byte) (err error) { - // The license field could be either a string or an array. - - // 1. An array - var arr []string - if err := json.Unmarshal(data, &arr); err == nil { - *licenses = arr - return nil - } - - // 2. A string - var str string - if err = json.Unmarshal(data, &str); err == nil { - *licenses = make([]string, 1) - (*licenses)[0] = str - return nil - } - - // debug the content we did not expect - if len(data) > 0 { - log.WithFields("license", string(data)).Debug("Unable to parse the following `license` value in package-lock.json") - } - - // 3. Unexpected - // In case we are unable to parse the license field, - // i.e if we have not covered the full specification, - // we do not want to throw an error, instead assign nil. - return nil -} - // lockDependency represents a single package dependency listed in the package.lock json file type lockDependency struct { Version string `json:"version"` @@ -73,6 +40,9 @@ type lockPackage struct { License packageLockLicense `json:"license"` } +// packageLockLicense +type packageLockLicense []string + // parsePackageLock parses a package-lock.json and returns the discovered JavaScript packages. func parsePackageLock(resolver source.FileResolver, _ *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { // in the case we find package-lock.json files in the node_modules directories, skip those @@ -125,6 +95,36 @@ func parsePackageLock(resolver source.FileResolver, _ *generic.Environment, read return pkgs, nil, nil } +func (licenses *packageLockLicense) UnmarshalJSON(data []byte) (err error) { + // The license field could be either a string or an array. + + // 1. An array + var arr []string + if err := json.Unmarshal(data, &arr); err == nil { + *licenses = arr + return nil + } + + // 2. A string + var str string + if err = json.Unmarshal(data, &str); err == nil { + *licenses = make([]string, 1) + (*licenses)[0] = str + return nil + } + + // debug the content we did not expect + if len(data) > 0 { + log.WithFields("license", string(data)).Debug("Unable to parse the following `license` value in package-lock.json") + } + + // 3. Unexpected + // In case we are unable to parse the license field, + // i.e if we have not covered the full specification, + // we do not want to throw an error, instead assign nil. + return nil +} + func getNameFromPath(path string) string { parts := strings.Split(path, "node_modules/") return parts[len(parts)-1] diff --git a/syft/pkg/cataloger/javascript/parse_package_lock_test.go b/syft/pkg/cataloger/javascript/parse_package_lock_test.go index c844f6bdd94..f0e98053d86 100644 --- a/syft/pkg/cataloger/javascript/parse_package_lock_test.go +++ b/syft/pkg/cataloger/javascript/parse_package_lock_test.go @@ -134,38 +134,50 @@ func TestParsePackageLockV2(t *testing.T) { Metadata: pkg.NpmPackageLockJSONMetadata{}, }, { - Name: "@types/prop-types", - Version: "15.7.5", - PURL: "pkg:npm/%40types/prop-types@15.7.5", - Language: pkg.JavaScript, - Type: pkg.NpmPkg, + Name: "@types/prop-types", + Version: "15.7.5", + PURL: "pkg:npm/%40types/prop-types@15.7.5", + Language: pkg.JavaScript, + Type: pkg.NpmPkg, + Licenses: []pkg.License{ + pkg.NewLicenseFromLocation("MIT", source.NewLocation(fixture)), + }, MetadataType: "NpmPackageLockJsonMetadata", Metadata: pkg.NpmPackageLockJSONMetadata{Resolved: "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", Integrity: "sha1-XxnSuFqY6VWANvajysyIGUIPBc8="}, }, { - Name: "@types/react", - Version: "18.0.17", - PURL: "pkg:npm/%40types/react@18.0.17", - Language: pkg.JavaScript, - Type: pkg.NpmPkg, + Name: "@types/react", + Version: "18.0.17", + PURL: "pkg:npm/%40types/react@18.0.17", + Language: pkg.JavaScript, + Type: pkg.NpmPkg, + Licenses: []pkg.License{ + pkg.NewLicenseFromLocation("MIT", source.NewLocation(fixture)), + }, MetadataType: "NpmPackageLockJsonMetadata", Metadata: pkg.NpmPackageLockJSONMetadata{Resolved: "https://registry.npmjs.org/@types/react/-/react-18.0.17.tgz", Integrity: "sha1-RYPZwyLWfv5LOak10iPtzHBQzPQ="}, }, { - Name: "@types/scheduler", - Version: "0.16.2", - PURL: "pkg:npm/%40types/scheduler@0.16.2", - Language: pkg.JavaScript, - Type: pkg.NpmPkg, + Name: "@types/scheduler", + Version: "0.16.2", + PURL: "pkg:npm/%40types/scheduler@0.16.2", + Language: pkg.JavaScript, + Type: pkg.NpmPkg, + Licenses: []pkg.License{ + pkg.NewLicenseFromLocation("MIT", source.NewLocation(fixture)), + }, MetadataType: "NpmPackageLockJsonMetadata", Metadata: pkg.NpmPackageLockJSONMetadata{Resolved: "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", Integrity: "sha1-GmL4lSVyPd4kuhsBsJK/XfitTTk="}, }, { - Name: "csstype", - Version: "3.1.0", - PURL: "pkg:npm/csstype@3.1.0", - Language: pkg.JavaScript, - Type: pkg.NpmPkg, + Name: "csstype", + Version: "3.1.0", + PURL: "pkg:npm/csstype@3.1.0", + Language: pkg.JavaScript, + Type: pkg.NpmPkg, + Licenses: []pkg.License{ + pkg.NewLicenseFromLocation("MIT", source.NewLocation(fixture)), + }, MetadataType: "NpmPackageLockJsonMetadata", Metadata: pkg.NpmPackageLockJSONMetadata{Resolved: "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz", Integrity: "sha1-TdysNxjXh8+d8NG30VAzklyPKfI="}, }, @@ -264,32 +276,35 @@ func TestParsePackageLockAlias(t *testing.T) { }, } + packageLockV1 := "test-fixtures/pkg-lock/alias-package-lock-1.json" + packageLockV2 := "test-fixtures/pkg-lock/alias-package-lock-2.json" + packageLocks := []string{packageLockV1, packageLockV2} + v2Pkg := pkg.Package{ - Name: "alias-check", - Version: "1.0.0", - PURL: "pkg:npm/alias-check@1.0.0", - Language: pkg.JavaScript, - Type: pkg.NpmPkg, + Name: "alias-check", + Version: "1.0.0", + PURL: "pkg:npm/alias-check@1.0.0", + Language: pkg.JavaScript, + Type: pkg.NpmPkg, + Licenses: []pkg.License{ + pkg.NewLicenseFromLocation("ISC", source.NewLocation(packageLockV2)), + }, MetadataType: "NpmPackageLockJsonMetadata", Metadata: pkg.NpmPackageLockJSONMetadata{}, } - packageLockV1 := "test-fixtures/pkg-lock/alias-package-lock-1.json" - packageLockV2 := "test-fixtures/pkg-lock/alias-package-lock-2.json" - packageLocks := []string{packageLockV1, packageLockV2} - - for _, packageLock := range packageLocks { + for _, pl := range packageLocks { expected := make([]pkg.Package, len(commonPkgs)) copy(expected, commonPkgs) - if packageLock == packageLockV2 { + if pl == packageLockV2 { expected = append(expected, v2Pkg) } for i := range expected { - expected[i].Locations.Add(source.NewLocation(packageLock)) + expected[i].Locations.Add(source.NewLocation(pl)) } - pkgtest.TestFileParser(t, packageLock, parsePackageLock, expected, expectedRelationships) + pkgtest.TestFileParser(t, pl, parsePackageLock, expected, expectedRelationships) } } @@ -298,28 +313,39 @@ func TestParsePackageLockLicenseWithArray(t *testing.T) { var expectedRelationships []artifact.Relationship expectedPkgs := []pkg.Package{ { - Name: "tmp", - Version: "1.0.0", - Language: pkg.JavaScript, - Type: pkg.NpmPkg, + Name: "tmp", + Version: "1.0.0", + Language: pkg.JavaScript, + Type: pkg.NpmPkg, + Licenses: []pkg.License{ + pkg.NewLicenseFromLocation("ISC", source.NewLocation(fixture)), + }, PURL: "pkg:npm/tmp@1.0.0", MetadataType: "NpmPackageLockJsonMetadata", Metadata: pkg.NpmPackageLockJSONMetadata{}, }, { - Name: "pause-stream", - Version: "0.0.11", - Language: pkg.JavaScript, - Type: pkg.NpmPkg, + Name: "pause-stream", + Version: "0.0.11", + Language: pkg.JavaScript, + Type: pkg.NpmPkg, + + Licenses: []pkg.License{ + pkg.NewLicenseFromLocation("MIT", source.NewLocation(fixture)), + pkg.NewLicenseFromLocation("Apache2", source.NewLocation(fixture)), + }, PURL: "pkg:npm/pause-stream@0.0.11", MetadataType: "NpmPackageLockJsonMetadata", Metadata: pkg.NpmPackageLockJSONMetadata{}, }, { - Name: "through", - Version: "2.3.8", - Language: pkg.JavaScript, - Type: pkg.NpmPkg, + Name: "through", + Version: "2.3.8", + Language: pkg.JavaScript, + Type: pkg.NpmPkg, + Licenses: []pkg.License{ + pkg.NewLicenseFromLocation("MIT", source.NewLocation(fixture)), + }, PURL: "pkg:npm/through@2.3.8", MetadataType: "NpmPackageLockJsonMetadata", Metadata: pkg.NpmPackageLockJSONMetadata{}, diff --git a/syft/pkg/cataloger/kernel/cataloger_test.go b/syft/pkg/cataloger/kernel/cataloger_test.go index 96e63a54cf6..dd2ed0a47a5 100644 --- a/syft/pkg/cataloger/kernel/cataloger_test.go +++ b/syft/pkg/cataloger/kernel/cataloger_test.go @@ -4,7 +4,6 @@ import ( "testing" "github.com/anchore/syft/syft/artifact" - "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" "github.com/anchore/syft/syft/source" @@ -49,10 +48,12 @@ func Test_KernelCataloger(t *testing.T) { ), ), Licenses: []pkg.License{ - { - Value: "GPL v2", - Type: license.Declared, - }, + pkg.NewLicenseFromLocation("GPL v2", + source.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", + ), + ), }, Type: pkg.LinuxKernelModulePkg, PURL: "pkg:generic/ttynull", diff --git a/syft/pkg/cataloger/kernel/package.go b/syft/pkg/cataloger/kernel/package.go index 85198320b71..679f8d04696 100644 --- a/syft/pkg/cataloger/kernel/package.go +++ b/syft/pkg/cataloger/kernel/package.go @@ -4,18 +4,17 @@ import ( "strings" "github.com/anchore/packageurl-go" - "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/source" ) const linuxKernelPackageName = "linux-kernel" -func newLinuxKernelPackage(metadata pkg.LinuxKernelMetadata, locations ...source.Location) pkg.Package { +func newLinuxKernelPackage(metadata pkg.LinuxKernelMetadata, archiveLocation source.Location) pkg.Package { p := pkg.Package{ Name: linuxKernelPackageName, Version: metadata.Version, - Locations: source.NewLocationSet(locations...), + Locations: source.NewLocationSet(archiveLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)), PURL: packageURL(linuxKernelPackageName, metadata.Version), Type: pkg.LinuxKernelPkg, MetadataType: pkg.LinuxKernelMetadataType, @@ -27,20 +26,12 @@ func newLinuxKernelPackage(metadata pkg.LinuxKernelMetadata, locations ...source return p } -func newLinuxKernelModulePackage(metadata pkg.LinuxKernelModuleMetadata, locations ...source.Location) pkg.Package { - licenses := make([]pkg.License, 0) - if metadata.License != "" { - licenses = append(licenses, pkg.License{ - Value: metadata.License, - Type: license.Declared, - }) - } - +func newLinuxKernelModulePackage(metadata pkg.LinuxKernelModuleMetadata, kmLocation source.Location) pkg.Package { p := pkg.Package{ Name: metadata.Name, Version: metadata.Version, - Locations: source.NewLocationSet(locations...), - Licenses: licenses, + Locations: source.NewLocationSet(kmLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)), + Licenses: pkg.NewLicensesFromLocation(kmLocation, metadata.License), PURL: packageURL(metadata.Name, metadata.Version), Type: pkg.LinuxKernelModulePkg, MetadataType: pkg.LinuxKernelModuleMetadataType, diff --git a/syft/pkg/cataloger/kernel/parse_linux_kernel_file.go b/syft/pkg/cataloger/kernel/parse_linux_kernel_file.go index 303e88192c6..0be32c5b658 100644 --- a/syft/pkg/cataloger/kernel/parse_linux_kernel_file.go +++ b/syft/pkg/cataloger/kernel/parse_linux_kernel_file.go @@ -37,7 +37,7 @@ func parseLinuxKernelFile(_ source.FileResolver, _ *generic.Environment, reader return []pkg.Package{ newLinuxKernelPackage( metadata, - reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), + reader.Location, ), }, nil, nil } diff --git a/syft/pkg/cataloger/kernel/parse_linux_kernel_module_file.go b/syft/pkg/cataloger/kernel/parse_linux_kernel_module_file.go index fb0d818a5ff..3adeb5632f7 100644 --- a/syft/pkg/cataloger/kernel/parse_linux_kernel_module_file.go +++ b/syft/pkg/cataloger/kernel/parse_linux_kernel_module_file.go @@ -32,7 +32,7 @@ func parseLinuxKernelModuleFile(_ source.FileResolver, _ *generic.Environment, r return []pkg.Package{ newLinuxKernelModulePackage( *metadata, - reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), + reader.Location, ), }, nil, nil } diff --git a/syft/pkg/cataloger/php/package.go b/syft/pkg/cataloger/php/package.go index 19838bedd0d..de1327c33f9 100644 --- a/syft/pkg/cataloger/php/package.go +++ b/syft/pkg/cataloger/php/package.go @@ -8,22 +8,12 @@ import ( "github.com/anchore/syft/syft/source" ) -func newComposerLockPackage(m parsedData, location ...source.Location) pkg.Package { - var licenseLocation source.Location - if len(location) > 0 { - // we want to use the composer lock location as the source for the license declaration - licenseLocation = location[0] - } - - licenses := make([]pkg.License, 0) - for _, l := range m.License { - licenses = append(licenses, pkg.NewLicense(l, "", licenseLocation)) - } +func newComposerLockPackage(m parsedData, indexLocation source.Location) pkg.Package { p := pkg.Package{ Name: m.Name, Version: m.Version, - Locations: source.NewLocationSet(location...), - Licenses: licenses, + Locations: source.NewLocationSet(indexLocation), + Licenses: pkg.NewLicensesFromLocation(indexLocation, m.License...), PURL: packageURL(m), Language: pkg.PHP, Type: pkg.PhpComposerPkg, diff --git a/syft/pkg/cataloger/php/parse_composer_lock_test.go b/syft/pkg/cataloger/php/parse_composer_lock_test.go index 5c91e488e7a..0debbd16194 100644 --- a/syft/pkg/cataloger/php/parse_composer_lock_test.go +++ b/syft/pkg/cataloger/php/parse_composer_lock_test.go @@ -4,7 +4,6 @@ import ( "testing" "github.com/anchore/syft/syft/artifact" - "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" "github.com/anchore/syft/syft/source" @@ -21,12 +20,7 @@ func TestParseComposerFileLock(t *testing.T) { PURL: "pkg:composer/adoy/fastcgi-client@1.0.2", Locations: locations, Licenses: []pkg.License{ - { - Value: "MIT", - SPDXExpression: "MIT", - Type: license.Declared, - Location: source.NewLocation(fixture), - }, + pkg.NewLicenseFromLocation("MIT", source.NewLocation(fixture)), }, Language: pkg.PHP, Type: pkg.PhpComposerPkg, @@ -67,12 +61,7 @@ func TestParseComposerFileLock(t *testing.T) { PURL: "pkg:composer/alcaeus/mongo-php-adapter@1.1.11", Language: pkg.PHP, Licenses: []pkg.License{ - { - Value: "MIT", - SPDXExpression: "MIT", - Type: license.Declared, - Location: source.NewLocation(fixture), - }, + pkg.NewLicenseFromLocation("MIT", source.NewLocation(fixture)), }, Type: pkg.PhpComposerPkg, MetadataType: pkg.PhpComposerJSONMetadataType, diff --git a/syft/pkg/cataloger/php/parse_installed_json_test.go b/syft/pkg/cataloger/php/parse_installed_json_test.go index 384b7e6f022..c0866cf7b14 100644 --- a/syft/pkg/cataloger/php/parse_installed_json_test.go +++ b/syft/pkg/cataloger/php/parse_installed_json_test.go @@ -143,7 +143,8 @@ func TestParseInstalledJsonComposerV1(t *testing.T) { for i := range expectedPkgs { expectedPkgs[i].Locations = locations for k := range expectedPkgs[i].Licenses { - expectedPkgs[i].Licenses[k].Location = source.NewLocation(fixture) + loc := source.NewLocation(fixture) + expectedPkgs[i].Licenses[k].Location = &loc } } pkgtest.TestFileParser(t, fixture, parseInstalledJSON, expectedPkgs, expectedRelationships) diff --git a/syft/pkg/cataloger/portage/cataloger_test.go b/syft/pkg/cataloger/portage/cataloger_test.go index 118310558df..a9ddae7b66f 100644 --- a/syft/pkg/cataloger/portage/cataloger_test.go +++ b/syft/pkg/cataloger/portage/cataloger_test.go @@ -5,7 +5,6 @@ import ( "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/file" - "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" "github.com/anchore/syft/syft/source" @@ -24,42 +23,7 @@ func TestPortageCataloger(t *testing.T) { source.NewLocation("var/db/pkg/app-containers/skopeo-1.5.1/SIZE"), expectedLicenseLocation, ), - Licenses: []pkg.License{ - { - Value: "Apache-2.0", - SPDXExpression: "Apache-2.0", - Type: license.Declared, - Location: expectedLicenseLocation, - }, - { - Value: "BSD", - Type: license.Declared, - Location: expectedLicenseLocation, - }, - { - Value: "BSD-2", - Type: license.Declared, - Location: expectedLicenseLocation, - }, - { - Value: "CC-BY-SA-4.0", - SPDXExpression: "CC-BY-SA-4.0", - Type: license.Declared, - Location: expectedLicenseLocation, - }, - { - Value: "ISC", - SPDXExpression: "ISC", - Type: license.Declared, - Location: expectedLicenseLocation, - }, - { - Value: "MIT", - SPDXExpression: "MIT", - Type: license.Declared, - Location: expectedLicenseLocation, - }, - }, + Licenses: pkg.NewLicensesFromLocation(expectedLicenseLocation, "Apache-2.0", "BSD", "BSD-2", "CC-BY-SA-4.0", "ISC", "MIT"), Type: pkg.PortagePkg, MetadataType: pkg.PortageMetadataType, Metadata: pkg.PortageMetadata{ diff --git a/syft/pkg/cataloger/portage/parse_portage_contents.go b/syft/pkg/cataloger/portage/parse_portage_contents.go index 9e94be6ae0a..52a68b49cd6 100644 --- a/syft/pkg/cataloger/portage/parse_portage_contents.go +++ b/syft/pkg/cataloger/portage/parse_portage_contents.go @@ -116,13 +116,9 @@ func addLicenses(resolver source.FileResolver, dbLocation source.Location, p *pk findings.Add(token) } } - licenseCandidates := findings.ToSlice() - licenses := make([]pkg.License, 0) - for _, licenseCandidate := range licenseCandidates { - licenses = append(licenses, pkg.NewLicense(licenseCandidate, "", *location)) - } - p.Licenses = licenses + licenseCandidates := findings.ToSlice() + p.Licenses = pkg.NewLicensesFromLocation(*location, licenseCandidates...) p.Locations.Add(location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.SupportingEvidenceAnnotation)) } diff --git a/syft/pkg/cataloger/python/cataloger_test.go b/syft/pkg/cataloger/python/cataloger_test.go index 6c9410cfb08..56bbe690a27 100644 --- a/syft/pkg/cataloger/python/cataloger_test.go +++ b/syft/pkg/cataloger/python/cataloger_test.go @@ -5,7 +5,6 @@ import ( "github.com/stretchr/testify/require" - "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" "github.com/anchore/syft/syft/source" @@ -25,7 +24,6 @@ func Test_PackageCataloger(t *testing.T) { PURL: "pkg:pypi/no-version", Type: pkg.PythonPkg, Language: pkg.Python, - Licenses: []pkg.License{}, FoundBy: "python-package-cataloger", MetadataType: pkg.PythonPackageMetadataType, Metadata: pkg.PythonPackageMetadata{ @@ -48,13 +46,8 @@ func Test_PackageCataloger(t *testing.T) { Type: pkg.PythonPkg, Language: pkg.Python, Licenses: []pkg.License{ - { - Value: "Apache 2.0", - Type: license.Declared, - Location: source.NewLocation("test-fixtures/egg-info/PKG-INFO"), - }, + pkg.NewLicenseFromLocation("Apache 2.0", source.NewLocation("test-fixtures/egg-info/PKG-INFO")), }, - //Licenses: []string{"Apache 2.0"}, FoundBy: "python-package-cataloger", MetadataType: pkg.PythonPackageMetadataType, Metadata: pkg.PythonPackageMetadata{ @@ -91,11 +84,7 @@ func Test_PackageCataloger(t *testing.T) { Type: pkg.PythonPkg, Language: pkg.Python, Licenses: []pkg.License{ - { - Value: "BSD License", - Type: license.Declared, - Location: source.NewLocation("test-fixtures/dist-info/METADATA"), - }, + pkg.NewLicenseFromLocation("BSD License", source.NewLocation("test-fixtures/dist-info/METADATA")), }, FoundBy: "python-package-cataloger", MetadataType: pkg.PythonPackageMetadataType, @@ -133,11 +122,7 @@ func Test_PackageCataloger(t *testing.T) { Type: pkg.PythonPkg, Language: pkg.Python, Licenses: []pkg.License{ - { - Value: "BSD License", - Type: license.Declared, - Location: source.NewLocation("test-fixtures/malformed-record/dist-info/METADATA"), - }, + pkg.NewLicenseFromLocation("BSD License", source.NewLocation("test-fixtures/malformed-record/dist-info/METADATA")), }, FoundBy: "python-package-cataloger", MetadataType: pkg.PythonPackageMetadataType, @@ -169,11 +154,7 @@ func Test_PackageCataloger(t *testing.T) { Type: pkg.PythonPkg, Language: pkg.Python, Licenses: []pkg.License{ - { - Value: "BSD License", - Type: license.Declared, - Location: source.NewLocation("test-fixtures/partial.dist-info/METADATA"), - }, + pkg.NewLicenseFromLocation("BSD License", source.NewLocation("test-fixtures/partial.dist-info/METADATA")), }, FoundBy: "python-package-cataloger", MetadataType: pkg.PythonPackageMetadataType, @@ -197,11 +178,7 @@ func Test_PackageCataloger(t *testing.T) { Type: pkg.PythonPkg, Language: pkg.Python, Licenses: []pkg.License{ - { - Value: "Apache 2.0", - Type: license.Declared, - Location: source.NewLocation("test-fixtures/test.egg-info"), - }, + pkg.NewLicenseFromLocation("Apache 2.0", source.NewLocation("test-fixtures/test.egg-info")), }, FoundBy: "python-package-cataloger", MetadataType: pkg.PythonPackageMetadataType, diff --git a/syft/pkg/cataloger/python/package.go b/syft/pkg/cataloger/python/package.go index 358c64591ba..51c87efe631 100644 --- a/syft/pkg/cataloger/python/package.go +++ b/syft/pkg/cataloger/python/package.go @@ -41,23 +41,20 @@ func newPackageForIndexWithMetadata(name, version string, metadata pkg.PythonPip } func newPackageForPackage(m parsedData, sources ...source.Location) pkg.Package { - licenses := make([]pkg.License, 0) - if m.Licenses != "" { - licenses = append(licenses, pkg.NewLicense(m.Licenses, "", m.LicenseLocation)) - } p := pkg.Package{ Name: m.Name, Version: m.Version, PURL: packageURL(m.Name, m.Version, &m.PythonPackageMetadata), Locations: source.NewLocationSet(sources...), + Licenses: pkg.NewLicensesFromLocation(m.LicenseLocation, m.Licenses), Language: pkg.Python, - Licenses: licenses, Type: pkg.PythonPkg, MetadataType: pkg.PythonPackageMetadataType, Metadata: m.PythonPackageMetadata, } p.SetID() + return p } diff --git a/syft/pkg/cataloger/python/parse_wheel_egg.go b/syft/pkg/cataloger/python/parse_wheel_egg.go index 60756bf7601..911e7801ca5 100644 --- a/syft/pkg/cataloger/python/parse_wheel_egg.go +++ b/syft/pkg/cataloger/python/parse_wheel_egg.go @@ -17,21 +17,21 @@ import ( // parseWheelOrEgg takes the primary metadata file reference and returns the python package it represents. func parseWheelOrEgg(resolver source.FileResolver, _ *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { - parsedData, sources, err := assembleEggOrWheelMetadata(resolver, reader.Location) + pd, sources, err := assembleEggOrWheelMetadata(resolver, reader.Location) if err != nil { return nil, nil, err } - if parsedData == nil { + if pd == nil { return nil, nil, nil } // This can happen for Python 2.7 where it is reported from an egg-info, but Python is // the actual runtime, it isn't a "package". The special-casing here allows to skip it - if parsedData.Name == "Python" { + if pd.Name == "Python" { return nil, nil, nil } - pkgs := []pkg.Package{newPackageForPackage(*parsedData, sources...)} + pkgs := []pkg.Package{newPackageForPackage(*pd, sources...)} return pkgs, nil, nil } @@ -171,12 +171,12 @@ func assembleEggOrWheelMetadata(resolver source.FileResolver, metadataLocation s } defer internal.CloseAndLogError(metadataContents, metadataLocation.VirtualPath) - parsedData, err := parseWheelOrEggMetadata(metadataLocation.RealPath, metadataContents) + pd, err := parseWheelOrEggMetadata(metadataLocation.RealPath, metadataContents) if err != nil { return nil, nil, err } - if parsedData.Name == "" { + if pd.Name == "" { return nil, nil, nil } @@ -186,14 +186,14 @@ func assembleEggOrWheelMetadata(resolver source.FileResolver, metadataLocation s return nil, nil, err } if len(r) == 0 { - r, s, err = fetchInstalledFiles(resolver, metadataLocation, parsedData.SitePackagesRootPath) + r, s, err = fetchInstalledFiles(resolver, metadataLocation, pd.SitePackagesRootPath) if err != nil { return nil, nil, err } } sources = append(sources, s...) - parsedData.Files = r + pd.Files = r // attach any top-level package names found for the given wheel/egg installation p, s, err := fetchTopLevelPackages(resolver, metadataLocation) @@ -201,7 +201,7 @@ func assembleEggOrWheelMetadata(resolver source.FileResolver, metadataLocation s return nil, nil, err } sources = append(sources, s...) - parsedData.TopLevelPackages = p + pd.TopLevelPackages = p // attach any direct-url package data found for the given wheel/egg installation d, s, err := fetchDirectURLData(resolver, metadataLocation) @@ -210,6 +210,6 @@ func assembleEggOrWheelMetadata(resolver source.FileResolver, metadataLocation s } sources = append(sources, s...) - parsedData.DirectURLOrigin = d - return &parsedData, sources, nil + pd.DirectURLOrigin = d + return &pd, sources, nil } diff --git a/syft/pkg/cataloger/python/parse_wheel_egg_metadata.go b/syft/pkg/cataloger/python/parse_wheel_egg_metadata.go index 2caf1c51f2f..55ac924f002 100644 --- a/syft/pkg/cataloger/python/parse_wheel_egg_metadata.go +++ b/syft/pkg/cataloger/python/parse_wheel_egg_metadata.go @@ -72,19 +72,19 @@ func parseWheelOrEggMetadata(path string, reader io.Reader) (parsedData, error) return parsedData{}, fmt.Errorf("failed to parse python wheel/egg: %w", err) } - var parsedData parsedData - if err := mapstructure.Decode(fields, &parsedData); err != nil { - return parsedData, fmt.Errorf("unable to parse APK metadata: %w", err) + var pd parsedData + if err := mapstructure.Decode(fields, &pd); err != nil { + return pd, fmt.Errorf("unable to parse APK metadata: %w", err) } // add additional metadata not stored in the egg/wheel metadata file - parsedData.SitePackagesRootPath = determineSitePackagesRootPath(path) - if parsedData.Licenses != "" { - parsedData.LicenseLocation = source.NewLocation(path) + pd.SitePackagesRootPath = determineSitePackagesRootPath(path) + if pd.Licenses != "" { + pd.LicenseLocation = source.NewLocation(path) } - return parsedData, nil + return pd, nil } // isEggRegularFile determines if the specified path is the regular file variant diff --git a/syft/pkg/cataloger/rpm/package.go b/syft/pkg/cataloger/rpm/package.go index 29ed77806c2..e5974922875 100644 --- a/syft/pkg/cataloger/rpm/package.go +++ b/syft/pkg/cataloger/rpm/package.go @@ -13,13 +13,13 @@ import ( "github.com/anchore/syft/syft/source" ) -func newPackage(location source.Location, pd parsedData, distro *linux.Release) pkg.Package { +func newPackage(dbOrRpmLocation source.Location, pd parsedData, distro *linux.Release) pkg.Package { p := pkg.Package{ Name: pd.Name, Version: toELVersion(pd.RpmMetadata), Licenses: pd.Licenses, PURL: packageURL(pd.RpmMetadata, distro), - Locations: source.NewLocationSet(location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)), + Locations: source.NewLocationSet(dbOrRpmLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)), Type: pkg.RpmPkg, MetadataType: pkg.RpmMetadataType, Metadata: pd.RpmMetadata, @@ -34,11 +34,9 @@ type parsedData struct { pkg.RpmMetadata } -func newParsedDatafromEntry(licenseLocation source.Location, entry rpmdb.PackageInfo, files []pkg.RpmdbFileRecord) parsedData { - // TODO: use entry to populate the pkg.RpmMetadata struct in package constructor - license := pkg.NewLicense(entry.License, "", licenseLocation) +func newParsedDataFromEntry(licenseLocation source.Location, entry rpmdb.PackageInfo, files []pkg.RpmdbFileRecord) parsedData { return parsedData{ - Licenses: []pkg.License{license}, + Licenses: pkg.NewLicensesFromLocation(licenseLocation, entry.License), RpmMetadata: pkg.RpmMetadata{ Name: entry.Name, Version: entry.Version, diff --git a/syft/pkg/cataloger/rpm/parse_rpm.go b/syft/pkg/cataloger/rpm/parse_rpm.go index 6d10d5118c9..6e866c5cabb 100644 --- a/syft/pkg/cataloger/rpm/parse_rpm.go +++ b/syft/pkg/cataloger/rpm/parse_rpm.go @@ -26,21 +26,16 @@ func parseRpm(_ source.FileResolver, _ *generic.Environment, reader source.Locat return nil, nil, err } - cl, _ := rpm.Header.GetStrings(rpmutils.LICENSE) + licenses, _ := rpm.Header.GetStrings(rpmutils.LICENSE) sourceRpm, _ := rpm.Header.GetString(rpmutils.SOURCERPM) vendor, _ := rpm.Header.GetString(rpmutils.VENDOR) digestAlgorithm := getDigestAlgorithm(rpm.Header) size, _ := rpm.Header.InstalledSize() files, _ := rpm.Header.GetFiles() - licenses := []pkg.License{} - for _, l := range cl { - licenses = append(licenses, pkg.NewLicense(l, "", reader.Location)) - } - pd := parsedData{ - licenses, - pkg.RpmMetadata{ + Licenses: pkg.NewLicensesFromLocation(reader.Location, licenses...), + RpmMetadata: pkg.RpmMetadata{ Name: nevra.Name, Version: nevra.Version, Epoch: parseEpoch(nevra.Epoch), diff --git a/syft/pkg/cataloger/rpm/parse_rpm_db.go b/syft/pkg/cataloger/rpm/parse_rpm_db.go index 503f2caf72f..ee4d64b4f02 100644 --- a/syft/pkg/cataloger/rpm/parse_rpm_db.go +++ b/syft/pkg/cataloger/rpm/parse_rpm_db.go @@ -60,7 +60,7 @@ func parseRpmDB(resolver source.FileResolver, env *generic.Environment, reader s p := newPackage( reader.Location, - newParsedDatafromEntry(reader.Location, *entry, extractRpmdbFileRecords(resolver, *entry)), + newParsedDataFromEntry(reader.Location, *entry, extractRpmdbFileRecords(resolver, *entry)), distro, ) diff --git a/syft/pkg/cataloger/rpm/parse_rpm_db_test.go b/syft/pkg/cataloger/rpm/parse_rpm_db_test.go index bbe0e4d0c7e..7e114b9b729 100644 --- a/syft/pkg/cataloger/rpm/parse_rpm_db_test.go +++ b/syft/pkg/cataloger/rpm/parse_rpm_db_test.go @@ -8,7 +8,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/anchore/syft/syft/file" - "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" "github.com/anchore/syft/syft/source" @@ -100,13 +99,7 @@ func TestParseRpmDB(t *testing.T) { Type: pkg.RpmPkg, MetadataType: pkg.RpmMetadataType, Licenses: []pkg.License{ - { - Value: "MIT", - SPDXExpression: "MIT", - Type: license.Declared, - URL: "", - Location: packagesLocation, - }, + pkg.NewLicenseFromLocation("MIT", packagesLocation), }, Metadata: pkg.RpmMetadata{ Name: "dive", @@ -135,13 +128,7 @@ func TestParseRpmDB(t *testing.T) { Type: pkg.RpmPkg, MetadataType: pkg.RpmMetadataType, Licenses: []pkg.License{ - { - Value: "MIT", - SPDXExpression: "MIT", - Type: license.Declared, - URL: "", - Location: packagesLocation, - }, + pkg.NewLicenseFromLocation("MIT", packagesLocation), }, Metadata: pkg.RpmMetadata{ Name: "dive", diff --git a/syft/pkg/cataloger/rpm/parse_rpm_test.go b/syft/pkg/cataloger/rpm/parse_rpm_test.go index 90a395d2093..f0c35c92cae 100644 --- a/syft/pkg/cataloger/rpm/parse_rpm_test.go +++ b/syft/pkg/cataloger/rpm/parse_rpm_test.go @@ -4,7 +4,6 @@ import ( "testing" "github.com/anchore/syft/syft/file" - "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" "github.com/anchore/syft/syft/source" @@ -29,13 +28,7 @@ func TestParseRpmFiles(t *testing.T) { Type: pkg.RpmPkg, MetadataType: pkg.RpmMetadataType, Licenses: []pkg.License{ - { - Value: "MIT", - SPDXExpression: "MIT", - Type: license.Declared, - URL: "", - Location: abcRpmLocation, - }, + pkg.NewLicenseFromLocation("MIT", abcRpmLocation), }, Metadata: pkg.RpmMetadata{ Name: "abc", @@ -64,13 +57,7 @@ func TestParseRpmFiles(t *testing.T) { Type: pkg.RpmPkg, MetadataType: pkg.RpmMetadataType, Licenses: []pkg.License{ - { - Value: "Public Domain", - SPDXExpression: "", - Type: license.Declared, - URL: "", - Location: zorkRpmLocation, - }, + pkg.NewLicenseFromLocation("Public Domain", zorkRpmLocation), }, Metadata: pkg.RpmMetadata{ Name: "zork", diff --git a/syft/pkg/cataloger/ruby/package.go b/syft/pkg/cataloger/ruby/package.go index 7088ad9586b..98b5b231ef6 100644 --- a/syft/pkg/cataloger/ruby/package.go +++ b/syft/pkg/cataloger/ruby/package.go @@ -21,16 +21,12 @@ func newGemfileLockPackage(name, version string, locations ...source.Location) p return p } -func newGemspecPackage(m gemData, locations ...source.Location) pkg.Package { - licenses := make([]pkg.License, 0) - for _, l := range m.Licenses { - licenses = append(licenses, pkg.NewLicense(l, "", locations[0])) - } +func newGemspecPackage(m gemData, gemSpecLocation source.Location) pkg.Package { p := pkg.Package{ Name: m.Name, Version: m.Version, - Locations: source.NewLocationSet(locations...), - Licenses: licenses, + Locations: source.NewLocationSet(gemSpecLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)), + Licenses: pkg.NewLicensesFromLocation(gemSpecLocation, m.Licenses...), PURL: packageURL(m.Name, m.Version), Language: pkg.Ruby, Type: pkg.GemPkg, diff --git a/syft/pkg/cataloger/ruby/parse_gemspec.go b/syft/pkg/cataloger/ruby/parse_gemspec.go index 74ee3489518..347caabbd01 100644 --- a/syft/pkg/cataloger/ruby/parse_gemspec.go +++ b/syft/pkg/cataloger/ruby/parse_gemspec.go @@ -102,7 +102,10 @@ func parseGemSpecEntries(_ source.FileResolver, _ *generic.Environment, reader s pkgs = append( pkgs, - newGemspecPackage(metadata, reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)), + newGemspecPackage( + metadata, + reader.Location, + ), ) } diff --git a/syft/pkg/cataloger/ruby/parse_gemspec_test.go b/syft/pkg/cataloger/ruby/parse_gemspec_test.go index ac86b6b6739..d18277c6ad4 100644 --- a/syft/pkg/cataloger/ruby/parse_gemspec_test.go +++ b/syft/pkg/cataloger/ruby/parse_gemspec_test.go @@ -3,7 +3,6 @@ package ruby import ( "testing" - "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" "github.com/anchore/syft/syft/source" @@ -21,12 +20,7 @@ func TestParseGemspec(t *testing.T) { Locations: locations, Type: pkg.GemPkg, Licenses: []pkg.License{ - { - Value: "MIT", - SPDXExpression: "MIT", - Location: source.NewLocation(fixture), - Type: license.Declared, - }, + pkg.NewLicenseFromLocation("MIT", source.NewLocation(fixture)), }, Language: pkg.Ruby, MetadataType: pkg.GemMetadataType, diff --git a/syft/pkg/license.go b/syft/pkg/license.go index d99e5503a4f..8a71e4c0c92 100644 --- a/syft/pkg/license.go +++ b/syft/pkg/license.go @@ -14,7 +14,7 @@ type License struct { Location *source.Location `json:"location,omitempty"` // on disk declaration } -func NewLicense(value string, url string, location *source.Location) License { +func NewLicense(value string) License { spdxExpression, err := license.ParseExpression(value) if err != nil { log.Trace("unable to parse license expression: %w", err) @@ -25,8 +25,38 @@ func NewLicense(value string, url string, location *source.Location) License { return License{ Value: value, SPDXExpression: spdxExpression, - URL: url, - Location: location, Type: license.Declared, } } + +func NewLicensesFromLocation(location source.Location, values ...string) (licenses []License) { + for _, v := range values { + if v == "" { + continue + } + licenses = append(licenses, NewLicenseFromLocation(v, location)) + } + return +} + +func NewLicenseFromLocation(value string, location source.Location) License { + l := NewLicense(value) + l.Location = &location + return l +} + +func NewLicensesFromURL(url string, values ...string) (licenses []License) { + for _, v := range values { + if v == "" { + continue + } + licenses = append(licenses, NewLicenseFromURL(v, url)) + } + return +} + +func NewLicenseFromURL(value string, url string) License { + l := NewLicense(value) + l.URL = url + return l +} diff --git a/syft/source/location.go b/syft/source/location.go index 70e140a1047..3abadd3fb1f 100644 --- a/syft/source/location.go +++ b/syft/source/location.go @@ -51,6 +51,12 @@ func (l Location) WithAnnotation(key, value string) Location { return l } +func (l Location) WithoutAnnotations() Location { + l.LocationMetadata.Annotations = map[string]string{} + + return l +} + // NewLocation creates a new Location representing a path without denoting a filesystem or FileCatalog reference. func NewLocation(realPath string) Location { return Location{ diff --git a/syft/source/unindexed_directory_resolver.go b/syft/source/unindexed_directory_resolver.go index e0b74d2edd2..e965fef5c34 100644 --- a/syft/source/unindexed_directory_resolver.go +++ b/syft/source/unindexed_directory_resolver.go @@ -231,13 +231,7 @@ func (u UnindexedDirectoryResolver) AllLocations() <-chan Location { return nil } p = strings.TrimPrefix(p, "/") - out <- Location{ - LocationData: LocationData{ - Coordinates: Coordinates{ - RealPath: p, - }, - }, - } + out <- NewLocation(p) return nil }) if err != nil { @@ -283,14 +277,8 @@ func (u UnindexedDirectoryResolver) newLocation(filePath string, resolveLinks bo } } - return &Location{ - LocationData: LocationData{ - Coordinates: Coordinates{ - RealPath: realPath, - }, - VirtualPath: virtualPath, - }, - } + l := NewVirtualLocation(realPath, virtualPath) + return &l } //nolint:gocognit diff --git a/test/integration/encode_decode_cycle_test.go b/test/integration/encode_decode_cycle_test.go index 54304b8ac46..67940212bb1 100644 --- a/test/integration/encode_decode_cycle_test.go +++ b/test/integration/encode_decode_cycle_test.go @@ -72,14 +72,14 @@ func TestEncodeDecodeEncodeCycleComparison(t *testing.T) { require.NotNil(t, format) by1, err := formats.Encode(originalSBOM, format) - assert.NoError(t, err) + require.NoError(t, err) newSBOM, newFormat, err := formats.Decode(bytes.NewReader(by1)) - assert.NoError(t, err) - assert.Equal(t, format.ID(), newFormat.ID()) + require.NoError(t, err) + require.Equal(t, format.ID(), newFormat.ID()) by2, err := formats.Encode(*newSBOM, format) - assert.NoError(t, err) + require.NoError(t, err) if test.redactor != nil { by1 = test.redactor(by1) From 4dab7166546d2e0cb617f7dedf19bb425e1cce01 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Wed, 26 Apr 2023 10:32:10 -0400 Subject: [PATCH 43/99] chore: update fixtures with new merged code Signed-off-by: Christopher Phillips --- .../common/cyclonedxhelpers/licenses.go | 3 --- .../common/cyclonedxhelpers/licenses_test.go | 3 --- .../TestCycloneDxDirectoryEncoder.golden | 11 +++++------ .../snapshot/TestCycloneDxImageEncoder.golden | 17 ++++++++--------- .../stereoscope-fixture-image-simple.golden | Bin 15360 -> 15360 bytes .../TestSPDXJSONDirectoryEncoder.golden | 8 ++++++-- .../snapshot/TestSPDXJSONImageEncoder.golden | 8 ++++++-- .../snapshot/TestSPDXRelationshipOrder.golden | 8 ++++++-- .../snapshot/TestSPDXJSONSPDXIDs.golden | 10 ++++++++-- .../TestSPDXTagValueDirectoryEncoder.golden | 8 ++++++-- .../TestSPDXTagValueImageEncoder.golden | 8 ++++++-- .../snapshot/TestDirectoryEncoder.golden | 2 +- .../TestEncodeFullJSONDocument.golden | 2 +- .../snapshot/TestImageEncoder.golden | 2 +- syft/pkg/package_test.go | 7 ++++++- 15 files changed, 60 insertions(+), 37 deletions(-) diff --git a/syft/formats/common/cyclonedxhelpers/licenses.go b/syft/formats/common/cyclonedxhelpers/licenses.go index 1098c91aeb6..33a6f57ceeb 100644 --- a/syft/formats/common/cyclonedxhelpers/licenses.go +++ b/syft/formats/common/cyclonedxhelpers/licenses.go @@ -12,9 +12,6 @@ func encodeLicenses(p pkg.Package) *cyclonedx.Licenses { for _, l := range p.Licenses { if l.SPDXExpression != "" { lc = append(lc, cyclonedx.LicenseChoice{ - License: &cyclonedx.License{ - ID: l.SPDXExpression, - }, Expression: l.SPDXExpression, }) } else { diff --git a/syft/formats/common/cyclonedxhelpers/licenses_test.go b/syft/formats/common/cyclonedxhelpers/licenses_test.go index 737d964384b..e847c1a732c 100644 --- a/syft/formats/common/cyclonedxhelpers/licenses_test.go +++ b/syft/formats/common/cyclonedxhelpers/licenses_test.go @@ -40,7 +40,6 @@ func Test_encodeLicense(t *testing.T) { }, expected: &cyclonedx.Licenses{ { - License: &cyclonedx.License{ID: "MIT"}, Expression: "MIT", }, }, @@ -61,11 +60,9 @@ func Test_encodeLicense(t *testing.T) { }, expected: &cyclonedx.Licenses{ { - License: &cyclonedx.License{ID: "MIT"}, Expression: "MIT", }, { - License: &cyclonedx.License{ID: "GPL-3.0-only"}, Expression: "GPL-3.0-only", }, }, diff --git a/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden b/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden index 7e7a3cdfe14..e5e98ca9dce 100644 --- a/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden +++ b/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden @@ -1,10 +1,11 @@ { + "$schema": "http://cyclonedx.org/schema/bom-1.4.schema.json", "bomFormat": "CycloneDX", "specVersion": "1.4", - "serialNumber": "urn:uuid:0b628da1-274e-4c24-821c-f9452f37db54", + "serialNumber": "urn:uuid:28fd9334-bfc4-403f-9335-9bcaa4b34e8b", "version": 1, "metadata": { - "timestamp": "2022-12-22T18:33:51-05:00", + "timestamp": "2023-04-26T10:30:38-04:00", "tools": [ { "vendor": "anchore", @@ -20,15 +21,13 @@ }, "components": [ { - "bom-ref": "1b1d0be59ac59d2c", + "bom-ref": "ed5945af814b9a95", "type": "library", "name": "package-1", "version": "1.0.1", "licenses": [ { - "license": { - "id": "MIT" - } + "expression": "MIT" } ], "cpe": "cpe:2.3:*:some:package:2:*:*:*:*:*:*:*", diff --git a/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden b/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden index de3018170ed..21f968e6072 100644 --- a/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden +++ b/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden @@ -1,10 +1,11 @@ { + "$schema": "http://cyclonedx.org/schema/bom-1.4.schema.json", "bomFormat": "CycloneDX", "specVersion": "1.4", - "serialNumber": "urn:uuid:542fc1a1-81ac-4b76-b9e2-8e6b9d8c840a", + "serialNumber": "urn:uuid:757286ea-45e3-4134-9ab5-8ac54f90688c", "version": 1, "metadata": { - "timestamp": "2022-12-22T18:33:51-05:00", + "timestamp": "2023-04-26T10:30:29-04:00", "tools": [ { "vendor": "anchore", @@ -13,7 +14,7 @@ } ], "component": { - "bom-ref": "ffd645a093c0fe70", + "bom-ref": "38160ebc2a6876e8", "type": "container", "name": "user-image-input", "version": "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368" @@ -21,15 +22,13 @@ }, "components": [ { - "bom-ref": "66ba429119b8bec6", + "bom-ref": "ed77c5cb0567a1a6", "type": "library", "name": "package-1", "version": "1.0.1", "licenses": [ { - "license": { - "id": "MIT" - } + "expression": "MIT" } ], "cpe": "cpe:2.3:*:some:package:1:*:*:*:*:*:*:*", @@ -53,7 +52,7 @@ }, { "name": "syft:location:0:layerID", - "value": "sha256:62058900d4ce269c900160b8dd255fe310c3a459dda236d041102fa070f84406" + "value": "sha256:ab62016f9bec7286af65604081564cadeeb364a48faca2346c3f5a5a1f5ef777" }, { "name": "syft:location:0:path", @@ -83,7 +82,7 @@ }, { "name": "syft:location:0:layerID", - "value": "sha256:623ad97366f39ae279f1925673cdacb4851ddf2e3266f04e63010ec080a098c1" + "value": "sha256:f1803845b6747d94d6e4ecce2331457e5f1c4fb97de5216f392a76f4582f63b2" }, { "name": "syft:location:0:path", diff --git a/syft/formats/cyclonedxjson/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden b/syft/formats/cyclonedxjson/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden index 11a1958c8935029370bb6a48ad18899403776949..f4aa1e7bb74e9d8c2cf36a69cd7a6d910a9a7087 100644 GIT binary patch literal 15360 zcmeHOTW{Mo6wdR0g{Qs7Ht&=S>>+Ccv_P>8ZPoz`f`H;B(PB%6Bo_$`|Mwlqj$^w^ zv_!U(q7)36*F*9p$TkutO) zGMZRoBVzjp%@0BhNzM<@{Mr1kQV%d=1aU4!RSrUg9&~uK==LQhMX7S=Qd>!zgKH~q zPxWzI{eL<6^!dZ-?@x6XVT1%@n|i;G19Egj|CkGW&}prcr#f|?_oM5-w-=P}(f0pu z41w16|4)&n4P$Dsf6U3|`i~hV-u@e4-AeW}NK7yY+D?PuFc_=crX|SYM54%0$w{n2 zA$c5QCv6l*1`9{9V-`8eonl!GO+a+Yy<@!;(r!4pXVHvrP9}* zOOvF*z^P$j4})b|Hi5xxCfXdTE)vhmyJh%vY}0i)b?h|N(1}I)lk{saNGHSLVeoDQ zW19y*jMUYlqhGS&WMqr`oWHXCCP}Z3lSOY&(lWmrXGsO}yzXiG+awt-MSWDU9WJWR zYGmF%9_PUyM`!PU?N5rlKg_fm_KP@42P^Sq^dX~8nnxiRT)YI(KFo~gBC2wIos?FW zle|)y8X3VrOa_<-!z7*D2D7agd{D0oFwgZ>em_ZItqO5x~tfH0O`|0@9QFgVwJr+SOR+q?LAhfR6W z9s!SlN5CWSh7f3bSlf~RmlEmyzx$Xbt@Xbf|1TBX@BjCqf9(Cg`+)h)w$a-D+xvf! zAb9ip55chh@8!$H5B~jU;s0$r4Jt?M(r1e!iMj~G?+l?q{qvsMr#!W<=s;|%_#OHG zF!SGk_VUhH`v352`v1f?{yzr%55NBJehQS=NztjCfSqeqEZu;+Z`Zfr5%36j1Uv$- zi-4k9V52c*hB%62hPmd55@sb288OmDSP;mHR2VT4T8<+cQi@{g3{|ez{``jt+dTgv z#+iTr?_*|6gGWdW4{Vy6x3VRk#u}%kqF}61SSr%W@)X&~{DGZIndQL2IiEIFLsDSM zIj@hQ^A;{+s2Lm`AJ@B|ZoU2O^w+n;onJa}MX_G{j?ClF>~2I(3k`lv+s^j zvk#-)VjClsCe9Y+|5?Xh+H4d5gLAsV1xGdi!}C91iuUvB!P@z5!J7=WvKIjB|76G6 znYx;kQrQ`x@|-v75Nb*#yq21SE~R=bAQM!E%t`E2$!Eoco+AMpry L1Uv%Y4FdlHH$Ie? literal 15360 zcmeHOZExE)5ccQ&3Qzl**nFpCU>~}sKnoO0(Pka6qA2)sOtjjPA<0F9Apdm%%?PJlu^wTR3f}9JyT$INc2+m0LY2Bo z+kCjL@($GhDlwJ2$Qy9Ytb$~en2A>+IZ9kvg#0`iT_ul-cgg5g^6bIcA**Z}Z~(B(HF&Ar5m|{4vYNJ{c9W@%S2tt-v&Z z<%AK&j!Ak<=oup;l8qP}NRkOgHD&)`*NePv%IXqzyV}xCq&m%{q*>c-Il>!@J(C9~ zC;gC*;g}AZ_f3CTmlIei|JJvyc5m}$Zjs3ZiUxaqxH<5pdka}WC)5Q(m;?gGWhr0tP6t%jdrI6?%$4P|rP|4?pM&h_=5YAJNDFF!+e z=O#b-`rq0AFCRUB@#OT+yZo?a|3~-pb^kM>h>ZLHT{hiS9UXLze0HTul=BrV(0Tsv zApVzZWBq4Da24nOAzp{8ByWT5uCD>cd{a{`qhswgO%cpt?3XzyKzi#9rAlcoiC`%g zUKpNQNsP1tFy!MF(O;Tr$E;^4>mDcoaY|$I0;)H+h~;rvH-(wBfi4O=DJR(Aw)|HO zE&9Nk2Tfz##f!!xI2i>qu3_a~Im1wY^Cnc-b$RowK?Je9KAl3bE@xgkEgUMbY%|AE zEzCJe9!{2s=O}qHG3QG~UzX$91TYj|G+M(-sO_VosV=8w-a?Q?i#70clsq*S#>?(4 zl)6E1->^M)ge&iF&=!2&*P@?*$}3RoyMrrTC;o7bBnK^r^2y(p_)mNk|Fby_6#1V| zT6A|6H-yKWXV{(GRY_a?k3>Mb{}ZMtG5}Qf2&I}M{yRuy`jg|oIoZYf?JkPDcm3 z?))!NY%~7@`QP~c=OFLQlK<`c8X%c=%h}RQ6HMD^GdBokZnS^%#EZz(ejsi)nA)#M zAa-^9-uOQf`JY2905|;KJOOGyBW~ya)JOaurVB>?Z$Iu>liFYw-8=)Xe_`qy8gQ*i zEJO=L3q%V<3;ci&PqIn?_abld!Ax4Gf-TyUKK3XcKB_mIfGV=884#+q6MM_z9S3#2P@x!egFUf diff --git a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONDirectoryEncoder.golden b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONDirectoryEncoder.golden index 14ad20c9cd4..c3460b3bdfa 100644 --- a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONDirectoryEncoder.golden +++ b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONDirectoryEncoder.golden @@ -3,14 +3,14 @@ "dataLicense": "CC0-1.0", "SPDXID": "SPDXRef-DOCUMENT", "name": "/some/path", - "documentNamespace": "https://anchore.com/syft/dir/some/path-df64dcf1-8330-4b6f-826d-2d6b0f40a1a1", + "documentNamespace": "https://anchore.com/syft/dir/some/path-a74d8ff2-0b3e-4fb0-aed3-ea9ade521d88", "creationInfo": { "licenseListVersion": "3.20", "creators": [ "Organization: Anchore, Inc", "Tool: syft-v0.42.0-bogus" ], - "created": "2023-04-24T14:38:59Z" + "created": "2023-04-26T14:27:17Z" }, "packages": [ { @@ -19,6 +19,8 @@ "versionInfo": "1.0.1", "downloadLocation": "NOASSERTION", "sourceInfo": "acquired package info from installed python package manifest file: /some/path/pkg1", + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "MIT", "copyrightText": "NOASSERTION", "externalRefs": [ { @@ -39,6 +41,8 @@ "versionInfo": "2.0.1", "downloadLocation": "NOASSERTION", "sourceInfo": "acquired package info from DPKG DB: /some/path/pkg1", + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "NOASSERTION", "copyrightText": "NOASSERTION", "externalRefs": [ { diff --git a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONImageEncoder.golden b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONImageEncoder.golden index ed19788c88d..8cf3f97cc38 100644 --- a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONImageEncoder.golden +++ b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONImageEncoder.golden @@ -3,14 +3,14 @@ "dataLicense": "CC0-1.0", "SPDXID": "SPDXRef-DOCUMENT", "name": "user-image-input", - "documentNamespace": "https://anchore.com/syft/image/user-image-input-75fc6304-ce2d-4c7b-bf76-fa7395294fef", + "documentNamespace": "https://anchore.com/syft/image/user-image-input-d9eae25d-b74d-40c0-a015-f6dd20c4446c", "creationInfo": { "licenseListVersion": "3.20", "creators": [ "Organization: Anchore, Inc", "Tool: syft-v0.42.0-bogus" ], - "created": "2023-04-24T14:39:32Z" + "created": "2023-04-26T14:27:17Z" }, "packages": [ { @@ -19,6 +19,8 @@ "versionInfo": "1.0.1", "downloadLocation": "NOASSERTION", "sourceInfo": "acquired package info from installed python package manifest file: /somefile-1.txt", + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "MIT", "copyrightText": "NOASSERTION", "externalRefs": [ { @@ -39,6 +41,8 @@ "versionInfo": "2.0.1", "downloadLocation": "NOASSERTION", "sourceInfo": "acquired package info from DPKG DB: /somefile-2.txt", + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "NOASSERTION", "copyrightText": "NOASSERTION", "externalRefs": [ { diff --git a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden index 1aa7a41d0fd..3bb9ce10752 100644 --- a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden +++ b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden @@ -3,14 +3,14 @@ "dataLicense": "CC0-1.0", "SPDXID": "SPDXRef-DOCUMENT", "name": "user-image-input", - "documentNamespace": "https://anchore.com/syft/image/user-image-input-a1463ff3-8a7e-4ad9-b326-2cfbde38e82f", + "documentNamespace": "https://anchore.com/syft/image/user-image-input-27afc914-f9a1-4d8a-bfa2-3bb67e694d84", "creationInfo": { "licenseListVersion": "3.20", "creators": [ "Organization: Anchore, Inc", "Tool: syft-v0.42.0-bogus" ], - "created": "2023-04-24T14:39:59Z" + "created": "2023-04-26T14:31:55Z" }, "packages": [ { @@ -19,6 +19,8 @@ "versionInfo": "1.0.1", "downloadLocation": "NOASSERTION", "sourceInfo": "acquired package info from installed python package manifest file: /somefile-1.txt", + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "MIT", "copyrightText": "NOASSERTION", "externalRefs": [ { @@ -39,6 +41,8 @@ "versionInfo": "2.0.1", "downloadLocation": "NOASSERTION", "sourceInfo": "acquired package info from DPKG DB: /somefile-2.txt", + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "NOASSERTION", "copyrightText": "NOASSERTION", "externalRefs": [ { diff --git a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXJSONSPDXIDs.golden b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXJSONSPDXIDs.golden index c6933cc54c3..c346fbd0d6c 100644 --- a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXJSONSPDXIDs.golden +++ b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXJSONSPDXIDs.golden @@ -2,11 +2,11 @@ SPDXVersion: SPDX-2.3 DataLicense: CC0-1.0 SPDXID: SPDXRef-DOCUMENT DocumentName: foobar/baz -DocumentNamespace: https://anchore.com/syft/dir/foobar/baz-33139e6b-26d4-415e-97ee-6b1a733e849f +DocumentNamespace: https://anchore.com/syft/dir/foobar/baz-8f6e6182-6963-48cd-bd09-4662663722a5 LicenseListVersion: 3.20 Creator: Organization: Anchore, Inc Creator: Tool: syft-v0.42.0-bogus -Created: 2023-04-24T14:40:55Z +Created: 2023-04-26T14:20:31Z ##### Package: @at-sign @@ -15,6 +15,8 @@ SPDXID: SPDXRef-Package---at-sign-3732f7a5679bdec4 PackageDownloadLocation: NOASSERTION FilesAnalyzed: false PackageSourceInfo: acquired package info from the following paths: +PackageLicenseConcluded: NOASSERTION +PackageLicenseDeclared: NOASSERTION PackageCopyrightText: NOASSERTION ##### Package: some/slashes @@ -24,6 +26,8 @@ SPDXID: SPDXRef-Package--some-slashes-1345166d4801153b PackageDownloadLocation: NOASSERTION FilesAnalyzed: false PackageSourceInfo: acquired package info from the following paths: +PackageLicenseConcluded: NOASSERTION +PackageLicenseDeclared: NOASSERTION PackageCopyrightText: NOASSERTION ##### Package: under_scores @@ -33,6 +37,8 @@ SPDXID: SPDXRef-Package--under-scores-290d5c77210978c1 PackageDownloadLocation: NOASSERTION FilesAnalyzed: false PackageSourceInfo: acquired package info from the following paths: +PackageLicenseConcluded: NOASSERTION +PackageLicenseDeclared: NOASSERTION PackageCopyrightText: NOASSERTION ##### Relationships diff --git a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueDirectoryEncoder.golden b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueDirectoryEncoder.golden index c14c15cf23c..e3cf2169c5e 100644 --- a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueDirectoryEncoder.golden +++ b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueDirectoryEncoder.golden @@ -2,11 +2,11 @@ SPDXVersion: SPDX-2.3 DataLicense: CC0-1.0 SPDXID: SPDXRef-DOCUMENT DocumentName: /some/path -DocumentNamespace: https://anchore.com/syft/dir/some/path-ee9cc47e-9791-41ab-bec4-eeb1d73f6355 +DocumentNamespace: https://anchore.com/syft/dir/some/path-16a6df79-9a0f-4d4b-a2ec-fc449b05086e LicenseListVersion: 3.20 Creator: Organization: Anchore, Inc Creator: Tool: syft-v0.42.0-bogus -Created: 2023-04-24T14:40:45Z +Created: 2023-04-26T14:21:21Z ##### Package: package-2 @@ -16,6 +16,8 @@ PackageVersion: 2.0.1 PackageDownloadLocation: NOASSERTION FilesAnalyzed: false PackageSourceInfo: acquired package info from DPKG DB: /some/path/pkg1 +PackageLicenseConcluded: NOASSERTION +PackageLicenseDeclared: NOASSERTION PackageCopyrightText: NOASSERTION ExternalRef: SECURITY cpe23Type cpe:2.3:*:some:package:2:*:*:*:*:*:*:* ExternalRef: PACKAGE-MANAGER purl pkg:deb/debian/package-2@2.0.1 @@ -28,6 +30,8 @@ PackageVersion: 1.0.1 PackageDownloadLocation: NOASSERTION FilesAnalyzed: false PackageSourceInfo: acquired package info from installed python package manifest file: /some/path/pkg1 +PackageLicenseConcluded: NOASSERTION +PackageLicenseDeclared: MIT PackageCopyrightText: NOASSERTION ExternalRef: SECURITY cpe23Type cpe:2.3:*:some:package:2:*:*:*:*:*:*:* ExternalRef: PACKAGE-MANAGER purl a-purl-2 diff --git a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueImageEncoder.golden b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueImageEncoder.golden index acc27ddfdd4..ff215249b47 100644 --- a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueImageEncoder.golden +++ b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueImageEncoder.golden @@ -2,11 +2,11 @@ SPDXVersion: SPDX-2.3 DataLicense: CC0-1.0 SPDXID: SPDXRef-DOCUMENT DocumentName: user-image-input -DocumentNamespace: https://anchore.com/syft/image/user-image-input-5b84390e-76e7-4849-8da1-7fae75cb9a37 +DocumentNamespace: https://anchore.com/syft/image/user-image-input-09477ff1-559a-4e1d-91b9-75d51d4cb73f LicenseListVersion: 3.20 Creator: Organization: Anchore, Inc Creator: Tool: syft-v0.42.0-bogus -Created: 2023-04-24T14:40:50Z +Created: 2023-04-26T14:21:00Z ##### Package: package-2 @@ -16,6 +16,8 @@ PackageVersion: 2.0.1 PackageDownloadLocation: NOASSERTION FilesAnalyzed: false PackageSourceInfo: acquired package info from DPKG DB: /somefile-2.txt +PackageLicenseConcluded: NOASSERTION +PackageLicenseDeclared: NOASSERTION PackageCopyrightText: NOASSERTION ExternalRef: SECURITY cpe23Type cpe:2.3:*:some:package:2:*:*:*:*:*:*:* ExternalRef: PACKAGE-MANAGER purl pkg:deb/debian/package-2@2.0.1 @@ -28,6 +30,8 @@ PackageVersion: 1.0.1 PackageDownloadLocation: NOASSERTION FilesAnalyzed: false PackageSourceInfo: acquired package info from installed python package manifest file: /somefile-1.txt +PackageLicenseConcluded: NOASSERTION +PackageLicenseDeclared: MIT PackageCopyrightText: NOASSERTION ExternalRef: SECURITY cpe23Type cpe:2.3:*:some:package:1:*:*:*:*:*:*:* ExternalRef: PACKAGE-MANAGER purl a-purl-1 diff --git a/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden b/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden index 7e570a96976..dc1626c4803 100644 --- a/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden +++ b/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden @@ -50,7 +50,7 @@ "path": "/some/path/pkg1" } ], - "licenses": null, + "licenses": [], "language": "", "cpes": [ "cpe:2.3:*:some:package:2:*:*:*:*:*:*:*" diff --git a/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden b/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden index cf0040eb125..a970a8edb00 100644 --- a/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden +++ b/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden @@ -45,7 +45,7 @@ "path": "/b/place/b" } ], - "licenses": null, + "licenses": [], "language": "", "cpes": [ "cpe:2.3:*:some:package:2:*:*:*:*:*:*:*" diff --git a/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden b/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden index 0027f1e3791..a1dfafa1456 100644 --- a/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden +++ b/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden @@ -47,7 +47,7 @@ "layerID": "sha256:f1803845b6747d94d6e4ecce2331457e5f1c4fb97de5216f392a76f4582f63b2" } ], - "licenses": null, + "licenses": [], "language": "", "cpes": [ "cpe:2.3:*:some:package:2:*:*:*:*:*:*:*" diff --git a/syft/pkg/package_test.go b/syft/pkg/package_test.go index 8645f397e03..1523e03d620 100644 --- a/syft/pkg/package_test.go +++ b/syft/pkg/package_test.go @@ -30,7 +30,12 @@ func TestIDUniqueness(t *testing.T) { ), Licenses: []License{ { - SPDXExpression: "MIT AND GPL-2.0-only", + + Value: "cc0-1.0", + Type: license.Declared, + }, + { + SPDXExpression: "MIT", Type: license.Declared, }, }, From 7bc3844772ef7358e26bb6f3cc9fe315fb331756 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Wed, 26 Apr 2023 10:38:21 -0400 Subject: [PATCH 44/99] chore: remove location from required fields Signed-off-by: Christopher Phillips --- schema/json/schema-8.0.0.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/schema/json/schema-8.0.0.json b/schema/json/schema-8.0.0.json index ce6916f1934..dc75a7062ad 100644 --- a/schema/json/schema-8.0.0.json +++ b/schema/json/schema-8.0.0.json @@ -760,8 +760,7 @@ "value", "spdx-expression", "type", - "url", - "location" + "url" ] }, "LinuxKernelMetadata": { From 12561f9c8053899bcfbbdad9a2f04257846f7514 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Wed, 26 Apr 2023 11:43:26 -0400 Subject: [PATCH 45/99] feat: update custom UnmarshalJSON for licenses Signed-off-by: Christopher Phillips --- syft/formats/syftjson/model/package.go | 29 ++++++- syft/formats/syftjson/model/package_test.go | 78 +++++++++++++++++++ syft/pkg/cataloger/sbom/cataloger_test.go | 1 - .../alpine/syft-json/sbom.syft.json | 62 +++++++++++---- syft/pkg/license.go | 12 ++- 5 files changed, 165 insertions(+), 17 deletions(-) diff --git a/syft/formats/syftjson/model/package.go b/syft/formats/syftjson/model/package.go index 00e5328901a..35c7902a909 100644 --- a/syft/formats/syftjson/model/package.go +++ b/syft/formats/syftjson/model/package.go @@ -27,12 +27,16 @@ type PackageBasicData struct { Type pkg.Type `json:"type"` FoundBy string `json:"foundBy"` Locations []source.Location `json:"locations"` - Licenses []pkg.License `json:"licenses"` + Licenses []pkg.License `json:"-"` Language pkg.Language `json:"language"` CPEs []string `json:"cpes"` PURL string `json:"purl"` } +type licenseUnpacker struct { + LicenseJSON json.RawMessage `json:"licenses"` +} + // PackageCustomData contains ambiguous values (type-wise) from pkg.Package. type PackageCustomData struct { MetadataType pkg.MetadataType `json:"metadataType,omitempty"` @@ -57,6 +61,16 @@ func (p *Package) UnmarshalJSON(b []byte) error { } p.PackageBasicData = basic + var licenseUnpacker licenseUnpacker + if err := json.Unmarshal(b, &licenseUnpacker); err != nil { + return err + } + + p.Licenses = make([]pkg.License, 0) + if len(licenseUnpacker.LicenseJSON) > 0 { + p.PackageBasicData.Licenses = parseLicenseJSON(licenseUnpacker.LicenseJSON) + } + var unpacker packageMetadataUnpacker if err := json.Unmarshal(b, &unpacker); err != nil { log.Warnf("failed to unmarshall into packageMetadataUnpacker: %v", err) @@ -72,6 +86,19 @@ func (p *Package) UnmarshalJSON(b []byte) error { return err } +func parseLicenseJSON(licenseJSON []byte) []pkg.License { + var complexLicenses []pkg.License + if err := json.Unmarshal(licenseJSON, &complexLicenses); err != nil { + var simpleLicense []string + if err := json.Unmarshal(licenseJSON, &simpleLicense); err != nil { + log.Warnf("failed to unmarshal package licenses: %v", err) + return nil + } + return pkg.NewLicensesFromValues(simpleLicense...) + } + return complexLicenses +} + func unpackMetadata(p *Package, unpacker packageMetadataUnpacker) error { p.MetadataType = pkg.CleanMetadataType(unpacker.MetadataType) diff --git a/syft/formats/syftjson/model/package_test.go b/syft/formats/syftjson/model/package_test.go index 3c5bdb93b76..380d4d0c073 100644 --- a/syft/formats/syftjson/model/package_test.go +++ b/syft/formats/syftjson/model/package_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/pkg" ) @@ -72,6 +73,83 @@ func TestUnmarshalPackageGolang(t *testing.T) { assert.Empty(t, p.Metadata) }, }, + { + name: "can handle package with []string licenses", + packageData: []byte(`{ + "id": "8b594519bc23da50", + "name": "gopkg.in/square/go-jose.v2", + "version": "v2.6.0", + "type": "go-module", + "foundBy": "go-mod-cataloger", + "locations": [ + { + "path": "/Users/hal/go/bin/syft" + } + ], + "licenses": ["MIT", "Apache-2.0"], + "language": "go", + "cpes": [], + "purl": "pkg:golang/gopkg.in/square/go-jose.v2@v2.6.0" + }`), + assert: func(p *Package) { + assert.Equal(t, []pkg.License{ + { + Value: "MIT", + SPDXExpression: "MIT", + Type: license.Declared, + }, + { + Value: "Apache-2.0", + SPDXExpression: "Apache-2.0", + Type: license.Declared, + }, + }, p.Licenses) + }, + }, + { + name: "can handle package with []pkg.License licenses", + packageData: []byte(`{ + "id": "8b594519bc23da50", + "name": "gopkg.in/square/go-jose.v2", + "version": "v2.6.0", + "type": "go-module", + "foundBy": "go-mod-cataloger", + "locations": [ + { + "path": "/Users/hal/go/bin/syft" + } + ], + "licenses": [ + { + "value": "MIT", + "spdxExpression": "MIT", + "type": "declared" + }, + { + "value": "Apache-2.0", + "spdxExpression": "Apache-2.0", + "type": "declared" + } + ], + "language": "go", + "cpes": [], + "purl": "pkg:golang/gopkg.in/square/go-jose.v2@v2.6.0" + }`), + assert: func(p *Package) { + assert.Equal(t, []pkg.License{ + { + Value: "MIT", + SPDXExpression: "MIT", + Type: license.Declared, + }, + { + Value: "Apache-2.0", + SPDXExpression: "Apache-2.0", + Type: license.Declared, + }, + }, p.Licenses) + }, + }, } for _, test := range tests { diff --git a/syft/pkg/cataloger/sbom/cataloger_test.go b/syft/pkg/cataloger/sbom/cataloger_test.go index 2cec27edbe0..0253ad63c68 100644 --- a/syft/pkg/cataloger/sbom/cataloger_test.go +++ b/syft/pkg/cataloger/sbom/cataloger_test.go @@ -32,7 +32,6 @@ func must(c cpe.CPE, e error) cpe.CPE { } func Test_parseSBOM(t *testing.T) { - // TODO: update fixtures to use new license format expectedPkgs := []pkg.Package{ { Name: "alpine-baselayout", diff --git a/syft/pkg/cataloger/sbom/test-fixtures/alpine/syft-json/sbom.syft.json b/syft/pkg/cataloger/sbom/test-fixtures/alpine/syft-json/sbom.syft.json index d424f9a53f0..c9e83d12045 100644 --- a/syft/pkg/cataloger/sbom/test-fixtures/alpine/syft-json/sbom.syft.json +++ b/syft/pkg/cataloger/sbom/test-fixtures/alpine/syft-json/sbom.syft.json @@ -12,7 +12,9 @@ "layerID": "sha256:e5e13b0c77cbb769548077189c3da2f0a764ceca06af49d8d558e759f5c232bd" } ], - "licenses": [], + "licenses": [ + "GPL-2.0-only" + ], "language": "", "cpes": [ "cpe:2.3:a:alpine-baselayout:alpine-baselayout:3.2.0-r23:*:*:*:*:*:*:*", @@ -393,7 +395,9 @@ "layerID": "sha256:e5e13b0c77cbb769548077189c3da2f0a764ceca06af49d8d558e759f5c232bd" } ], - "licenses": [], + "licenses": [ + "GPL-2.0-only" + ], "language": "", "cpes": [ "cpe:2.3:a:alpine-baselayout-data:alpine-baselayout-data:3.2.0-r23:*:*:*:*:*:*:*", @@ -554,7 +558,9 @@ "layerID": "sha256:e5e13b0c77cbb769548077189c3da2f0a764ceca06af49d8d558e759f5c232bd" } ], - "licenses": [], + "licenses": [ + "MIT" + ], "language": "", "cpes": [ "cpe:2.3:a:alpine-keys:alpine-keys:2.4-r1:*:*:*:*:*:*:*", @@ -989,7 +995,9 @@ "layerID": "sha256:e5e13b0c77cbb769548077189c3da2f0a764ceca06af49d8d558e759f5c232bd" } ], - "licenses": [], + "licenses": [ + "GPL-2.0-only" + ], "language": "", "cpes": [ "cpe:2.3:a:apk-tools:apk-tools:2.12.9-r3:*:*:*:*:*:*:*", @@ -1095,7 +1103,9 @@ "layerID": "sha256:e5e13b0c77cbb769548077189c3da2f0a764ceca06af49d8d558e759f5c232bd" } ], - "licenses": [], + "licenses": [ + "GPL-2.0-only" + ], "language": "", "cpes": [ "cpe:2.3:a:busybox:busybox:1.35.0-r17:*:*:*:*:*:*:*" @@ -1266,7 +1276,11 @@ "layerID": "sha256:e5e13b0c77cbb769548077189c3da2f0a764ceca06af49d8d558e759f5c232bd" } ], - "licenses": [], + "licenses": [ + "MPL-2.0", + "AND", + "MIT" + ], "language": "", "cpes": [ "cpe:2.3:a:ca-certificates-bundle:ca-certificates-bundle:20220614-r0:*:*:*:*:*:*:*", @@ -1341,7 +1355,11 @@ "layerID": "sha256:e5e13b0c77cbb769548077189c3da2f0a764ceca06af49d8d558e759f5c232bd" } ], - "licenses": [], + "licenses": [ + "BSD-2-Clause", + "AND", + "BSD-3-Clause" + ], "language": "", "cpes": [ "cpe:2.3:a:libc-utils:libc-utils:0.7.2-r3:*:*:*:*:*:*:*", @@ -1385,7 +1403,9 @@ "layerID": "sha256:e5e13b0c77cbb769548077189c3da2f0a764ceca06af49d8d558e759f5c232bd" } ], - "licenses": [], + "licenses": [ + "OpenSSL" + ], "language": "", "cpes": [ "cpe:2.3:a:libcrypto1.1:libcrypto1.1:1.1.1s-r0:*:*:*:*:*:*:*" @@ -1562,7 +1582,9 @@ "layerID": "sha256:e5e13b0c77cbb769548077189c3da2f0a764ceca06af49d8d558e759f5c232bd" } ], - "licenses": [], + "licenses": [ + "OpenSSL" + ], "language": "", "cpes": [ "cpe:2.3:a:libssl1.1:libssl1.1:1.1.1s-r0:*:*:*:*:*:*:*" @@ -1634,7 +1656,9 @@ "layerID": "sha256:e5e13b0c77cbb769548077189c3da2f0a764ceca06af49d8d558e759f5c232bd" } ], - "licenses": [], + "licenses": [ + "MIT" + ], "language": "", "cpes": [ "cpe:2.3:a:musl:musl:1.2.3-r1:*:*:*:*:*:*:*" @@ -1697,7 +1721,11 @@ "layerID": "sha256:e5e13b0c77cbb769548077189c3da2f0a764ceca06af49d8d558e759f5c232bd" } ], - "licenses": [], + "licenses": [ + "MIT", + "BSD", + "GPL2+" + ], "language": "", "cpes": [ "cpe:2.3:a:musl-utils:musl-utils:1.2.3-r1:*:*:*:*:*:*:*", @@ -1808,7 +1836,9 @@ "layerID": "sha256:e5e13b0c77cbb769548077189c3da2f0a764ceca06af49d8d558e759f5c232bd" } ], - "licenses": [], + "licenses": [ + "GPL-2.0-only" + ], "language": "", "cpes": [ "cpe:2.3:a:scanelf:scanelf:1.3.4-r0:*:*:*:*:*:*:*" @@ -1866,7 +1896,9 @@ "layerID": "sha256:e5e13b0c77cbb769548077189c3da2f0a764ceca06af49d8d558e759f5c232bd" } ], - "licenses": [], + "licenses": [ + "GPL-2.0-only" + ], "language": "", "cpes": [ "cpe:2.3:a:ssl-client:ssl-client:1.35.0-r17:*:*:*:*:*:*:*", @@ -1931,7 +1963,9 @@ "layerID": "sha256:e5e13b0c77cbb769548077189c3da2f0a764ceca06af49d8d558e759f5c232bd" } ], - "licenses": [], + "licenses": [ + "Zlib" + ], "language": "", "cpes": [ "cpe:2.3:a:zlib:zlib:1.2.12-r3:*:*:*:*:*:*:*" diff --git a/syft/pkg/license.go b/syft/pkg/license.go index 8a71e4c0c92..060df850fb2 100644 --- a/syft/pkg/license.go +++ b/syft/pkg/license.go @@ -8,7 +8,7 @@ import ( type License struct { Value string `json:"value"` - SPDXExpression string `json:"spdx-expression"` + SPDXExpression string `json:"spdxExpression"` Type license.Type `json:"type"` URL string `json:"url"` // external sources Location *source.Location `json:"location,omitempty"` // on disk declaration @@ -29,6 +29,16 @@ func NewLicense(value string) License { } } +func NewLicensesFromValues(values ...string) (licenses []License) { + for _, v := range values { + if v == "" { + continue + } + licenses = append(licenses, NewLicense(v)) + } + return +} + func NewLicensesFromLocation(location source.Location, values ...string) (licenses []License) { for _, v := range values { if v == "" { From 33bda0a767c598e81d9a04fc590688f475bc0af4 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Wed, 26 Apr 2023 12:44:58 -0400 Subject: [PATCH 46/99] chore: go mod updates Signed-off-by: Christopher Phillips --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index c4b62f8c63c..e43ecd14695 100644 --- a/go.mod +++ b/go.mod @@ -149,7 +149,7 @@ require ( gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect modernc.org/libc v1.22.4 // indirect - modernc.org/sqlite v1.21.2 // indirect + modernc.org/sqlite v1.22.0 // indirect ) require ( diff --git a/go.sum b/go.sum index 45c8ff0f565..c8b192d392c 100644 --- a/go.sum +++ b/go.sum @@ -1174,8 +1174,8 @@ modernc.org/libc v1.22.4 h1:wymSbZb0AlrjdAVX3cjreCHTPCpPARbQXNz6BHPzdwQ= modernc.org/libc v1.22.4/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY= modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ= modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds= -modernc.org/sqlite v1.21.2 h1:ixuUG0QS413Vfzyx6FWx6PYTmHaOegTY+hjzhn7L+a0= -modernc.org/sqlite v1.21.2/go.mod h1:cxbLkB5WS32DnQqeH4h4o1B0eMr8W/y8/RGuxQ3JsC0= +modernc.org/sqlite v1.22.0 h1:Uo+wEWePCspy4SAu0w2VbzUHEftOs7yoaWX/cYjsq84= +modernc.org/sqlite v1.22.0/go.mod h1:cxbLkB5WS32DnQqeH4h4o1B0eMr8W/y8/RGuxQ3JsC0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= From edb7f1c4e0bb3328fec8cf36bc8477b17be6b115 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Wed, 26 Apr 2023 12:52:53 -0400 Subject: [PATCH 47/99] chore: sa fixes Signed-off-by: Christopher Phillips --- syft/formats/common/spdxhelpers/to_syft_model.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/syft/formats/common/spdxhelpers/to_syft_model.go b/syft/formats/common/spdxhelpers/to_syft_model.go index c48d4b1fd57..309f6e0a6e3 100644 --- a/syft/formats/common/spdxhelpers/to_syft_model.go +++ b/syft/formats/common/spdxhelpers/to_syft_model.go @@ -6,8 +6,6 @@ import ( "strconv" "strings" - "github.com/anchore/syft/syft/license" - "github.com/spdx/tools-golang/spdx" "github.com/anchore/packageurl-go" @@ -16,6 +14,7 @@ import ( "github.com/anchore/syft/syft/cpe" "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/formats/common/util" + "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/linux" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/sbom" From 927bdc105bccbddcbb045a5851e0379a6cb91218 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Wed, 26 Apr 2023 13:37:37 -0400 Subject: [PATCH 48/99] chore: wip Signed-off-by: Christopher Phillips --- .../snapshot/TestSPDXRelationshipOrder.golden | 6 ++-- .../TestEncodeFullJSONDocument.golden | 13 --------- .../snapshot/TestImageEncoder.golden | 13 --------- syft/pkg/cataloger/sbom/cataloger_test.go | 28 +++++++++---------- 4 files changed, 17 insertions(+), 43 deletions(-) diff --git a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden index 3bb9ce10752..79a3fc45909 100644 --- a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden +++ b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden @@ -3,14 +3,14 @@ "dataLicense": "CC0-1.0", "SPDXID": "SPDXRef-DOCUMENT", "name": "user-image-input", - "documentNamespace": "https://anchore.com/syft/image/user-image-input-27afc914-f9a1-4d8a-bfa2-3bb67e694d84", + "documentNamespace":"", "creationInfo": { - "licenseListVersion": "3.20", + "licenseListVersion":"", "creators": [ "Organization: Anchore, Inc", "Tool: syft-v0.42.0-bogus" ], - "created": "2023-04-26T14:31:55Z" + "created":"" }, "packages": [ { diff --git a/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden b/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden index a970a8edb00..35beebb9147 100644 --- a/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden +++ b/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden @@ -11,14 +11,6 @@ "path": "/a/place/a" } ], - "licenses": [ - { - "value": "MIT", - "spdx-expression": "MIT", - "type": "declared", - "url": "" - } - ], "language": "python", "cpes": [ "cpe:2.3:*:some:package:1:*:*:*:*:*:*:*" @@ -45,7 +37,6 @@ "path": "/b/place/b" } ], - "licenses": [], "language": "", "cpes": [ "cpe:2.3:*:some:package:2:*:*:*:*:*:*:*" @@ -191,9 +182,5 @@ "configuration": { "config-key": "config-value" } - }, - "schema": { - "version": "8.0.0", - "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-8.0.0.json" } } diff --git a/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden b/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden index a1dfafa1456..a8e9dc75d72 100644 --- a/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden +++ b/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden @@ -12,14 +12,6 @@ "layerID": "sha256:ab62016f9bec7286af65604081564cadeeb364a48faca2346c3f5a5a1f5ef777" } ], - "licenses": [ - { - "value": "MIT", - "spdx-expression": "MIT", - "type": "declared", - "url": "" - } - ], "language": "python", "cpes": [ "cpe:2.3:*:some:package:1:*:*:*:*:*:*:*" @@ -47,7 +39,6 @@ "layerID": "sha256:f1803845b6747d94d6e4ecce2331457e5f1c4fb97de5216f392a76f4582f63b2" } ], - "licenses": [], "language": "", "cpes": [ "cpe:2.3:*:some:package:2:*:*:*:*:*:*:*" @@ -114,9 +105,5 @@ "configuration": { "config-key": "config-value" } - }, - "schema": { - "version": "8.0.0", - "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-8.0.0.json" } } diff --git a/syft/pkg/cataloger/sbom/cataloger_test.go b/syft/pkg/cataloger/sbom/cataloger_test.go index 0253ad63c68..2c566a0cb36 100644 --- a/syft/pkg/cataloger/sbom/cataloger_test.go +++ b/syft/pkg/cataloger/sbom/cataloger_test.go @@ -38,7 +38,7 @@ func Test_parseSBOM(t *testing.T) { Version: "3.2.0-r23", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []pkg.License{}, + Licenses: make([]pkg.License, 0), FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/alpine-baselayout@3.2.0-r23?arch=x86_64&upstream=alpine-baselayout&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -55,7 +55,7 @@ func Test_parseSBOM(t *testing.T) { Version: "3.2.0-r23", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []pkg.License{}, + Licenses: make([]pkg.License, 0), FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/alpine-baselayout-data@3.2.0-r23?arch=x86_64&upstream=alpine-baselayout&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -76,7 +76,7 @@ func Test_parseSBOM(t *testing.T) { Version: "2.4-r1", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []pkg.License{}, + Licenses: make([]pkg.License, 0), FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/alpine-keys@2.4-r1?arch=x86_64&upstream=alpine-keys&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -93,7 +93,7 @@ func Test_parseSBOM(t *testing.T) { Version: "2.12.9-r3", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []pkg.License{}, + Licenses: make([]pkg.License, 0), FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/apk-tools@2.12.9-r3?arch=x86_64&upstream=apk-tools&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -110,7 +110,7 @@ func Test_parseSBOM(t *testing.T) { Version: "1.35.0-r17", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []pkg.License{}, + Licenses: make([]pkg.License, 0), FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/busybox@1.35.0-r17?arch=x86_64&upstream=busybox&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -122,7 +122,7 @@ func Test_parseSBOM(t *testing.T) { Version: "20220614-r0", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []pkg.License{}, + Licenses: make([]pkg.License, 0), FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/ca-certificates-bundle@20220614-r0?arch=x86_64&upstream=ca-certificates&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -143,7 +143,7 @@ func Test_parseSBOM(t *testing.T) { Version: "0.7.2-r3", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []pkg.License{}, + Licenses: make([]pkg.License, 0), FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/libc-utils@0.7.2-r3?arch=x86_64&upstream=libc-dev&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -160,7 +160,7 @@ func Test_parseSBOM(t *testing.T) { Version: "1.1.1s-r0", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []pkg.License{}, + Licenses: make([]pkg.License, 0), FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/libcrypto1.1@1.1.1s-r0?arch=x86_64&upstream=openssl&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -172,7 +172,7 @@ func Test_parseSBOM(t *testing.T) { Version: "1.1.1s-r0", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []pkg.License{}, + Licenses: make([]pkg.License, 0), FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/libssl1.1@1.1.1s-r0?arch=x86_64&upstream=openssl&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -184,7 +184,7 @@ func Test_parseSBOM(t *testing.T) { Version: "1.2.3-r1", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []pkg.License{}, + Licenses: make([]pkg.License, 0), FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/musl@1.2.3-r1?arch=x86_64&upstream=musl&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -196,7 +196,7 @@ func Test_parseSBOM(t *testing.T) { Version: "1.2.3-r1", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []pkg.License{}, + Licenses: make([]pkg.License, 0), FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/musl-utils@1.2.3-r1?arch=x86_64&upstream=musl&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -213,7 +213,7 @@ func Test_parseSBOM(t *testing.T) { Version: "1.3.4-r0", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []pkg.License{}, + Licenses: make([]pkg.License, 0), FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/scanelf@1.3.4-r0?arch=x86_64&upstream=pax-utils&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -225,7 +225,7 @@ func Test_parseSBOM(t *testing.T) { Version: "1.35.0-r17", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []pkg.License{}, + Licenses: make([]pkg.License, 0), FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/ssl_client@1.35.0-r17?arch=x86_64&upstream=busybox&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -242,7 +242,7 @@ func Test_parseSBOM(t *testing.T) { Version: "1.2.12-r3", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []pkg.License{}, + Licenses: make([]pkg.License, 0), FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/zlib@1.2.12-r3?arch=x86_64&upstream=zlib&distro=alpine-3.16.3", CPEs: mustCPEs( From f03dbed489f8fcb0cf2e2d1f96b5a5b412174583 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Wed, 26 Apr 2023 13:50:16 -0400 Subject: [PATCH 49/99] chore: fixtures regenerated Signed-off-by: Christopher Phillips --- .../TestCycloneDxDirectoryEncoder.golden | 12 +++++------- .../snapshot/TestCycloneDxImageEncoder.golden | 18 ++++++++---------- .../stereoscope-fixture-image-simple.golden | Bin 15360 -> 15360 bytes .../TestSPDXJSONDirectoryEncoder.golden | 6 +++--- .../snapshot/TestSPDXJSONImageEncoder.golden | 6 +++--- .../snapshot/TestSPDXJSONSPDXIDs.golden | 6 +++--- .../snapshot/TestSPDXRelationshipOrder.golden | 10 +++++++--- .../TestSPDXTagValueDirectoryEncoder.golden | 6 +++--- .../TestSPDXTagValueImageEncoder.golden | 6 +++--- .../snapshot/TestDirectoryEncoder.golden | 13 ------------- 10 files changed, 35 insertions(+), 48 deletions(-) diff --git a/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden b/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden index 095d2c3b88a..137bd369489 100644 --- a/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden +++ b/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden @@ -1,7 +1,7 @@ - + - 2022-11-07T09:11:06-05:00 + redacted anchore @@ -9,18 +9,16 @@ v0.42.0-bogus - + /some/path - + package-1 1.0.1 - - MIT - + MIT cpe:2.3:*:some:package:2:*:*:*:*:*:*:* a-purl-2 diff --git a/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden b/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden index 814962bedb3..2c33e352903 100644 --- a/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden +++ b/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden @@ -1,7 +1,7 @@ - + - 2022-11-07T09:11:06-05:00 + redacted anchore @@ -9,19 +9,17 @@ v0.42.0-bogus - + user-image-input - sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368 + redacted - + package-1 1.0.1 - - MIT - + MIT cpe:2.3:*:some:package:1:*:*:*:*:*:*:* a-purl-1 @@ -30,7 +28,7 @@ python PythonPackageMetadata python - sha256:fb6beecb75b39f4bb813dbf177e501edd5ddb3e69bb45cedeb78c676ee1b7a59 + redacted /somefile-1.txt @@ -43,7 +41,7 @@ the-cataloger-2 DpkgMetadata deb - sha256:319b588ce64253a87b533c8ed01cf0025e0eac98e7b516e12532957e1244fdec + redacted /somefile-2.txt 0 diff --git a/syft/formats/cyclonedxxml/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden b/syft/formats/cyclonedxxml/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden index 11a1958c8935029370bb6a48ad18899403776949..f4aa1e7bb74e9d8c2cf36a69cd7a6d910a9a7087 100644 GIT binary patch literal 15360 zcmeHOTW{Mo6wdR0g{Qs7Ht&=S>>+Ccv_P>8ZPoz`f`H;B(PB%6Bo_$`|Mwlqj$^w^ zv_!U(q7)36*F*9p$TkutO) zGMZRoBVzjp%@0BhNzM<@{Mr1kQV%d=1aU4!RSrUg9&~uK==LQhMX7S=Qd>!zgKH~q zPxWzI{eL<6^!dZ-?@x6XVT1%@n|i;G19Egj|CkGW&}prcr#f|?_oM5-w-=P}(f0pu z41w16|4)&n4P$Dsf6U3|`i~hV-u@e4-AeW}NK7yY+D?PuFc_=crX|SYM54%0$w{n2 zA$c5QCv6l*1`9{9V-`8eonl!GO+a+Yy<@!;(r!4pXVHvrP9}* zOOvF*z^P$j4})b|Hi5xxCfXdTE)vhmyJh%vY}0i)b?h|N(1}I)lk{saNGHSLVeoDQ zW19y*jMUYlqhGS&WMqr`oWHXCCP}Z3lSOY&(lWmrXGsO}yzXiG+awt-MSWDU9WJWR zYGmF%9_PUyM`!PU?N5rlKg_fm_KP@42P^Sq^dX~8nnxiRT)YI(KFo~gBC2wIos?FW zle|)y8X3VrOa_<-!z7*D2D7agd{D0oFwgZ>em_ZItqO5x~tfH0O`|0@9QFgVwJr+SOR+q?LAhfR6W z9s!SlN5CWSh7f3bSlf~RmlEmyzx$Xbt@Xbf|1TBX@BjCqf9(Cg`+)h)w$a-D+xvf! zAb9ip55chh@8!$H5B~jU;s0$r4Jt?M(r1e!iMj~G?+l?q{qvsMr#!W<=s;|%_#OHG zF!SGk_VUhH`v352`v1f?{yzr%55NBJehQS=NztjCfSqeqEZu;+Z`Zfr5%36j1Uv$- zi-4k9V52c*hB%62hPmd55@sb288OmDSP;mHR2VT4T8<+cQi@{g3{|ez{``jt+dTgv z#+iTr?_*|6gGWdW4{Vy6x3VRk#u}%kqF}61SSr%W@)X&~{DGZIndQL2IiEIFLsDSM zIj@hQ^A;{+s2Lm`AJ@B|ZoU2O^w+n;onJa}MX_G{j?ClF>~2I(3k`lv+s^j zvk#-)VjClsCe9Y+|5?Xh+H4d5gLAsV1xGdi!}C91iuUvB!P@z5!J7=WvKIjB|76G6 znYx;kQrQ`x@|-v75Nb*#yq21SE~R=bAQM!E%t`E2$!Eoco+AMpry L1Uv%Y4FdlHH$Ie? literal 15360 zcmeHOZExE)5ccQ&3Qzl**nFpCU>~}sKnoO0(Pka6qA2)sOtjjPA<0F9Apdm%%?PJlu^wTR3f}9JyT$INc2+m0LY2Bo z+kCjL@($GhDlwJ2$Qy9Ytb$~en2A>+IZ9kvg#0`iT_ul-cgg5g^6bIcA**Z}Z~(B(HF&Ar5m|{4vYNJ{c9W@%S2tt-v&Z z<%AK&j!Ak<=oup;l8qP}NRkOgHD&)`*NePv%IXqzyV}xCq&m%{q*>c-Il>!@J(C9~ zC;gC*;g}AZ_f3CTmlIei|JJvyc5m}$Zjs3ZiUxaqxH<5pdka}WC)5Q(m;?gGWhr0tP6t%jdrI6?%$4P|rP|4?pM&h_=5YAJNDFF!+e z=O#b-`rq0AFCRUB@#OT+yZo?a|3~-pb^kM>h>ZLHT{hiS9UXLze0HTul=BrV(0Tsv zApVzZWBq4Da24nOAzp{8ByWT5uCD>cd{a{`qhswgO%cpt?3XzyKzi#9rAlcoiC`%g zUKpNQNsP1tFy!MF(O;Tr$E;^4>mDcoaY|$I0;)H+h~;rvH-(wBfi4O=DJR(Aw)|HO zE&9Nk2Tfz##f!!xI2i>qu3_a~Im1wY^Cnc-b$RowK?Je9KAl3bE@xgkEgUMbY%|AE zEzCJe9!{2s=O}qHG3QG~UzX$91TYj|G+M(-sO_VosV=8w-a?Q?i#70clsq*S#>?(4 zl)6E1->^M)ge&iF&=!2&*P@?*$}3RoyMrrTC;o7bBnK^r^2y(p_)mNk|Fby_6#1V| zT6A|6H-yKWXV{(GRY_a?k3>Mb{}ZMtG5}Qf2&I}M{yRuy`jg|oIoZYf?JkPDcm3 z?))!NY%~7@`QP~c=OFLQlK<`c8X%c=%h}RQ6HMD^GdBokZnS^%#EZz(ejsi)nA)#M zAa-^9-uOQf`JY2905|;KJOOGyBW~ya)JOaurVB>?Z$Iu>liFYw-8=)Xe_`qy8gQ*i zEJO=L3q%V<3;ci&PqIn?_abld!Ax4Gf-TyUKK3XcKB_mIfGV=884#+q6MM_z9S3#2P@x!egFUf diff --git a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONDirectoryEncoder.golden b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONDirectoryEncoder.golden index c3460b3bdfa..4b8fdc17693 100644 --- a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONDirectoryEncoder.golden +++ b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONDirectoryEncoder.golden @@ -3,14 +3,14 @@ "dataLicense": "CC0-1.0", "SPDXID": "SPDXRef-DOCUMENT", "name": "/some/path", - "documentNamespace": "https://anchore.com/syft/dir/some/path-a74d8ff2-0b3e-4fb0-aed3-ea9ade521d88", + "documentNamespace":"", "creationInfo": { - "licenseListVersion": "3.20", + "licenseListVersion":"", "creators": [ "Organization: Anchore, Inc", "Tool: syft-v0.42.0-bogus" ], - "created": "2023-04-26T14:27:17Z" + "created":"" }, "packages": [ { diff --git a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONImageEncoder.golden b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONImageEncoder.golden index 8cf3f97cc38..dc36af406d5 100644 --- a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONImageEncoder.golden +++ b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONImageEncoder.golden @@ -3,14 +3,14 @@ "dataLicense": "CC0-1.0", "SPDXID": "SPDXRef-DOCUMENT", "name": "user-image-input", - "documentNamespace": "https://anchore.com/syft/image/user-image-input-d9eae25d-b74d-40c0-a015-f6dd20c4446c", + "documentNamespace":"", "creationInfo": { - "licenseListVersion": "3.20", + "licenseListVersion":"", "creators": [ "Organization: Anchore, Inc", "Tool: syft-v0.42.0-bogus" ], - "created": "2023-04-26T14:27:17Z" + "created":"" }, "packages": [ { diff --git a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXJSONSPDXIDs.golden b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXJSONSPDXIDs.golden index c346fbd0d6c..9a3ba9cdb3b 100644 --- a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXJSONSPDXIDs.golden +++ b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXJSONSPDXIDs.golden @@ -2,11 +2,11 @@ SPDXVersion: SPDX-2.3 DataLicense: CC0-1.0 SPDXID: SPDXRef-DOCUMENT DocumentName: foobar/baz -DocumentNamespace: https://anchore.com/syft/dir/foobar/baz-8f6e6182-6963-48cd-bd09-4662663722a5 -LicenseListVersion: 3.20 +redacted +redacted Creator: Organization: Anchore, Inc Creator: Tool: syft-v0.42.0-bogus -Created: 2023-04-26T14:20:31Z +redacted ##### Package: @at-sign diff --git a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden index 063a97ca427..f90c82e2d05 100644 --- a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden +++ b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden @@ -2,11 +2,11 @@ SPDXVersion: SPDX-2.3 DataLicense: CC0-1.0 SPDXID: SPDXRef-DOCUMENT DocumentName: user-image-input -DocumentNamespace: https://anchore.com/syft/image/user-image-input-d8df09c1-4a0f-4da2-a74b-d9f0c24d5ee6 -LicenseListVersion: 3.20 +redacted +redacted Creator: Organization: Anchore, Inc Creator: Tool: syft-v0.42.0-bogus -Created: 2023-04-24T14:42:14Z +redacted ##### Unpackaged files @@ -54,6 +54,8 @@ PackageVersion: 2.0.1 PackageDownloadLocation: NOASSERTION FilesAnalyzed: false PackageSourceInfo: acquired package info from DPKG DB: /somefile-2.txt +PackageLicenseConcluded: NOASSERTION +PackageLicenseDeclared: NOASSERTION PackageCopyrightText: NOASSERTION ExternalRef: SECURITY cpe23Type cpe:2.3:*:some:package:2:*:*:*:*:*:*:* ExternalRef: PACKAGE-MANAGER purl pkg:deb/debian/package-2@2.0.1 @@ -66,6 +68,8 @@ PackageVersion: 1.0.1 PackageDownloadLocation: NOASSERTION FilesAnalyzed: false PackageSourceInfo: acquired package info from installed python package manifest file: /somefile-1.txt +PackageLicenseConcluded: NOASSERTION +PackageLicenseDeclared: MIT PackageCopyrightText: NOASSERTION ExternalRef: SECURITY cpe23Type cpe:2.3:*:some:package:1:*:*:*:*:*:*:* ExternalRef: PACKAGE-MANAGER purl a-purl-1 diff --git a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueDirectoryEncoder.golden b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueDirectoryEncoder.golden index e3cf2169c5e..be06bd19198 100644 --- a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueDirectoryEncoder.golden +++ b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueDirectoryEncoder.golden @@ -2,11 +2,11 @@ SPDXVersion: SPDX-2.3 DataLicense: CC0-1.0 SPDXID: SPDXRef-DOCUMENT DocumentName: /some/path -DocumentNamespace: https://anchore.com/syft/dir/some/path-16a6df79-9a0f-4d4b-a2ec-fc449b05086e -LicenseListVersion: 3.20 +redacted +redacted Creator: Organization: Anchore, Inc Creator: Tool: syft-v0.42.0-bogus -Created: 2023-04-26T14:21:21Z +redacted ##### Package: package-2 diff --git a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueImageEncoder.golden b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueImageEncoder.golden index ff215249b47..0f7c7ce59d2 100644 --- a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueImageEncoder.golden +++ b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueImageEncoder.golden @@ -2,11 +2,11 @@ SPDXVersion: SPDX-2.3 DataLicense: CC0-1.0 SPDXID: SPDXRef-DOCUMENT DocumentName: user-image-input -DocumentNamespace: https://anchore.com/syft/image/user-image-input-09477ff1-559a-4e1d-91b9-75d51d4cb73f -LicenseListVersion: 3.20 +redacted +redacted Creator: Organization: Anchore, Inc Creator: Tool: syft-v0.42.0-bogus -Created: 2023-04-26T14:21:00Z +redacted ##### Package: package-2 diff --git a/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden b/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden index dc1626c4803..217f7ee1022 100644 --- a/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden +++ b/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden @@ -11,14 +11,6 @@ "path": "/some/path/pkg1" } ], - "licenses": [ - { - "value": "MIT", - "spdx-expression": "MIT", - "type": "declared", - "url": "" - } - ], "language": "python", "cpes": [ "cpe:2.3:*:some:package:2:*:*:*:*:*:*:*" @@ -50,7 +42,6 @@ "path": "/some/path/pkg1" } ], - "licenses": [], "language": "", "cpes": [ "cpe:2.3:*:some:package:2:*:*:*:*:*:*:*" @@ -91,9 +82,5 @@ "configuration": { "config-key": "config-value" } - }, - "schema": { - "version": "8.0.0", - "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-8.0.0.json" } } From 43fd3e5473e3d4a60b9f57764005361de31d333a Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Wed, 26 Apr 2023 14:26:58 -0400 Subject: [PATCH 50/99] chore: update schema definition to use different unmarshal Signed-off-by: Christopher Phillips --- schema/json/schema-8.0.0.json | 147 +++++++++++------- syft/formats/syftjson/model/package.go | 40 ++--- syft/formats/syftjson/model/package_test.go | 4 +- .../snapshot/TestDirectoryEncoder.golden | 9 ++ .../TestEncodeFullJSONDocument.golden | 9 ++ .../snapshot/TestImageEncoder.golden | 9 ++ 6 files changed, 133 insertions(+), 85 deletions(-) diff --git a/schema/json/schema-8.0.0.json b/schema/json/schema-8.0.0.json index 027071574fb..8013dfa9f0f 100644 --- a/schema/json/schema-8.0.0.json +++ b/schema/json/schema-8.0.0.json @@ -737,6 +737,32 @@ "kb" ] }, + "License": { + "properties": { + "value": { + "type": "string" + }, + "spdxExpression": { + "type": "string" + }, + "type": { + "type": "string" + }, + "url": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Location" + } + }, + "type": "object", + "required": [ + "value", + "spdxExpression", + "type", + "url" + ] + }, "LinuxKernelMetadata": { "properties": { "name": { @@ -1029,6 +1055,63 @@ }, "type": "array" }, + "licenses": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$defs": { + "License": { + "properties": { + "value": { + "type": "string" + }, + "spdxExpression": { + "type": "string" + }, + "type": { + "type": "string" + }, + "url": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Location" + } + }, + "type": "object", + "required": [ + "value", + "spdxExpression", + "type", + "url" + ] + }, + "Location": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + }, + "annotations": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path" + ] + } + }, + "items": { + "$ref": "#/$defs/License" + }, + "type": "array" + }, "language": { "type": "string" }, @@ -1134,63 +1217,6 @@ "$ref": "#/$defs/RpmMetadata" } ] - }, - "licenses": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$defs": { - "License": { - "properties": { - "value": { - "type": "string" - }, - "spdxExpression": { - "type": "string" - }, - "type": { - "type": "string" - }, - "url": { - "type": "string" - }, - "location": { - "$ref": "#/$defs/Location" - } - }, - "type": "object", - "required": [ - "value", - "spdxExpression", - "type", - "url" - ] - }, - "Location": { - "properties": { - "path": { - "type": "string" - }, - "layerID": { - "type": "string" - }, - "annotations": { - "patternProperties": { - ".*": { - "type": "string" - } - }, - "type": "object" - } - }, - "type": "object", - "required": [ - "path" - ] - } - }, - "items": { - "$ref": "#/$defs/License" - }, - "type": "array" } }, "type": "object", @@ -1201,6 +1227,7 @@ "type", "foundBy", "locations", + "licenses", "language", "cpes", "purl" @@ -1788,6 +1815,12 @@ "type", "target" ] + }, + "customLicense": { + "items": { + "$ref": "#/$defs/License" + }, + "type": "array" } } } diff --git a/syft/formats/syftjson/model/package.go b/syft/formats/syftjson/model/package.go index 35c7902a909..cee46ca7982 100644 --- a/syft/formats/syftjson/model/package.go +++ b/syft/formats/syftjson/model/package.go @@ -27,14 +27,25 @@ type PackageBasicData struct { Type pkg.Type `json:"type"` FoundBy string `json:"foundBy"` Locations []source.Location `json:"locations"` - Licenses []pkg.License `json:"-"` + Licenses licenses `json:"licenses"` Language pkg.Language `json:"language"` CPEs []string `json:"cpes"` PURL string `json:"purl"` } -type licenseUnpacker struct { - LicenseJSON json.RawMessage `json:"licenses"` +type licenses []pkg.License + +func (f *licenses) UnmarshalJSON(b []byte) error { + var licenses []pkg.License + if err := json.Unmarshal(b, &licenses); err != nil { + var simpleLicense []string + if err := json.Unmarshal(b, &simpleLicense); err != nil { + return fmt.Errorf("unable to unmarshal license: %w", err) + } + licenses = pkg.NewLicensesFromValues(simpleLicense...) + } + *f = licenses + return nil } // PackageCustomData contains ambiguous values (type-wise) from pkg.Package. @@ -61,16 +72,6 @@ func (p *Package) UnmarshalJSON(b []byte) error { } p.PackageBasicData = basic - var licenseUnpacker licenseUnpacker - if err := json.Unmarshal(b, &licenseUnpacker); err != nil { - return err - } - - p.Licenses = make([]pkg.License, 0) - if len(licenseUnpacker.LicenseJSON) > 0 { - p.PackageBasicData.Licenses = parseLicenseJSON(licenseUnpacker.LicenseJSON) - } - var unpacker packageMetadataUnpacker if err := json.Unmarshal(b, &unpacker); err != nil { log.Warnf("failed to unmarshall into packageMetadataUnpacker: %v", err) @@ -86,19 +87,6 @@ func (p *Package) UnmarshalJSON(b []byte) error { return err } -func parseLicenseJSON(licenseJSON []byte) []pkg.License { - var complexLicenses []pkg.License - if err := json.Unmarshal(licenseJSON, &complexLicenses); err != nil { - var simpleLicense []string - if err := json.Unmarshal(licenseJSON, &simpleLicense); err != nil { - log.Warnf("failed to unmarshal package licenses: %v", err) - return nil - } - return pkg.NewLicensesFromValues(simpleLicense...) - } - return complexLicenses -} - func unpackMetadata(p *Package, unpacker packageMetadataUnpacker) error { p.MetadataType = pkg.CleanMetadataType(unpacker.MetadataType) diff --git a/syft/formats/syftjson/model/package_test.go b/syft/formats/syftjson/model/package_test.go index 380d4d0c073..e9978810760 100644 --- a/syft/formats/syftjson/model/package_test.go +++ b/syft/formats/syftjson/model/package_test.go @@ -92,7 +92,7 @@ func TestUnmarshalPackageGolang(t *testing.T) { "purl": "pkg:golang/gopkg.in/square/go-jose.v2@v2.6.0" }`), assert: func(p *Package) { - assert.Equal(t, []pkg.License{ + assert.Equal(t, licenses{ { Value: "MIT", SPDXExpression: "MIT", @@ -136,7 +136,7 @@ func TestUnmarshalPackageGolang(t *testing.T) { "purl": "pkg:golang/gopkg.in/square/go-jose.v2@v2.6.0" }`), assert: func(p *Package) { - assert.Equal(t, []pkg.License{ + assert.Equal(t, licenses{ { Value: "MIT", SPDXExpression: "MIT", diff --git a/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden b/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden index 217f7ee1022..1b061fd5bb0 100644 --- a/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden +++ b/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden @@ -11,6 +11,14 @@ "path": "/some/path/pkg1" } ], + "licenses": [ + { + "value": "MIT", + "spdxExpression": "MIT", + "type": "declared", + "url": "" + } + ], "language": "python", "cpes": [ "cpe:2.3:*:some:package:2:*:*:*:*:*:*:*" @@ -42,6 +50,7 @@ "path": "/some/path/pkg1" } ], + "licenses": [], "language": "", "cpes": [ "cpe:2.3:*:some:package:2:*:*:*:*:*:*:*" diff --git a/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden b/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden index 35beebb9147..9c22fc58b63 100644 --- a/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden +++ b/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden @@ -11,6 +11,14 @@ "path": "/a/place/a" } ], + "licenses": [ + { + "value": "MIT", + "spdxExpression": "MIT", + "type": "declared", + "url": "" + } + ], "language": "python", "cpes": [ "cpe:2.3:*:some:package:1:*:*:*:*:*:*:*" @@ -37,6 +45,7 @@ "path": "/b/place/b" } ], + "licenses": [], "language": "", "cpes": [ "cpe:2.3:*:some:package:2:*:*:*:*:*:*:*" diff --git a/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden b/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden index a8e9dc75d72..eb5a4176b0a 100644 --- a/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden +++ b/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden @@ -12,6 +12,14 @@ "layerID": "sha256:ab62016f9bec7286af65604081564cadeeb364a48faca2346c3f5a5a1f5ef777" } ], + "licenses": [ + { + "value": "MIT", + "spdxExpression": "MIT", + "type": "declared", + "url": "" + } + ], "language": "python", "cpes": [ "cpe:2.3:*:some:package:1:*:*:*:*:*:*:*" @@ -39,6 +47,7 @@ "layerID": "sha256:f1803845b6747d94d6e4ecce2331457e5f1c4fb97de5216f392a76f4582f63b2" } ], + "licenses": [], "language": "", "cpes": [ "cpe:2.3:*:some:package:2:*:*:*:*:*:*:*" From 30431a79bbfc11be64b273f36806feee77746c6b Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Wed, 26 Apr 2023 14:56:12 -0400 Subject: [PATCH 51/99] test: add detected licenses back into SBOM cataloger Signed-off-by: Christopher Phillips --- syft/pkg/cataloger/sbom/cataloger_test.go | 187 ++++++++++++++---- .../alpine/syft-json/sbom.syft.json | 2 - syft/pkg/license.go | 2 +- 3 files changed, 146 insertions(+), 45 deletions(-) diff --git a/syft/pkg/cataloger/sbom/cataloger_test.go b/syft/pkg/cataloger/sbom/cataloger_test.go index 2c566a0cb36..417a3800a35 100644 --- a/syft/pkg/cataloger/sbom/cataloger_test.go +++ b/syft/pkg/cataloger/sbom/cataloger_test.go @@ -8,6 +8,7 @@ import ( "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/cpe" "github.com/anchore/syft/syft/formats/syftjson" + "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" "github.com/anchore/syft/syft/sbom" @@ -38,9 +39,15 @@ func Test_parseSBOM(t *testing.T) { Version: "3.2.0-r23", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: make([]pkg.License, 0), - FoundBy: "sbom-cataloger", - PURL: "pkg:apk/alpine/alpine-baselayout@3.2.0-r23?arch=x86_64&upstream=alpine-baselayout&distro=alpine-3.16.3", + Licenses: []pkg.License{ + { + Value: "GPL-2.0-only", + SPDXExpression: "GPL-2.0-only", + Type: license.Declared, + }, + }, + FoundBy: "sbom-cataloger", + PURL: "pkg:apk/alpine/alpine-baselayout@3.2.0-r23?arch=x86_64&upstream=alpine-baselayout&distro=alpine-3.16.3", CPEs: mustCPEs( "cpe:2.3:a:alpine-baselayout:alpine-baselayout:3.2.0-r23:*:*:*:*:*:*:*", "cpe:2.3:a:alpine-baselayout:alpine_baselayout:3.2.0-r23:*:*:*:*:*:*:*", @@ -55,9 +62,15 @@ func Test_parseSBOM(t *testing.T) { Version: "3.2.0-r23", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: make([]pkg.License, 0), - FoundBy: "sbom-cataloger", - PURL: "pkg:apk/alpine/alpine-baselayout-data@3.2.0-r23?arch=x86_64&upstream=alpine-baselayout&distro=alpine-3.16.3", + Licenses: []pkg.License{ + { + Value: "GPL-2.0-only", + SPDXExpression: "GPL-2.0-only", + Type: license.Declared, + }, + }, + FoundBy: "sbom-cataloger", + PURL: "pkg:apk/alpine/alpine-baselayout-data@3.2.0-r23?arch=x86_64&upstream=alpine-baselayout&distro=alpine-3.16.3", CPEs: mustCPEs( "cpe:2.3:a:alpine-baselayout-data:alpine-baselayout-data:3.2.0-r23:*:*:*:*:*:*:*", "cpe:2.3:a:alpine-baselayout-data:alpine_baselayout_data:3.2.0-r23:*:*:*:*:*:*:*", @@ -76,9 +89,15 @@ func Test_parseSBOM(t *testing.T) { Version: "2.4-r1", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: make([]pkg.License, 0), - FoundBy: "sbom-cataloger", - PURL: "pkg:apk/alpine/alpine-keys@2.4-r1?arch=x86_64&upstream=alpine-keys&distro=alpine-3.16.3", + Licenses: []pkg.License{ + { + Value: "MIT", + SPDXExpression: "MIT", + Type: license.Declared, + }, + }, + FoundBy: "sbom-cataloger", + PURL: "pkg:apk/alpine/alpine-keys@2.4-r1?arch=x86_64&upstream=alpine-keys&distro=alpine-3.16.3", CPEs: mustCPEs( "cpe:2.3:a:alpine-keys:alpine-keys:2.4-r1:*:*:*:*:*:*:*", "cpe:2.3:a:alpine-keys:alpine_keys:2.4-r1:*:*:*:*:*:*:*", @@ -93,9 +112,15 @@ func Test_parseSBOM(t *testing.T) { Version: "2.12.9-r3", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: make([]pkg.License, 0), - FoundBy: "sbom-cataloger", - PURL: "pkg:apk/alpine/apk-tools@2.12.9-r3?arch=x86_64&upstream=apk-tools&distro=alpine-3.16.3", + Licenses: []pkg.License{ + { + Value: "GPL-2.0-only", + SPDXExpression: "GPL-2.0-only", + Type: license.Declared, + }, + }, + FoundBy: "sbom-cataloger", + PURL: "pkg:apk/alpine/apk-tools@2.12.9-r3?arch=x86_64&upstream=apk-tools&distro=alpine-3.16.3", CPEs: mustCPEs( "cpe:2.3:a:apk-tools:apk-tools:2.12.9-r3:*:*:*:*:*:*:*", "cpe:2.3:a:apk-tools:apk_tools:2.12.9-r3:*:*:*:*:*:*:*", @@ -110,9 +135,15 @@ func Test_parseSBOM(t *testing.T) { Version: "1.35.0-r17", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: make([]pkg.License, 0), - FoundBy: "sbom-cataloger", - PURL: "pkg:apk/alpine/busybox@1.35.0-r17?arch=x86_64&upstream=busybox&distro=alpine-3.16.3", + Licenses: []pkg.License{ + { + Value: "GPL-2.0-only", + SPDXExpression: "GPL-2.0-only", + Type: license.Declared, + }, + }, + FoundBy: "sbom-cataloger", + PURL: "pkg:apk/alpine/busybox@1.35.0-r17?arch=x86_64&upstream=busybox&distro=alpine-3.16.3", CPEs: mustCPEs( "cpe:2.3:a:busybox:busybox:1.35.0-r17:*:*:*:*:*:*:*", ), @@ -122,9 +153,20 @@ func Test_parseSBOM(t *testing.T) { Version: "20220614-r0", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: make([]pkg.License, 0), - FoundBy: "sbom-cataloger", - PURL: "pkg:apk/alpine/ca-certificates-bundle@20220614-r0?arch=x86_64&upstream=ca-certificates&distro=alpine-3.16.3", + Licenses: []pkg.License{ + { + Value: "MPL-2.0", + SPDXExpression: "MPL-2.0", + Type: license.Declared, + }, + { + Value: "MIT", + SPDXExpression: "MIT", + Type: license.Declared, + }, + }, + FoundBy: "sbom-cataloger", + PURL: "pkg:apk/alpine/ca-certificates-bundle@20220614-r0?arch=x86_64&upstream=ca-certificates&distro=alpine-3.16.3", CPEs: mustCPEs( "cpe:2.3:a:ca-certificates-bundle:ca-certificates-bundle:20220614-r0:*:*:*:*:*:*:*", "cpe:2.3:a:ca-certificates-bundle:ca_certificates_bundle:20220614-r0:*:*:*:*:*:*:*", @@ -143,9 +185,20 @@ func Test_parseSBOM(t *testing.T) { Version: "0.7.2-r3", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: make([]pkg.License, 0), - FoundBy: "sbom-cataloger", - PURL: "pkg:apk/alpine/libc-utils@0.7.2-r3?arch=x86_64&upstream=libc-dev&distro=alpine-3.16.3", + Licenses: []pkg.License{ + { + Value: "BSD-2-Clause", + SPDXExpression: "BSD-2-Clause", + Type: license.Declared, + }, + { + Value: "BSD-3-Clause", + SPDXExpression: "BSD-3-Clause", + Type: license.Declared, + }, + }, + FoundBy: "sbom-cataloger", + PURL: "pkg:apk/alpine/libc-utils@0.7.2-r3?arch=x86_64&upstream=libc-dev&distro=alpine-3.16.3", CPEs: mustCPEs( "cpe:2.3:a:libc-utils:libc-utils:0.7.2-r3:*:*:*:*:*:*:*", "cpe:2.3:a:libc-utils:libc_utils:0.7.2-r3:*:*:*:*:*:*:*", @@ -160,9 +213,15 @@ func Test_parseSBOM(t *testing.T) { Version: "1.1.1s-r0", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: make([]pkg.License, 0), - FoundBy: "sbom-cataloger", - PURL: "pkg:apk/alpine/libcrypto1.1@1.1.1s-r0?arch=x86_64&upstream=openssl&distro=alpine-3.16.3", + Licenses: []pkg.License{ + { + Value: "OpenSSL", + SPDXExpression: "OpenSSL", + Type: license.Declared, + }, + }, + FoundBy: "sbom-cataloger", + PURL: "pkg:apk/alpine/libcrypto1.1@1.1.1s-r0?arch=x86_64&upstream=openssl&distro=alpine-3.16.3", CPEs: mustCPEs( "cpe:2.3:a:libcrypto1.1:libcrypto1.1:1.1.1s-r0:*:*:*:*:*:*:*", ), @@ -172,9 +231,15 @@ func Test_parseSBOM(t *testing.T) { Version: "1.1.1s-r0", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: make([]pkg.License, 0), - FoundBy: "sbom-cataloger", - PURL: "pkg:apk/alpine/libssl1.1@1.1.1s-r0?arch=x86_64&upstream=openssl&distro=alpine-3.16.3", + Licenses: []pkg.License{ + { + Value: "OpenSSL", + SPDXExpression: "OpenSSL", + Type: license.Declared, + }, + }, + FoundBy: "sbom-cataloger", + PURL: "pkg:apk/alpine/libssl1.1@1.1.1s-r0?arch=x86_64&upstream=openssl&distro=alpine-3.16.3", CPEs: mustCPEs( "cpe:2.3:a:libssl1.1:libssl1.1:1.1.1s-r0:*:*:*:*:*:*:*", ), @@ -184,9 +249,15 @@ func Test_parseSBOM(t *testing.T) { Version: "1.2.3-r1", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: make([]pkg.License, 0), - FoundBy: "sbom-cataloger", - PURL: "pkg:apk/alpine/musl@1.2.3-r1?arch=x86_64&upstream=musl&distro=alpine-3.16.3", + Licenses: []pkg.License{ + { + Value: "MIT", + SPDXExpression: "MIT", + Type: license.Declared, + }, + }, + FoundBy: "sbom-cataloger", + PURL: "pkg:apk/alpine/musl@1.2.3-r1?arch=x86_64&upstream=musl&distro=alpine-3.16.3", CPEs: mustCPEs( "cpe:2.3:a:musl:musl:1.2.3-r1:*:*:*:*:*:*:*", ), @@ -196,9 +267,23 @@ func Test_parseSBOM(t *testing.T) { Version: "1.2.3-r1", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: make([]pkg.License, 0), - FoundBy: "sbom-cataloger", - PURL: "pkg:apk/alpine/musl-utils@1.2.3-r1?arch=x86_64&upstream=musl&distro=alpine-3.16.3", + Licenses: []pkg.License{ + { + Value: "MIT", + SPDXExpression: "MIT", + Type: license.Declared, + }, + { + Value: "BSD", + Type: license.Declared, + }, + { + Value: "GPL2+", + Type: license.Declared, + }, + }, + FoundBy: "sbom-cataloger", + PURL: "pkg:apk/alpine/musl-utils@1.2.3-r1?arch=x86_64&upstream=musl&distro=alpine-3.16.3", CPEs: mustCPEs( "cpe:2.3:a:musl-utils:musl-utils:1.2.3-r1:*:*:*:*:*:*:*", "cpe:2.3:a:musl-utils:musl_utils:1.2.3-r1:*:*:*:*:*:*:*", @@ -213,9 +298,15 @@ func Test_parseSBOM(t *testing.T) { Version: "1.3.4-r0", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: make([]pkg.License, 0), - FoundBy: "sbom-cataloger", - PURL: "pkg:apk/alpine/scanelf@1.3.4-r0?arch=x86_64&upstream=pax-utils&distro=alpine-3.16.3", + Licenses: []pkg.License{ + { + Value: "GPL-2.0-only", + SPDXExpression: "GPL-2.0-only", + Type: license.Declared, + }, + }, + FoundBy: "sbom-cataloger", + PURL: "pkg:apk/alpine/scanelf@1.3.4-r0?arch=x86_64&upstream=pax-utils&distro=alpine-3.16.3", CPEs: mustCPEs( "cpe:2.3:a:scanelf:scanelf:1.3.4-r0:*:*:*:*:*:*:*", ), @@ -225,9 +316,15 @@ func Test_parseSBOM(t *testing.T) { Version: "1.35.0-r17", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: make([]pkg.License, 0), - FoundBy: "sbom-cataloger", - PURL: "pkg:apk/alpine/ssl_client@1.35.0-r17?arch=x86_64&upstream=busybox&distro=alpine-3.16.3", + Licenses: []pkg.License{ + { + Value: "GPL-2.0-only", + SPDXExpression: "GPL-2.0-only", + Type: license.Declared, + }, + }, + FoundBy: "sbom-cataloger", + PURL: "pkg:apk/alpine/ssl_client@1.35.0-r17?arch=x86_64&upstream=busybox&distro=alpine-3.16.3", CPEs: mustCPEs( "cpe:2.3:a:ssl-client:ssl-client:1.35.0-r17:*:*:*:*:*:*:*", "cpe:2.3:a:ssl-client:ssl_client:1.35.0-r17:*:*:*:*:*:*:*", @@ -242,9 +339,15 @@ func Test_parseSBOM(t *testing.T) { Version: "1.2.12-r3", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: make([]pkg.License, 0), - FoundBy: "sbom-cataloger", - PURL: "pkg:apk/alpine/zlib@1.2.12-r3?arch=x86_64&upstream=zlib&distro=alpine-3.16.3", + Licenses: []pkg.License{ + { + Value: "Zlib", + SPDXExpression: "Zlib", + Type: license.Declared, + }, + }, + FoundBy: "sbom-cataloger", + PURL: "pkg:apk/alpine/zlib@1.2.12-r3?arch=x86_64&upstream=zlib&distro=alpine-3.16.3", CPEs: mustCPEs( "cpe:2.3:a:zlib:zlib:1.2.12-r3:*:*:*:*:*:*:*", ), diff --git a/syft/pkg/cataloger/sbom/test-fixtures/alpine/syft-json/sbom.syft.json b/syft/pkg/cataloger/sbom/test-fixtures/alpine/syft-json/sbom.syft.json index c9e83d12045..414ebb1bcdb 100644 --- a/syft/pkg/cataloger/sbom/test-fixtures/alpine/syft-json/sbom.syft.json +++ b/syft/pkg/cataloger/sbom/test-fixtures/alpine/syft-json/sbom.syft.json @@ -1278,7 +1278,6 @@ ], "licenses": [ "MPL-2.0", - "AND", "MIT" ], "language": "", @@ -1357,7 +1356,6 @@ ], "licenses": [ "BSD-2-Clause", - "AND", "BSD-3-Clause" ], "language": "", diff --git a/syft/pkg/license.go b/syft/pkg/license.go index 060df850fb2..08edd94c883 100644 --- a/syft/pkg/license.go +++ b/syft/pkg/license.go @@ -31,7 +31,7 @@ func NewLicense(value string) License { func NewLicensesFromValues(values ...string) (licenses []License) { for _, v := range values { - if v == "" { + if v == "" || v == "AND" { continue } licenses = append(licenses, NewLicense(v)) From 10e3fdd1f1fa1b6ed8dea5bb108fb169372e6615 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Wed, 26 Apr 2023 15:23:03 -0400 Subject: [PATCH 52/99] chore: remove panic potential from test Signed-off-by: Christopher Phillips --- syft/formats/cyclonedxxml/decoder_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/syft/formats/cyclonedxxml/decoder_test.go b/syft/formats/cyclonedxxml/decoder_test.go index 7a664333995..36bbdf8cb51 100644 --- a/syft/formats/cyclonedxxml/decoder_test.go +++ b/syft/formats/cyclonedxxml/decoder_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func Test_decodeXML(t *testing.T) { @@ -34,7 +35,7 @@ func Test_decodeXML(t *testing.T) { for _, test := range tests { t.Run(test.file, func(t *testing.T) { reader, err := os.Open("test-fixtures/" + test.file) - assert.NoError(t, err) + require.NoError(t, err) if test.err { err = Format().Validate(reader) @@ -44,7 +45,7 @@ func Test_decodeXML(t *testing.T) { bom, err := Format().Decode(reader) - assert.NoError(t, err) + require.NoError(t, err) split := strings.SplitN(test.distro, ":", 2) name := split[0] From 33227b929b6c2cf29e57d648810ca4e4075e0c1e Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Wed, 26 Apr 2023 15:36:10 -0400 Subject: [PATCH 53/99] feat: schema licenses update Signed-off-by: Christopher Phillips --- schema/json/schema-8.0.0.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/schema/json/schema-8.0.0.json b/schema/json/schema-8.0.0.json index 8013dfa9f0f..d9e32f38bbf 100644 --- a/schema/json/schema-8.0.0.json +++ b/schema/json/schema-8.0.0.json @@ -1816,7 +1816,7 @@ "target" ] }, - "customLicense": { + "licenses": { "items": { "$ref": "#/$defs/License" }, From cfff14ea4638c96deccd175205199e85a547b5d5 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 26 Apr 2023 17:25:17 -0400 Subject: [PATCH 54/99] license locations have the same id across layers Signed-off-by: Alex Goodman --- syft/pkg/license.go | 22 ++++++++++++++ syft/pkg/license_test.go | 30 +++++++++++++++++++ .../integration/package_deduplication_test.go | 1 + 3 files changed, 53 insertions(+) create mode 100644 syft/pkg/license_test.go diff --git a/syft/pkg/license.go b/syft/pkg/license.go index 08edd94c883..839a5ded97d 100644 --- a/syft/pkg/license.go +++ b/syft/pkg/license.go @@ -1,6 +1,8 @@ package pkg import ( + "github.com/mitchellh/hashstructure/v2" + "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/source" @@ -70,3 +72,23 @@ func NewLicenseFromURL(value string, url string) License { l.URL = url return l } + +// this is a bit of a hack to not infinitely recurse when hashing a license +type noLayerLicense License + +func (s License) Hash() (uint64, error) { + if s.Location != nil { + l := *s.Location + // much like the location set hash function, we should not consider the file system ID when hashing + // so that licenses found in different layers (at the same path) are not considered different. + l.FileSystemID = "" + s.Location = &l + } + + return hashstructure.Hash(noLayerLicense(s), hashstructure.FormatV2, + &hashstructure.HashOptions{ + ZeroNil: true, + SlicesAsSets: true, + }, + ) +} diff --git a/syft/pkg/license_test.go b/syft/pkg/license_test.go new file mode 100644 index 00000000000..d9e40ddce11 --- /dev/null +++ b/syft/pkg/license_test.go @@ -0,0 +1,30 @@ +package pkg + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/source" +) + +func Test_Hash(t *testing.T) { + + loc1 := source.NewLocation("place!") + loc1.FileSystemID = "fs1" + loc2 := source.NewLocation("place!") + loc2.FileSystemID = "fs2" // important! there is a different file system ID + + lic1 := NewLicenseFromLocation("MIT", loc1) + lic2 := NewLicenseFromLocation("MIT", loc2) + + hash1, err := artifact.IDByHash(lic1) + require.NoError(t, err) + + hash2, err := artifact.IDByHash(lic2) + require.NoError(t, err) + + assert.Equal(t, hash1, hash2) +} diff --git a/test/integration/package_deduplication_test.go b/test/integration/package_deduplication_test.go index 1d33062d53b..ff860c6293a 100644 --- a/test/integration/package_deduplication_test.go +++ b/test/integration/package_deduplication_test.go @@ -77,6 +77,7 @@ func TestPackageDeduplication(t *testing.T) { // with multiple packages with the same name, something is wrong (or this is the wrong fixture) require.Len(t, pkgs, expectedInstanceCount) + for _, p := range pkgs { nameVersion := fmt.Sprintf("%s-%s", name, p.Version) expectedLocationCount, ok := tt.locationCount[nameVersion] From 27c8b62cc69090da1ed9e4bd69ef651d1a4c2513 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Wed, 26 Apr 2023 16:52:56 -0400 Subject: [PATCH 55/99] feat: protect against deprecated license Signed-off-by: Christopher Phillips --- syft/file/license.go | 1 - syft/license/license.go | 10 +++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/syft/file/license.go b/syft/file/license.go index 525141cbdc3..fc43a26f3e9 100644 --- a/syft/file/license.go +++ b/syft/file/license.go @@ -25,7 +25,6 @@ func NewLicense(value string) License { log.Trace("unable to parse license expression: %w", err) } - // TODO: run location against classifier to form evidence return License{ Value: value, SPDXExpression: spdxExpression, diff --git a/syft/license/license.go b/syft/license/license.go index 7f971aa5fa7..a1e6c3b2c39 100644 --- a/syft/license/license.go +++ b/syft/license/license.go @@ -5,6 +5,8 @@ import ( "fmt" "github.com/github/go-spdx/v2/spdxexp" + + "github.com/anchore/syft/internal/spdxlicense" ) type Type string @@ -15,8 +17,14 @@ const ( ) func ParseExpression(expression string) (string, error) { - // returns true if all licenses are valid + licenseID, exists := spdxlicense.ID(expression) + if exists { + return licenseID, nil + } + + // If it doesn't exist initially in the SPDX list it might be a more complex expression // ignored variable is any invalid expressions + // TODO: contribute to spdxexp to expose deprecated license IDs valid, _ := spdxexp.ValidateLicenses([]string{expression}) if !valid { return "", fmt.Errorf("failed to validate spdx expression: %s", expression) From 742bbaac853beff8e9021e065ee48705521bbf5f Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Thu, 27 Apr 2023 09:49:13 -0400 Subject: [PATCH 56/99] feat: regenerate new merged schema Signed-off-by: Christopher Phillips --- schema/json/schema-8.0.0.json | 1864 +++++++++++++++++++++++++++++++++ 1 file changed, 1864 insertions(+) create mode 100644 schema/json/schema-8.0.0.json diff --git a/schema/json/schema-8.0.0.json b/schema/json/schema-8.0.0.json new file mode 100644 index 00000000000..e617e229301 --- /dev/null +++ b/schema/json/schema-8.0.0.json @@ -0,0 +1,1864 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/anchore/syft/syft/formats/syftjson/model/document", + "$ref": "#/$defs/Document", + "$defs": { + "AlpmFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "type": { + "type": "string" + }, + "uid": { + "type": "string" + }, + "gid": { + "type": "string" + }, + "time": { + "type": "string", + "format": "date-time" + }, + "size": { + "type": "string" + }, + "link": { + "type": "string" + }, + "digest": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object" + }, + "AlpmMetadata": { + "properties": { + "basepackage": { + "type": "string" + }, + "package": { + "type": "string" + }, + "version": { + "type": "string" + }, + "description": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "packager": { + "type": "string" + }, + "url": { + "type": "string" + }, + "validation": { + "type": "string" + }, + "reason": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/AlpmFileRecord" + }, + "type": "array" + }, + "backup": { + "items": { + "$ref": "#/$defs/AlpmFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "basepackage", + "package", + "version", + "description", + "architecture", + "size", + "packager", + "url", + "validation", + "reason", + "files", + "backup" + ] + }, + "ApkFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "ownerUid": { + "type": "string" + }, + "ownerGid": { + "type": "string" + }, + "permissions": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "ApkMetadata": { + "properties": { + "package": { + "type": "string" + }, + "originPackage": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "version": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "url": { + "type": "string" + }, + "description": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "installedSize": { + "type": "integer" + }, + "pullDependencies": { + "items": { + "type": "string" + }, + "type": "array" + }, + "provides": { + "items": { + "type": "string" + }, + "type": "array" + }, + "pullChecksum": { + "type": "string" + }, + "gitCommitOfApkPort": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/ApkFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "originPackage", + "maintainer", + "version", + "architecture", + "url", + "description", + "size", + "installedSize", + "pullDependencies", + "provides", + "pullChecksum", + "gitCommitOfApkPort", + "files" + ] + }, + "BinaryMetadata": { + "properties": { + "matches": { + "items": { + "$ref": "#/$defs/ClassifierMatch" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "matches" + ] + }, + "CargoPackageMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "type": "string" + }, + "checksum": { + "type": "string" + }, + "dependencies": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "checksum", + "dependencies" + ] + }, + "ClassifierMatch": { + "properties": { + "classifier": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Location" + } + }, + "type": "object", + "required": [ + "classifier", + "location" + ] + }, + "CocoapodsMetadata": { + "properties": { + "checksum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "checksum" + ] + }, + "ConanLockMetadata": { + "properties": { + "ref": { + "type": "string" + }, + "package_id": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "requires": { + "type": "string" + }, + "build_requires": { + "type": "string" + }, + "py_requires": { + "type": "string" + }, + "options": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "path": { + "type": "string" + }, + "context": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "ConanMetadata": { + "properties": { + "ref": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "Coordinates": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "DartPubMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "hosted_url": { + "type": "string" + }, + "vcs_url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Descriptor": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "configuration": true + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Digest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algorithm", + "value" + ] + }, + "Document": { + "properties": { + "artifacts": { + "items": { + "$ref": "#/$defs/Package" + }, + "type": "array" + }, + "artifactRelationships": { + "items": { + "$ref": "#/$defs/Relationship" + }, + "type": "array" + }, + "files": { + "items": { + "$ref": "#/$defs/File" + }, + "type": "array" + }, + "secrets": { + "items": { + "$ref": "#/$defs/Secrets" + }, + "type": "array" + }, + "source": { + "$ref": "#/$defs/Source" + }, + "distro": { + "$ref": "#/$defs/LinuxRelease" + }, + "descriptor": { + "$ref": "#/$defs/Descriptor" + }, + "schema": { + "$ref": "#/$defs/Schema" + } + }, + "type": "object", + "required": [ + "artifacts", + "artifactRelationships", + "source", + "distro", + "descriptor", + "schema" + ] + }, + "DotnetDepsMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "path": { + "type": "string" + }, + "sha512": { + "type": "string" + }, + "hashPath": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "path", + "sha512", + "hashPath" + ] + }, + "DpkgFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "isConfigFile": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "path", + "isConfigFile" + ] + }, + "DpkgMetadata": { + "properties": { + "package": { + "type": "string" + }, + "source": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "installedSize": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/DpkgFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "source", + "version", + "sourceVersion", + "architecture", + "maintainer", + "installedSize", + "files" + ] + }, + "File": { + "properties": { + "id": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Coordinates" + }, + "metadata": { + "$ref": "#/$defs/FileMetadataEntry" + }, + "contents": { + "type": "string" + }, + "digests": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "id", + "location" + ] + }, + "FileMetadataEntry": { + "properties": { + "mode": { + "type": "integer" + }, + "type": { + "type": "string" + }, + "linkDestination": { + "type": "string" + }, + "userID": { + "type": "integer" + }, + "groupID": { + "type": "integer" + }, + "mimeType": { + "type": "string" + }, + "size": { + "type": "integer" + } + }, + "type": "object", + "required": [ + "mode", + "type", + "userID", + "groupID", + "mimeType", + "size" + ] + }, + "GemMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "type": "string" + }, + "type": "array" + }, + "homepage": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "GolangBinMetadata": { + "properties": { + "goBuildSettings": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "goCompiledVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "h1Digest": { + "type": "string" + }, + "mainModule": { + "type": "string" + } + }, + "type": "object", + "required": [ + "goCompiledVersion", + "architecture" + ] + }, + "GolangModMetadata": { + "properties": { + "h1Digest": { + "type": "string" + } + }, + "type": "object" + }, + "HackageMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "snapshotURL": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "IDLikes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "JavaManifest": { + "properties": { + "main": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "namedSections": { + "patternProperties": { + ".*": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "JavaMetadata": { + "properties": { + "virtualPath": { + "type": "string" + }, + "manifest": { + "$ref": "#/$defs/JavaManifest" + }, + "pomProperties": { + "$ref": "#/$defs/PomProperties" + }, + "pomProject": { + "$ref": "#/$defs/PomProject" + }, + "digest": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "virtualPath" + ] + }, + "KbPackageMetadata": { + "properties": { + "product_id": { + "type": "string" + }, + "kb": { + "type": "string" + } + }, + "type": "object", + "required": [ + "product_id", + "kb" + ] + }, + "License": { + "properties": { + "value": { + "type": "string" + }, + "spdxExpression": { + "type": "string" + }, + "type": { + "type": "string" + }, + "url": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Location" + } + }, + "type": "object", + "required": [ + "value", + "spdxExpression", + "type", + "url" + ] + }, + "LinuxKernelMetadata": { + "properties": { + "name": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "version": { + "type": "string" + }, + "extendedVersion": { + "type": "string" + }, + "buildTime": { + "type": "string" + }, + "author": { + "type": "string" + }, + "format": { + "type": "string" + }, + "rwRootFS": { + "type": "boolean" + }, + "swapDevice": { + "type": "integer" + }, + "rootDevice": { + "type": "integer" + }, + "videoMode": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "architecture", + "version" + ] + }, + "LinuxKernelModuleMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "path": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "license": { + "type": "string" + }, + "kernelVersion": { + "type": "string" + }, + "versionMagic": { + "type": "string" + }, + "parameters": { + "patternProperties": { + ".*": { + "$ref": "#/$defs/LinuxKernelModuleParameter" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "LinuxKernelModuleParameter": { + "properties": { + "type": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "type": "object" + }, + "LinuxRelease": { + "properties": { + "prettyName": { + "type": "string" + }, + "name": { + "type": "string" + }, + "id": { + "type": "string" + }, + "idLike": { + "$ref": "#/$defs/IDLikes" + }, + "version": { + "type": "string" + }, + "versionID": { + "type": "string" + }, + "versionCodename": { + "type": "string" + }, + "buildID": { + "type": "string" + }, + "imageID": { + "type": "string" + }, + "imageVersion": { + "type": "string" + }, + "variant": { + "type": "string" + }, + "variantID": { + "type": "string" + }, + "homeURL": { + "type": "string" + }, + "supportURL": { + "type": "string" + }, + "bugReportURL": { + "type": "string" + }, + "privacyPolicyURL": { + "type": "string" + }, + "cpeName": { + "type": "string" + }, + "supportEnd": { + "type": "string" + } + }, + "type": "object" + }, + "Location": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + }, + "annotations": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "MixLockMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "pkgHashExt": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "pkgHash", + "pkgHashExt" + ] + }, + "NixStoreMetadata": { + "properties": { + "outputHash": { + "type": "string" + }, + "output": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "outputHash", + "files" + ] + }, + "NpmPackageJSONMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "author": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + }, + "private": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "homepage", + "description", + "url", + "private" + ] + }, + "NpmPackageLockJSONMetadata": { + "properties": { + "resolved": { + "type": "string" + }, + "integrity": { + "type": "string" + } + }, + "type": "object", + "required": [ + "resolved", + "integrity" + ] + }, + "Package": { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "type": { + "type": "string" + }, + "foundBy": { + "type": "string" + }, + "locations": { + "items": { + "$ref": "#/$defs/Location" + }, + "type": "array" + }, + "licenses": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$defs": { + "License": { + "properties": { + "value": { + "type": "string" + }, + "spdxExpression": { + "type": "string" + }, + "type": { + "type": "string" + }, + "url": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Location" + } + }, + "type": "object", + "required": [ + "value", + "spdxExpression", + "type", + "url" + ] + }, + "Location": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + }, + "annotations": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path" + ] + } + }, + "items": { + "$ref": "#/$defs/License" + }, + "type": "array" + }, + "language": { + "type": "string" + }, + "cpes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "purl": { + "type": "string" + }, + "metadataType": { + "type": "string" + }, + "metadata": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "#/$defs/AlpmMetadata" + }, + { + "$ref": "#/$defs/ApkMetadata" + }, + { + "$ref": "#/$defs/BinaryMetadata" + }, + { + "$ref": "#/$defs/CargoPackageMetadata" + }, + { + "$ref": "#/$defs/CocoapodsMetadata" + }, + { + "$ref": "#/$defs/ConanLockMetadata" + }, + { + "$ref": "#/$defs/ConanMetadata" + }, + { + "$ref": "#/$defs/DartPubMetadata" + }, + { + "$ref": "#/$defs/DotnetDepsMetadata" + }, + { + "$ref": "#/$defs/DpkgMetadata" + }, + { + "$ref": "#/$defs/GemMetadata" + }, + { + "$ref": "#/$defs/GolangBinMetadata" + }, + { + "$ref": "#/$defs/GolangModMetadata" + }, + { + "$ref": "#/$defs/HackageMetadata" + }, + { + "$ref": "#/$defs/JavaMetadata" + }, + { + "$ref": "#/$defs/KbPackageMetadata" + }, + { + "$ref": "#/$defs/LinuxKernelMetadata" + }, + { + "$ref": "#/$defs/LinuxKernelModuleMetadata" + }, + { + "$ref": "#/$defs/MixLockMetadata" + }, + { + "$ref": "#/$defs/NixStoreMetadata" + }, + { + "$ref": "#/$defs/NpmPackageJSONMetadata" + }, + { + "$ref": "#/$defs/NpmPackageLockJSONMetadata" + }, + { + "$ref": "#/$defs/PhpComposerJSONMetadata" + }, + { + "$ref": "#/$defs/PortageMetadata" + }, + { + "$ref": "#/$defs/PythonPackageMetadata" + }, + { + "$ref": "#/$defs/PythonPipfileLockMetadata" + }, + { + "$ref": "#/$defs/PythonRequirementsMetadata" + }, + { + "$ref": "#/$defs/RebarLockMetadata" + }, + { + "$ref": "#/$defs/RpmMetadata" + } + ] + } + }, + "type": "object", + "required": [ + "id", + "name", + "version", + "type", + "foundBy", + "locations", + "licenses", + "language", + "cpes", + "purl" + ] + }, + "PhpComposerAuthors": { + "properties": { + "name": { + "type": "string" + }, + "email": { + "type": "string" + }, + "homepage": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name" + ] + }, + "PhpComposerExternalReference": { + "properties": { + "type": { + "type": "string" + }, + "url": { + "type": "string" + }, + "reference": { + "type": "string" + }, + "shasum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "type", + "url", + "reference" + ] + }, + "PhpComposerJSONMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "dist": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "require": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "provide": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "require-dev": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "suggest": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "license": { + "items": { + "type": "string" + }, + "type": "array" + }, + "type": { + "type": "string" + }, + "notification-url": { + "type": "string" + }, + "bin": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "$ref": "#/$defs/PhpComposerAuthors" + }, + "type": "array" + }, + "description": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "keywords": { + "items": { + "type": "string" + }, + "type": "array" + }, + "time": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "dist" + ] + }, + "PomParent": { + "properties": { + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "type": "object", + "required": [ + "groupId", + "artifactId", + "version" + ] + }, + "PomProject": { + "properties": { + "path": { + "type": "string" + }, + "parent": { + "$ref": "#/$defs/PomParent" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "groupId", + "artifactId", + "version", + "name" + ] + }, + "PomProperties": { + "properties": { + "path": { + "type": "string" + }, + "name": { + "type": "string" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "extraFields": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path", + "name", + "groupId", + "artifactId", + "version" + ] + }, + "PortageFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "PortageMetadata": { + "properties": { + "installedSize": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/PortageFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "installedSize", + "files" + ] + }, + "PythonDirectURLOriginInfo": { + "properties": { + "url": { + "type": "string" + }, + "commitId": { + "type": "string" + }, + "vcs": { + "type": "string" + } + }, + "type": "object", + "required": [ + "url" + ] + }, + "PythonFileDigest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algorithm", + "value" + ] + }, + "PythonFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/PythonFileDigest" + }, + "size": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "PythonPackageMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "author": { + "type": "string" + }, + "authorEmail": { + "type": "string" + }, + "platform": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/PythonFileRecord" + }, + "type": "array" + }, + "sitePackagesRootPath": { + "type": "string" + }, + "topLevelPackages": { + "items": { + "type": "string" + }, + "type": "array" + }, + "directUrlOrigin": { + "$ref": "#/$defs/PythonDirectURLOriginInfo" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "authorEmail", + "platform", + "sitePackagesRootPath" + ] + }, + "PythonPipfileLockMetadata": { + "properties": { + "hashes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "index": { + "type": "string" + } + }, + "type": "object", + "required": [ + "hashes", + "index" + ] + }, + "PythonRequirementsMetadata": { + "properties": { + "name": { + "type": "string" + }, + "extras": { + "items": { + "type": "string" + }, + "type": "array" + }, + "versionConstraint": { + "type": "string" + }, + "url": { + "type": "string" + }, + "markers": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "name", + "extras", + "versionConstraint", + "url", + "markers" + ] + }, + "RebarLockMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "pkgHashExt": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "pkgHash", + "pkgHashExt" + ] + }, + "Relationship": { + "properties": { + "parent": { + "type": "string" + }, + "child": { + "type": "string" + }, + "type": { + "type": "string" + }, + "metadata": true + }, + "type": "object", + "required": [ + "parent", + "child", + "type" + ] + }, + "RpmMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "epoch": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "architecture": { + "type": "string" + }, + "release": { + "type": "string" + }, + "sourceRpm": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "vendor": { + "type": "string" + }, + "modularityLabel": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/RpmdbFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "epoch", + "architecture", + "release", + "sourceRpm", + "size", + "vendor", + "modularityLabel", + "files" + ] + }, + "RpmdbFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "mode": { + "type": "integer" + }, + "size": { + "type": "integer" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "userName": { + "type": "string" + }, + "groupName": { + "type": "string" + }, + "flags": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "mode", + "size", + "digest", + "userName", + "groupName", + "flags" + ] + }, + "Schema": { + "properties": { + "version": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "version", + "url" + ] + }, + "SearchResult": { + "properties": { + "classification": { + "type": "string" + }, + "lineNumber": { + "type": "integer" + }, + "lineOffset": { + "type": "integer" + }, + "seekPosition": { + "type": "integer" + }, + "length": { + "type": "integer" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "classification", + "lineNumber", + "lineOffset", + "seekPosition", + "length" + ] + }, + "Secrets": { + "properties": { + "location": { + "$ref": "#/$defs/Coordinates" + }, + "secrets": { + "items": { + "$ref": "#/$defs/SearchResult" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "location", + "secrets" + ] + }, + "Source": { + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string" + }, + "target": true + }, + "type": "object", + "required": [ + "id", + "type", + "target" + ] + }, + "licenses": { + "items": { + "$ref": "#/$defs/License" + }, + "type": "array" + } + } +} From 3fd38eb39517bbb601e93fd5382d6214a56eb71d Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Thu, 27 Apr 2023 10:05:29 -0400 Subject: [PATCH 57/99] chore: update unit tests with new fixtures and license values Signed-off-by: Christopher Phillips --- .../TestCycloneDxDirectoryEncoder.golden | 10 +++++----- .../snapshot/TestCycloneDxImageEncoder.golden | 16 +++++++-------- .../TestCycloneDxDirectoryEncoder.golden | 8 ++++---- .../snapshot/TestCycloneDxImageEncoder.golden | 14 ++++++------- .../TestSPDXJSONDirectoryEncoder.golden | 8 ++++---- .../snapshot/TestSPDXJSONImageEncoder.golden | 8 ++++---- .../snapshot/TestSPDXRelationshipOrder.golden | 20 +++++++++---------- .../snapshot/TestSPDXJSONSPDXIDs.golden | 6 +++--- .../snapshot/TestSPDXRelationshipOrder.golden | 20 +++++++++---------- .../TestSPDXTagValueDirectoryEncoder.golden | 8 ++++---- .../TestSPDXTagValueImageEncoder.golden | 8 ++++---- syft/pkg/cataloger/alpm/cataloger_test.go | 2 +- syft/pkg/cataloger/apkdb/parse_apk_db_test.go | 12 ++++++----- syft/pkg/cataloger/deb/cataloger_test.go | 16 ++++++++------- syft/pkg/cataloger/sbom/cataloger_test.go | 5 +++-- 15 files changed, 83 insertions(+), 78 deletions(-) diff --git a/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden b/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden index ebb1da46632..59bedc9b98c 100644 --- a/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden +++ b/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden @@ -2,10 +2,10 @@ "$schema": "http://cyclonedx.org/schema/bom-1.4.schema.json", "bomFormat": "CycloneDX", "specVersion": "1.4", - "serialNumber": "urn:uuid:redacted", + "serialNumber": "urn:uuid:5ece76b7-85ec-469e-8c91-8bef299ea8fa", "version": 1, "metadata": { - "timestamp": "timestamp:redacted", + "timestamp": "2023-04-27T10:03:32-04:00", "tools": [ { "vendor": "anchore", @@ -14,14 +14,14 @@ } ], "component": { - "bom-ref": "redacted", + "bom-ref": "163686ac6e30c752", "type": "file", "name": "/some/path" } }, "components": [ { - "bom-ref": "redacted", + "bom-ref": "3fdbdcde4d17d261", "type": "library", "name": "package-1", "version": "1.0.1", @@ -56,7 +56,7 @@ ] }, { - "bom-ref": "redacted", + "bom-ref": "pkg:deb/debian/package-2@2.0.1?package-id=db4abfe497c180d3", "type": "library", "name": "package-2", "version": "2.0.1", diff --git a/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden b/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden index e1bc23d89a2..1540ca7617c 100644 --- a/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden +++ b/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden @@ -2,10 +2,10 @@ "$schema": "http://cyclonedx.org/schema/bom-1.4.schema.json", "bomFormat": "CycloneDX", "specVersion": "1.4", - "serialNumber": "urn:uuid:redacted", + "serialNumber": "urn:uuid:32d7fb1f-f37b-4abc-91cc-15d977d750db", "version": 1, "metadata": { - "timestamp": "timestamp:redacted", + "timestamp": "2023-04-27T10:03:32-04:00", "tools": [ { "vendor": "anchore", @@ -14,15 +14,15 @@ } ], "component": { - "bom-ref": "redacted", + "bom-ref": "38160ebc2a6876e8", "type": "container", "name": "user-image-input", - "version": "sha256:redacted" + "version": "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368" } }, "components": [ { - "bom-ref": "redacted", + "bom-ref": "911cef0da9e28043", "type": "library", "name": "package-1", "version": "1.0.1", @@ -52,7 +52,7 @@ }, { "name": "syft:location:0:layerID", - "value": "sha256:redacted" + "value": "sha256:ab62016f9bec7286af65604081564cadeeb364a48faca2346c3f5a5a1f5ef777" }, { "name": "syft:location:0:path", @@ -61,7 +61,7 @@ ] }, { - "bom-ref": "redacted", + "bom-ref": "pkg:deb/debian/package-2@2.0.1?package-id=958443e2d9304af4", "type": "library", "name": "package-2", "version": "2.0.1", @@ -82,7 +82,7 @@ }, { "name": "syft:location:0:layerID", - "value": "sha256:redacted" + "value": "sha256:f1803845b6747d94d6e4ecce2331457e5f1c4fb97de5216f392a76f4582f63b2" }, { "name": "syft:location:0:path", diff --git a/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden b/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden index 137bd369489..f3a61a9c4f2 100644 --- a/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden +++ b/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden @@ -1,7 +1,7 @@ - + - redacted + 2023-04-27T10:04:54-04:00 anchore @@ -9,12 +9,12 @@ v0.42.0-bogus - + /some/path - + package-1 1.0.1 diff --git a/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden b/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden index 2c33e352903..52966f7a23c 100644 --- a/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden +++ b/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden @@ -1,7 +1,7 @@ - + - redacted + 2023-04-27T10:04:54-04:00 anchore @@ -9,13 +9,13 @@ v0.42.0-bogus - + user-image-input - redacted + sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368 - + package-1 1.0.1 @@ -28,7 +28,7 @@ python PythonPackageMetadata python - redacted + sha256:ab62016f9bec7286af65604081564cadeeb364a48faca2346c3f5a5a1f5ef777 /somefile-1.txt @@ -41,7 +41,7 @@ the-cataloger-2 DpkgMetadata deb - redacted + sha256:f1803845b6747d94d6e4ecce2331457e5f1c4fb97de5216f392a76f4582f63b2 /somefile-2.txt 0 diff --git a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONDirectoryEncoder.golden b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONDirectoryEncoder.golden index 4b8fdc17693..e62150316d7 100644 --- a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONDirectoryEncoder.golden +++ b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONDirectoryEncoder.golden @@ -3,19 +3,19 @@ "dataLicense": "CC0-1.0", "SPDXID": "SPDXRef-DOCUMENT", "name": "/some/path", - "documentNamespace":"", + "documentNamespace": "https://anchore.com/syft/dir/some/path-afe11355-9222-421b-bbb1-df3792222872", "creationInfo": { - "licenseListVersion":"", + "licenseListVersion": "3.20", "creators": [ "Organization: Anchore, Inc", "Tool: syft-v0.42.0-bogus" ], - "created":"" + "created": "2023-04-27T13:57:38Z" }, "packages": [ { "name": "package-1", - "SPDXID": "SPDXRef-Package-python-package-1-ed5945af814b9a95", + "SPDXID": "SPDXRef-Package-python-package-1-3fdbdcde4d17d261", "versionInfo": "1.0.1", "downloadLocation": "NOASSERTION", "sourceInfo": "acquired package info from installed python package manifest file: /some/path/pkg1", diff --git a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONImageEncoder.golden b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONImageEncoder.golden index dc36af406d5..7b213c3c84b 100644 --- a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONImageEncoder.golden +++ b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONImageEncoder.golden @@ -3,19 +3,19 @@ "dataLicense": "CC0-1.0", "SPDXID": "SPDXRef-DOCUMENT", "name": "user-image-input", - "documentNamespace":"", + "documentNamespace": "https://anchore.com/syft/image/user-image-input-03ea2ef0-c9cf-4c82-a3bd-095fa6b34bdb", "creationInfo": { - "licenseListVersion":"", + "licenseListVersion": "3.20", "creators": [ "Organization: Anchore, Inc", "Tool: syft-v0.42.0-bogus" ], - "created":"" + "created": "2023-04-27T13:57:38Z" }, "packages": [ { "name": "package-1", - "SPDXID": "SPDXRef-Package-python-package-1-ed77c5cb0567a1a6", + "SPDXID": "SPDXRef-Package-python-package-1-911cef0da9e28043", "versionInfo": "1.0.1", "downloadLocation": "NOASSERTION", "sourceInfo": "acquired package info from installed python package manifest file: /somefile-1.txt", diff --git a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden index 79a3fc45909..89babba2dd8 100644 --- a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden +++ b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden @@ -3,19 +3,19 @@ "dataLicense": "CC0-1.0", "SPDXID": "SPDXRef-DOCUMENT", "name": "user-image-input", - "documentNamespace":"", + "documentNamespace": "https://anchore.com/syft/image/user-image-input-c20c2bcd-33a1-42cc-b3de-2c8cf5534d01", "creationInfo": { - "licenseListVersion":"", + "licenseListVersion": "3.20", "creators": [ "Organization: Anchore, Inc", "Tool: syft-v0.42.0-bogus" ], - "created":"" + "created": "2023-04-27T13:57:38Z" }, "packages": [ { "name": "package-1", - "SPDXID": "SPDXRef-Package-python-package-1-ed77c5cb0567a1a6", + "SPDXID": "SPDXRef-Package-python-package-1-911cef0da9e28043", "versionInfo": "1.0.1", "downloadLocation": "NOASSERTION", "sourceInfo": "acquired package info from installed python package manifest file: /somefile-1.txt", @@ -152,32 +152,32 @@ ], "relationships": [ { - "spdxElementId": "SPDXRef-Package-python-package-1-ed77c5cb0567a1a6", + "spdxElementId": "SPDXRef-Package-python-package-1-911cef0da9e28043", "relatedSpdxElement": "SPDXRef-5265a4dde3edbf7c", "relationshipType": "CONTAINS" }, { - "spdxElementId": "SPDXRef-Package-python-package-1-ed77c5cb0567a1a6", + "spdxElementId": "SPDXRef-Package-python-package-1-911cef0da9e28043", "relatedSpdxElement": "SPDXRef-839d99ee67d9d174", "relationshipType": "CONTAINS" }, { - "spdxElementId": "SPDXRef-Package-python-package-1-ed77c5cb0567a1a6", + "spdxElementId": "SPDXRef-Package-python-package-1-911cef0da9e28043", "relatedSpdxElement": "SPDXRef-9c2f7510199b17f6", "relationshipType": "CONTAINS" }, { - "spdxElementId": "SPDXRef-Package-python-package-1-ed77c5cb0567a1a6", + "spdxElementId": "SPDXRef-Package-python-package-1-911cef0da9e28043", "relatedSpdxElement": "SPDXRef-c641caa71518099f", "relationshipType": "CONTAINS" }, { - "spdxElementId": "SPDXRef-Package-python-package-1-ed77c5cb0567a1a6", + "spdxElementId": "SPDXRef-Package-python-package-1-911cef0da9e28043", "relatedSpdxElement": "SPDXRef-c6f5b29dca12661f", "relationshipType": "CONTAINS" }, { - "spdxElementId": "SPDXRef-Package-python-package-1-ed77c5cb0567a1a6", + "spdxElementId": "SPDXRef-Package-python-package-1-911cef0da9e28043", "relatedSpdxElement": "SPDXRef-f9e49132a4b96ccd", "relationshipType": "CONTAINS" }, diff --git a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXJSONSPDXIDs.golden b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXJSONSPDXIDs.golden index 9a3ba9cdb3b..ada7bbd2d89 100644 --- a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXJSONSPDXIDs.golden +++ b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXJSONSPDXIDs.golden @@ -2,11 +2,11 @@ SPDXVersion: SPDX-2.3 DataLicense: CC0-1.0 SPDXID: SPDXRef-DOCUMENT DocumentName: foobar/baz -redacted -redacted +DocumentNamespace: https://anchore.com/syft/dir/foobar/baz-c24cdbf9-e3f7-4624-9ea0-f66db3c70cbc +LicenseListVersion: 3.20 Creator: Organization: Anchore, Inc Creator: Tool: syft-v0.42.0-bogus -redacted +Created: 2023-04-27T13:58:15Z ##### Package: @at-sign diff --git a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden index f90c82e2d05..7f0e8c89f78 100644 --- a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden +++ b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden @@ -2,11 +2,11 @@ SPDXVersion: SPDX-2.3 DataLicense: CC0-1.0 SPDXID: SPDXRef-DOCUMENT DocumentName: user-image-input -redacted -redacted +DocumentNamespace: https://anchore.com/syft/image/user-image-input-51400c36-c4ca-4d24-b03b-60dbddc8e637 +LicenseListVersion: 3.20 Creator: Organization: Anchore, Inc Creator: Tool: syft-v0.42.0-bogus -redacted +Created: 2023-04-27T13:58:15Z ##### Unpackaged files @@ -63,7 +63,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-ed77c5cb0567a1a6 +SPDXID: SPDXRef-Package-python-package-1-911cef0da9e28043 PackageVersion: 1.0.1 PackageDownloadLocation: NOASSERTION FilesAnalyzed: false @@ -76,11 +76,11 @@ ExternalRef: PACKAGE-MANAGER purl a-purl-1 ##### Relationships -Relationship: SPDXRef-Package-python-package-1-ed77c5cb0567a1a6 CONTAINS SPDXRef-5265a4dde3edbf7c -Relationship: SPDXRef-Package-python-package-1-ed77c5cb0567a1a6 CONTAINS SPDXRef-839d99ee67d9d174 -Relationship: SPDXRef-Package-python-package-1-ed77c5cb0567a1a6 CONTAINS SPDXRef-9c2f7510199b17f6 -Relationship: SPDXRef-Package-python-package-1-ed77c5cb0567a1a6 CONTAINS SPDXRef-c641caa71518099f -Relationship: SPDXRef-Package-python-package-1-ed77c5cb0567a1a6 CONTAINS SPDXRef-c6f5b29dca12661f -Relationship: SPDXRef-Package-python-package-1-ed77c5cb0567a1a6 CONTAINS SPDXRef-f9e49132a4b96ccd +Relationship: SPDXRef-Package-python-package-1-911cef0da9e28043 CONTAINS SPDXRef-5265a4dde3edbf7c +Relationship: SPDXRef-Package-python-package-1-911cef0da9e28043 CONTAINS SPDXRef-839d99ee67d9d174 +Relationship: SPDXRef-Package-python-package-1-911cef0da9e28043 CONTAINS SPDXRef-9c2f7510199b17f6 +Relationship: SPDXRef-Package-python-package-1-911cef0da9e28043 CONTAINS SPDXRef-c641caa71518099f +Relationship: SPDXRef-Package-python-package-1-911cef0da9e28043 CONTAINS SPDXRef-c6f5b29dca12661f +Relationship: SPDXRef-Package-python-package-1-911cef0da9e28043 CONTAINS SPDXRef-f9e49132a4b96ccd Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-DOCUMENT diff --git a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueDirectoryEncoder.golden b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueDirectoryEncoder.golden index be06bd19198..894a07becbe 100644 --- a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueDirectoryEncoder.golden +++ b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueDirectoryEncoder.golden @@ -2,11 +2,11 @@ SPDXVersion: SPDX-2.3 DataLicense: CC0-1.0 SPDXID: SPDXRef-DOCUMENT DocumentName: /some/path -redacted -redacted +DocumentNamespace: https://anchore.com/syft/dir/some/path-8f4a87ea-f0d1-4541-bef9-5ea1892a42d9 +LicenseListVersion: 3.20 Creator: Organization: Anchore, Inc Creator: Tool: syft-v0.42.0-bogus -redacted +Created: 2023-04-27T13:58:14Z ##### Package: package-2 @@ -25,7 +25,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-ed5945af814b9a95 +SPDXID: SPDXRef-Package-python-package-1-3fdbdcde4d17d261 PackageVersion: 1.0.1 PackageDownloadLocation: NOASSERTION FilesAnalyzed: false diff --git a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueImageEncoder.golden b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueImageEncoder.golden index 0f7c7ce59d2..d22b09f8b00 100644 --- a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueImageEncoder.golden +++ b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueImageEncoder.golden @@ -2,11 +2,11 @@ SPDXVersion: SPDX-2.3 DataLicense: CC0-1.0 SPDXID: SPDXRef-DOCUMENT DocumentName: user-image-input -redacted -redacted +DocumentNamespace: https://anchore.com/syft/image/user-image-input-09fc593f-d792-49a5-bd67-10c26b1c472b +LicenseListVersion: 3.20 Creator: Organization: Anchore, Inc Creator: Tool: syft-v0.42.0-bogus -redacted +Created: 2023-04-27T13:58:15Z ##### Package: package-2 @@ -25,7 +25,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-ed77c5cb0567a1a6 +SPDXID: SPDXRef-Package-python-package-1-911cef0da9e28043 PackageVersion: 1.0.1 PackageDownloadLocation: NOASSERTION FilesAnalyzed: false diff --git a/syft/pkg/cataloger/alpm/cataloger_test.go b/syft/pkg/cataloger/alpm/cataloger_test.go index 77ed11909ce..f4eb4b034c8 100644 --- a/syft/pkg/cataloger/alpm/cataloger_test.go +++ b/syft/pkg/cataloger/alpm/cataloger_test.go @@ -24,7 +24,7 @@ func TestAlpmCataloger(t *testing.T) { Licenses: []pkg.License{ { Value: "LGPL3", - SPDXExpression: "", + SPDXExpression: "LGPL-3.0-only", Type: license.Declared, Location: &dbLocation, }, diff --git a/syft/pkg/cataloger/apkdb/parse_apk_db_test.go b/syft/pkg/cataloger/apkdb/parse_apk_db_test.go index e7615640243..ceb61b5a29a 100644 --- a/syft/pkg/cataloger/apkdb/parse_apk_db_test.go +++ b/syft/pkg/cataloger/apkdb/parse_apk_db_test.go @@ -99,8 +99,9 @@ func TestSinglePackageDetails(t *testing.T) { Type: license.Declared, }, { - Value: "GPL2+", - Type: license.Declared, + Value: "GPL2+", + SPDXExpression: "GPL-2.0-or-later", + Type: license.Declared, }, }, Type: pkg.ApkPkg, @@ -767,9 +768,10 @@ func TestMultiplePackages(t *testing.T) { Location: &location, }, { - Value: "GPL2+", - Type: license.Declared, - Location: &location, + Value: "GPL2+", + SPDXExpression: "GPL-2.0-or-later", + Type: license.Declared, + Location: &location, }, }, MetadataType: pkg.ApkMetadataType, diff --git a/syft/pkg/cataloger/deb/cataloger_test.go b/syft/pkg/cataloger/deb/cataloger_test.go index 674f12c5aa1..2715e953eb0 100644 --- a/syft/pkg/cataloger/deb/cataloger_test.go +++ b/syft/pkg/cataloger/deb/cataloger_test.go @@ -19,18 +19,20 @@ func TestDpkgCataloger(t *testing.T) { FoundBy: "dpkgdb-cataloger", Licenses: []pkg.License{ { - Value: "GPL-1", - Location: &licenseLocation, - Type: license.Declared, + Value: "GPL-1", + SPDXExpression: "GPL-1.0-only", + Location: &licenseLocation, + Type: license.Declared, }, { - Value: "GPL-2", - Location: &licenseLocation, - Type: license.Declared, + Value: "GPL-2", + SPDXExpression: "GPL-2.0-only", + Location: &licenseLocation, + Type: license.Declared, }, { Value: "LGPL-2.1", - SPDXExpression: "LGPL-2.1", + SPDXExpression: "LGPL-2.1-only", Location: &licenseLocation, Type: license.Declared, }, diff --git a/syft/pkg/cataloger/sbom/cataloger_test.go b/syft/pkg/cataloger/sbom/cataloger_test.go index 417a3800a35..21664b1a097 100644 --- a/syft/pkg/cataloger/sbom/cataloger_test.go +++ b/syft/pkg/cataloger/sbom/cataloger_test.go @@ -278,8 +278,9 @@ func Test_parseSBOM(t *testing.T) { Type: license.Declared, }, { - Value: "GPL2+", - Type: license.Declared, + Value: "GPL2+", + SPDXExpression: "GPL-2.0-or-later", + Type: license.Declared, }, }, FoundBy: "sbom-cataloger", From 031b40f32ee5c03d0916e8d9553b405f30578aa8 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Thu, 27 Apr 2023 10:33:00 -0400 Subject: [PATCH 58/99] chore: cyclonedx license format valid Signed-off-by: Christopher Phillips --- .../common/cyclonedxhelpers/licenses.go | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/syft/formats/common/cyclonedxhelpers/licenses.go b/syft/formats/common/cyclonedxhelpers/licenses.go index 33a6f57ceeb..1c71d4a29fa 100644 --- a/syft/formats/common/cyclonedxhelpers/licenses.go +++ b/syft/formats/common/cyclonedxhelpers/licenses.go @@ -10,18 +10,13 @@ import ( func encodeLicenses(p pkg.Package) *cyclonedx.Licenses { lc := cyclonedx.Licenses{} for _, l := range p.Licenses { - if l.SPDXExpression != "" { - lc = append(lc, cyclonedx.LicenseChoice{ - Expression: l.SPDXExpression, - }) - } else { - // not found so append the licenseName as is - lc = append(lc, cyclonedx.LicenseChoice{ - License: &cyclonedx.License{ - Name: l.Value, - }, - }) - } + // not found so append the licenseName as is + lc = append(lc, cyclonedx.LicenseChoice{ + License: &cyclonedx.License{ + Name: l.Value, + URL: l.URL, + }, + }) } if len(lc) > 0 { From 3c592fe7c4b801158101a381147c38a223e4dd71 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Thu, 27 Apr 2023 10:51:18 -0400 Subject: [PATCH 59/99] chore: update unit tests for valid cyclonedx Signed-off-by: Christopher Phillips --- syft/formats/common/cyclonedxhelpers/licenses.go | 10 +++++++++- .../common/cyclonedxhelpers/licenses_test.go | 15 ++++++++++++--- .../snapshot/TestCycloneDxDirectoryEncoder.golden | 9 ++++++--- .../snapshot/TestCycloneDxImageEncoder.golden | 9 ++++++--- .../snapshot/TestCycloneDxDirectoryEncoder.golden | 9 ++++++--- .../snapshot/TestCycloneDxImageEncoder.golden | 9 ++++++--- 6 files changed, 45 insertions(+), 16 deletions(-) diff --git a/syft/formats/common/cyclonedxhelpers/licenses.go b/syft/formats/common/cyclonedxhelpers/licenses.go index 1c71d4a29fa..7e368cf585e 100644 --- a/syft/formats/common/cyclonedxhelpers/licenses.go +++ b/syft/formats/common/cyclonedxhelpers/licenses.go @@ -3,16 +3,24 @@ package cyclonedxhelpers import ( "github.com/CycloneDX/cyclonedx-go" + "github.com/anchore/syft/internal/spdxlicense" "github.com/anchore/syft/syft/pkg" ) // This should be a function that just surfaces licenses already validated in the package struct func encodeLicenses(p pkg.Package) *cyclonedx.Licenses { + // TODO: if all licenses are SPDX expressions, then we can combine them into a single SPDX expression + // and use that rather than individual licenses lc := cyclonedx.Licenses{} for _, l := range p.Licenses { - // not found so append the licenseName as is + var id string + if value, exists := spdxlicense.ID(l.SPDXExpression); exists { + id = value + } + lc = append(lc, cyclonedx.LicenseChoice{ License: &cyclonedx.License{ + ID: id, Name: l.Value, URL: l.URL, }, diff --git a/syft/formats/common/cyclonedxhelpers/licenses_test.go b/syft/formats/common/cyclonedxhelpers/licenses_test.go index e847c1a732c..66500eb5034 100644 --- a/syft/formats/common/cyclonedxhelpers/licenses_test.go +++ b/syft/formats/common/cyclonedxhelpers/licenses_test.go @@ -40,7 +40,10 @@ func Test_encodeLicense(t *testing.T) { }, expected: &cyclonedx.Licenses{ { - Expression: "MIT", + License: &cyclonedx.License{ + ID: "MIT", + Name: "mit", + }, }, }, }, @@ -60,10 +63,16 @@ func Test_encodeLicense(t *testing.T) { }, expected: &cyclonedx.Licenses{ { - Expression: "MIT", + License: &cyclonedx.License{ + ID: "MIT", + Name: "mit", + }, }, { - Expression: "GPL-3.0-only", + License: &cyclonedx.License{ + ID: "GPL-3.0-only", + Name: "gpl-3.0-only", + }, }, }, }, diff --git a/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden b/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden index 59bedc9b98c..9731919cb20 100644 --- a/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden +++ b/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden @@ -2,10 +2,10 @@ "$schema": "http://cyclonedx.org/schema/bom-1.4.schema.json", "bomFormat": "CycloneDX", "specVersion": "1.4", - "serialNumber": "urn:uuid:5ece76b7-85ec-469e-8c91-8bef299ea8fa", + "serialNumber": "urn:uuid:df68c273-b086-4f71-a130-c8dca769a42c", "version": 1, "metadata": { - "timestamp": "2023-04-27T10:03:32-04:00", + "timestamp": "2023-04-27T10:45:37-04:00", "tools": [ { "vendor": "anchore", @@ -27,7 +27,10 @@ "version": "1.0.1", "licenses": [ { - "expression": "MIT" + "license": { + "id": "MIT", + "name": "MIT" + } } ], "cpe": "cpe:2.3:*:some:package:2:*:*:*:*:*:*:*", diff --git a/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden b/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden index 1540ca7617c..97f6dbd0ed7 100644 --- a/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden +++ b/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden @@ -2,10 +2,10 @@ "$schema": "http://cyclonedx.org/schema/bom-1.4.schema.json", "bomFormat": "CycloneDX", "specVersion": "1.4", - "serialNumber": "urn:uuid:32d7fb1f-f37b-4abc-91cc-15d977d750db", + "serialNumber": "urn:uuid:b395857b-1710-4503-b97f-ad9101cc66ee", "version": 1, "metadata": { - "timestamp": "2023-04-27T10:03:32-04:00", + "timestamp": "2023-04-27T10:45:37-04:00", "tools": [ { "vendor": "anchore", @@ -28,7 +28,10 @@ "version": "1.0.1", "licenses": [ { - "expression": "MIT" + "license": { + "id": "MIT", + "name": "MIT" + } } ], "cpe": "cpe:2.3:*:some:package:1:*:*:*:*:*:*:*", diff --git a/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden b/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden index f3a61a9c4f2..1b684e839fa 100644 --- a/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden +++ b/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden @@ -1,7 +1,7 @@ - + - 2023-04-27T10:04:54-04:00 + 2023-04-27T10:45:43-04:00 anchore @@ -18,7 +18,10 @@ package-1 1.0.1 - MIT + + MIT + MIT + cpe:2.3:*:some:package:2:*:*:*:*:*:*:* a-purl-2 diff --git a/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden b/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden index 52966f7a23c..2c47f559c8e 100644 --- a/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden +++ b/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden @@ -1,7 +1,7 @@ - + - 2023-04-27T10:04:54-04:00 + 2023-04-27T10:45:43-04:00 anchore @@ -19,7 +19,10 @@ package-1 1.0.1 - MIT + + MIT + MIT + cpe:2.3:*:some:package:1:*:*:*:*:*:*:* a-purl-1 From 9f61e3b5d9edb65a1871127e8f1a9e1423287b72 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Thu, 27 Apr 2023 11:01:30 -0400 Subject: [PATCH 60/99] chore: update decode to simplify cycle Signed-off-by: Christopher Phillips --- .../common/cyclonedxhelpers/licenses.go | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/syft/formats/common/cyclonedxhelpers/licenses.go b/syft/formats/common/cyclonedxhelpers/licenses.go index 7e368cf585e..0ba82f56ba4 100644 --- a/syft/formats/common/cyclonedxhelpers/licenses.go +++ b/syft/formats/common/cyclonedxhelpers/licenses.go @@ -40,24 +40,10 @@ func decodeLicenses(c *cyclonedx.Component) []pkg.License { } for _, l := range *c.Licenses { - var url string - // priority: Expression -> ID -> Name - licenseValue := l.Expression - if l.License != nil { - url = l.License.URL - switch { - case l.License.ID != "": - licenseValue = l.License.ID - case l.License.Name != "": - licenseValue = l.License.Name - } - } - - if licenseValue == "" { + if l.License == nil { continue } - - licenses = append(licenses, pkg.NewLicenseFromURL(licenseValue, url)) + licenses = append(licenses, pkg.NewLicenseFromURL(l.License.Name, l.License.URL)) } return licenses From 2c59388799bcf4c806f0f71be6822b8726219302 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Thu, 27 Apr 2023 11:19:56 -0400 Subject: [PATCH 61/99] chore: cyclonedx mutually exclusive fields updated Signed-off-by: Christopher Phillips --- .../common/cyclonedxhelpers/licenses.go | 34 +++++++++++++------ .../common/cyclonedxhelpers/licenses_test.go | 10 ++---- .../TestCycloneDxDirectoryEncoder.golden | 7 ++-- .../snapshot/TestCycloneDxImageEncoder.golden | 7 ++-- .../TestCycloneDxDirectoryEncoder.golden | 5 ++- .../snapshot/TestCycloneDxImageEncoder.golden | 5 ++- 6 files changed, 36 insertions(+), 32 deletions(-) diff --git a/syft/formats/common/cyclonedxhelpers/licenses.go b/syft/formats/common/cyclonedxhelpers/licenses.go index 0ba82f56ba4..64ff0b6c598 100644 --- a/syft/formats/common/cyclonedxhelpers/licenses.go +++ b/syft/formats/common/cyclonedxhelpers/licenses.go @@ -13,18 +13,21 @@ func encodeLicenses(p pkg.Package) *cyclonedx.Licenses { // and use that rather than individual licenses lc := cyclonedx.Licenses{} for _, l := range p.Licenses { - var id string if value, exists := spdxlicense.ID(l.SPDXExpression); exists { - id = value + lc = append(lc, cyclonedx.LicenseChoice{ + License: &cyclonedx.License{ + ID: value, + URL: l.URL, + }, + }) + } else { + lc = append(lc, cyclonedx.LicenseChoice{ + License: &cyclonedx.License{ + Name: l.Value, + URL: l.URL, + }, + }) } - - lc = append(lc, cyclonedx.LicenseChoice{ - License: &cyclonedx.License{ - ID: id, - Name: l.Value, - URL: l.URL, - }, - }) } if len(lc) > 0 { @@ -43,7 +46,16 @@ func decodeLicenses(c *cyclonedx.Component) []pkg.License { if l.License == nil { continue } - licenses = append(licenses, pkg.NewLicenseFromURL(l.License.Name, l.License.URL)) + // these fields are mutually exclusive in the spec + switch { + case l.License.ID != "": + licenses = append(licenses, pkg.NewLicenseFromURL(l.License.ID, l.License.URL)) + case l.License.Name != "": + licenses = append(licenses, pkg.NewLicenseFromURL(l.License.Name, l.License.URL)) + case l.Expression != "": + licenses = append(licenses, pkg.NewLicenseFromURL(l.Expression, l.License.URL)) + default: + } } return licenses diff --git a/syft/formats/common/cyclonedxhelpers/licenses_test.go b/syft/formats/common/cyclonedxhelpers/licenses_test.go index 66500eb5034..ec30075d4a3 100644 --- a/syft/formats/common/cyclonedxhelpers/licenses_test.go +++ b/syft/formats/common/cyclonedxhelpers/licenses_test.go @@ -41,8 +41,7 @@ func Test_encodeLicense(t *testing.T) { expected: &cyclonedx.Licenses{ { License: &cyclonedx.License{ - ID: "MIT", - Name: "mit", + ID: "MIT", }, }, }, @@ -64,14 +63,12 @@ func Test_encodeLicense(t *testing.T) { expected: &cyclonedx.Licenses{ { License: &cyclonedx.License{ - ID: "MIT", - Name: "mit", + ID: "MIT", }, }, { License: &cyclonedx.License{ - ID: "GPL-3.0-only", - Name: "gpl-3.0-only", + ID: "GPL-3.0-only", }, }, }, @@ -122,7 +119,6 @@ func TestDecodeLicenses(t *testing.T) { License: &cyclonedx.License{ ID: "MIT", }, - Expression: "MIT", }, }, }, diff --git a/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden b/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden index 9731919cb20..f2181441624 100644 --- a/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden +++ b/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden @@ -2,10 +2,10 @@ "$schema": "http://cyclonedx.org/schema/bom-1.4.schema.json", "bomFormat": "CycloneDX", "specVersion": "1.4", - "serialNumber": "urn:uuid:df68c273-b086-4f71-a130-c8dca769a42c", + "serialNumber": "urn:uuid:13076761-264d-419c-ab7d-a4ac65d6456a", "version": 1, "metadata": { - "timestamp": "2023-04-27T10:45:37-04:00", + "timestamp": "2023-04-27T11:12:15-04:00", "tools": [ { "vendor": "anchore", @@ -28,8 +28,7 @@ "licenses": [ { "license": { - "id": "MIT", - "name": "MIT" + "id": "MIT" } } ], diff --git a/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden b/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden index 97f6dbd0ed7..b6294886692 100644 --- a/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden +++ b/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden @@ -2,10 +2,10 @@ "$schema": "http://cyclonedx.org/schema/bom-1.4.schema.json", "bomFormat": "CycloneDX", "specVersion": "1.4", - "serialNumber": "urn:uuid:b395857b-1710-4503-b97f-ad9101cc66ee", + "serialNumber": "urn:uuid:4536288a-9645-4c51-ab08-e5bb638c8b7c", "version": 1, "metadata": { - "timestamp": "2023-04-27T10:45:37-04:00", + "timestamp": "2023-04-27T11:12:15-04:00", "tools": [ { "vendor": "anchore", @@ -29,8 +29,7 @@ "licenses": [ { "license": { - "id": "MIT", - "name": "MIT" + "id": "MIT" } } ], diff --git a/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden b/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden index 1b684e839fa..3c74bad6a13 100644 --- a/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden +++ b/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden @@ -1,7 +1,7 @@ - + - 2023-04-27T10:45:43-04:00 + 2023-04-27T11:12:15-04:00 anchore @@ -20,7 +20,6 @@ MIT - MIT cpe:2.3:*:some:package:2:*:*:*:*:*:*:* diff --git a/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden b/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden index 2c47f559c8e..1cb76a6ec50 100644 --- a/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden +++ b/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden @@ -1,7 +1,7 @@ - + - 2023-04-27T10:45:43-04:00 + 2023-04-27T11:12:15-04:00 anchore @@ -21,7 +21,6 @@ MIT - MIT cpe:2.3:*:some:package:1:*:*:*:*:*:*:* From 7b5f0c0f19026ee6e8c5a9aa9728dfde23737a0a Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Thu, 27 Apr 2023 15:35:09 -0400 Subject: [PATCH 62/99] chore: update tests with new case coverage for cyclonedx encoding Signed-off-by: Christopher Phillips --- .../common/cyclonedxhelpers/licenses.go | 136 +++++++++++++++--- .../common/cyclonedxhelpers/licenses_test.go | 80 ++++++++--- 2 files changed, 183 insertions(+), 33 deletions(-) diff --git a/syft/formats/common/cyclonedxhelpers/licenses.go b/syft/formats/common/cyclonedxhelpers/licenses.go index 64ff0b6c598..87082b1f1f4 100644 --- a/syft/formats/common/cyclonedxhelpers/licenses.go +++ b/syft/formats/common/cyclonedxhelpers/licenses.go @@ -1,6 +1,9 @@ package cyclonedxhelpers import ( + "fmt" + "strings" + "github.com/CycloneDX/cyclonedx-go" "github.com/anchore/syft/internal/spdxlicense" @@ -9,30 +12,35 @@ import ( // This should be a function that just surfaces licenses already validated in the package struct func encodeLicenses(p pkg.Package) *cyclonedx.Licenses { - // TODO: if all licenses are SPDX expressions, then we can combine them into a single SPDX expression - // and use that rather than individual licenses - lc := cyclonedx.Licenses{} - for _, l := range p.Licenses { - if value, exists := spdxlicense.ID(l.SPDXExpression); exists { - lc = append(lc, cyclonedx.LicenseChoice{ + spdxc, otherc, ex := separateLicenses(p) + if len(otherc) > 0 { + // found non spdx related licenses + // build individual license choices for each + // complex expressions are not combined and set as NAME fields + for _, e := range ex { + otherc = append(otherc, cyclonedx.LicenseChoice{ License: &cyclonedx.License{ - ID: value, - URL: l.URL, - }, - }) - } else { - lc = append(lc, cyclonedx.LicenseChoice{ - License: &cyclonedx.License{ - Name: l.Value, - URL: l.URL, + Name: e, }, }) } + otherc = append(otherc, spdxc...) + return &otherc + } + + if len(ex) > 0 { + // TODO: merge all complex expressions and ID into a single SPDX expression + return &cyclonedx.Licenses{ + cyclonedx.LicenseChoice{ + Expression: mergeSPDX(ex, spdxc), + }, + } } - if len(lc) > 0 { - return &lc + if len(spdxc) > 0 { + return &spdxc } + return nil } @@ -60,3 +68,97 @@ func decodeLicenses(c *cyclonedx.Component) []pkg.License { return licenses } + +func separateLicenses(p pkg.Package) (spdx, other cyclonedx.Licenses, expressions []string) { + spdxc := cyclonedx.Licenses{} + otherc := cyclonedx.Licenses{} + ex := make([]string, 0) + /* + pkg.License can be a couple of things: + - Complex SPDX expression + - Some other Valid license ID + - Some non standard non spdx license + + To determine if an expression is a singular ID we first run it against the SPDX license list. + + The weird case we run into is if there is a package with a license that is not a valid SPDX expression + and a license that is a valid complex expression. In this case we will surface the valid complex expression + as a license choice and the invalid expression as a license string. + + */ + for _, l := range p.Licenses { + // singular expression case + if value, exists := spdxlicense.ID(l.SPDXExpression); exists { + spdxc = append(spdxc, cyclonedx.LicenseChoice{ + License: &cyclonedx.License{ + ID: value, + URL: l.URL, + }, + }) + continue + } + if l.SPDXExpression != "" { + // COMPLEX EXPRESSION CASE: do we instead break the spdx expression out + // into individual licenses OR combine singular licenses into a single expression? + ex = append(ex, l.SPDXExpression) + continue + } + + // license string that are not valid spdx expressions or ids + otherc = append(otherc, cyclonedx.LicenseChoice{ + License: &cyclonedx.License{ + Name: l.Value, + URL: l.URL, + }, + }) + } + return spdxc, otherc, ex +} + +func mergeSPDX(ex []string, spdxc cyclonedx.Licenses) string { + var candidate []string + for _, e := range ex { + // if the expression does not have balanced parens add them + if !strings.HasPrefix(e, "(") && !strings.HasSuffix(e, ")") { + e = "(" + e + ")" + candidate = append(candidate, e) + } + } + + for _, l := range spdxc { + candidate = append(candidate, l.License.ID) + } + + if len(candidate) == 1 { + return reduceOuter(strings.Join(candidate, " AND ")) + } + + return strings.Join(candidate, " AND ") +} + +func reduceOuter(expression string) string { + var ( + sb strings.Builder + openCount int + ) + + for _, c := range expression { + if string(c) == "(" && openCount > 0 { + fmt.Fprintf(&sb, "%c", c) + } + if string(c) == "(" { + openCount++ + continue + } + if string(c) == ")" && openCount > 1 { + fmt.Fprintf(&sb, "%c", c) + } + if string(c) == ")" { + openCount-- + continue + } + fmt.Fprintf(&sb, "%c", c) + } + + return sb.String() +} diff --git a/syft/formats/common/cyclonedxhelpers/licenses_test.go b/syft/formats/common/cyclonedxhelpers/licenses_test.go index ec30075d4a3..8e209d648d5 100644 --- a/syft/formats/common/cyclonedxhelpers/licenses_test.go +++ b/syft/formats/common/cyclonedxhelpers/licenses_test.go @@ -23,56 +23,104 @@ func Test_encodeLicense(t *testing.T) { }, { name: "no SPDX licenses", - input: pkg.Package{ - Licenses: []pkg.License{}, - }, - expected: nil, - }, - { - name: "with SPDX license", input: pkg.Package{ Licenses: []pkg.License{ { - Value: "mit", - SPDXExpression: "MIT", + Value: "RandomLicense", }, }, }, expected: &cyclonedx.Licenses{ { License: &cyclonedx.License{ - ID: "MIT", + Name: "RandomLicense", }, }, }, }, { - name: "with SPDX license expression", + name: "single SPDX ID and Non SPDX ID", input: pkg.Package{ Licenses: []pkg.License{ { - Value: "mit", SPDXExpression: "MIT", + Value: "mit", }, { - Value: "gpl-3.0-only", - SPDXExpression: "GPL-3.0-only", + Value: "FOOBAR", }, }, }, expected: &cyclonedx.Licenses{ { License: &cyclonedx.License{ - ID: "MIT", + Name: "FOOBAR", }, }, { License: &cyclonedx.License{ - ID: "GPL-3.0-only", + ID: "MIT", }, }, }, }, + { + name: "with complex SPDX license expression", + input: pkg.Package{ + Licenses: []pkg.License{ + { + SPDXExpression: "MIT AND GPL-3.0-only", + }, + }, + }, + expected: &cyclonedx.Licenses{ + { + Expression: "MIT AND GPL-3.0-only", + }, + }, + }, + { + name: "with multiple complex SPDX license expression", + input: pkg.Package{ + Licenses: []pkg.License{ + { + SPDXExpression: "MIT AND GPL-3.0-only", + }, + { + SPDXExpression: "MIT AND GPL-3.0-only WITH Classpath-exception-2.0", + }, + }, + }, + expected: &cyclonedx.Licenses{ + { + Expression: "(MIT AND GPL-3.0-only) AND (MIT AND GPL-3.0-only WITH Classpath-exception-2.0)", + }, + }, + }, + // TODO: do we drop the non SPDX ID license and do a single expression + // OR do we keep the non SPDX ID license and do multiple licenses where the complex + // expressions are set as the NAME field? + //{ + // name: "with multiple complex SPDX license expression and a non spdx id", + // input: pkg.Package{ + // Licenses: []pkg.License{ + // { + // SPDXExpression: "MIT AND GPL-3.0-only", + // }, + // { + // SPDXExpression: "MIT AND GPL-3.0-only WITH Classpath-exception-2.0", + // }, + // { + // Value: "FOOBAR", + // }, + // }, + // }, + // expected: &cyclonedx.Licenses{ + // { + // Expression: "(MIT AND GPL-3.0-only) AND (MIT AND GPL-3.0-only WITH Classpath-exception-2.0)", + // }, + // }, + //}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { From a5f8b41dce622b54f154d611c9e3544360caf8f3 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Thu, 27 Apr 2023 16:04:37 -0400 Subject: [PATCH 63/99] chore: first set of PR review comments Signed-off-by: Christopher Phillips --- schema/json/generate.go | 9 ------- syft/formats/common/spdxhelpers/license.go | 10 +++++--- .../common/spdxhelpers/license_test.go | 24 +++++++++++++++++++ .../common/spdxhelpers/to_format_model.go | 6 ++--- .../common/spdxhelpers/to_syft_model.go | 18 ++++++++++---- 5 files changed, 47 insertions(+), 20 deletions(-) diff --git a/schema/json/generate.go b/schema/json/generate.go index 1610d0aa0c4..bbebc3cc5c0 100644 --- a/schema/json/generate.go +++ b/schema/json/generate.go @@ -74,17 +74,8 @@ func build() *jsonschema.Schema { } documentSchema := reflector.ReflectFromType(reflect.TypeOf(&syftjsonModel.Document{})) metadataSchema := reflector.ReflectFromType(reflect.TypeOf(&artifactMetadataContainer{})) - licenseSchema := reflector.ReflectFromType(reflect.TypeOf(&[]pkg.License{})) - // TODO: inject source definitions - // inject the definitions of licenses into the schema definitions - // we do this since the license tag is "-" in the syftjson model - // - is used for previous versions of syftjson where licenses could be []string - // because we migrated to a more complex struct we have to manually set this in the generate code - // in order to maintain backwards compatibility - documentSchema.Definitions["Package"].Properties.Set("licenses", licenseSchema) - // inject the definitions of all metadatas into the schema definitions var metadataNames []string diff --git a/syft/formats/common/spdxhelpers/license.go b/syft/formats/common/spdxhelpers/license.go index 4e5fa1c1cfe..10ccba29624 100644 --- a/syft/formats/common/spdxhelpers/license.go +++ b/syft/formats/common/spdxhelpers/license.go @@ -47,19 +47,23 @@ func joinLicenses(licenses []string) string { return NOASSERTION } - for i, v := range licenses { + var newLicenses []string + + for _, v := range licenses { // check if license does not start or end with parens if !strings.HasPrefix(v, "(") && !strings.HasSuffix(v, ")") { // if license contains AND, OR, or WITH, then wrap in parens if strings.Contains(v, " AND ") || strings.Contains(v, " OR ") || strings.Contains(v, " WITH ") { - licenses[i] = "(" + v + ")" + newLicenses = append(newLicenses, "("+v+")") + continue } } + newLicenses = append(newLicenses, v) } - return strings.Join(licenses, " AND ") + return strings.Join(newLicenses, " AND ") } func parseLicenses(raw []pkg.License) (concluded, declared []string) { diff --git a/syft/formats/common/spdxhelpers/license_test.go b/syft/formats/common/spdxhelpers/license_test.go index 0980da74e33..a16c6fd37b5 100644 --- a/syft/formats/common/spdxhelpers/license_test.go +++ b/syft/formats/common/spdxhelpers/license_test.go @@ -142,3 +142,27 @@ func Test_License(t *testing.T) { }) } } + +func Test_joinLicenses1(t *testing.T) { + tests := []struct { + name string + args []string + want string + }{ + { + name: "multiple licenses", + args: []string{"MIT", "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"}, + 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(tt.args), "joinLicenses(%v)", tt.args) + }) + } +} diff --git a/syft/formats/common/spdxhelpers/to_format_model.go b/syft/formats/common/spdxhelpers/to_format_model.go index 44e27429b32..2e807569039 100644 --- a/syft/formats/common/spdxhelpers/to_format_model.go +++ b/syft/formats/common/spdxhelpers/to_format_model.go @@ -522,13 +522,13 @@ func toFileTypes(metadata *source.FileMetadata) (ty []string) { func toOtherLicenses(catalog *pkg.Collection) []*spdx.OtherLicense { licenses := map[string]bool{} for _, p := range catalog.Sorted() { - d, c := parseLicenses(p.Licenses) - for _, license := range d { + declaredLicenses, concludedLicenses := parseLicenses(p.Licenses) + for _, license := range declaredLicenses { if strings.HasPrefix(license, spdxlicense.LicenseRefPrefix) { licenses[license] = true } } - for _, license := range c { + for _, license := range concludedLicenses { if strings.HasPrefix(license, spdxlicense.LicenseRefPrefix) { licenses[license] = true } diff --git a/syft/formats/common/spdxhelpers/to_syft_model.go b/syft/formats/common/spdxhelpers/to_syft_model.go index 23769875da4..16c0bebbf38 100644 --- a/syft/formats/common/spdxhelpers/to_syft_model.go +++ b/syft/formats/common/spdxhelpers/to_syft_model.go @@ -297,21 +297,29 @@ func parseSPDXLicenses(p *spdx.Package) []pkg.License { licenses := make([]pkg.License, 0) // concluded - if p.PackageLicenseConcluded != NOASSERTION && p.PackageLicenseConcluded != NONE { - l := pkg.NewLicense(p.PackageLicenseConcluded) + if p.PackageLicenseConcluded != NOASSERTION && p.PackageLicenseConcluded != NONE && p.PackageLicenseConcluded != "" { + l := pkg.NewLicense(cleanSPDXID(p.PackageLicenseConcluded)) l.Type = license.Concluded licenses = append(licenses, l) } // declared - if p.PackageLicenseDeclared != NOASSERTION && p.PackageLicenseDeclared != NONE { - licenses = append(licenses, pkg.NewLicense(p.PackageLicenseDeclared)) + if p.PackageLicenseDeclared != NOASSERTION && p.PackageLicenseDeclared != NONE && p.PackageLicenseDeclared != "" { + l := pkg.NewLicense(cleanSPDXID(p.PackageLicenseDeclared)) + l.Type = license.Declared + licenses = append(licenses, l) } - // TODO: do we need other licenses? These are at the document level return licenses } +func cleanSPDXID(id string) string { + if strings.HasPrefix(id, "LicenseRef-") { + return strings.TrimPrefix(id, "LicenseRef-") + } + return id +} + //nolint:funlen func extractMetadata(p *spdx.Package, info pkgInfo) (pkg.MetadataType, interface{}) { arch := info.qualifierValue(pkg.PURLQualifierArch) From 34355245a75ea101ef1ff7ea4c0541b7e2fd8e59 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Thu, 27 Apr 2023 16:41:36 -0400 Subject: [PATCH 64/99] chore: PR feedback surrounding license constructor usage Signed-off-by: Christopher Phillips --- syft/file/license.go | 1 - .../common/cyclonedxhelpers/licenses_test.go | 25 +-- syft/formats/common/spdxhelpers/license.go | 8 +- .../common/spdxhelpers/license_test.go | 55 ++---- .../spdxhelpers/to_format_model_test.go | 16 +- syft/formats/internal/testutils/utils.go | 20 +-- syft/formats/syftjson/encoder_test.go | 9 +- syft/pkg/cataloger/alpm/cataloger_test.go | 15 +- syft/pkg/cataloger/apkdb/parse_apk_db_test.go | 56 +------ syft/pkg/cataloger/deb/cataloger_test.go | 22 +-- .../php/parse_installed_json_test.go | 13 +- syft/pkg/cataloger/sbom/cataloger_test.go | 156 ++++-------------- 12 files changed, 76 insertions(+), 320 deletions(-) diff --git a/syft/file/license.go b/syft/file/license.go index fc43a26f3e9..2d5841b11db 100644 --- a/syft/file/license.go +++ b/syft/file/license.go @@ -19,7 +19,6 @@ type LicenseEvidence struct { } func NewLicense(value string) License { - // TODO: enhance license package with more helpers for validation spdxExpression, err := license.ParseExpression(value) if err != nil { log.Trace("unable to parse license expression: %w", err) diff --git a/syft/formats/common/cyclonedxhelpers/licenses_test.go b/syft/formats/common/cyclonedxhelpers/licenses_test.go index 8e209d648d5..ce7616e336f 100644 --- a/syft/formats/common/cyclonedxhelpers/licenses_test.go +++ b/syft/formats/common/cyclonedxhelpers/licenses_test.go @@ -25,9 +25,7 @@ func Test_encodeLicense(t *testing.T) { name: "no SPDX licenses", input: pkg.Package{ Licenses: []pkg.License{ - { - Value: "RandomLicense", - }, + pkg.NewLicense("RandomLicense"), }, }, expected: &cyclonedx.Licenses{ @@ -42,13 +40,8 @@ func Test_encodeLicense(t *testing.T) { name: "single SPDX ID and Non SPDX ID", input: pkg.Package{ Licenses: []pkg.License{ - { - SPDXExpression: "MIT", - Value: "mit", - }, - { - Value: "FOOBAR", - }, + pkg.NewLicense("mit"), + pkg.NewLicense("FOOBAR"), }, }, expected: &cyclonedx.Licenses{ @@ -68,9 +61,7 @@ func Test_encodeLicense(t *testing.T) { name: "with complex SPDX license expression", input: pkg.Package{ Licenses: []pkg.License{ - { - SPDXExpression: "MIT AND GPL-3.0-only", - }, + pkg.NewLicense("MIT AND GPL-3.0-only"), }, }, expected: &cyclonedx.Licenses{ @@ -83,12 +74,8 @@ func Test_encodeLicense(t *testing.T) { name: "with multiple complex SPDX license expression", input: pkg.Package{ Licenses: []pkg.License{ - { - SPDXExpression: "MIT AND GPL-3.0-only", - }, - { - SPDXExpression: "MIT AND GPL-3.0-only WITH Classpath-exception-2.0", - }, + pkg.NewLicense("MIT AND GPL-3.0-only"), + pkg.NewLicense("MIT AND GPL-3.0-only WITH Classpath-exception-2.0"), }, }, expected: &cyclonedx.Licenses{ diff --git a/syft/formats/common/spdxhelpers/license.go b/syft/formats/common/spdxhelpers/license.go index 10ccba29624..218536f0772 100644 --- a/syft/formats/common/spdxhelpers/license.go +++ b/syft/formats/common/spdxhelpers/license.go @@ -67,8 +67,6 @@ func joinLicenses(licenses []string) string { } func parseLicenses(raw []pkg.License) (concluded, declared []string) { - c := make([]string, 0) - d := make([]string, 0) for _, l := range raw { var candidate string if l.SPDXExpression != "" { @@ -80,10 +78,10 @@ func parseLicenses(raw []pkg.License) (concluded, declared []string) { switch l.Type { case license.Concluded: - c = append(c, candidate) + concluded = append(concluded, candidate) case license.Declared: - d = append(d, candidate) + declared = append(declared, candidate) } } - return c, d + return concluded, declared } diff --git a/syft/formats/common/spdxhelpers/license_test.go b/syft/formats/common/spdxhelpers/license_test.go index a16c6fd37b5..d21617d7cf7 100644 --- a/syft/formats/common/spdxhelpers/license_test.go +++ b/syft/formats/common/spdxhelpers/license_test.go @@ -5,7 +5,6 @@ import ( "github.com/stretchr/testify/assert" - "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/pkg" ) @@ -29,12 +28,7 @@ func Test_License(t *testing.T) { { name: "no SPDX licenses", input: pkg.Package{ - Licenses: []pkg.License{ - { - Value: "made-up", - Type: license.Declared, - }, - }, + Licenses: []pkg.License{pkg.NewLicense("made-up")}, }, expected: struct{ concluded, declared string }{ concluded: "NOASSERTION", @@ -44,12 +38,7 @@ func Test_License(t *testing.T) { { name: "with SPDX license", input: pkg.Package{ - Licenses: []pkg.License{ - { - SPDXExpression: "MIT", - Type: license.Declared, - }, - }, + Licenses: []pkg.License{pkg.NewLicense("MIT")}, }, expected: struct { concluded string @@ -63,14 +52,8 @@ func Test_License(t *testing.T) { name: "with SPDX license expression", input: pkg.Package{ Licenses: []pkg.License{ - { - SPDXExpression: "MIT", - Type: license.Declared, - }, - { - SPDXExpression: "GPL-3.0-only", - Type: license.Declared, - }, + pkg.NewLicense("MIT"), + pkg.NewLicense("GPL-3.0-only"), }, }, expected: struct { @@ -85,18 +68,9 @@ func Test_License(t *testing.T) { name: "includes valid LicenseRef-", input: pkg.Package{ Licenses: []pkg.License{ - { - Value: "one thing first", - Type: license.Declared, - }, - { - Value: "two things/#$^second", - Type: license.Declared, - }, - { - Value: "MIT", - Type: license.Declared, - }, + pkg.NewLicense("one thing first"), + pkg.NewLicense("two things/#$^second"), + pkg.NewLicense("MIT"), }, }, expected: struct { @@ -111,18 +85,9 @@ func Test_License(t *testing.T) { name: "join parentheses correctly", input: pkg.Package{ Licenses: []pkg.License{ - { - Value: "one thing first", - Type: license.Declared, - }, - { - SPDXExpression: "(MIT AND GPL-3.0-only)", - Type: license.Declared, - }, - { - SPDXExpression: "MIT OR APACHE-2.0", - Type: license.Declared, - }, + pkg.NewLicense("one thing first"), + pkg.NewLicense("MIT AND GPL-3.0-only"), + pkg.NewLicense("MIT OR APACHE-2.0"), }, }, expected: struct { diff --git a/syft/formats/common/spdxhelpers/to_format_model_test.go b/syft/formats/common/spdxhelpers/to_format_model_test.go index ca58107c25f..23cc2542ca0 100644 --- a/syft/formats/common/spdxhelpers/to_format_model_test.go +++ b/syft/formats/common/spdxhelpers/to_format_model_test.go @@ -10,7 +10,6 @@ import ( "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/file" - "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/sbom" "github.com/anchore/syft/syft/source" @@ -456,10 +455,7 @@ func Test_OtherLicenses(t *testing.T) { name: "single licenseRef", pkg: pkg.Package{ Licenses: []pkg.License{ - { - Value: "foobar", - Type: license.Declared, // Only testing licenses not in spdx list - }, + pkg.NewLicense("foobar"), }, }, expected: []*spdx.OtherLicense{ @@ -473,14 +469,8 @@ func Test_OtherLicenses(t *testing.T) { name: "multiple licenseRef", pkg: pkg.Package{ Licenses: []pkg.License{ - { - Value: "internal made up license name", - Type: license.Declared, // Only testing licenses not in spdx list - }, - { - Value: "new apple license 2.0", - Type: license.Declared, // Only testing licenses not in spdx list - }, + pkg.NewLicense("internal made up license name"), + pkg.NewLicense("new apple license 2.0"), }, }, expected: []*spdx.OtherLicense{ diff --git a/syft/formats/internal/testutils/utils.go b/syft/formats/internal/testutils/utils.go index 8defd0e851f..a6efec20ba1 100644 --- a/syft/formats/internal/testutils/utils.go +++ b/syft/formats/internal/testutils/utils.go @@ -17,7 +17,6 @@ import ( "github.com/anchore/stereoscope/pkg/imagetest" "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/cpe" - "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/linux" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/sbom" @@ -164,12 +163,7 @@ func populateImageCatalog(catalog *pkg.Collection, img *image.Image) { Language: pkg.Python, MetadataType: pkg.PythonPackageMetadataType, Licenses: []pkg.License{ - { - Value: "MIT", - SPDXExpression: "MIT", - Type: license.Declared, - URL: "", - }, + pkg.NewLicense("MIT"), }, Metadata: pkg.PythonPackageMetadata{ Name: "package-1", @@ -277,12 +271,7 @@ func newDirectoryCatalog() *pkg.Collection { Language: pkg.Python, MetadataType: pkg.PythonPackageMetadataType, Licenses: []pkg.License{ - { - Value: "MIT", - SPDXExpression: "MIT", - Type: license.Declared, - URL: "", - }, + pkg.NewLicense("MIT"), }, Metadata: pkg.PythonPackageMetadata{ Name: "package-1", @@ -335,10 +324,7 @@ func newDirectoryCatalogWithAuthorField() *pkg.Collection { Language: pkg.Python, MetadataType: pkg.PythonPackageMetadataType, Licenses: []pkg.License{ - { - Value: "MIT", - SPDXExpression: "MIT", - }, + pkg.NewLicense("MIT"), }, Metadata: pkg.PythonPackageMetadata{ Name: "package-1", diff --git a/syft/formats/syftjson/encoder_test.go b/syft/formats/syftjson/encoder_test.go index c6210d7052c..09edb3b5bc8 100644 --- a/syft/formats/syftjson/encoder_test.go +++ b/syft/formats/syftjson/encoder_test.go @@ -10,7 +10,6 @@ import ( "github.com/anchore/syft/syft/cpe" "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/formats/internal/testutils" - "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/linux" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/sbom" @@ -62,13 +61,7 @@ func TestEncodeFullJSONDocument(t *testing.T) { FoundBy: "the-cataloger-1", Language: pkg.Python, MetadataType: pkg.PythonPackageMetadataType, - Licenses: []pkg.License{ - { - SPDXExpression: "MIT", - Value: "MIT", - Type: license.Declared, - }, - }, + Licenses: []pkg.License{pkg.NewLicense("MIT")}, Metadata: pkg.PythonPackageMetadata{ Name: "package-1", Version: "1.0.1", diff --git a/syft/pkg/cataloger/alpm/cataloger_test.go b/syft/pkg/cataloger/alpm/cataloger_test.go index f4eb4b034c8..e8478bea246 100644 --- a/syft/pkg/cataloger/alpm/cataloger_test.go +++ b/syft/pkg/cataloger/alpm/cataloger_test.go @@ -7,7 +7,6 @@ import ( "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/file" - "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" "github.com/anchore/syft/syft/source" @@ -22,18 +21,8 @@ func TestAlpmCataloger(t *testing.T) { Type: pkg.AlpmPkg, FoundBy: "alpmdb-cataloger", Licenses: []pkg.License{ - { - Value: "LGPL3", - SPDXExpression: "LGPL-3.0-only", - Type: license.Declared, - Location: &dbLocation, - }, - { - Value: "GPL", - SPDXExpression: "", - Type: license.Declared, - Location: &dbLocation, - }, + pkg.NewLicenseFromLocation("LGPL3", dbLocation), + pkg.NewLicenseFromLocation("GPL", dbLocation), }, Locations: source.NewLocationSet(dbLocation), CPEs: nil, diff --git a/syft/pkg/cataloger/apkdb/parse_apk_db_test.go b/syft/pkg/cataloger/apkdb/parse_apk_db_test.go index ceb61b5a29a..3519feaea0d 100644 --- a/syft/pkg/cataloger/apkdb/parse_apk_db_test.go +++ b/syft/pkg/cataloger/apkdb/parse_apk_db_test.go @@ -14,7 +14,6 @@ import ( "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/file" - "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/linux" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/generic" @@ -89,20 +88,9 @@ func TestSinglePackageDetails(t *testing.T) { Name: "musl-utils", Version: "1.1.24-r2", Licenses: []pkg.License{ - { - Value: "MIT", - SPDXExpression: "MIT", - Type: license.Declared, - }, - { - Value: "BSD", - Type: license.Declared, - }, - { - Value: "GPL2+", - SPDXExpression: "GPL-2.0-or-later", - Type: license.Declared, - }, + pkg.NewLicense("MIT"), + pkg.NewLicense("BSD"), + pkg.NewLicense("GPL2+"), }, Type: pkg.ApkPkg, MetadataType: pkg.ApkMetadataType, @@ -190,11 +178,7 @@ func TestSinglePackageDetails(t *testing.T) { Name: "alpine-baselayout-data", Version: "3.4.0-r0", Licenses: []pkg.License{ - { - Value: "GPL-2.0-only", - SPDXExpression: "GPL-2.0-only", - Type: license.Declared, - }, + pkg.NewLicense("GPL-2.0-only"), }, Type: pkg.ApkPkg, MetadataType: pkg.ApkMetadataType, @@ -239,11 +223,7 @@ func TestSinglePackageDetails(t *testing.T) { Name: "alpine-baselayout", Version: "3.2.0-r6", Licenses: []pkg.License{ - { - Value: "GPL-2.0-only", - SPDXExpression: "GPL-2.0-only", - Type: license.Declared, - }, + pkg.NewLicense("GPL-2.0-only"), }, Type: pkg.ApkPkg, PURL: "", @@ -722,11 +702,7 @@ func TestMultiplePackages(t *testing.T) { Name: "libc-utils", Version: "0.7.2-r0", Licenses: []pkg.License{ - { - Value: "BSD", - Type: license.Declared, - Location: &location, - }, + pkg.NewLicenseFromLocation("BSD", location), }, Type: pkg.ApkPkg, PURL: "pkg:apk/alpine/libc-utils@0.7.2-r0?arch=x86_64&upstream=libc-dev&distro=alpine-3.12", @@ -756,23 +732,9 @@ func TestMultiplePackages(t *testing.T) { PURL: "pkg:apk/alpine/musl-utils@1.1.24-r2?arch=x86_64&upstream=musl&distro=alpine-3.12", Locations: fixtureLocationSet, Licenses: []pkg.License{ - { - Value: "MIT", - SPDXExpression: "MIT", - Type: license.Declared, - Location: &location, - }, - { - Value: "BSD", - Type: license.Declared, - Location: &location, - }, - { - Value: "GPL2+", - SPDXExpression: "GPL-2.0-or-later", - Type: license.Declared, - Location: &location, - }, + pkg.NewLicenseFromLocation("MIT", location), + pkg.NewLicenseFromLocation("BSD", location), + pkg.NewLicenseFromLocation("GPL2+", location), }, MetadataType: pkg.ApkMetadataType, Metadata: pkg.ApkMetadata{ diff --git a/syft/pkg/cataloger/deb/cataloger_test.go b/syft/pkg/cataloger/deb/cataloger_test.go index 2715e953eb0..8199407f6c8 100644 --- a/syft/pkg/cataloger/deb/cataloger_test.go +++ b/syft/pkg/cataloger/deb/cataloger_test.go @@ -4,7 +4,6 @@ import ( "testing" "github.com/anchore/syft/syft/file" - "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" "github.com/anchore/syft/syft/source" @@ -18,24 +17,9 @@ func TestDpkgCataloger(t *testing.T) { Version: "1.1.8-3.6", FoundBy: "dpkgdb-cataloger", Licenses: []pkg.License{ - { - Value: "GPL-1", - SPDXExpression: "GPL-1.0-only", - Location: &licenseLocation, - Type: license.Declared, - }, - { - Value: "GPL-2", - SPDXExpression: "GPL-2.0-only", - Location: &licenseLocation, - Type: license.Declared, - }, - { - Value: "LGPL-2.1", - SPDXExpression: "LGPL-2.1-only", - Location: &licenseLocation, - Type: license.Declared, - }, + pkg.NewLicenseFromLocation("GPL-1", licenseLocation), + pkg.NewLicenseFromLocation("GPL-2", licenseLocation), + pkg.NewLicenseFromLocation("LGPL-2.1", licenseLocation), }, Locations: source.NewLocationSet( source.NewVirtualLocation("/var/lib/dpkg/status", "/var/lib/dpkg/status"), diff --git a/syft/pkg/cataloger/php/parse_installed_json_test.go b/syft/pkg/cataloger/php/parse_installed_json_test.go index c0866cf7b14..df5ba82f06d 100644 --- a/syft/pkg/cataloger/php/parse_installed_json_test.go +++ b/syft/pkg/cataloger/php/parse_installed_json_test.go @@ -4,7 +4,6 @@ import ( "testing" "github.com/anchore/syft/syft/artifact" - "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" "github.com/anchore/syft/syft/source" @@ -26,11 +25,7 @@ func TestParseInstalledJsonComposerV1(t *testing.T) { Type: pkg.PhpComposerPkg, MetadataType: pkg.PhpComposerJSONMetadataType, Licenses: []pkg.License{ - { - Value: "MIT", - SPDXExpression: "MIT", - Type: license.Declared, - }, + pkg.NewLicense("MIT"), }, Metadata: pkg.PhpComposerJSONMetadata{ Name: "asm89/stack-cors", @@ -79,11 +74,7 @@ func TestParseInstalledJsonComposerV1(t *testing.T) { Language: pkg.PHP, Type: pkg.PhpComposerPkg, Licenses: []pkg.License{ - { - Value: "MIT", - SPDXExpression: "MIT", - Type: license.Declared, - }, + pkg.NewLicense("MIT"), }, MetadataType: pkg.PhpComposerJSONMetadataType, Metadata: pkg.PhpComposerJSONMetadata{ diff --git a/syft/pkg/cataloger/sbom/cataloger_test.go b/syft/pkg/cataloger/sbom/cataloger_test.go index 21664b1a097..3d29bfaa3f7 100644 --- a/syft/pkg/cataloger/sbom/cataloger_test.go +++ b/syft/pkg/cataloger/sbom/cataloger_test.go @@ -8,7 +8,6 @@ import ( "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/cpe" "github.com/anchore/syft/syft/formats/syftjson" - "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" "github.com/anchore/syft/syft/sbom" @@ -39,15 +38,9 @@ func Test_parseSBOM(t *testing.T) { Version: "3.2.0-r23", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []pkg.License{ - { - Value: "GPL-2.0-only", - SPDXExpression: "GPL-2.0-only", - Type: license.Declared, - }, - }, - FoundBy: "sbom-cataloger", - PURL: "pkg:apk/alpine/alpine-baselayout@3.2.0-r23?arch=x86_64&upstream=alpine-baselayout&distro=alpine-3.16.3", + Licenses: []pkg.License{pkg.NewLicense("GPL-2.0-only")}, + FoundBy: "sbom-cataloger", + PURL: "pkg:apk/alpine/alpine-baselayout@3.2.0-r23?arch=x86_64&upstream=alpine-baselayout&distro=alpine-3.16.3", CPEs: mustCPEs( "cpe:2.3:a:alpine-baselayout:alpine-baselayout:3.2.0-r23:*:*:*:*:*:*:*", "cpe:2.3:a:alpine-baselayout:alpine_baselayout:3.2.0-r23:*:*:*:*:*:*:*", @@ -62,15 +55,9 @@ func Test_parseSBOM(t *testing.T) { Version: "3.2.0-r23", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []pkg.License{ - { - Value: "GPL-2.0-only", - SPDXExpression: "GPL-2.0-only", - Type: license.Declared, - }, - }, - FoundBy: "sbom-cataloger", - PURL: "pkg:apk/alpine/alpine-baselayout-data@3.2.0-r23?arch=x86_64&upstream=alpine-baselayout&distro=alpine-3.16.3", + Licenses: []pkg.License{pkg.NewLicense("GPL-2.0-only")}, + FoundBy: "sbom-cataloger", + PURL: "pkg:apk/alpine/alpine-baselayout-data@3.2.0-r23?arch=x86_64&upstream=alpine-baselayout&distro=alpine-3.16.3", CPEs: mustCPEs( "cpe:2.3:a:alpine-baselayout-data:alpine-baselayout-data:3.2.0-r23:*:*:*:*:*:*:*", "cpe:2.3:a:alpine-baselayout-data:alpine_baselayout_data:3.2.0-r23:*:*:*:*:*:*:*", @@ -89,15 +76,9 @@ func Test_parseSBOM(t *testing.T) { Version: "2.4-r1", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []pkg.License{ - { - Value: "MIT", - SPDXExpression: "MIT", - Type: license.Declared, - }, - }, - FoundBy: "sbom-cataloger", - PURL: "pkg:apk/alpine/alpine-keys@2.4-r1?arch=x86_64&upstream=alpine-keys&distro=alpine-3.16.3", + Licenses: []pkg.License{pkg.NewLicense("MIT")}, + FoundBy: "sbom-cataloger", + PURL: "pkg:apk/alpine/alpine-keys@2.4-r1?arch=x86_64&upstream=alpine-keys&distro=alpine-3.16.3", CPEs: mustCPEs( "cpe:2.3:a:alpine-keys:alpine-keys:2.4-r1:*:*:*:*:*:*:*", "cpe:2.3:a:alpine-keys:alpine_keys:2.4-r1:*:*:*:*:*:*:*", @@ -112,15 +93,9 @@ func Test_parseSBOM(t *testing.T) { Version: "2.12.9-r3", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []pkg.License{ - { - Value: "GPL-2.0-only", - SPDXExpression: "GPL-2.0-only", - Type: license.Declared, - }, - }, - FoundBy: "sbom-cataloger", - PURL: "pkg:apk/alpine/apk-tools@2.12.9-r3?arch=x86_64&upstream=apk-tools&distro=alpine-3.16.3", + Licenses: []pkg.License{pkg.NewLicense("GPL-2.0-only")}, + FoundBy: "sbom-cataloger", + PURL: "pkg:apk/alpine/apk-tools@2.12.9-r3?arch=x86_64&upstream=apk-tools&distro=alpine-3.16.3", CPEs: mustCPEs( "cpe:2.3:a:apk-tools:apk-tools:2.12.9-r3:*:*:*:*:*:*:*", "cpe:2.3:a:apk-tools:apk_tools:2.12.9-r3:*:*:*:*:*:*:*", @@ -135,15 +110,9 @@ func Test_parseSBOM(t *testing.T) { Version: "1.35.0-r17", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []pkg.License{ - { - Value: "GPL-2.0-only", - SPDXExpression: "GPL-2.0-only", - Type: license.Declared, - }, - }, - FoundBy: "sbom-cataloger", - PURL: "pkg:apk/alpine/busybox@1.35.0-r17?arch=x86_64&upstream=busybox&distro=alpine-3.16.3", + Licenses: []pkg.License{pkg.NewLicense("GPL-2.0-only")}, + FoundBy: "sbom-cataloger", + PURL: "pkg:apk/alpine/busybox@1.35.0-r17?arch=x86_64&upstream=busybox&distro=alpine-3.16.3", CPEs: mustCPEs( "cpe:2.3:a:busybox:busybox:1.35.0-r17:*:*:*:*:*:*:*", ), @@ -154,16 +123,8 @@ func Test_parseSBOM(t *testing.T) { Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), Licenses: []pkg.License{ - { - Value: "MPL-2.0", - SPDXExpression: "MPL-2.0", - Type: license.Declared, - }, - { - Value: "MIT", - SPDXExpression: "MIT", - Type: license.Declared, - }, + pkg.NewLicense("MPL-2.0"), + pkg.NewLicense("MIT"), }, FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/ca-certificates-bundle@20220614-r0?arch=x86_64&upstream=ca-certificates&distro=alpine-3.16.3", @@ -186,16 +147,8 @@ func Test_parseSBOM(t *testing.T) { Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), Licenses: []pkg.License{ - { - Value: "BSD-2-Clause", - SPDXExpression: "BSD-2-Clause", - Type: license.Declared, - }, - { - Value: "BSD-3-Clause", - SPDXExpression: "BSD-3-Clause", - Type: license.Declared, - }, + pkg.NewLicense("BSD-2-Clause"), + pkg.NewLicense("BSD-3-Clause"), }, FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/libc-utils@0.7.2-r3?arch=x86_64&upstream=libc-dev&distro=alpine-3.16.3", @@ -213,15 +166,9 @@ func Test_parseSBOM(t *testing.T) { Version: "1.1.1s-r0", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []pkg.License{ - { - Value: "OpenSSL", - SPDXExpression: "OpenSSL", - Type: license.Declared, - }, - }, - FoundBy: "sbom-cataloger", - PURL: "pkg:apk/alpine/libcrypto1.1@1.1.1s-r0?arch=x86_64&upstream=openssl&distro=alpine-3.16.3", + Licenses: []pkg.License{pkg.NewLicense("OpenSSL")}, // SPDX expression is not set + FoundBy: "sbom-cataloger", + PURL: "pkg:apk/alpine/libcrypto1.1@1.1.1s-r0?arch=x86_64&upstream=openssl&distro=alpine-3.16.3", CPEs: mustCPEs( "cpe:2.3:a:libcrypto1.1:libcrypto1.1:1.1.1s-r0:*:*:*:*:*:*:*", ), @@ -231,15 +178,9 @@ func Test_parseSBOM(t *testing.T) { Version: "1.1.1s-r0", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []pkg.License{ - { - Value: "OpenSSL", - SPDXExpression: "OpenSSL", - Type: license.Declared, - }, - }, - FoundBy: "sbom-cataloger", - PURL: "pkg:apk/alpine/libssl1.1@1.1.1s-r0?arch=x86_64&upstream=openssl&distro=alpine-3.16.3", + Licenses: []pkg.License{pkg.NewLicense("OpenSSL")}, // SPDX expression is not set + FoundBy: "sbom-cataloger", + PURL: "pkg:apk/alpine/libssl1.1@1.1.1s-r0?arch=x86_64&upstream=openssl&distro=alpine-3.16.3", CPEs: mustCPEs( "cpe:2.3:a:libssl1.1:libssl1.1:1.1.1s-r0:*:*:*:*:*:*:*", ), @@ -249,15 +190,9 @@ func Test_parseSBOM(t *testing.T) { Version: "1.2.3-r1", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []pkg.License{ - { - Value: "MIT", - SPDXExpression: "MIT", - Type: license.Declared, - }, - }, - FoundBy: "sbom-cataloger", - PURL: "pkg:apk/alpine/musl@1.2.3-r1?arch=x86_64&upstream=musl&distro=alpine-3.16.3", + Licenses: []pkg.License{pkg.NewLicense("MIT")}, // SPDX expression is not set + FoundBy: "sbom-cataloger", + PURL: "pkg:apk/alpine/musl@1.2.3-r1?arch=x86_64&upstream=musl&distro=alpine-3.16.3", CPEs: mustCPEs( "cpe:2.3:a:musl:musl:1.2.3-r1:*:*:*:*:*:*:*", ), @@ -268,20 +203,9 @@ func Test_parseSBOM(t *testing.T) { Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), Licenses: []pkg.License{ - { - Value: "MIT", - SPDXExpression: "MIT", - Type: license.Declared, - }, - { - Value: "BSD", - Type: license.Declared, - }, - { - Value: "GPL2+", - SPDXExpression: "GPL-2.0-or-later", - Type: license.Declared, - }, + pkg.NewLicense("MIT"), + pkg.NewLicense("BSD"), + pkg.NewLicense("GPL2+"), // SPDX expression is not set }, FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/musl-utils@1.2.3-r1?arch=x86_64&upstream=musl&distro=alpine-3.16.3", @@ -300,11 +224,7 @@ func Test_parseSBOM(t *testing.T) { Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), Licenses: []pkg.License{ - { - Value: "GPL-2.0-only", - SPDXExpression: "GPL-2.0-only", - Type: license.Declared, - }, + pkg.NewLicense("GPL-2.0-only"), }, FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/scanelf@1.3.4-r0?arch=x86_64&upstream=pax-utils&distro=alpine-3.16.3", @@ -318,11 +238,7 @@ func Test_parseSBOM(t *testing.T) { Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), Licenses: []pkg.License{ - { - Value: "GPL-2.0-only", - SPDXExpression: "GPL-2.0-only", - Type: license.Declared, - }, + pkg.NewLicense("GPL-2.0-only"), }, FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/ssl_client@1.35.0-r17?arch=x86_64&upstream=busybox&distro=alpine-3.16.3", @@ -341,11 +257,7 @@ func Test_parseSBOM(t *testing.T) { Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), Licenses: []pkg.License{ - { - Value: "Zlib", - SPDXExpression: "Zlib", - Type: license.Declared, - }, + pkg.NewLicense("Zlib"), }, FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/zlib@1.2.12-r3?arch=x86_64&upstream=zlib&distro=alpine-3.16.3", From 75d9550a466fed8b107d1eaa2cc4a3c83c9d76b3 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Thu, 27 Apr 2023 16:45:11 -0400 Subject: [PATCH 65/99] chore: update implicit fields to named Signed-off-by: Christopher Phillips --- syft/pkg/cataloger/alpm/package_test.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/syft/pkg/cataloger/alpm/package_test.go b/syft/pkg/cataloger/alpm/package_test.go index cf0eeb3a40b..e41e88791dc 100644 --- a/syft/pkg/cataloger/alpm/package_test.go +++ b/syft/pkg/cataloger/alpm/package_test.go @@ -20,8 +20,8 @@ func Test_PackageURL(t *testing.T) { { name: "bad distro id", metadata: &parsedData{ - "", - pkg.AlpmMetadata{ + Licenses: "", + AlpmMetadata: pkg.AlpmMetadata{ Package: "p", Version: "v", Architecture: "a", @@ -36,8 +36,8 @@ func Test_PackageURL(t *testing.T) { { name: "gocase", metadata: &parsedData{ - "", - pkg.AlpmMetadata{ + Licenses: "", + AlpmMetadata: pkg.AlpmMetadata{ Package: "p", Version: "v", Architecture: "a", @@ -52,8 +52,8 @@ func Test_PackageURL(t *testing.T) { { name: "missing architecture", metadata: &parsedData{ - "", - pkg.AlpmMetadata{ + Licenses: "", + AlpmMetadata: pkg.AlpmMetadata{ Package: "p", Version: "v", }, @@ -65,8 +65,8 @@ func Test_PackageURL(t *testing.T) { }, { metadata: &parsedData{ - "", - pkg.AlpmMetadata{ + Licenses: "", + AlpmMetadata: pkg.AlpmMetadata{ Package: "python", Version: "3.10.0", Architecture: "any", @@ -80,8 +80,8 @@ func Test_PackageURL(t *testing.T) { }, { metadata: &parsedData{ - "", - pkg.AlpmMetadata{ + Licenses: "", + AlpmMetadata: pkg.AlpmMetadata{ Package: "g plus plus", Version: "v84", Architecture: "x86_64", @@ -96,8 +96,8 @@ func Test_PackageURL(t *testing.T) { { name: "add source information as qualifier", metadata: &parsedData{ - "", - pkg.AlpmMetadata{ + Licenses: "", + AlpmMetadata: pkg.AlpmMetadata{ Package: "p", Version: "v", Architecture: "a", From 999d1a001f89aff00ac3e175a76f37d02e908027 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Thu, 27 Apr 2023 16:50:16 -0400 Subject: [PATCH 66/99] chore: small pr feedback updates Signed-off-by: Christopher Phillips --- syft/formats/common/spdxhelpers/license_test.go | 2 +- syft/pkg/cataloger/alpm/parse_alpm_db.go | 5 +---- syft/pkg/cataloger/apkdb/package.go | 4 ++-- syft/pkg/cataloger/portage/parse_portage_contents.go | 1 - 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/syft/formats/common/spdxhelpers/license_test.go b/syft/formats/common/spdxhelpers/license_test.go index d21617d7cf7..60856f00572 100644 --- a/syft/formats/common/spdxhelpers/license_test.go +++ b/syft/formats/common/spdxhelpers/license_test.go @@ -78,7 +78,7 @@ func Test_License(t *testing.T) { declared string }{ concluded: "NOASSERTION", - declared: "LicenseRef-one-thing-first AND LicenseRef-two-things----second AND LicenseRef-MIT", + declared: "LicenseRef-one-thing-first AND LicenseRef-two-things----second AND MIT", }, }, { diff --git a/syft/pkg/cataloger/alpm/parse_alpm_db.go b/syft/pkg/cataloger/alpm/parse_alpm_db.go index 88f4769b778..987a52c076a 100644 --- a/syft/pkg/cataloger/alpm/parse_alpm_db.go +++ b/syft/pkg/cataloger/alpm/parse_alpm_db.go @@ -191,10 +191,7 @@ func parseDatabase(b *bufio.Scanner) (*parsedData, error) { } func parsePkgFiles(pkgFields map[string]interface{}) (*parsedData, error) { - var ( - entry parsedData - ) - + var entry parsedData if err := mapstructure.Decode(pkgFields, &entry); err != nil { return nil, fmt.Errorf("unable to parse ALPM metadata: %w", err) } diff --git a/syft/pkg/cataloger/apkdb/package.go b/syft/pkg/cataloger/apkdb/package.go index 5c0496ce19a..291fb20da89 100644 --- a/syft/pkg/cataloger/apkdb/package.go +++ b/syft/pkg/cataloger/apkdb/package.go @@ -17,7 +17,7 @@ func newPackage(d parsedData, release *linux.Release, dbLocation source.Location Version: d.Version, Locations: source.NewLocationSet(dbLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)), Licenses: pkg.NewLicensesFromLocation(dbLocation, licenseStrings...), - PURL: packageURL(d, release), + PURL: packageURL(d.ApkMetadata, release), Type: pkg.ApkPkg, MetadataType: pkg.ApkMetadataType, Metadata: d.ApkMetadata, @@ -29,7 +29,7 @@ func newPackage(d parsedData, release *linux.Release, dbLocation source.Location } // packageURL returns the PURL for the specific Alpine package (see https://github.com/package-url/purl-spec) -func packageURL(m parsedData, distro *linux.Release) string { +func packageURL(m pkg.ApkMetadata, distro *linux.Release) string { if distro == nil { return "" } diff --git a/syft/pkg/cataloger/portage/parse_portage_contents.go b/syft/pkg/cataloger/portage/parse_portage_contents.go index 52a68b49cd6..379c7f9fab1 100644 --- a/syft/pkg/cataloger/portage/parse_portage_contents.go +++ b/syft/pkg/cataloger/portage/parse_portage_contents.go @@ -49,7 +49,6 @@ func parsePortageContents(resolver source.FileResolver, _ *generic.Environment, Files: make([]pkg.PortageFileRecord, 0), }, } - // TODO: update this to use latest license parsing and validation tooling addLicenses(resolver, reader.Location, &p) addSize(resolver, reader.Location, &p) addFiles(resolver, reader.Location, &p) From f77d538330873bac685f1c826649ba21273f5f0e Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Thu, 27 Apr 2023 16:52:20 -0400 Subject: [PATCH 67/99] chore: update schema Signed-off-by: Christopher Phillips --- schema/json/schema-8.0.0.json | 56 +---------------------------------- 1 file changed, 1 insertion(+), 55 deletions(-) diff --git a/schema/json/schema-8.0.0.json b/schema/json/schema-8.0.0.json index e617e229301..de119dff2ac 100644 --- a/schema/json/schema-8.0.0.json +++ b/schema/json/schema-8.0.0.json @@ -1056,61 +1056,7 @@ "type": "array" }, "licenses": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$defs": { - "License": { - "properties": { - "value": { - "type": "string" - }, - "spdxExpression": { - "type": "string" - }, - "type": { - "type": "string" - }, - "url": { - "type": "string" - }, - "location": { - "$ref": "#/$defs/Location" - } - }, - "type": "object", - "required": [ - "value", - "spdxExpression", - "type", - "url" - ] - }, - "Location": { - "properties": { - "path": { - "type": "string" - }, - "layerID": { - "type": "string" - }, - "annotations": { - "patternProperties": { - ".*": { - "type": "string" - } - }, - "type": "object" - } - }, - "type": "object", - "required": [ - "path" - ] - } - }, - "items": { - "$ref": "#/$defs/License" - }, - "type": "array" + "$ref": "#/$defs/licenses" }, "language": { "type": "string" From 6b2a23ce9ac65a11305fcf3259d246122a4b3035 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Thu, 27 Apr 2023 17:00:48 -0400 Subject: [PATCH 68/99] chore: update side affect from failing test Signed-off-by: Christopher Phillips --- syft/pkg/cataloger/apkdb/package_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syft/pkg/cataloger/apkdb/package_test.go b/syft/pkg/cataloger/apkdb/package_test.go index 6b94b3f70f5..9ebe4e4ddc4 100644 --- a/syft/pkg/cataloger/apkdb/package_test.go +++ b/syft/pkg/cataloger/apkdb/package_test.go @@ -134,7 +134,7 @@ func Test_PackageURL(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - actual := packageURL(test.metadata, &test.distro) + actual := packageURL(test.metadata.ApkMetadata, &test.distro) if actual != test.expected { dmp := diffmatchpatch.New() diffs := dmp.DiffMain(test.expected, actual, true) From 73da8dadbc6b9345da6eb8f687e27321e502bb00 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Thu, 27 Apr 2023 17:24:58 -0400 Subject: [PATCH 69/99] chore: add back old tests Signed-off-by: Christopher Phillips --- syft/pkg/package_test.go | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/syft/pkg/package_test.go b/syft/pkg/package_test.go index 1523e03d620..e8b4a8aae74 100644 --- a/syft/pkg/package_test.go +++ b/syft/pkg/package_test.go @@ -8,7 +8,6 @@ import ( "github.com/stretchr/testify/require" "github.com/anchore/syft/syft/cpe" - "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/source" ) @@ -29,15 +28,8 @@ func TestIDUniqueness(t *testing.T) { originalLocation, ), Licenses: []License{ - { - - Value: "cc0-1.0", - Type: license.Declared, - }, - { - SPDXExpression: "MIT", - Type: license.Declared, - }, + NewLicense("MIT"), + NewLicense("cc0-1.0"), }, Language: "math", Type: PythonPkg, @@ -86,6 +78,18 @@ func TestIDUniqueness(t *testing.T) { }, expectedIDComparison: assert.Equal, }, + { + name: "licenses order is ignored", + transform: func(pkg Package) Package { + // note: same as the original package, only a different order + pkg.Licenses = []License{ + NewLicense("cc0-1.0"), + NewLicense("MIT"), + } + return pkg + }, + expectedIDComparison: assert.Equal, + }, { name: "name is reflected", transform: func(pkg Package) Package { @@ -104,6 +108,14 @@ func TestIDUniqueness(t *testing.T) { }, expectedIDComparison: assert.NotEqual, }, + { + name: "licenses is reflected", + transform: func(pkg Package) Package { + pkg.Licenses = []License{NewLicense("new!")} + return pkg + }, + expectedIDComparison: assert.NotEqual, + }, { name: "same path for different filesystem is NOT reflected", transform: func(pkg Package) Package { From 39cca157b2bd65467f2c45d0918852a56569a567 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Thu, 27 Apr 2023 17:41:43 -0400 Subject: [PATCH 70/99] chore: remove old TODO Signed-off-by: Christopher Phillips --- syft/formats/common/cyclonedxhelpers/licenses.go | 1 - 1 file changed, 1 deletion(-) diff --git a/syft/formats/common/cyclonedxhelpers/licenses.go b/syft/formats/common/cyclonedxhelpers/licenses.go index 87082b1f1f4..1ce0febe39d 100644 --- a/syft/formats/common/cyclonedxhelpers/licenses.go +++ b/syft/formats/common/cyclonedxhelpers/licenses.go @@ -29,7 +29,6 @@ func encodeLicenses(p pkg.Package) *cyclonedx.Licenses { } if len(ex) > 0 { - // TODO: merge all complex expressions and ID into a single SPDX expression return &cyclonedx.Licenses{ cyclonedx.LicenseChoice{ Expression: mergeSPDX(ex, spdxc), From 341c1d183358799504dc613fc80782cdfbe40211 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Fri, 28 Apr 2023 10:13:39 -0400 Subject: [PATCH 71/99] chore: update tests with license struct Signed-off-by: Christopher Phillips --- syft/formats/syftjson/model/package_test.go | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/syft/formats/syftjson/model/package_test.go b/syft/formats/syftjson/model/package_test.go index e9978810760..b9c17030efb 100644 --- a/syft/formats/syftjson/model/package_test.go +++ b/syft/formats/syftjson/model/package_test.go @@ -12,7 +12,6 @@ import ( "github.com/anchore/syft/syft/pkg" ) -// TODO: add new license struct to this test func TestUnmarshalPackageGolang(t *testing.T) { tests := []struct { name string @@ -32,7 +31,14 @@ func TestUnmarshalPackageGolang(t *testing.T) { "path": "/Users/hal/go/bin/syft" } ], - "licenses": [], + "licenses": [ + { + "value": "MIT", + "spdxExpression": "MIT", + "type": "declared", + "url": "" + } + ], "language": "go", "cpes": [], "purl": "pkg:golang/gopkg.in/square/go-jose.v2@v2.6.0", @@ -63,7 +69,14 @@ func TestUnmarshalPackageGolang(t *testing.T) { "path": "/Users/hal/go/bin/syft" } ], - "licenses": [], + "licenses": [ + { + "value": "MIT", + "spdxExpression": "MIT", + "type": "declared", + "url": "" + } + ], "language": "go", "cpes": [], "purl": "pkg:golang/gopkg.in/square/go-jose.v2@v2.6.0" From c9ac65a6eb095f0f3902b7ff8b7162aa890053d2 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Fri, 28 Apr 2023 10:27:01 -0400 Subject: [PATCH 72/99] chore: add parse tests for license.go Signed-off-by: Christopher Phillips --- syft/license/license_test.go | 41 ++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/syft/license/license_test.go b/syft/license/license_test.go index 1f0d8ff2c18..6387f38da18 100644 --- a/syft/license/license_test.go +++ b/syft/license/license_test.go @@ -1 +1,42 @@ package license + +import "testing" + +func TestParseExpression(t *testing.T) { + tests := []struct { + name string + expression string + want string + wantErr bool + }{ + { + name: "valid single ID expression returns SPDX ID", + expression: "mit", + want: "MIT", + wantErr: false, + }, + { + name: "Valid SPDX expression returns SPDX expression", + expression: "MIT OR Apache-2.0", + want: "MIT OR Apache-2.0", + }, + { + name: "Invalid SPDX expression returns error", + expression: "MIT OR Apache-2.0 OR invalid", + want: "", + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := ParseExpression(tt.expression) + if (err != nil) != tt.wantErr { + t.Errorf("ParseExpression() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("ParseExpression() got = %v, want %v", got, tt.want) + } + }) + } +} From 34da46497d15fe394e67013711ad790a975713a7 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Fri, 28 Apr 2023 10:54:11 -0400 Subject: [PATCH 73/99] [wip] add license sort interface + license set impl Signed-off-by: Alex Goodman --- syft/pkg/license.go | 39 ++++++++++++++ syft/pkg/license_set.go | 114 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 153 insertions(+) create mode 100644 syft/pkg/license_set.go diff --git a/syft/pkg/license.go b/syft/pkg/license.go index 839a5ded97d..e4f6f8d4707 100644 --- a/syft/pkg/license.go +++ b/syft/pkg/license.go @@ -2,12 +2,15 @@ package pkg import ( "github.com/mitchellh/hashstructure/v2" + "sort" "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/source" ) +var _ sort.Interface = (*Licenses)(nil) + type License struct { Value string `json:"value"` SPDXExpression string `json:"spdxExpression"` @@ -16,6 +19,42 @@ type License struct { Location *source.Location `json:"location,omitempty"` // on disk declaration } +type Licenses []License + +func (l Licenses) Len() int { + return len(l) +} + +func (l Licenses) Less(i, j int) bool { + if l[i].Value == l[j].Value { + if l[i].SPDXExpression == l[j].SPDXExpression { + if l[i].Type == l[j].Type { + if l[i].URL == l[j].URL { + if l[i].Location == nil && l[j].Location == nil { + return false + } + if l[i].Location == nil { + return true + } + if l[j].Location == nil { + return false + } + sl := source.Locations{*l[i].Location, *l[j].Location} + return sl.Less(0, 1) + } + return l[i].URL < l[j].URL + } + return l[i].Type < l[j].Type + } + return l[i].SPDXExpression < l[j].SPDXExpression + } + return l[i].Value < l[j].Value +} + +func (l Licenses) Swap(i, j int) { + l[i], l[j] = l[j], l[i] +} + func NewLicense(value string) License { spdxExpression, err := license.ParseExpression(value) if err != nil { diff --git a/syft/pkg/license_set.go b/syft/pkg/license_set.go new file mode 100644 index 00000000000..4cf7ad3f36c --- /dev/null +++ b/syft/pkg/license_set.go @@ -0,0 +1,114 @@ +package pkg + +import ( + "fmt" + "github.com/anchore/syft/internal/log" + "github.com/mitchellh/hashstructure/v2" + "sort" +) + +type LicenseSet struct { + set map[uint64]Licenses +} + +func NewLicenseSet(licenses ...License) (s LicenseSet) { + for _, l := range licenses { + s.Add(l) + } + + return s +} + +func (s *LicenseSet) get(license License) (uint64, int, *License, error) { + id, err := license.Hash() + if err != nil { + return 0, 0, nil, fmt.Errorf("could not get the hash for a license: %w", err) + } + licenses, ok := s.set[id] + if !ok { + return id, 0, nil, nil + } + + if license.Location == nil { + switch len(licenses) { + case 0: + return id, 0, nil, nil + case 1: + return id, 0, &licenses[0], nil + default: + log.Debugf("license set contains multiple licenses with the same hash (when there is no location): %#v returns %#v", license, licenses) + // we don't know what the right answer is + return id, 0, nil, nil + } + } + + // I'm only hitting this if the FS id is different, since that's the only reason today that you can have + // the same hash ID but different information on the license + for idx, l := range licenses { + if l.Location.FileSystemID == license.Location.FileSystemID { + return id, idx, &licenses[idx], nil + } + } + + return id, 0, nil, nil +} + +func (s *LicenseSet) Add(licenses ...License) { + if s.set == nil { + s.set = make(map[uint64]Licenses) + } + for _, l := range licenses { + if id, _, v, err := s.get(l); v == nil && err == nil { + // doesn't exist, add it + s.set[id] = append(s.set[id], l) + sort.Sort(s.set[id]) + } else if err != nil { + log.Debugf("license set failed to add license %#v: %+v", l, err) + } + } +} + +func (s LicenseSet) Remove(licenses ...License) { + if s.set == nil { + return + } + for _, l := range licenses { + id, idx, v, err := s.get(l) + if err != nil { + log.Debugf("license set failed to remove license %#v: %+v", l, err) + } + if v == nil { + continue + } + // remove the license from the specific index already found + s.set[id] = append(s.set[id][:idx], s.set[id][idx+1:]...) + } +} + +func (s LicenseSet) Contains(l License) bool { + if s.set == nil { + return false + } + _, _, v, err := s.get(l) + return v != nil && err == nil +} + +func (s LicenseSet) ToSlice() []License { + if s.set == nil { + return nil + } + var licenses []License + for _, v := range s.set { + licenses = append(licenses, v...) + } + sort.Sort(Licenses(licenses)) + return licenses +} + +func (s LicenseSet) Hash() (uint64, error) { + // access paths and filesystem IDs are not considered when hashing a license set, only the real paths + return hashstructure.Hash(s.ToSlice(), hashstructure.FormatV2, &hashstructure.HashOptions{ + ZeroNil: true, + SlicesAsSets: true, + }) +} From 3b581c77731f3e7c06a0b7fbc0aa1ab9dc958bc1 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Fri, 28 Apr 2023 11:31:39 -0400 Subject: [PATCH 74/99] chore: add sorting tests for licenses Signed-off-by: Christopher Phillips --- syft/pkg/license_test.go | 66 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/syft/pkg/license_test.go b/syft/pkg/license_test.go index d9e40ddce11..fbeb391840f 100644 --- a/syft/pkg/license_test.go +++ b/syft/pkg/license_test.go @@ -1,6 +1,7 @@ package pkg import ( + "sort" "testing" "github.com/stretchr/testify/assert" @@ -28,3 +29,68 @@ func Test_Hash(t *testing.T) { assert.Equal(t, hash1, hash2) } + +func Test_Sort(t *testing.T) { + tests := []struct { + name string + licenses Licenses + expected Licenses + }{ + { + name: "empty", + licenses: []License{}, + expected: []License{}, + }, + { + name: "single", + licenses: []License{ + NewLicenseFromLocation("MIT", source.NewLocation("place!")), + }, + expected: []License{ + NewLicenseFromLocation("MIT", source.NewLocation("place!")), + }, + }, + { + name: "multiple", + licenses: []License{ + NewLicenseFromLocation("MIT", source.NewLocation("place!")), + NewLicenseFromURL("MIT", "https://github.com/anchore/syft/blob/main/LICENSE"), + NewLicenseFromLocation("Apache", source.NewLocation("area!")), + NewLicenseFromLocation("gpl2+", source.NewLocation("area!")), + }, + expected: Licenses{ + NewLicenseFromLocation("Apache", source.NewLocation("area!")), + NewLicenseFromLocation("MIT", source.NewLocation("place!")), + NewLicenseFromURL("MIT", "https://github.com/anchore/syft/blob/main/LICENSE"), + NewLicenseFromLocation("gpl2+", source.NewLocation("area!")), + }, + }, + { + name: "multiple with location variants", + licenses: []License{ + NewLicenseFromLocation("MIT", source.NewLocation("place!")), + NewLicenseFromLocation("MIT", source.NewLocation("park!")), + NewLicense("MIT"), + NewLicense("AAL"), + NewLicense("Adobe-2006"), + NewLicenseFromLocation("Apache", source.NewLocation("area!")), + }, + expected: Licenses{ + NewLicense("AAL"), + NewLicense("Adobe-2006"), + NewLicenseFromLocation("Apache", source.NewLocation("area!")), + NewLicense("MIT"), + NewLicenseFromLocation("MIT", source.NewLocation("park!")), + NewLicenseFromLocation("MIT", source.NewLocation("place!")), + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + sort.Sort(test.licenses) + assert.Equal(t, test.expected, test.licenses) + }) + + } +} From 2cfd64e10a88f3bbb60e09aba123d0408f051575 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Fri, 28 Apr 2023 11:33:09 -0400 Subject: [PATCH 75/99] [wip] add license set tests Signed-off-by: Alex Goodman --- syft/pkg/license_set.go | 1 - syft/pkg/license_set_test.go | 79 ++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 syft/pkg/license_set_test.go diff --git a/syft/pkg/license_set.go b/syft/pkg/license_set.go index 4cf7ad3f36c..7d8fef75244 100644 --- a/syft/pkg/license_set.go +++ b/syft/pkg/license_set.go @@ -61,7 +61,6 @@ func (s *LicenseSet) Add(licenses ...License) { if id, _, v, err := s.get(l); v == nil && err == nil { // doesn't exist, add it s.set[id] = append(s.set[id], l) - sort.Sort(s.set[id]) } else if err != nil { log.Debugf("license set failed to add license %#v: %+v", l, err) } diff --git a/syft/pkg/license_set_test.go b/syft/pkg/license_set_test.go new file mode 100644 index 00000000000..49dc12d52da --- /dev/null +++ b/syft/pkg/license_set_test.go @@ -0,0 +1,79 @@ +package pkg + +import ( + "github.com/anchore/syft/syft/source" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestLicenseSet_Add(t *testing.T) { + tests := []struct { + name string + licenses []License + want []License + }{ + { + name: "add one simple license", + licenses: []License{ + NewLicense("MIT"), + }, + want: []License{ + NewLicense("MIT"), + }, + }, + { + name: "keep multiple licenses sorted", + licenses: []License{ + NewLicense("MIT"), + NewLicense("Apache-2.0"), + }, + want: []License{ + NewLicense("Apache-2.0"), + NewLicense("MIT"), + }, + }, + { + name: "duplicate licenses with locations", + licenses: []License{ + NewLicenseFromLocation("MIT", source.NewLocationFromCoordinates(source.Coordinates{RealPath: "/place", FileSystemID: "1"})), + NewLicenseFromLocation("MIT", source.NewLocationFromCoordinates(source.Coordinates{RealPath: "/place", FileSystemID: "1"})), + }, + want: []License{ + NewLicenseFromLocation("MIT", source.NewLocationFromCoordinates(source.Coordinates{RealPath: "/place", FileSystemID: "1"})), + }, + }, + { + name: "same licenses with different locations", + licenses: []License{ + NewLicense("MIT"), + NewLicenseFromLocation("MIT", source.NewLocationFromCoordinates(source.Coordinates{RealPath: "/place", FileSystemID: "2"})), + NewLicenseFromLocation("MIT", source.NewLocationFromCoordinates(source.Coordinates{RealPath: "/place", FileSystemID: "1"})), + }, + want: []License{ + NewLicense("MIT"), + NewLicenseFromLocation("MIT", source.NewLocationFromCoordinates(source.Coordinates{RealPath: "/place", FileSystemID: "1"})), + NewLicenseFromLocation("MIT", source.NewLocationFromCoordinates(source.Coordinates{RealPath: "/place", FileSystemID: "2"})), + }, + }, + { + name: "same license from different sources", + licenses: []License{ + NewLicense("MIT"), + NewLicenseFromLocation("MIT", source.NewLocation("/place")), + NewLicenseFromURL("MIT", "https://example.com"), + }, + want: []License{ + NewLicense("MIT"), + NewLicenseFromLocation("MIT", source.NewLocation("/place")), + NewLicenseFromURL("MIT", "https://example.com"), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := NewLicenseSet() + s.Add(tt.licenses...) + assert.Equal(t, tt.want, s.ToSlice()) + }) + } +} From f1350ffc855f4057656f86daf47bfe24751d0eab Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Fri, 28 Apr 2023 11:40:35 -0400 Subject: [PATCH 76/99] chore: SA changes Signed-off-by: Christopher Phillips --- syft/pkg/license.go | 3 ++- syft/pkg/license_set.go | 6 ++++-- syft/pkg/license_set_test.go | 6 ++++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/syft/pkg/license.go b/syft/pkg/license.go index e4f6f8d4707..bfa75bc64fc 100644 --- a/syft/pkg/license.go +++ b/syft/pkg/license.go @@ -1,9 +1,10 @@ package pkg import ( - "github.com/mitchellh/hashstructure/v2" "sort" + "github.com/mitchellh/hashstructure/v2" + "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/source" diff --git a/syft/pkg/license_set.go b/syft/pkg/license_set.go index 7d8fef75244..519605dfc47 100644 --- a/syft/pkg/license_set.go +++ b/syft/pkg/license_set.go @@ -2,9 +2,11 @@ package pkg import ( "fmt" - "github.com/anchore/syft/internal/log" - "github.com/mitchellh/hashstructure/v2" "sort" + + "github.com/mitchellh/hashstructure/v2" + + "github.com/anchore/syft/internal/log" ) type LicenseSet struct { diff --git a/syft/pkg/license_set_test.go b/syft/pkg/license_set_test.go index 49dc12d52da..347c8bfa650 100644 --- a/syft/pkg/license_set_test.go +++ b/syft/pkg/license_set_test.go @@ -1,9 +1,11 @@ package pkg import ( - "github.com/anchore/syft/syft/source" - "github.com/stretchr/testify/assert" "testing" + + "github.com/stretchr/testify/assert" + + "github.com/anchore/syft/syft/source" ) func TestLicenseSet_Add(t *testing.T) { From 699cbc7fa47aea1679fb5bb450d9133d158685db Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Wed, 10 May 2023 10:19:50 -0400 Subject: [PATCH 77/99] 1577 license set (#1798) Signed-off-by: Christopher Phillips --- internal/licenses/parser.go | 2 +- internal/stringset.go | 22 +++- schema/json/schema-8.0.0.json | 15 ++- .../common/cyclonedxhelpers/component.go | 2 +- .../common/cyclonedxhelpers/decoder_test.go | 3 +- .../external_references_test.go | 2 +- .../common/cyclonedxhelpers/licenses.go | 59 ++++++++- .../common/cyclonedxhelpers/licenses_test.go | 20 +-- syft/formats/common/spdxhelpers/license.go | 4 +- .../common/spdxhelpers/license_test.go | 25 ++-- .../common/spdxhelpers/to_format_model.go | 2 +- .../spdxhelpers/to_format_model_test.go | 10 +- .../common/spdxhelpers/to_syft_model.go | 2 +- .../TestCycloneDxDirectoryEncoder.golden | 6 +- .../snapshot/TestCycloneDxImageEncoder.golden | 6 +- .../TestCycloneDxDirectoryEncoder.golden | 6 +- .../snapshot/TestCycloneDxImageEncoder.golden | 6 +- syft/formats/internal/testutils/utils.go | 12 +- .../TestSPDXJSONDirectoryEncoder.golden | 6 +- .../snapshot/TestSPDXJSONImageEncoder.golden | 6 +- .../snapshot/TestSPDXRelationshipOrder.golden | 18 +-- .../snapshot/TestSPDXJSONSPDXIDs.golden | 4 +- .../snapshot/TestSPDXRelationshipOrder.golden | 18 +-- .../TestSPDXTagValueDirectoryEncoder.golden | 6 +- .../TestSPDXTagValueImageEncoder.golden | 6 +- syft/formats/syftjson/encoder_test.go | 2 +- syft/formats/syftjson/model/package.go | 30 ++++- syft/formats/syftjson/model/package_test.go | 10 +- .../snapshot/TestDirectoryEncoder.golden | 5 +- .../TestEncodeFullJSONDocument.golden | 5 +- .../snapshot/TestImageEncoder.golden | 5 +- syft/formats/syftjson/to_format_model.go | 19 ++- syft/formats/syftjson/to_syft_model.go | 16 ++- syft/pkg/cataloger/alpm/cataloger_test.go | 8 +- syft/pkg/cataloger/alpm/package.go | 2 +- syft/pkg/cataloger/apkdb/package.go | 2 +- syft/pkg/cataloger/apkdb/parse_apk_db_test.go | 39 +++--- syft/pkg/cataloger/deb/cataloger_test.go | 10 +- syft/pkg/cataloger/deb/package.go | 4 +- syft/pkg/cataloger/deb/parse_dpkg_db_test.go | 2 +- syft/pkg/cataloger/golang/licenses_test.go | 13 +- syft/pkg/cataloger/golang/package.go | 2 +- .../cataloger/golang/parse_go_binary_test.go | 3 - syft/pkg/cataloger/golang/parse_go_mod.go | 4 +- .../pkg/cataloger/golang/parse_go_mod_test.go | 12 -- .../internal/pkgtest/test_generic_parser.go | 85 +++++++++++++ syft/pkg/cataloger/java/archive_parser.go | 8 +- .../pkg/cataloger/java/archive_parser_test.go | 6 +- .../cataloger/javascript/cataloger_test.go | 12 +- syft/pkg/cataloger/javascript/package.go | 7 +- .../javascript/parse_package_json_test.go | 38 +++--- .../javascript/parse_package_lock_test.go | 50 ++++---- syft/pkg/cataloger/kernel/cataloger_test.go | 6 +- syft/pkg/cataloger/kernel/package.go | 2 +- syft/pkg/cataloger/php/package.go | 2 +- .../cataloger/php/parse_composer_lock_test.go | 12 +- .../php/parse_installed_json_test.go | 16 ++- syft/pkg/cataloger/portage/cataloger_test.go | 2 +- .../portage/parse_portage_contents.go | 2 +- syft/pkg/cataloger/python/cataloger_test.go | 30 ++--- syft/pkg/cataloger/python/package.go | 2 +- syft/pkg/cataloger/rpm/package.go | 2 +- syft/pkg/cataloger/rpm/parse_rpm_db_test.go | 12 +- syft/pkg/cataloger/rpm/parse_rpm_test.go | 12 +- syft/pkg/cataloger/ruby/package.go | 2 +- syft/pkg/cataloger/ruby/parse_gemspec_test.go | 6 +- .../cataloger/rust/parse_cargo_lock_test.go | 20 +-- syft/pkg/cataloger/sbom/cataloger_test.go | 40 +++--- syft/pkg/license.go | 120 ++++++++++-------- syft/pkg/license_set.go | 81 ++++-------- syft/pkg/license_set_test.go | 69 +++++++--- syft/pkg/license_test.go | 36 +++--- syft/pkg/package.go | 3 +- syft/pkg/package_test.go | 32 ++++- syft/source/location_set.go | 4 + 75 files changed, 723 insertions(+), 457 deletions(-) diff --git a/internal/licenses/parser.go b/internal/licenses/parser.go index 65b495d1b6e..98168af37b9 100644 --- a/internal/licenses/parser.go +++ b/internal/licenses/parser.go @@ -30,7 +30,7 @@ func Parse(reader io.Reader, l source.Location) (licenses []pkg.License, err err for _, m := range cov.Match { // TODO: spdx ID validation here? - lic := pkg.NewLicenseFromLocation(m.ID, l) + lic := pkg.NewLicenseFromLocations(m.ID, l) lic.Type = license.Concluded licenses = append(licenses, lic) diff --git a/internal/stringset.go b/internal/stringset.go index 536e0a8a71d..1d44652ec6a 100644 --- a/internal/stringset.go +++ b/internal/stringset.go @@ -15,8 +15,10 @@ func NewStringSet(start ...string) StringSet { } // Add a string to the set. -func (s StringSet) Add(i string) { - s[i] = struct{}{} +func (s StringSet) Add(i ...string) { + for _, str := range i { + s[str] = struct{}{} + } } // Remove a string from the set. @@ -41,3 +43,19 @@ func (s StringSet) ToSlice() []string { sort.Strings(ret) return ret } + +func (s StringSet) Equals(o StringSet) bool { + if len(s) != len(o) { + return false + } + for k := range s { + if !o.Contains(k) { + return false + } + } + return true +} + +func (s StringSet) Empty() bool { + return len(s) < 1 +} diff --git a/schema/json/schema-8.0.0.json b/schema/json/schema-8.0.0.json index de119dff2ac..be9b9807f05 100644 --- a/schema/json/schema-8.0.0.json +++ b/schema/json/schema-8.0.0.json @@ -749,10 +749,16 @@ "type": "string" }, "url": { - "type": "string" + "items": { + "type": "string" + }, + "type": "array" }, - "location": { - "$ref": "#/$defs/Location" + "locations": { + "items": { + "$ref": "#/$defs/Location" + }, + "type": "array" } }, "type": "object", @@ -760,7 +766,8 @@ "value", "spdxExpression", "type", - "url" + "url", + "locations" ] }, "LinuxKernelMetadata": { diff --git a/syft/formats/common/cyclonedxhelpers/component.go b/syft/formats/common/cyclonedxhelpers/component.go index cf3516cedd7..e51c9d11250 100644 --- a/syft/formats/common/cyclonedxhelpers/component.go +++ b/syft/formats/common/cyclonedxhelpers/component.go @@ -78,7 +78,7 @@ func decodeComponent(c *cyclonedx.Component) *pkg.Package { Name: c.Name, Version: c.Version, Locations: decodeLocations(values), - Licenses: decodeLicenses(c), + Licenses: pkg.NewLicenseSet(decodeLicenses(c)...), CPEs: decodeCPEs(c), PURL: c.PackageURL, } diff --git a/syft/formats/common/cyclonedxhelpers/decoder_test.go b/syft/formats/common/cyclonedxhelpers/decoder_test.go index 4daa4f8c8b8..acf7cf6380d 100644 --- a/syft/formats/common/cyclonedxhelpers/decoder_test.go +++ b/syft/formats/common/cyclonedxhelpers/decoder_test.go @@ -279,8 +279,7 @@ func Test_missingDataDecode(t *testing.T) { }, }, }) - - assert.Len(t, pkg.Licenses, 0) + assert.Equal(t, pkg.Licenses.Empty(), true) } func Test_missingComponentsDecode(t *testing.T) { diff --git a/syft/formats/common/cyclonedxhelpers/external_references_test.go b/syft/formats/common/cyclonedxhelpers/external_references_test.go index 0dd8795574e..c6ce0355b4d 100644 --- a/syft/formats/common/cyclonedxhelpers/external_references_test.go +++ b/syft/formats/common/cyclonedxhelpers/external_references_test.go @@ -50,7 +50,7 @@ func Test_encodeExternalReferences(t *testing.T) { Language: pkg.Rust, Type: pkg.RustPkg, MetadataType: pkg.RustCargoPackageMetadataType, - Licenses: nil, + Licenses: pkg.NewLicenseSet(), Metadata: pkg.CargoPackageMetadata{ Name: "ansi_term", Version: "0.12.1", diff --git a/syft/formats/common/cyclonedxhelpers/licenses.go b/syft/formats/common/cyclonedxhelpers/licenses.go index 1ce0febe39d..a8e812ae5bf 100644 --- a/syft/formats/common/cyclonedxhelpers/licenses.go +++ b/syft/formats/common/cyclonedxhelpers/licenses.go @@ -56,11 +56,11 @@ func decodeLicenses(c *cyclonedx.Component) []pkg.License { // these fields are mutually exclusive in the spec switch { case l.License.ID != "": - licenses = append(licenses, pkg.NewLicenseFromURL(l.License.ID, l.License.URL)) + licenses = append(licenses, pkg.LicenseFromURLs(l.License.ID, l.License.URL)) case l.License.Name != "": - licenses = append(licenses, pkg.NewLicenseFromURL(l.License.Name, l.License.URL)) + licenses = append(licenses, pkg.LicenseFromURLs(l.License.Name, l.License.URL)) case l.Expression != "": - licenses = append(licenses, pkg.NewLicenseFromURL(l.Expression, l.License.URL)) + licenses = append(licenses, pkg.LicenseFromURLs(l.Expression, l.License.URL)) default: } } @@ -68,6 +68,7 @@ func decodeLicenses(c *cyclonedx.Component) []pkg.License { return licenses } +// nolint:funlen func separateLicenses(p pkg.Package) (spdx, other cyclonedx.Licenses, expressions []string) { spdxc := cyclonedx.Licenses{} otherc := cyclonedx.Licenses{} @@ -85,15 +86,41 @@ func separateLicenses(p pkg.Package) (spdx, other cyclonedx.Licenses, expression as a license choice and the invalid expression as a license string. */ - for _, l := range p.Licenses { + // dedupe spdxlicenseID + seen := make(map[string]bool) + for _, l := range p.Licenses.ToSlice() { // singular expression case if value, exists := spdxlicense.ID(l.SPDXExpression); exists { + // we do 1 license -> many URL in our internal model + // this fans out different URL to single cyclone licenses + if !l.URL.Empty() { + for _, url := range l.URL.ToSlice() { + if url != "" { + spdxc = append(spdxc, cyclonedx.LicenseChoice{ + License: &cyclonedx.License{ + ID: value, + URL: url, + }, + }) + continue + } + spdxc = append(spdxc, cyclonedx.LicenseChoice{ + License: &cyclonedx.License{ + ID: value, + }, + }) + continue + } + } + if _, exists := seen[value]; exists { + continue + } spdxc = append(spdxc, cyclonedx.LicenseChoice{ License: &cyclonedx.License{ - ID: value, - URL: l.URL, + ID: value, }, }) + seen[value] = true continue } if l.SPDXExpression != "" { @@ -104,10 +131,28 @@ func separateLicenses(p pkg.Package) (spdx, other cyclonedx.Licenses, expression } // license string that are not valid spdx expressions or ids + if !l.URL.Empty() { + for _, url := range l.URL.ToSlice() { + if url != "" { + otherc = append(otherc, cyclonedx.LicenseChoice{ + License: &cyclonedx.License{ + Name: l.Value, + URL: url, + }, + }) + continue + } + otherc = append(otherc, cyclonedx.LicenseChoice{ + License: &cyclonedx.License{ + Name: l.Value, + }, + }) + } + continue + } otherc = append(otherc, cyclonedx.LicenseChoice{ License: &cyclonedx.License{ Name: l.Value, - URL: l.URL, }, }) } diff --git a/syft/formats/common/cyclonedxhelpers/licenses_test.go b/syft/formats/common/cyclonedxhelpers/licenses_test.go index ce7616e336f..478d5674ca6 100644 --- a/syft/formats/common/cyclonedxhelpers/licenses_test.go +++ b/syft/formats/common/cyclonedxhelpers/licenses_test.go @@ -6,6 +6,7 @@ import ( "github.com/CycloneDX/cyclonedx-go" "github.com/stretchr/testify/assert" + "github.com/anchore/syft/internal" "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/pkg" ) @@ -24,9 +25,9 @@ func Test_encodeLicense(t *testing.T) { { name: "no SPDX licenses", input: pkg.Package{ - Licenses: []pkg.License{ + Licenses: pkg.NewLicenseSet( pkg.NewLicense("RandomLicense"), - }, + ), }, expected: &cyclonedx.Licenses{ { @@ -39,10 +40,10 @@ func Test_encodeLicense(t *testing.T) { { name: "single SPDX ID and Non SPDX ID", input: pkg.Package{ - Licenses: []pkg.License{ + Licenses: pkg.NewLicenseSet( pkg.NewLicense("mit"), pkg.NewLicense("FOOBAR"), - }, + ), }, expected: &cyclonedx.Licenses{ { @@ -60,9 +61,9 @@ func Test_encodeLicense(t *testing.T) { { name: "with complex SPDX license expression", input: pkg.Package{ - Licenses: []pkg.License{ + Licenses: pkg.NewLicenseSet( pkg.NewLicense("MIT AND GPL-3.0-only"), - }, + ), }, expected: &cyclonedx.Licenses{ { @@ -73,10 +74,10 @@ func Test_encodeLicense(t *testing.T) { { name: "with multiple complex SPDX license expression", input: pkg.Package{ - Licenses: []pkg.License{ + Licenses: pkg.NewLicenseSet( pkg.NewLicense("MIT AND GPL-3.0-only"), pkg.NewLicense("MIT AND GPL-3.0-only WITH Classpath-exception-2.0"), - }, + ), }, expected: &cyclonedx.Licenses{ { @@ -143,6 +144,7 @@ func TestDecodeLicenses(t *testing.T) { Value: "RandomLicense", // CycloneDX specification doesn't give a field for determining the license type Type: license.Declared, + URL: internal.NewStringSet(), }, }, }, @@ -162,6 +164,7 @@ func TestDecodeLicenses(t *testing.T) { Value: "MIT", SPDXExpression: "MIT", Type: license.Declared, + URL: internal.NewStringSet(), }, }, }, @@ -180,6 +183,7 @@ func TestDecodeLicenses(t *testing.T) { Value: "MIT AND GPL-3.0-only WITH Classpath-exception-2.0", SPDXExpression: "MIT AND GPL-3.0-only WITH Classpath-exception-2.0", Type: license.Declared, + URL: internal.NewStringSet(), }, }, }, diff --git a/syft/formats/common/spdxhelpers/license.go b/syft/formats/common/spdxhelpers/license.go index 218536f0772..4b0d896a1d1 100644 --- a/syft/formats/common/spdxhelpers/license.go +++ b/syft/formats/common/spdxhelpers/license.go @@ -18,14 +18,14 @@ func License(p pkg.Package) (concluded, declared string) { // (ii) the SPDX file creator has made no attempt to determine this field; or // (iii) the SPDX file creator has intentionally provided no information (no meaning should be implied by doing so). - if len(p.Licenses) == 0 { + if p.Licenses.Empty() { return NOASSERTION, NOASSERTION } // 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) + pc, pd := parseLicenses(p.Licenses.ToSlice()) for i, v := range pc { if strings.HasPrefix(v, spdxlicense.LicenseRefPrefix) { diff --git a/syft/formats/common/spdxhelpers/license_test.go b/syft/formats/common/spdxhelpers/license_test.go index 60856f00572..43e5600f72d 100644 --- a/syft/formats/common/spdxhelpers/license_test.go +++ b/syft/formats/common/spdxhelpers/license_test.go @@ -28,7 +28,7 @@ func Test_License(t *testing.T) { { name: "no SPDX licenses", input: pkg.Package{ - Licenses: []pkg.License{pkg.NewLicense("made-up")}, + Licenses: pkg.NewLicenseSet(pkg.NewLicense("made-up")), }, expected: struct{ concluded, declared string }{ concluded: "NOASSERTION", @@ -38,7 +38,7 @@ func Test_License(t *testing.T) { { name: "with SPDX license", input: pkg.Package{ - Licenses: []pkg.License{pkg.NewLicense("MIT")}, + Licenses: pkg.NewLicenseSet(pkg.NewLicense("MIT")), }, expected: struct { concluded string @@ -51,51 +51,54 @@ func Test_License(t *testing.T) { { name: "with SPDX license expression", input: pkg.Package{ - Licenses: []pkg.License{ + Licenses: pkg.NewLicenseSet( pkg.NewLicense("MIT"), pkg.NewLicense("GPL-3.0-only"), - }, + ), }, expected: struct { concluded string declared string }{ concluded: "NOASSERTION", - declared: "MIT AND GPL-3.0-only", + // because we sort licenses alphabetically GPL ends up at the start + declared: "GPL-3.0-only AND MIT", }, }, { name: "includes valid LicenseRef-", input: pkg.Package{ - Licenses: []pkg.License{ + Licenses: pkg.NewLicenseSet( pkg.NewLicense("one thing first"), pkg.NewLicense("two things/#$^second"), pkg.NewLicense("MIT"), - }, + ), }, expected: struct { concluded string declared string }{ concluded: "NOASSERTION", - declared: "LicenseRef-one-thing-first AND LicenseRef-two-things----second AND MIT", + // because we separate licenses between valid SPDX and non valid, valid ID always end at the front + declared: "MIT AND LicenseRef-one-thing-first AND LicenseRef-two-things----second", }, }, { name: "join parentheses correctly", input: pkg.Package{ - Licenses: []pkg.License{ + Licenses: pkg.NewLicenseSet( pkg.NewLicense("one thing first"), pkg.NewLicense("MIT AND GPL-3.0-only"), pkg.NewLicense("MIT OR APACHE-2.0"), - }, + ), }, expected: struct { concluded string declared string }{ concluded: "NOASSERTION", - declared: "LicenseRef-one-thing-first AND (MIT AND GPL-3.0-only) AND (MIT OR APACHE-2.0)", + // because we separate licenses between valid SPDX and non valid, valid ID always end at the front + declared: "(MIT AND GPL-3.0-only) AND (MIT OR APACHE-2.0) AND LicenseRef-one-thing-first", }, }, } diff --git a/syft/formats/common/spdxhelpers/to_format_model.go b/syft/formats/common/spdxhelpers/to_format_model.go index fab27fe7531..4c39dbe3bee 100644 --- a/syft/formats/common/spdxhelpers/to_format_model.go +++ b/syft/formats/common/spdxhelpers/to_format_model.go @@ -540,7 +540,7 @@ func toFileTypes(metadata *source.FileMetadata) (ty []string) { func toOtherLicenses(catalog *pkg.Collection) []*spdx.OtherLicense { licenses := map[string]bool{} for _, p := range catalog.Sorted() { - declaredLicenses, concludedLicenses := parseLicenses(p.Licenses) + declaredLicenses, concludedLicenses := parseLicenses(p.Licenses.ToSlice()) for _, license := range declaredLicenses { if strings.HasPrefix(license, spdxlicense.LicenseRefPrefix) { licenses[license] = true diff --git a/syft/formats/common/spdxhelpers/to_format_model_test.go b/syft/formats/common/spdxhelpers/to_format_model_test.go index 1b94293d9a7..170de95ea8c 100644 --- a/syft/formats/common/spdxhelpers/to_format_model_test.go +++ b/syft/formats/common/spdxhelpers/to_format_model_test.go @@ -448,16 +448,16 @@ func Test_OtherLicenses(t *testing.T) { { name: "no licenseRef", pkg: pkg.Package{ - Licenses: []pkg.License{}, + Licenses: pkg.NewLicenseSet(), }, expected: nil, }, { name: "single licenseRef", pkg: pkg.Package{ - Licenses: []pkg.License{ + Licenses: pkg.NewLicenseSet( pkg.NewLicense("foobar"), - }, + ), }, expected: []*spdx.OtherLicense{ { @@ -469,10 +469,10 @@ func Test_OtherLicenses(t *testing.T) { { name: "multiple licenseRef", pkg: pkg.Package{ - Licenses: []pkg.License{ + Licenses: pkg.NewLicenseSet( pkg.NewLicense("internal made up license name"), pkg.NewLicense("new apple license 2.0"), - }, + ), }, expected: []*spdx.OtherLicense{ { diff --git a/syft/formats/common/spdxhelpers/to_syft_model.go b/syft/formats/common/spdxhelpers/to_syft_model.go index d7ce59ef1a7..a31cee81218 100644 --- a/syft/formats/common/spdxhelpers/to_syft_model.go +++ b/syft/formats/common/spdxhelpers/to_syft_model.go @@ -280,7 +280,7 @@ func toSyftPackage(p *spdx.Package) *pkg.Package { Type: info.typ, Name: p.PackageName, Version: p.PackageVersion, - Licenses: parseSPDXLicenses(p), + Licenses: pkg.NewLicenseSet(parseSPDXLicenses(p)...), CPEs: extractCPEs(p), PURL: info.purl.String(), Language: info.lang, diff --git a/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden b/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden index f2181441624..3cab5be0e47 100644 --- a/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden +++ b/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden @@ -2,10 +2,10 @@ "$schema": "http://cyclonedx.org/schema/bom-1.4.schema.json", "bomFormat": "CycloneDX", "specVersion": "1.4", - "serialNumber": "urn:uuid:13076761-264d-419c-ab7d-a4ac65d6456a", + "serialNumber": "urn:uuid:1b71a5b4-4bc5-4548-a51a-212e631976cd", "version": 1, "metadata": { - "timestamp": "2023-04-27T11:12:15-04:00", + "timestamp": "2023-05-08T14:40:32-04:00", "tools": [ { "vendor": "anchore", @@ -21,7 +21,7 @@ }, "components": [ { - "bom-ref": "3fdbdcde4d17d261", + "bom-ref": "8c7e1242588c971a", "type": "library", "name": "package-1", "version": "1.0.1", diff --git a/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden b/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden index b6294886692..4d4765f5468 100644 --- a/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden +++ b/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden @@ -2,10 +2,10 @@ "$schema": "http://cyclonedx.org/schema/bom-1.4.schema.json", "bomFormat": "CycloneDX", "specVersion": "1.4", - "serialNumber": "urn:uuid:4536288a-9645-4c51-ab08-e5bb638c8b7c", + "serialNumber": "urn:uuid:1695d6ae-0ddf-4e77-9c9d-74df1bdd8d5b", "version": 1, "metadata": { - "timestamp": "2023-04-27T11:12:15-04:00", + "timestamp": "2023-05-08T14:40:32-04:00", "tools": [ { "vendor": "anchore", @@ -22,7 +22,7 @@ }, "components": [ { - "bom-ref": "911cef0da9e28043", + "bom-ref": "ec2e0c93617507ef", "type": "library", "name": "package-1", "version": "1.0.1", diff --git a/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden b/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden index 3c74bad6a13..32eaf274708 100644 --- a/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden +++ b/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden @@ -1,7 +1,7 @@ - + - 2023-04-27T11:12:15-04:00 + 2023-05-08T14:40:52-04:00 anchore @@ -14,7 +14,7 @@ - + package-1 1.0.1 diff --git a/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden b/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden index 1cb76a6ec50..67ad5f052a5 100644 --- a/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden +++ b/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden @@ -1,7 +1,7 @@ - + - 2023-04-27T11:12:15-04:00 + 2023-05-08T14:40:52-04:00 anchore @@ -15,7 +15,7 @@ - + package-1 1.0.1 diff --git a/syft/formats/internal/testutils/utils.go b/syft/formats/internal/testutils/utils.go index 9c5d0ab22d6..7ddf942173b 100644 --- a/syft/formats/internal/testutils/utils.go +++ b/syft/formats/internal/testutils/utils.go @@ -162,9 +162,9 @@ func populateImageCatalog(catalog *pkg.Collection, img *image.Image) { FoundBy: "the-cataloger-1", Language: pkg.Python, MetadataType: pkg.PythonPackageMetadataType, - Licenses: []pkg.License{ + Licenses: pkg.NewLicenseSet( pkg.NewLicense("MIT"), - }, + ), Metadata: pkg.PythonPackageMetadata{ Name: "package-1", Version: "1.0.1", @@ -270,9 +270,9 @@ func newDirectoryCatalog() *pkg.Collection { ), Language: pkg.Python, MetadataType: pkg.PythonPackageMetadataType, - Licenses: []pkg.License{ + Licenses: pkg.NewLicenseSet( pkg.NewLicense("MIT"), - }, + ), Metadata: pkg.PythonPackageMetadata{ Name: "package-1", Version: "1.0.1", @@ -323,9 +323,9 @@ func newDirectoryCatalogWithAuthorField() *pkg.Collection { ), Language: pkg.Python, MetadataType: pkg.PythonPackageMetadataType, - Licenses: []pkg.License{ + Licenses: pkg.NewLicenseSet( pkg.NewLicense("MIT"), - }, + ), Metadata: pkg.PythonPackageMetadata{ Name: "package-1", Version: "1.0.1", diff --git a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONDirectoryEncoder.golden b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONDirectoryEncoder.golden index 83299233cd7..de775cd15f0 100644 --- a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONDirectoryEncoder.golden +++ b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONDirectoryEncoder.golden @@ -3,19 +3,19 @@ "dataLicense": "CC0-1.0", "SPDXID": "SPDXRef-DOCUMENT", "name": "/some/path", - "documentNamespace": "https://anchore.com/syft/dir/some/path-72483330-d29b-4bca-8733-2d0f2b0ae64d", + "documentNamespace": "https://anchore.com/syft/dir/some/path-5ea40e59-d91a-4682-a016-da45ddd540e4", "creationInfo": { "licenseListVersion": "3.20", "creators": [ "Organization: Anchore, Inc", "Tool: syft-v0.42.0-bogus" ], - "created": "2023-05-04T20:37:22Z" + "created": "2023-05-09T17:11:26Z" }, "packages": [ { "name": "package-1", - "SPDXID": "SPDXRef-Package-python-package-1-3fdbdcde4d17d261", + "SPDXID": "SPDXRef-Package-python-package-1-9265397e5e15168a", "versionInfo": "1.0.1", "downloadLocation": "NOASSERTION", "sourceInfo": "acquired package info from installed python package manifest file: /some/path/pkg1", diff --git a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONImageEncoder.golden b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONImageEncoder.golden index f15876a4225..8b08d7608b9 100644 --- a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONImageEncoder.golden +++ b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONImageEncoder.golden @@ -3,19 +3,19 @@ "dataLicense": "CC0-1.0", "SPDXID": "SPDXRef-DOCUMENT", "name": "user-image-input", - "documentNamespace": "https://anchore.com/syft/image/user-image-input-1fe53177-9e14-4830-9281-d0670fbd1c12", + "documentNamespace": "https://anchore.com/syft/image/user-image-input-2cc737fb-af51-4e4b-9395-cceabcc305eb", "creationInfo": { "licenseListVersion": "3.20", "creators": [ "Organization: Anchore, Inc", "Tool: syft-v0.42.0-bogus" ], - "created": "2023-05-04T20:37:23Z" + "created": "2023-05-09T17:11:26Z" }, "packages": [ { "name": "package-1", - "SPDXID": "SPDXRef-Package-python-package-1-911cef0da9e28043", + "SPDXID": "SPDXRef-Package-python-package-1-125840abc1c66dd7", "versionInfo": "1.0.1", "downloadLocation": "NOASSERTION", "sourceInfo": "acquired package info from installed python package manifest file: /somefile-1.txt", diff --git a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden index 4d86d0a96e2..e269ed53586 100644 --- a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden +++ b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden @@ -3,19 +3,19 @@ "dataLicense": "CC0-1.0", "SPDXID": "SPDXRef-DOCUMENT", "name": "user-image-input", - "documentNamespace": "https://anchore.com/syft/image/user-image-input-b4637c15-e4ca-421f-967f-e8d0c7cd4bf8", + "documentNamespace": "https://anchore.com/syft/image/user-image-input-1de3ac0e-5829-4294-9198-8d8fcdb5dd51", "creationInfo": { "licenseListVersion": "3.20", "creators": [ "Organization: Anchore, Inc", "Tool: syft-v0.42.0-bogus" ], - "created": "2023-05-04T20:37:23Z" + "created": "2023-05-09T17:11:26Z" }, "packages": [ { "name": "package-1", - "SPDXID": "SPDXRef-Package-python-package-1-911cef0da9e28043", + "SPDXID": "SPDXRef-Package-python-package-1-125840abc1c66dd7", "versionInfo": "1.0.1", "downloadLocation": "NOASSERTION", "sourceInfo": "acquired package info from installed python package manifest file: /somefile-1.txt", @@ -152,32 +152,32 @@ ], "relationships": [ { - "spdxElementId": "SPDXRef-Package-python-package-1-911cef0da9e28043", + "spdxElementId": "SPDXRef-Package-python-package-1-125840abc1c66dd7", "relatedSpdxElement": "SPDXRef-File-f1-5265a4dde3edbf7c", "relationshipType": "CONTAINS" }, { - "spdxElementId": "SPDXRef-Package-python-package-1-911cef0da9e28043", + "spdxElementId": "SPDXRef-Package-python-package-1-125840abc1c66dd7", "relatedSpdxElement": "SPDXRef-File-z1-f5-839d99ee67d9d174", "relationshipType": "CONTAINS" }, { - "spdxElementId": "SPDXRef-Package-python-package-1-911cef0da9e28043", + "spdxElementId": "SPDXRef-Package-python-package-1-125840abc1c66dd7", "relatedSpdxElement": "SPDXRef-File-a1-f6-9c2f7510199b17f6", "relationshipType": "CONTAINS" }, { - "spdxElementId": "SPDXRef-Package-python-package-1-911cef0da9e28043", + "spdxElementId": "SPDXRef-Package-python-package-1-125840abc1c66dd7", "relatedSpdxElement": "SPDXRef-File-d2-f4-c641caa71518099f", "relationshipType": "CONTAINS" }, { - "spdxElementId": "SPDXRef-Package-python-package-1-911cef0da9e28043", + "spdxElementId": "SPDXRef-Package-python-package-1-125840abc1c66dd7", "relatedSpdxElement": "SPDXRef-File-d1-f3-c6f5b29dca12661f", "relationshipType": "CONTAINS" }, { - "spdxElementId": "SPDXRef-Package-python-package-1-911cef0da9e28043", + "spdxElementId": "SPDXRef-Package-python-package-1-125840abc1c66dd7", "relatedSpdxElement": "SPDXRef-File-f2-f9e49132a4b96ccd", "relationshipType": "CONTAINS" }, diff --git a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXJSONSPDXIDs.golden b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXJSONSPDXIDs.golden index 70e3d959f77..ca1775a5de1 100644 --- a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXJSONSPDXIDs.golden +++ b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXJSONSPDXIDs.golden @@ -2,11 +2,11 @@ SPDXVersion: SPDX-2.3 DataLicense: CC0-1.0 SPDXID: SPDXRef-DOCUMENT DocumentName: foobar/baz -DocumentNamespace: https://anchore.com/syft/dir/foobar/baz-2bb5b039-33f5-471d-96ea-86e1fc70015a +DocumentNamespace: https://anchore.com/syft/dir/foobar/baz-1813dede-1ac5-4c44-a640-4c56e213d575 LicenseListVersion: 3.20 Creator: Organization: Anchore, Inc Creator: Tool: syft-v0.42.0-bogus -Created: 2023-05-04T20:37:42Z +Created: 2023-05-09T17:11:49Z ##### Package: @at-sign diff --git a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden index a0b64d68453..339b17c2c1c 100644 --- a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden +++ b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden @@ -2,11 +2,11 @@ SPDXVersion: SPDX-2.3 DataLicense: CC0-1.0 SPDXID: SPDXRef-DOCUMENT DocumentName: user-image-input -DocumentNamespace: https://anchore.com/syft/image/user-image-input-b6c2db3a-7cc1-4507-ac76-36a30169e284 +DocumentNamespace: https://anchore.com/syft/image/user-image-input-96ea886a-3297-4847-b211-6da405ff1f8f LicenseListVersion: 3.20 Creator: Organization: Anchore, Inc Creator: Tool: syft-v0.42.0-bogus -Created: 2023-05-04T20:37:42Z +Created: 2023-05-09T17:11:49Z ##### Unpackaged files @@ -63,7 +63,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-911cef0da9e28043 +SPDXID: SPDXRef-Package-python-package-1-125840abc1c66dd7 PackageVersion: 1.0.1 PackageDownloadLocation: NOASSERTION FilesAnalyzed: false @@ -76,11 +76,11 @@ ExternalRef: PACKAGE-MANAGER purl a-purl-1 ##### Relationships -Relationship: SPDXRef-Package-python-package-1-911cef0da9e28043 CONTAINS SPDXRef-File-f1-5265a4dde3edbf7c -Relationship: SPDXRef-Package-python-package-1-911cef0da9e28043 CONTAINS SPDXRef-File-z1-f5-839d99ee67d9d174 -Relationship: SPDXRef-Package-python-package-1-911cef0da9e28043 CONTAINS SPDXRef-File-a1-f6-9c2f7510199b17f6 -Relationship: SPDXRef-Package-python-package-1-911cef0da9e28043 CONTAINS SPDXRef-File-d2-f4-c641caa71518099f -Relationship: SPDXRef-Package-python-package-1-911cef0da9e28043 CONTAINS SPDXRef-File-d1-f3-c6f5b29dca12661f -Relationship: SPDXRef-Package-python-package-1-911cef0da9e28043 CONTAINS SPDXRef-File-f2-f9e49132a4b96ccd +Relationship: SPDXRef-Package-python-package-1-125840abc1c66dd7 CONTAINS SPDXRef-File-f1-5265a4dde3edbf7c +Relationship: SPDXRef-Package-python-package-1-125840abc1c66dd7 CONTAINS SPDXRef-File-z1-f5-839d99ee67d9d174 +Relationship: SPDXRef-Package-python-package-1-125840abc1c66dd7 CONTAINS SPDXRef-File-a1-f6-9c2f7510199b17f6 +Relationship: SPDXRef-Package-python-package-1-125840abc1c66dd7 CONTAINS SPDXRef-File-d2-f4-c641caa71518099f +Relationship: SPDXRef-Package-python-package-1-125840abc1c66dd7 CONTAINS SPDXRef-File-d1-f3-c6f5b29dca12661f +Relationship: SPDXRef-Package-python-package-1-125840abc1c66dd7 CONTAINS SPDXRef-File-f2-f9e49132a4b96ccd Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-DOCUMENT diff --git a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueDirectoryEncoder.golden b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueDirectoryEncoder.golden index af6a06dbc04..818d62e7d79 100644 --- a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueDirectoryEncoder.golden +++ b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueDirectoryEncoder.golden @@ -2,11 +2,11 @@ SPDXVersion: SPDX-2.3 DataLicense: CC0-1.0 SPDXID: SPDXRef-DOCUMENT DocumentName: /some/path -DocumentNamespace: https://anchore.com/syft/dir/some/path-2380f2c5-b2f2-46c5-8193-ffe8588ad05b +DocumentNamespace: https://anchore.com/syft/dir/some/path-f7bdb1ee-7fef-48e7-a386-6ee3836d4a28 LicenseListVersion: 3.20 Creator: Organization: Anchore, Inc Creator: Tool: syft-v0.42.0-bogus -Created: 2023-05-04T20:37:41Z +Created: 2023-05-09T17:11:49Z ##### Package: package-2 @@ -25,7 +25,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-3fdbdcde4d17d261 +SPDXID: SPDXRef-Package-python-package-1-9265397e5e15168a PackageVersion: 1.0.1 PackageDownloadLocation: NOASSERTION FilesAnalyzed: false diff --git a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueImageEncoder.golden b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueImageEncoder.golden index df3fca45c3f..867e8e0396f 100644 --- a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueImageEncoder.golden +++ b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueImageEncoder.golden @@ -2,11 +2,11 @@ SPDXVersion: SPDX-2.3 DataLicense: CC0-1.0 SPDXID: SPDXRef-DOCUMENT DocumentName: user-image-input -DocumentNamespace: https://anchore.com/syft/image/user-image-input-79b38e34-5eff-450a-a969-545dc36ec5c7 +DocumentNamespace: https://anchore.com/syft/image/user-image-input-44d44a85-2207-4b51-bd73-d0c7b080f6d3 LicenseListVersion: 3.20 Creator: Organization: Anchore, Inc Creator: Tool: syft-v0.42.0-bogus -Created: 2023-05-04T20:37:42Z +Created: 2023-05-09T17:11:49Z ##### Package: package-2 @@ -25,7 +25,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-911cef0da9e28043 +SPDXID: SPDXRef-Package-python-package-1-125840abc1c66dd7 PackageVersion: 1.0.1 PackageDownloadLocation: NOASSERTION FilesAnalyzed: false diff --git a/syft/formats/syftjson/encoder_test.go b/syft/formats/syftjson/encoder_test.go index ed41fec62b0..c42cc75c410 100644 --- a/syft/formats/syftjson/encoder_test.go +++ b/syft/formats/syftjson/encoder_test.go @@ -61,7 +61,7 @@ func TestEncodeFullJSONDocument(t *testing.T) { FoundBy: "the-cataloger-1", Language: pkg.Python, MetadataType: pkg.PythonPackageMetadataType, - Licenses: []pkg.License{pkg.NewLicense("MIT")}, + Licenses: pkg.NewLicenseSet(pkg.NewLicense("MIT")), Metadata: pkg.PythonPackageMetadata{ Name: "package-1", Version: "1.0.1", diff --git a/syft/formats/syftjson/model/package.go b/syft/formats/syftjson/model/package.go index cee46ca7982..c739cdada49 100644 --- a/syft/formats/syftjson/model/package.go +++ b/syft/formats/syftjson/model/package.go @@ -7,6 +7,7 @@ import ( "reflect" "github.com/anchore/syft/internal/log" + "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/source" ) @@ -33,16 +34,39 @@ type PackageBasicData struct { PURL string `json:"purl"` } -type licenses []pkg.License +type licenses []License + +type License struct { + Value string `json:"value"` + SPDXExpression string `json:"spdxExpression"` + Type license.Type `json:"type"` + URL []string `json:"url"` + Location []source.Location `json:"locations"` +} + +func newModelLicensesFromValues(licenses []string) (ml []License) { + for _, v := range licenses { + expression, err := license.ParseExpression(v) + if err != nil { + log.Trace("could not find valid spdx expression for %s: %w", v, err) + } + ml = append(ml, License{ + Value: v, + SPDXExpression: expression, + Type: license.Declared, + }) + } + return ml +} func (f *licenses) UnmarshalJSON(b []byte) error { - var licenses []pkg.License + var licenses []License if err := json.Unmarshal(b, &licenses); err != nil { var simpleLicense []string if err := json.Unmarshal(b, &simpleLicense); err != nil { return fmt.Errorf("unable to unmarshal license: %w", err) } - licenses = pkg.NewLicensesFromValues(simpleLicense...) + licenses = newModelLicensesFromValues(simpleLicense) } *f = licenses return nil diff --git a/syft/formats/syftjson/model/package_test.go b/syft/formats/syftjson/model/package_test.go index b9c17030efb..8027764d85e 100644 --- a/syft/formats/syftjson/model/package_test.go +++ b/syft/formats/syftjson/model/package_test.go @@ -36,7 +36,7 @@ func TestUnmarshalPackageGolang(t *testing.T) { "value": "MIT", "spdxExpression": "MIT", "type": "declared", - "url": "" + "url": [] } ], "language": "go", @@ -74,7 +74,13 @@ func TestUnmarshalPackageGolang(t *testing.T) { "value": "MIT", "spdxExpression": "MIT", "type": "declared", - "url": "" + "url": ["https://www.github.com"] + }, + { + "value": "MIT", + "spdxExpression": "MIT", + "type": "declared", + "locations": [{"path": "/Users/hal/go/bin/syft"}] } ], "language": "go", diff --git a/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden b/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden index d553f570f50..96fbd86a06e 100644 --- a/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden +++ b/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden @@ -1,7 +1,7 @@ { "artifacts": [ { - "id": "3fdbdcde4d17d261", + "id": "9265397e5e15168a", "name": "package-1", "version": "1.0.1", "type": "python", @@ -16,7 +16,8 @@ "value": "MIT", "spdxExpression": "MIT", "type": "declared", - "url": "" + "url": [], + "locations": null } ], "language": "python", diff --git a/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden b/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden index b1b3fbef4de..a6c1f6d696a 100644 --- a/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden +++ b/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden @@ -1,7 +1,7 @@ { "artifacts": [ { - "id": "1cd17a1c31293765", + "id": "271e49ba46e0b601", "name": "package-1", "version": "1.0.1", "type": "python", @@ -16,7 +16,8 @@ "value": "MIT", "spdxExpression": "MIT", "type": "declared", - "url": "" + "url": [], + "locations": null } ], "language": "python", diff --git a/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden b/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden index 9eaac5b46df..9a4f2e0b944 100644 --- a/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden +++ b/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden @@ -1,7 +1,7 @@ { "artifacts": [ { - "id": "911cef0da9e28043", + "id": "125840abc1c66dd7", "name": "package-1", "version": "1.0.1", "type": "python", @@ -17,7 +17,8 @@ "value": "MIT", "spdxExpression": "MIT", "type": "declared", - "url": "" + "url": [], + "locations": null } ], "language": "python", diff --git a/syft/formats/syftjson/to_format_model.go b/syft/formats/syftjson/to_format_model.go index 75d8e4f07c9..63fea8a8982 100644 --- a/syft/formats/syftjson/to_format_model.go +++ b/syft/formats/syftjson/to_format_model.go @@ -184,6 +184,19 @@ func toPackageModels(catalog *pkg.Collection) []model.Package { return artifacts } +func toLicenseModel(pkgLicenses []pkg.License) (modelLicenses []model.License) { + for _, l := range pkgLicenses { + modelLicenses = append(modelLicenses, model.License{ + Value: l.Value, + SPDXExpression: l.SPDXExpression, + Type: l.Type, + URL: l.URL.ToSlice(), + Location: l.Location.ToSlice(), + }) + } + return +} + // toPackageModel crates a new Package from the given pkg.Package. func toPackageModel(p pkg.Package) model.Package { var cpes = make([]string, len(p.CPEs)) @@ -193,9 +206,9 @@ func toPackageModel(p pkg.Package) model.Package { // we want to make sure all catalogers are // initializing the array; this is a good choke point for this check - var licenses = make([]pkg.License, 0) - if p.Licenses != nil { - licenses = p.Licenses + var licenses = make([]model.License, 0) + if !p.Licenses.Empty() { + licenses = toLicenseModel(p.Licenses.ToSlice()) } return model.Package{ diff --git a/syft/formats/syftjson/to_syft_model.go b/syft/formats/syftjson/to_syft_model.go index cbc03726f58..559dab755e8 100644 --- a/syft/formats/syftjson/to_syft_model.go +++ b/syft/formats/syftjson/to_syft_model.go @@ -8,6 +8,7 @@ import ( "github.com/google/go-cmp/cmp" stereoscopeFile "github.com/anchore/stereoscope/pkg/file" + "github.com/anchore/syft/internal" "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/cpe" @@ -80,6 +81,19 @@ func toSyftFiles(files []model.File) sbom.Artifacts { return ret } +func toSyftLicenses(m []model.License) (p []pkg.License) { + for _, l := range m { + p = append(p, pkg.License{ + Value: l.Value, + SPDXExpression: l.SPDXExpression, + Type: l.Type, + URL: internal.NewStringSet(l.URL...), + Location: source.NewLocationSet(l.Location...), + }) + } + return +} + func toSyftFileType(ty string) stereoscopeFile.Type { switch ty { case "SymbolicLink": @@ -281,7 +295,7 @@ func toSyftPackage(p model.Package, idAliases map[string]string) pkg.Package { Version: p.Version, FoundBy: p.FoundBy, Locations: source.NewLocationSet(p.Locations...), - Licenses: p.Licenses, + Licenses: pkg.NewLicenseSet(toSyftLicenses(p.Licenses)...), Language: p.Language, Type: p.Type, CPEs: cpes, diff --git a/syft/pkg/cataloger/alpm/cataloger_test.go b/syft/pkg/cataloger/alpm/cataloger_test.go index e8478bea246..1dedded2eff 100644 --- a/syft/pkg/cataloger/alpm/cataloger_test.go +++ b/syft/pkg/cataloger/alpm/cataloger_test.go @@ -20,10 +20,10 @@ func TestAlpmCataloger(t *testing.T) { Version: "6.2.1-2", Type: pkg.AlpmPkg, FoundBy: "alpmdb-cataloger", - Licenses: []pkg.License{ - pkg.NewLicenseFromLocation("LGPL3", dbLocation), - pkg.NewLicenseFromLocation("GPL", dbLocation), - }, + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocations("LGPL3", dbLocation), + pkg.NewLicenseFromLocations("GPL", dbLocation), + ), Locations: source.NewLocationSet(dbLocation), CPEs: nil, PURL: "", diff --git a/syft/pkg/cataloger/alpm/package.go b/syft/pkg/cataloger/alpm/package.go index 3e6ee3f9941..2c85db47c62 100644 --- a/syft/pkg/cataloger/alpm/package.go +++ b/syft/pkg/cataloger/alpm/package.go @@ -16,7 +16,7 @@ func newPackage(m *parsedData, release *linux.Release, dbLocation source.Locatio Name: m.Package, Version: m.Version, Locations: source.NewLocationSet(dbLocation), - Licenses: pkg.NewLicensesFromLocation(dbLocation.WithoutAnnotations(), licenseCandidates...), + Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(dbLocation.WithoutAnnotations(), licenseCandidates...)...), Type: pkg.AlpmPkg, PURL: packageURL(m, release), MetadataType: pkg.AlpmMetadataType, diff --git a/syft/pkg/cataloger/apkdb/package.go b/syft/pkg/cataloger/apkdb/package.go index 291fb20da89..392fe277154 100644 --- a/syft/pkg/cataloger/apkdb/package.go +++ b/syft/pkg/cataloger/apkdb/package.go @@ -16,7 +16,7 @@ func newPackage(d parsedData, release *linux.Release, dbLocation source.Location Name: d.Package, Version: d.Version, Locations: source.NewLocationSet(dbLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)), - Licenses: pkg.NewLicensesFromLocation(dbLocation, licenseStrings...), + Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(dbLocation, licenseStrings...)...), PURL: packageURL(d.ApkMetadata, release), Type: pkg.ApkPkg, MetadataType: pkg.ApkMetadataType, diff --git a/syft/pkg/cataloger/apkdb/parse_apk_db_test.go b/syft/pkg/cataloger/apkdb/parse_apk_db_test.go index 3519feaea0d..ba26eb4f09a 100644 --- a/syft/pkg/cataloger/apkdb/parse_apk_db_test.go +++ b/syft/pkg/cataloger/apkdb/parse_apk_db_test.go @@ -87,11 +87,11 @@ func TestSinglePackageDetails(t *testing.T) { expected: pkg.Package{ Name: "musl-utils", Version: "1.1.24-r2", - Licenses: []pkg.License{ + Licenses: pkg.NewLicenseSet( pkg.NewLicense("MIT"), pkg.NewLicense("BSD"), pkg.NewLicense("GPL2+"), - }, + ), Type: pkg.ApkPkg, MetadataType: pkg.ApkMetadataType, Metadata: pkg.ApkMetadata{ @@ -177,9 +177,9 @@ func TestSinglePackageDetails(t *testing.T) { expected: pkg.Package{ Name: "alpine-baselayout-data", Version: "3.4.0-r0", - Licenses: []pkg.License{ + Licenses: pkg.NewLicenseSet( pkg.NewLicense("GPL-2.0-only"), - }, + ), Type: pkg.ApkPkg, MetadataType: pkg.ApkMetadataType, Metadata: pkg.ApkMetadata{ @@ -222,9 +222,9 @@ func TestSinglePackageDetails(t *testing.T) { expected: pkg.Package{ Name: "alpine-baselayout", Version: "3.2.0-r6", - Licenses: []pkg.License{ + Licenses: pkg.NewLicenseSet( pkg.NewLicense("GPL-2.0-only"), - }, + ), Type: pkg.ApkPkg, PURL: "", MetadataType: pkg.ApkMetadataType, @@ -680,15 +680,14 @@ func TestSinglePackageDetails(t *testing.T) { for _, test := range tests { t.Run(test.fixture, func(t *testing.T) { - fixtureLocation := source.NewLocation(test.fixture) test.expected.Locations = source.NewLocationSet(fixtureLocation) - for i := range test.expected.Licenses { - test.expected.Licenses[i].Location = &fixtureLocation + licenses := test.expected.Licenses.ToSlice() + for i := range licenses { + licenses[i].Location.Add(fixtureLocation) } - + test.expected.Licenses = pkg.NewLicenseSet(licenses...) pkgtest.TestFileParser(t, test.fixture, parseApkDB, []pkg.Package{test.expected}, nil) - }) } } @@ -701,9 +700,9 @@ func TestMultiplePackages(t *testing.T) { { Name: "libc-utils", Version: "0.7.2-r0", - Licenses: []pkg.License{ - pkg.NewLicenseFromLocation("BSD", location), - }, + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocations("BSD", location), + ), Type: pkg.ApkPkg, PURL: "pkg:apk/alpine/libc-utils@0.7.2-r0?arch=x86_64&upstream=libc-dev&distro=alpine-3.12", Locations: fixtureLocationSet, @@ -731,11 +730,11 @@ func TestMultiplePackages(t *testing.T) { Type: pkg.ApkPkg, PURL: "pkg:apk/alpine/musl-utils@1.1.24-r2?arch=x86_64&upstream=musl&distro=alpine-3.12", Locations: fixtureLocationSet, - Licenses: []pkg.License{ - pkg.NewLicenseFromLocation("MIT", location), - pkg.NewLicenseFromLocation("BSD", location), - pkg.NewLicenseFromLocation("GPL2+", location), - }, + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocations("MIT", location), + pkg.NewLicenseFromLocations("BSD", location), + pkg.NewLicenseFromLocations("GPL2+", location), + ), MetadataType: pkg.ApkMetadataType, Metadata: pkg.ApkMetadata{ Package: "musl-utils", @@ -1025,7 +1024,7 @@ func Test_discoverPackageDependencies(t *testing.T) { t.Run(test.name, func(t *testing.T) { pkgs, wantRelationships := test.genFn() gotRelationships := discoverPackageDependencies(pkgs) - d := cmp.Diff(wantRelationships, gotRelationships, cmpopts.IgnoreUnexported(pkg.Package{}, source.LocationSet{})) + d := cmp.Diff(wantRelationships, gotRelationships, cmpopts.IgnoreUnexported(pkg.Package{}, source.LocationSet{}, pkg.LicenseSet{})) if d != "" { t.Fail() t.Log(d) diff --git a/syft/pkg/cataloger/deb/cataloger_test.go b/syft/pkg/cataloger/deb/cataloger_test.go index 8199407f6c8..ab3415d5402 100644 --- a/syft/pkg/cataloger/deb/cataloger_test.go +++ b/syft/pkg/cataloger/deb/cataloger_test.go @@ -16,11 +16,11 @@ func TestDpkgCataloger(t *testing.T) { Name: "libpam-runtime", Version: "1.1.8-3.6", FoundBy: "dpkgdb-cataloger", - Licenses: []pkg.License{ - pkg.NewLicenseFromLocation("GPL-1", licenseLocation), - pkg.NewLicenseFromLocation("GPL-2", licenseLocation), - pkg.NewLicenseFromLocation("LGPL-2.1", licenseLocation), - }, + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocations("GPL-1", licenseLocation), + pkg.NewLicenseFromLocations("GPL-2", licenseLocation), + pkg.NewLicenseFromLocations("LGPL-2.1", licenseLocation), + ), Locations: source.NewLocationSet( source.NewVirtualLocation("/var/lib/dpkg/status", "/var/lib/dpkg/status"), source.NewVirtualLocation("/var/lib/dpkg/info/libpam-runtime.md5sums", "/var/lib/dpkg/info/libpam-runtime.md5sums"), diff --git a/syft/pkg/cataloger/deb/package.go b/syft/pkg/cataloger/deb/package.go index 2e6ea875f90..1685051c959 100644 --- a/syft/pkg/cataloger/deb/package.go +++ b/syft/pkg/cataloger/deb/package.go @@ -27,7 +27,7 @@ func newDpkgPackage(d pkg.DpkgMetadata, dbLocation source.Location, resolver sou p := pkg.Package{ Name: d.Package, Version: d.Version, - Licenses: licenses, + Licenses: pkg.NewLicenseSet(licenses...), Locations: source.NewLocationSet(dbLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)), PURL: packageURL(d, release), Type: pkg.DebPkg, @@ -98,7 +98,7 @@ func addLicenses(resolver source.FileResolver, dbLocation source.Location, p *pk // attach the licenses licenseStrs := parseLicensesFromCopyright(copyrightReader) for _, licenseStr := range licenseStrs { - p.Licenses = append(p.Licenses, pkg.NewLicenseFromLocation(licenseStr, copyrightLocation.WithoutAnnotations())) + p.Licenses.Add(pkg.NewLicenseFromLocations(licenseStr, copyrightLocation.WithoutAnnotations())) } // keep a record of the file where this was discovered p.Locations.Add(*copyrightLocation) diff --git a/syft/pkg/cataloger/deb/parse_dpkg_db_test.go b/syft/pkg/cataloger/deb/parse_dpkg_db_test.go index edfc65d477c..fc4e51633ad 100644 --- a/syft/pkg/cataloger/deb/parse_dpkg_db_test.go +++ b/syft/pkg/cataloger/deb/parse_dpkg_db_test.go @@ -307,7 +307,7 @@ Installed-Size: 10kib Name: "apt", Type: "deb", PURL: "pkg:deb/debian/apt?distro=debian-10", - Licenses: []pkg.License{}, + Licenses: pkg.NewLicenseSet(), Locations: source.NewLocationSet(source.NewLocation("place")), MetadataType: "DpkgMetadata", Metadata: pkg.DpkgMetadata{ diff --git a/syft/pkg/cataloger/golang/licenses_test.go b/syft/pkg/cataloger/golang/licenses_test.go index 186afa0468b..64b66b3f6ff 100644 --- a/syft/pkg/cataloger/golang/licenses_test.go +++ b/syft/pkg/cataloger/golang/licenses_test.go @@ -13,6 +13,7 @@ import ( "github.com/stretchr/testify/require" + "github.com/anchore/syft/internal" "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/source" @@ -34,7 +35,8 @@ func Test_LocalLicenseSearch(t *testing.T) { Value: "Apache-2.0", SPDXExpression: "Apache-2.0", Type: license.Concluded, - Location: &loc1, + Location: source.NewLocationSet(loc1), + URL: internal.NewStringSet(), }, }, { @@ -44,7 +46,8 @@ func Test_LocalLicenseSearch(t *testing.T) { Value: "MIT", SPDXExpression: "MIT", Type: license.Concluded, - Location: &loc2, + Location: source.NewLocationSet(loc2), + URL: internal.NewStringSet(), }, }, } @@ -123,7 +126,8 @@ func Test_RemoteProxyLicenseSearch(t *testing.T) { Value: "Apache-2.0", SPDXExpression: "Apache-2.0", Type: license.Concluded, - Location: &loc1, + Location: source.NewLocationSet(loc1), + URL: internal.NewStringSet(), }, }, { @@ -133,7 +137,8 @@ func Test_RemoteProxyLicenseSearch(t *testing.T) { Value: "MIT", SPDXExpression: "MIT", Type: license.Concluded, - Location: &loc2, + Location: source.NewLocationSet(loc2), + URL: internal.NewStringSet(), }, }, } diff --git a/syft/pkg/cataloger/golang/package.go b/syft/pkg/cataloger/golang/package.go index de12e7880d7..2e4c2808994 100644 --- a/syft/pkg/cataloger/golang/package.go +++ b/syft/pkg/cataloger/golang/package.go @@ -24,7 +24,7 @@ func (c *goBinaryCataloger) newGoBinaryPackage(resolver source.FileResolver, dep p := pkg.Package{ Name: dep.Path, Version: dep.Version, - Licenses: licenses, + Licenses: pkg.NewLicenseSet(licenses...), PURL: packageURL(dep.Path, dep.Version), Language: pkg.Go, Type: pkg.GoModulePkg, diff --git a/syft/pkg/cataloger/golang/parse_go_binary_test.go b/syft/pkg/cataloger/golang/parse_go_binary_test.go index 0ae0b57359b..d180d158fdc 100644 --- a/syft/pkg/cataloger/golang/parse_go_binary_test.go +++ b/syft/pkg/cataloger/golang/parse_go_binary_test.go @@ -497,9 +497,6 @@ func TestBuildGoPkgInfo(t *testing.T) { t.Run(test.name, func(t *testing.T) { for i := range test.expected { p := &test.expected[i] - if p.Licenses == nil { - p.Licenses = []pkg.License{} - } p.SetID() } location := source.NewLocationFromCoordinates( diff --git a/syft/pkg/cataloger/golang/parse_go_mod.go b/syft/pkg/cataloger/golang/parse_go_mod.go index 203ba318919..3fdc45b9a71 100644 --- a/syft/pkg/cataloger/golang/parse_go_mod.go +++ b/syft/pkg/cataloger/golang/parse_go_mod.go @@ -50,7 +50,7 @@ func (c *goModCataloger) parseGoModFile(resolver source.FileResolver, _ *generic packages[m.Mod.Path] = pkg.Package{ Name: m.Mod.Path, Version: m.Mod.Version, - Licenses: licenses, + Licenses: pkg.NewLicenseSet(licenses...), Locations: source.NewLocationSet(reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)), PURL: packageURL(m.Mod.Path, m.Mod.Version), Language: pkg.Go, @@ -72,7 +72,7 @@ func (c *goModCataloger) parseGoModFile(resolver source.FileResolver, _ *generic packages[m.New.Path] = pkg.Package{ Name: m.New.Path, Version: m.New.Version, - Licenses: licenses, + Licenses: pkg.NewLicenseSet(licenses...), Locations: source.NewLocationSet(reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)), PURL: packageURL(m.New.Path, m.New.Version), Language: pkg.Go, diff --git a/syft/pkg/cataloger/golang/parse_go_mod_test.go b/syft/pkg/cataloger/golang/parse_go_mod_test.go index 56ab179eb6e..83b75beb108 100644 --- a/syft/pkg/cataloger/golang/parse_go_mod_test.go +++ b/syft/pkg/cataloger/golang/parse_go_mod_test.go @@ -88,12 +88,6 @@ func TestParseGoMod(t *testing.T) { for _, test := range tests { t.Run(test.fixture, func(t *testing.T) { - for i := range test.expected { - p := &test.expected[i] - if p.Licenses == nil { - p.Licenses = []pkg.License{} - } - } c := goModCataloger{} pkgtest.NewCatalogTester(). FromFile(t, test.fixture). @@ -154,12 +148,6 @@ func Test_GoSumHashes(t *testing.T) { for _, test := range tests { t.Run(test.fixture, func(t *testing.T) { - for i := range test.expected { - p := &test.expected[i] - if p.Licenses == nil { - p.Licenses = []pkg.License{} - } - } pkgtest.NewCatalogTester(). FromDirectory(t, test.fixture). Expects(test.expected, nil). diff --git a/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go b/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go index acccd7b18e3..5929fc97062 100644 --- a/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go +++ b/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go @@ -21,6 +21,7 @@ import ( ) type locationComparer func(x, y source.Location) bool +type licenseComparer func(x, y pkg.License) bool type CatalogTester struct { expectedPkgs []pkg.Package @@ -36,12 +37,14 @@ type CatalogTester struct { wantErr require.ErrorAssertionFunc compareOptions []cmp.Option locationComparer locationComparer + licenseComparer licenseComparer } func NewCatalogTester() *CatalogTester { return &CatalogTester{ wantErr: require.NoError, locationComparer: DefaultLocationComparer, + licenseComparer: DefaultLicenseComparer, ignoreUnfulfilledPathResponses: map[string][]string{ "FilesByPath": { // most catalogers search for a linux release, which will not be fulfilled in testing @@ -59,6 +62,25 @@ func DefaultLocationComparer(x, y source.Location) bool { return cmp.Equal(x.Coordinates, y.Coordinates) && cmp.Equal(x.VirtualPath, y.VirtualPath) } +func DefaultLicenseComparer(x, y pkg.License) bool { + return cmp.Equal(x, y, cmp.Comparer(DefaultLocationComparer), cmp.Comparer( + func(x, y source.LocationSet) bool { + xs := x.ToSlice() + ys := y.ToSlice() + if len(xs) != len(ys) { + return false + } + for i, xe := range xs { + ye := ys[i] + if !DefaultLocationComparer(xe, ye) { + return false + } + } + return true + }, + )) +} + func (p *CatalogTester) FromDirectory(t *testing.T, path string) *CatalogTester { t.Helper() @@ -139,6 +161,26 @@ func (p *CatalogTester) IgnoreLocationLayer() *CatalogTester { p.locationComparer = func(x, y source.Location) bool { return cmp.Equal(x.Coordinates.RealPath, y.Coordinates.RealPath) && cmp.Equal(x.VirtualPath, y.VirtualPath) } + + // we need to update the license comparer to use the ignored location layer + p.licenseComparer = func(x, y pkg.License) bool { + return cmp.Equal(x, y, cmp.Comparer(p.locationComparer), cmp.Comparer( + func(x, y source.LocationSet) bool { + xs := x.ToSlice() + ys := y.ToSlice() + if len(xs) != len(ys) { + return false + } + for i, xe := range xs { + ye := ys[i] + if !p.locationComparer(xe, ye) { + return false + } + } + + return true + })) + } return p } @@ -209,6 +251,7 @@ func (p *CatalogTester) TestCataloger(t *testing.T, cataloger pkg.Cataloger) { } } +// nolint:funlen func (p *CatalogTester) assertPkgs(t *testing.T, pkgs []pkg.Package, relationships []artifact.Relationship) { t.Helper() @@ -233,9 +276,30 @@ func (p *CatalogTester) assertPkgs(t *testing.T, pkgs []pkg.Package, relationshi return true }, ), + cmp.Comparer( + func(x, y pkg.LicenseSet) bool { + xs := x.ToSlice() + ys := y.ToSlice() + + if len(xs) != len(ys) { + return false + } + for i, xe := range xs { + ye := ys[i] + if !p.licenseComparer(xe, ye) { + return false + } + } + + return true + }, + ), cmp.Comparer( p.locationComparer, ), + cmp.Comparer( + p.licenseComparer, + ), ) { @@ -299,9 +363,30 @@ func AssertPackagesEqual(t *testing.T, a, b pkg.Package) { return true }, ), + cmp.Comparer( + func(x, y pkg.LicenseSet) bool { + xs := x.ToSlice() + ys := y.ToSlice() + + if len(xs) != len(ys) { + return false + } + for i, xe := range xs { + ye := ys[i] + if !DefaultLicenseComparer(xe, ye) { + return false + } + } + + return true + }, + ), cmp.Comparer( DefaultLocationComparer, ), + cmp.Comparer( + DefaultLicenseComparer, + ), } if diff := cmp.Diff(a, b, opts...); diff != "" { diff --git a/syft/pkg/cataloger/java/archive_parser.go b/syft/pkg/cataloger/java/archive_parser.go index 2536d304688..d6c0ad926f5 100644 --- a/syft/pkg/cataloger/java/archive_parser.go +++ b/syft/pkg/cataloger/java/archive_parser.go @@ -185,15 +185,13 @@ func (j *archiveParser) discoverMainPackage() (*pkg.Package, error) { log.Warnf("failed to create digest for file=%q: %+v", j.archivePath, err) } + // we use j.location because we want to associate the license declaration with where we discovered the contents in the manifest + licenses := pkg.NewLicensesFromLocation(j.location, selectLicenses(manifest)...) return &pkg.Package{ Name: selectName(manifest, j.fileInfo), Version: selectVersion(manifest, j.fileInfo), Language: pkg.Java, - Licenses: pkg.NewLicensesFromLocation( - // we use j.location because we want to associate the license declaration with where we discovered the contents in the manifest - j.location, - selectLicenses(manifest)..., - ), + Licenses: pkg.NewLicenseSet(licenses...), Locations: source.NewLocationSet( j.location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), ), diff --git a/syft/pkg/cataloger/java/archive_parser_test.go b/syft/pkg/cataloger/java/archive_parser_test.go index 09fd7adf95c..5385dec7382 100644 --- a/syft/pkg/cataloger/java/archive_parser_test.go +++ b/syft/pkg/cataloger/java/archive_parser_test.go @@ -99,9 +99,9 @@ func TestParseJar(t *testing.T) { Name: "example-jenkins-plugin", Version: "1.0-SNAPSHOT", PURL: "pkg:maven/io.jenkins.plugins/example-jenkins-plugin@1.0-SNAPSHOT", - Licenses: []pkg.License{ - pkg.NewLicenseFromLocation("MIT License", source.NewLocation("test-fixtures/java-builds/packages/example-jenkins-plugin.hpi")), - }, + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocations("MIT License", source.NewLocation("test-fixtures/java-builds/packages/example-jenkins-plugin.hpi")), + ), Language: pkg.Java, Type: pkg.JenkinsPluginPkg, MetadataType: pkg.JavaMetadataType, diff --git a/syft/pkg/cataloger/javascript/cataloger_test.go b/syft/pkg/cataloger/javascript/cataloger_test.go index e800ae3295b..5b9c18f0ed2 100644 --- a/syft/pkg/cataloger/javascript/cataloger_test.go +++ b/syft/pkg/cataloger/javascript/cataloger_test.go @@ -19,9 +19,9 @@ func Test_JavascriptCataloger(t *testing.T) { Locations: locationSet, Language: pkg.JavaScript, Type: pkg.NpmPkg, - Licenses: []pkg.License{ - pkg.NewLicenseFromLocation("MIT", source.NewLocation("package-lock.json")), - }, + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocations("MIT", source.NewLocation("package-lock.json")), + ), MetadataType: pkg.NpmPackageLockJSONMetadataType, Metadata: pkg.NpmPackageLockJSONMetadata{Resolved: "https://registry.npmjs.org/@actions/core/-/core-1.6.0.tgz", Integrity: "sha512-NB1UAZomZlCV/LmJqkLhNTqtKfFXJZAUPcfl/zqG7EfsQdeUJtaWO98SGbuQ3pydJ3fHl2CvI/51OKYlCYYcaw=="}, }, @@ -44,9 +44,9 @@ func Test_JavascriptCataloger(t *testing.T) { Locations: locationSet, Language: pkg.JavaScript, Type: pkg.NpmPkg, - Licenses: []pkg.License{ - pkg.NewLicenseFromLocation("MIT", source.NewLocation("package-lock.json")), - }, + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocations("MIT", source.NewLocation("package-lock.json")), + ), MetadataType: pkg.NpmPackageLockJSONMetadataType, Metadata: pkg.NpmPackageLockJSONMetadata{Resolved: "https://registry.npmjs.org/cowsay/-/cowsay-1.4.0.tgz", Integrity: "sha512-rdg5k5PsHFVJheO/pmE3aDg2rUDDTfPJau6yYkZYlHFktUz+UxbE+IgnUAEyyCyv4noL5ltxXD0gZzmHPCy/9g=="}, }, diff --git a/syft/pkg/cataloger/javascript/package.go b/syft/pkg/cataloger/javascript/package.go index 1c1ae9db24c..468854a3927 100644 --- a/syft/pkg/cataloger/javascript/package.go +++ b/syft/pkg/cataloger/javascript/package.go @@ -18,13 +18,14 @@ func newPackageJSONPackage(u packageJSON, indexLocation source.Location) pkg.Pac log.Warnf("unable to extract licenses from javascript package.json: %+v", err) } + license := pkg.NewLicensesFromLocation(indexLocation, licenseCandidates...) p := pkg.Package{ Name: u.Name, Version: u.Version, PURL: packageURL(u.Name, u.Version), Locations: source.NewLocationSet(indexLocation), Language: pkg.JavaScript, - Licenses: pkg.NewLicensesFromLocation(indexLocation, licenseCandidates...), + Licenses: pkg.NewLicenseSet(license...), Type: pkg.NpmPkg, MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ @@ -83,7 +84,7 @@ func newPackageLockV2Package(resolver source.FileResolver, location source.Locat Name: name, Version: u.Version, Locations: source.NewLocationSet(location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)), - Licenses: pkg.NewLicensesFromLocation(location, u.License...), + Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(location, u.License...)...), PURL: packageURL(name, u.Version), Language: pkg.JavaScript, Type: pkg.NpmPkg, @@ -125,7 +126,7 @@ func newYarnLockPackage(resolver source.FileResolver, location source.Location, func finalizeLockPkg(resolver source.FileResolver, location source.Location, p pkg.Package) pkg.Package { licenseCandidate := addLicenses(p.Name, resolver, location) - p.Licenses = append(p.Licenses, pkg.NewLicensesFromLocation(location, licenseCandidate...)...) + p.Licenses.Add(pkg.NewLicensesFromLocation(location, licenseCandidate...)...) p.SetID() return p } diff --git a/syft/pkg/cataloger/javascript/parse_package_json_test.go b/syft/pkg/cataloger/javascript/parse_package_json_test.go index dbeb44bc382..c0e0b17b088 100644 --- a/syft/pkg/cataloger/javascript/parse_package_json_test.go +++ b/syft/pkg/cataloger/javascript/parse_package_json_test.go @@ -23,9 +23,9 @@ func TestParsePackageJSON(t *testing.T) { PURL: "pkg:npm/npm@6.14.6", Type: pkg.NpmPkg, Language: pkg.JavaScript, - Licenses: []pkg.License{ - pkg.NewLicenseFromLocation("Artistic-2.0", source.NewLocation("test-fixtures/pkg-json/package.json")), - }, + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocations("Artistic-2.0", source.NewLocation("test-fixtures/pkg-json/package.json")), + ), MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ Name: "npm", @@ -45,9 +45,9 @@ func TestParsePackageJSON(t *testing.T) { PURL: "pkg:npm/npm@6.14.6", Type: pkg.NpmPkg, Language: pkg.JavaScript, - Licenses: []pkg.License{ - pkg.NewLicenseFromLocation("ISC", source.NewLocation("test-fixtures/pkg-json/package-license-object.json")), - }, + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocations("ISC", source.NewLocation("test-fixtures/pkg-json/package-license-object.json")), + ), MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ Name: "npm", @@ -66,10 +66,10 @@ func TestParsePackageJSON(t *testing.T) { Version: "6.14.6", PURL: "pkg:npm/npm@6.14.6", Type: pkg.NpmPkg, - Licenses: []pkg.License{ - pkg.NewLicenseFromLocation("MIT", source.NewLocation("test-fixtures/pkg-json/package-license-objects.json")), - pkg.NewLicenseFromLocation("Apache-2.0", source.NewLocation("test-fixtures/pkg-json/package-license-objects.json")), - }, + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocations("MIT", source.NewLocation("test-fixtures/pkg-json/package-license-objects.json")), + pkg.NewLicenseFromLocations("Apache-2.0", source.NewLocation("test-fixtures/pkg-json/package-license-objects.json")), + ), Language: pkg.JavaScript, MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ @@ -127,9 +127,9 @@ func TestParsePackageJSON(t *testing.T) { Version: "6.14.6", PURL: "pkg:npm/npm@6.14.6", Type: pkg.NpmPkg, - Licenses: []pkg.License{ - pkg.NewLicenseFromLocation("Artistic-2.0", source.NewLocation("test-fixtures/pkg-json/package-nested-author.json")), - }, + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocations("Artistic-2.0", source.NewLocation("test-fixtures/pkg-json/package-nested-author.json")), + ), Language: pkg.JavaScript, MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ @@ -149,9 +149,9 @@ func TestParsePackageJSON(t *testing.T) { Version: "1.1.1", PURL: "pkg:npm/function-bind@1.1.1", Type: pkg.NpmPkg, - Licenses: []pkg.License{ - pkg.NewLicenseFromLocation("MIT", source.NewLocation("test-fixtures/pkg-json/package-repo-string.json")), - }, + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocations("MIT", source.NewLocation("test-fixtures/pkg-json/package-repo-string.json")), + ), Language: pkg.JavaScript, MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ @@ -171,9 +171,9 @@ func TestParsePackageJSON(t *testing.T) { Version: "6.14.6", PURL: "pkg:npm/npm@6.14.6", Type: pkg.NpmPkg, - Licenses: []pkg.License{ - pkg.NewLicenseFromLocation("Artistic-2.0", source.NewLocation("test-fixtures/pkg-json/package-private.json")), - }, + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocations("Artistic-2.0", source.NewLocation("test-fixtures/pkg-json/package-private.json")), + ), Language: pkg.JavaScript, MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ diff --git a/syft/pkg/cataloger/javascript/parse_package_lock_test.go b/syft/pkg/cataloger/javascript/parse_package_lock_test.go index f0e98053d86..dec36fb5f3f 100644 --- a/syft/pkg/cataloger/javascript/parse_package_lock_test.go +++ b/syft/pkg/cataloger/javascript/parse_package_lock_test.go @@ -139,9 +139,9 @@ func TestParsePackageLockV2(t *testing.T) { PURL: "pkg:npm/%40types/prop-types@15.7.5", Language: pkg.JavaScript, Type: pkg.NpmPkg, - Licenses: []pkg.License{ - pkg.NewLicenseFromLocation("MIT", source.NewLocation(fixture)), - }, + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocations("MIT", source.NewLocation(fixture)), + ), MetadataType: "NpmPackageLockJsonMetadata", Metadata: pkg.NpmPackageLockJSONMetadata{Resolved: "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", Integrity: "sha1-XxnSuFqY6VWANvajysyIGUIPBc8="}, }, @@ -151,9 +151,9 @@ func TestParsePackageLockV2(t *testing.T) { PURL: "pkg:npm/%40types/react@18.0.17", Language: pkg.JavaScript, Type: pkg.NpmPkg, - Licenses: []pkg.License{ - pkg.NewLicenseFromLocation("MIT", source.NewLocation(fixture)), - }, + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocations("MIT", source.NewLocation(fixture)), + ), MetadataType: "NpmPackageLockJsonMetadata", Metadata: pkg.NpmPackageLockJSONMetadata{Resolved: "https://registry.npmjs.org/@types/react/-/react-18.0.17.tgz", Integrity: "sha1-RYPZwyLWfv5LOak10iPtzHBQzPQ="}, }, @@ -163,9 +163,9 @@ func TestParsePackageLockV2(t *testing.T) { PURL: "pkg:npm/%40types/scheduler@0.16.2", Language: pkg.JavaScript, Type: pkg.NpmPkg, - Licenses: []pkg.License{ - pkg.NewLicenseFromLocation("MIT", source.NewLocation(fixture)), - }, + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocations("MIT", source.NewLocation(fixture)), + ), MetadataType: "NpmPackageLockJsonMetadata", Metadata: pkg.NpmPackageLockJSONMetadata{Resolved: "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", Integrity: "sha1-GmL4lSVyPd4kuhsBsJK/XfitTTk="}, }, @@ -175,9 +175,9 @@ func TestParsePackageLockV2(t *testing.T) { PURL: "pkg:npm/csstype@3.1.0", Language: pkg.JavaScript, Type: pkg.NpmPkg, - Licenses: []pkg.License{ - pkg.NewLicenseFromLocation("MIT", source.NewLocation(fixture)), - }, + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocations("MIT", source.NewLocation(fixture)), + ), MetadataType: "NpmPackageLockJsonMetadata", Metadata: pkg.NpmPackageLockJSONMetadata{Resolved: "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz", Integrity: "sha1-TdysNxjXh8+d8NG30VAzklyPKfI="}, }, @@ -286,9 +286,9 @@ func TestParsePackageLockAlias(t *testing.T) { PURL: "pkg:npm/alias-check@1.0.0", Language: pkg.JavaScript, Type: pkg.NpmPkg, - Licenses: []pkg.License{ - pkg.NewLicenseFromLocation("ISC", source.NewLocation(packageLockV2)), - }, + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocations("ISC", source.NewLocation(packageLockV2)), + ), MetadataType: "NpmPackageLockJsonMetadata", Metadata: pkg.NpmPackageLockJSONMetadata{}, } @@ -317,9 +317,9 @@ func TestParsePackageLockLicenseWithArray(t *testing.T) { Version: "1.0.0", Language: pkg.JavaScript, Type: pkg.NpmPkg, - Licenses: []pkg.License{ - pkg.NewLicenseFromLocation("ISC", source.NewLocation(fixture)), - }, + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocations("ISC", source.NewLocation(fixture)), + ), PURL: "pkg:npm/tmp@1.0.0", MetadataType: "NpmPackageLockJsonMetadata", Metadata: pkg.NpmPackageLockJSONMetadata{}, @@ -330,10 +330,10 @@ func TestParsePackageLockLicenseWithArray(t *testing.T) { Language: pkg.JavaScript, Type: pkg.NpmPkg, - Licenses: []pkg.License{ - pkg.NewLicenseFromLocation("MIT", source.NewLocation(fixture)), - pkg.NewLicenseFromLocation("Apache2", source.NewLocation(fixture)), - }, + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocations("MIT", source.NewLocation(fixture)), + pkg.NewLicenseFromLocations("Apache2", source.NewLocation(fixture)), + ), PURL: "pkg:npm/pause-stream@0.0.11", MetadataType: "NpmPackageLockJsonMetadata", Metadata: pkg.NpmPackageLockJSONMetadata{}, @@ -343,9 +343,9 @@ func TestParsePackageLockLicenseWithArray(t *testing.T) { Version: "2.3.8", Language: pkg.JavaScript, Type: pkg.NpmPkg, - Licenses: []pkg.License{ - pkg.NewLicenseFromLocation("MIT", source.NewLocation(fixture)), - }, + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocations("MIT", source.NewLocation(fixture)), + ), PURL: "pkg:npm/through@2.3.8", MetadataType: "NpmPackageLockJsonMetadata", Metadata: pkg.NpmPackageLockJSONMetadata{}, diff --git a/syft/pkg/cataloger/kernel/cataloger_test.go b/syft/pkg/cataloger/kernel/cataloger_test.go index dd2ed0a47a5..b223acf1a49 100644 --- a/syft/pkg/cataloger/kernel/cataloger_test.go +++ b/syft/pkg/cataloger/kernel/cataloger_test.go @@ -47,14 +47,14 @@ func Test_KernelCataloger(t *testing.T) { "/lib/modules/6.0.7-301.fc37.x86_64/kernel/drivers/tty/ttynull.ko", ), ), - Licenses: []pkg.License{ - pkg.NewLicenseFromLocation("GPL v2", + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocations("GPL v2", source.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", ), ), - }, + ), Type: pkg.LinuxKernelModulePkg, PURL: "pkg:generic/ttynull", MetadataType: pkg.LinuxKernelModuleMetadataType, diff --git a/syft/pkg/cataloger/kernel/package.go b/syft/pkg/cataloger/kernel/package.go index 679f8d04696..3ea60668827 100644 --- a/syft/pkg/cataloger/kernel/package.go +++ b/syft/pkg/cataloger/kernel/package.go @@ -31,7 +31,7 @@ func newLinuxKernelModulePackage(metadata pkg.LinuxKernelModuleMetadata, kmLocat Name: metadata.Name, Version: metadata.Version, Locations: source.NewLocationSet(kmLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)), - Licenses: pkg.NewLicensesFromLocation(kmLocation, metadata.License), + Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(kmLocation, metadata.License)...), PURL: packageURL(metadata.Name, metadata.Version), Type: pkg.LinuxKernelModulePkg, MetadataType: pkg.LinuxKernelModuleMetadataType, diff --git a/syft/pkg/cataloger/php/package.go b/syft/pkg/cataloger/php/package.go index de1327c33f9..507fd26dab2 100644 --- a/syft/pkg/cataloger/php/package.go +++ b/syft/pkg/cataloger/php/package.go @@ -13,7 +13,7 @@ func newComposerLockPackage(m parsedData, indexLocation source.Location) pkg.Pac Name: m.Name, Version: m.Version, Locations: source.NewLocationSet(indexLocation), - Licenses: pkg.NewLicensesFromLocation(indexLocation, m.License...), + Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(indexLocation, m.License...)...), PURL: packageURL(m), Language: pkg.PHP, Type: pkg.PhpComposerPkg, diff --git a/syft/pkg/cataloger/php/parse_composer_lock_test.go b/syft/pkg/cataloger/php/parse_composer_lock_test.go index 0debbd16194..ad7814a97d5 100644 --- a/syft/pkg/cataloger/php/parse_composer_lock_test.go +++ b/syft/pkg/cataloger/php/parse_composer_lock_test.go @@ -19,9 +19,9 @@ func TestParseComposerFileLock(t *testing.T) { Version: "1.0.2", PURL: "pkg:composer/adoy/fastcgi-client@1.0.2", Locations: locations, - Licenses: []pkg.License{ - pkg.NewLicenseFromLocation("MIT", source.NewLocation(fixture)), - }, + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocations("MIT", source.NewLocation(fixture)), + ), Language: pkg.PHP, Type: pkg.PhpComposerPkg, MetadataType: pkg.PhpComposerJSONMetadataType, @@ -60,9 +60,9 @@ func TestParseComposerFileLock(t *testing.T) { Locations: locations, PURL: "pkg:composer/alcaeus/mongo-php-adapter@1.1.11", Language: pkg.PHP, - Licenses: []pkg.License{ - pkg.NewLicenseFromLocation("MIT", source.NewLocation(fixture)), - }, + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocations("MIT", source.NewLocation(fixture)), + ), Type: pkg.PhpComposerPkg, MetadataType: pkg.PhpComposerJSONMetadataType, Metadata: pkg.PhpComposerJSONMetadata{ diff --git a/syft/pkg/cataloger/php/parse_installed_json_test.go b/syft/pkg/cataloger/php/parse_installed_json_test.go index df5ba82f06d..4b914640aaa 100644 --- a/syft/pkg/cataloger/php/parse_installed_json_test.go +++ b/syft/pkg/cataloger/php/parse_installed_json_test.go @@ -24,9 +24,9 @@ func TestParseInstalledJsonComposerV1(t *testing.T) { Language: pkg.PHP, Type: pkg.PhpComposerPkg, MetadataType: pkg.PhpComposerJSONMetadataType, - Licenses: []pkg.License{ + Licenses: pkg.NewLicenseSet( pkg.NewLicense("MIT"), - }, + ), Metadata: pkg.PhpComposerJSONMetadata{ Name: "asm89/stack-cors", Version: "1.3.0", @@ -73,9 +73,9 @@ func TestParseInstalledJsonComposerV1(t *testing.T) { PURL: "pkg:composer/behat/mink@v1.8.1", Language: pkg.PHP, Type: pkg.PhpComposerPkg, - Licenses: []pkg.License{ + Licenses: pkg.NewLicenseSet( pkg.NewLicense("MIT"), - }, + ), MetadataType: pkg.PhpComposerJSONMetadataType, Metadata: pkg.PhpComposerJSONMetadata{ Name: "behat/mink", @@ -133,10 +133,12 @@ func TestParseInstalledJsonComposerV1(t *testing.T) { locations := source.NewLocationSet(source.NewLocation(fixture)) for i := range expectedPkgs { expectedPkgs[i].Locations = locations - for k := range expectedPkgs[i].Licenses { - loc := source.NewLocation(fixture) - expectedPkgs[i].Licenses[k].Location = &loc + locationLicenses := pkg.NewLicenseSet() + for _, license := range expectedPkgs[i].Licenses.ToSlice() { + license.Location = locations + locationLicenses.Add(license) } + expectedPkgs[i].Licenses = locationLicenses } pkgtest.TestFileParser(t, fixture, parseInstalledJSON, expectedPkgs, expectedRelationships) }) diff --git a/syft/pkg/cataloger/portage/cataloger_test.go b/syft/pkg/cataloger/portage/cataloger_test.go index a9ddae7b66f..b2ff5f26d17 100644 --- a/syft/pkg/cataloger/portage/cataloger_test.go +++ b/syft/pkg/cataloger/portage/cataloger_test.go @@ -23,7 +23,7 @@ func TestPortageCataloger(t *testing.T) { source.NewLocation("var/db/pkg/app-containers/skopeo-1.5.1/SIZE"), expectedLicenseLocation, ), - Licenses: pkg.NewLicensesFromLocation(expectedLicenseLocation, "Apache-2.0", "BSD", "BSD-2", "CC-BY-SA-4.0", "ISC", "MIT"), + Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(expectedLicenseLocation, "Apache-2.0", "BSD", "BSD-2", "CC-BY-SA-4.0", "ISC", "MIT")...), Type: pkg.PortagePkg, MetadataType: pkg.PortageMetadataType, Metadata: pkg.PortageMetadata{ diff --git a/syft/pkg/cataloger/portage/parse_portage_contents.go b/syft/pkg/cataloger/portage/parse_portage_contents.go index 379c7f9fab1..ac93c6ea05e 100644 --- a/syft/pkg/cataloger/portage/parse_portage_contents.go +++ b/syft/pkg/cataloger/portage/parse_portage_contents.go @@ -117,7 +117,7 @@ func addLicenses(resolver source.FileResolver, dbLocation source.Location, p *pk } licenseCandidates := findings.ToSlice() - p.Licenses = pkg.NewLicensesFromLocation(*location, licenseCandidates...) + p.Licenses = pkg.NewLicenseSet(pkg.NewLicensesFromLocation(*location, licenseCandidates...)...) p.Locations.Add(location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.SupportingEvidenceAnnotation)) } diff --git a/syft/pkg/cataloger/python/cataloger_test.go b/syft/pkg/cataloger/python/cataloger_test.go index 56bbe690a27..10522f21524 100644 --- a/syft/pkg/cataloger/python/cataloger_test.go +++ b/syft/pkg/cataloger/python/cataloger_test.go @@ -45,9 +45,9 @@ func Test_PackageCataloger(t *testing.T) { PURL: "pkg:pypi/requests@2.22.0", Type: pkg.PythonPkg, Language: pkg.Python, - Licenses: []pkg.License{ - pkg.NewLicenseFromLocation("Apache 2.0", source.NewLocation("test-fixtures/egg-info/PKG-INFO")), - }, + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocations("Apache 2.0", source.NewLocation("test-fixtures/egg-info/PKG-INFO")), + ), FoundBy: "python-package-cataloger", MetadataType: pkg.PythonPackageMetadataType, Metadata: pkg.PythonPackageMetadata{ @@ -83,9 +83,9 @@ func Test_PackageCataloger(t *testing.T) { PURL: "pkg:pypi/Pygments@2.6.1?vcs_url=git+https://github.com/python-test/test.git%40aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", Type: pkg.PythonPkg, Language: pkg.Python, - Licenses: []pkg.License{ - pkg.NewLicenseFromLocation("BSD License", source.NewLocation("test-fixtures/dist-info/METADATA")), - }, + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocations("BSD License", source.NewLocation("test-fixtures/dist-info/METADATA")), + ), FoundBy: "python-package-cataloger", MetadataType: pkg.PythonPackageMetadataType, Metadata: pkg.PythonPackageMetadata{ @@ -121,9 +121,9 @@ func Test_PackageCataloger(t *testing.T) { PURL: "pkg:pypi/Pygments@2.6.1", Type: pkg.PythonPkg, Language: pkg.Python, - Licenses: []pkg.License{ - pkg.NewLicenseFromLocation("BSD License", source.NewLocation("test-fixtures/malformed-record/dist-info/METADATA")), - }, + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocations("BSD License", source.NewLocation("test-fixtures/malformed-record/dist-info/METADATA")), + ), FoundBy: "python-package-cataloger", MetadataType: pkg.PythonPackageMetadataType, Metadata: pkg.PythonPackageMetadata{ @@ -153,9 +153,9 @@ func Test_PackageCataloger(t *testing.T) { PURL: "pkg:pypi/Pygments@2.6.1", Type: pkg.PythonPkg, Language: pkg.Python, - Licenses: []pkg.License{ - pkg.NewLicenseFromLocation("BSD License", source.NewLocation("test-fixtures/partial.dist-info/METADATA")), - }, + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocations("BSD License", source.NewLocation("test-fixtures/partial.dist-info/METADATA")), + ), FoundBy: "python-package-cataloger", MetadataType: pkg.PythonPackageMetadataType, Metadata: pkg.PythonPackageMetadata{ @@ -177,9 +177,9 @@ func Test_PackageCataloger(t *testing.T) { PURL: "pkg:pypi/requests@2.22.0", Type: pkg.PythonPkg, Language: pkg.Python, - Licenses: []pkg.License{ - pkg.NewLicenseFromLocation("Apache 2.0", source.NewLocation("test-fixtures/test.egg-info")), - }, + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocations("Apache 2.0", source.NewLocation("test-fixtures/test.egg-info")), + ), FoundBy: "python-package-cataloger", MetadataType: pkg.PythonPackageMetadataType, Metadata: pkg.PythonPackageMetadata{ diff --git a/syft/pkg/cataloger/python/package.go b/syft/pkg/cataloger/python/package.go index 6c6d73d1268..68f7f1dccfe 100644 --- a/syft/pkg/cataloger/python/package.go +++ b/syft/pkg/cataloger/python/package.go @@ -63,7 +63,7 @@ func newPackageForPackage(m parsedData, sources ...source.Location) pkg.Package Version: m.Version, PURL: packageURL(m.Name, m.Version, &m.PythonPackageMetadata), Locations: source.NewLocationSet(sources...), - Licenses: pkg.NewLicensesFromLocation(m.LicenseLocation, m.Licenses), + Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(m.LicenseLocation, m.Licenses)...), Language: pkg.Python, Type: pkg.PythonPkg, MetadataType: pkg.PythonPackageMetadataType, diff --git a/syft/pkg/cataloger/rpm/package.go b/syft/pkg/cataloger/rpm/package.go index e5974922875..53c0925b906 100644 --- a/syft/pkg/cataloger/rpm/package.go +++ b/syft/pkg/cataloger/rpm/package.go @@ -17,7 +17,7 @@ func newPackage(dbOrRpmLocation source.Location, pd parsedData, distro *linux.Re p := pkg.Package{ Name: pd.Name, Version: toELVersion(pd.RpmMetadata), - Licenses: pd.Licenses, + Licenses: pkg.NewLicenseSet(pd.Licenses...), PURL: packageURL(pd.RpmMetadata, distro), Locations: source.NewLocationSet(dbOrRpmLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)), Type: pkg.RpmPkg, diff --git a/syft/pkg/cataloger/rpm/parse_rpm_db_test.go b/syft/pkg/cataloger/rpm/parse_rpm_db_test.go index 7e114b9b729..b58a01744a5 100644 --- a/syft/pkg/cataloger/rpm/parse_rpm_db_test.go +++ b/syft/pkg/cataloger/rpm/parse_rpm_db_test.go @@ -98,9 +98,9 @@ func TestParseRpmDB(t *testing.T) { Locations: source.NewLocationSet(packagesLocation), Type: pkg.RpmPkg, MetadataType: pkg.RpmMetadataType, - Licenses: []pkg.License{ - pkg.NewLicenseFromLocation("MIT", packagesLocation), - }, + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocations("MIT", packagesLocation), + ), Metadata: pkg.RpmMetadata{ Name: "dive", Epoch: nil, @@ -127,9 +127,9 @@ func TestParseRpmDB(t *testing.T) { Locations: source.NewLocationSet(packagesLocation), Type: pkg.RpmPkg, MetadataType: pkg.RpmMetadataType, - Licenses: []pkg.License{ - pkg.NewLicenseFromLocation("MIT", packagesLocation), - }, + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocations("MIT", packagesLocation), + ), Metadata: pkg.RpmMetadata{ Name: "dive", Epoch: nil, diff --git a/syft/pkg/cataloger/rpm/parse_rpm_test.go b/syft/pkg/cataloger/rpm/parse_rpm_test.go index f0c35c92cae..253d99f59dd 100644 --- a/syft/pkg/cataloger/rpm/parse_rpm_test.go +++ b/syft/pkg/cataloger/rpm/parse_rpm_test.go @@ -27,9 +27,9 @@ func TestParseRpmFiles(t *testing.T) { FoundBy: "rpm-file-cataloger", Type: pkg.RpmPkg, MetadataType: pkg.RpmMetadataType, - Licenses: []pkg.License{ - pkg.NewLicenseFromLocation("MIT", abcRpmLocation), - }, + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocations("MIT", abcRpmLocation), + ), Metadata: pkg.RpmMetadata{ Name: "abc", Epoch: intRef(0), @@ -56,9 +56,9 @@ func TestParseRpmFiles(t *testing.T) { FoundBy: "rpm-file-cataloger", Type: pkg.RpmPkg, MetadataType: pkg.RpmMetadataType, - Licenses: []pkg.License{ - pkg.NewLicenseFromLocation("Public Domain", zorkRpmLocation), - }, + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocations("Public Domain", zorkRpmLocation), + ), Metadata: pkg.RpmMetadata{ Name: "zork", Epoch: intRef(0), diff --git a/syft/pkg/cataloger/ruby/package.go b/syft/pkg/cataloger/ruby/package.go index 98b5b231ef6..973d67350fb 100644 --- a/syft/pkg/cataloger/ruby/package.go +++ b/syft/pkg/cataloger/ruby/package.go @@ -26,7 +26,7 @@ func newGemspecPackage(m gemData, gemSpecLocation source.Location) pkg.Package { Name: m.Name, Version: m.Version, Locations: source.NewLocationSet(gemSpecLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)), - Licenses: pkg.NewLicensesFromLocation(gemSpecLocation, m.Licenses...), + Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(gemSpecLocation, m.Licenses...)...), PURL: packageURL(m.Name, m.Version), Language: pkg.Ruby, Type: pkg.GemPkg, diff --git a/syft/pkg/cataloger/ruby/parse_gemspec_test.go b/syft/pkg/cataloger/ruby/parse_gemspec_test.go index d18277c6ad4..53cb59ecfa1 100644 --- a/syft/pkg/cataloger/ruby/parse_gemspec_test.go +++ b/syft/pkg/cataloger/ruby/parse_gemspec_test.go @@ -19,9 +19,9 @@ func TestParseGemspec(t *testing.T) { PURL: "pkg:gem/bundler@2.1.4", Locations: locations, Type: pkg.GemPkg, - Licenses: []pkg.License{ - pkg.NewLicenseFromLocation("MIT", source.NewLocation(fixture)), - }, + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocations("MIT", source.NewLocation(fixture)), + ), Language: pkg.Ruby, MetadataType: pkg.GemMetadataType, Metadata: pkg.GemMetadata{ diff --git a/syft/pkg/cataloger/rust/parse_cargo_lock_test.go b/syft/pkg/cataloger/rust/parse_cargo_lock_test.go index 25fba560efd..d05f62d6245 100644 --- a/syft/pkg/cataloger/rust/parse_cargo_lock_test.go +++ b/syft/pkg/cataloger/rust/parse_cargo_lock_test.go @@ -21,7 +21,7 @@ func TestParseCargoLock(t *testing.T) { Language: pkg.Rust, Type: pkg.RustPkg, MetadataType: pkg.RustCargoPackageMetadataType, - Licenses: nil, + Licenses: pkg.NewLicenseSet(), Metadata: pkg.CargoPackageMetadata{ Name: "ansi_term", Version: "0.12.1", @@ -40,7 +40,7 @@ func TestParseCargoLock(t *testing.T) { Language: pkg.Rust, Type: pkg.RustPkg, MetadataType: pkg.RustCargoPackageMetadataType, - Licenses: nil, + Licenses: pkg.NewLicenseSet(), Metadata: pkg.CargoPackageMetadata{ Name: "matches", Version: "0.1.8", @@ -57,7 +57,7 @@ func TestParseCargoLock(t *testing.T) { Language: pkg.Rust, Type: pkg.RustPkg, MetadataType: pkg.RustCargoPackageMetadataType, - Licenses: nil, + Licenses: pkg.NewLicenseSet(), Metadata: pkg.CargoPackageMetadata{ Name: "memchr", Version: "2.3.3", @@ -74,7 +74,7 @@ func TestParseCargoLock(t *testing.T) { Language: pkg.Rust, Type: pkg.RustPkg, MetadataType: pkg.RustCargoPackageMetadataType, - Licenses: nil, + Licenses: pkg.NewLicenseSet(), Metadata: pkg.CargoPackageMetadata{ Name: "natord", Version: "1.0.9", @@ -91,7 +91,7 @@ func TestParseCargoLock(t *testing.T) { Language: pkg.Rust, Type: pkg.RustPkg, MetadataType: pkg.RustCargoPackageMetadataType, - Licenses: nil, + Licenses: pkg.NewLicenseSet(), Metadata: pkg.CargoPackageMetadata{ Name: "nom", Version: "4.2.3", @@ -111,7 +111,7 @@ func TestParseCargoLock(t *testing.T) { Language: pkg.Rust, Type: pkg.RustPkg, MetadataType: pkg.RustCargoPackageMetadataType, - Licenses: nil, + Licenses: pkg.NewLicenseSet(), Metadata: pkg.CargoPackageMetadata{ Name: "unicode-bidi", Version: "0.3.4", @@ -130,7 +130,7 @@ func TestParseCargoLock(t *testing.T) { Language: pkg.Rust, Type: pkg.RustPkg, MetadataType: pkg.RustCargoPackageMetadataType, - Licenses: nil, + Licenses: pkg.NewLicenseSet(), Metadata: pkg.CargoPackageMetadata{ Name: "version_check", Version: "0.1.5", @@ -147,7 +147,7 @@ func TestParseCargoLock(t *testing.T) { Language: pkg.Rust, Type: pkg.RustPkg, MetadataType: pkg.RustCargoPackageMetadataType, - Licenses: nil, + Licenses: pkg.NewLicenseSet(), Metadata: pkg.CargoPackageMetadata{ Name: "winapi", Version: "0.3.9", @@ -167,7 +167,7 @@ func TestParseCargoLock(t *testing.T) { Language: pkg.Rust, Type: pkg.RustPkg, MetadataType: pkg.RustCargoPackageMetadataType, - Licenses: nil, + Licenses: pkg.NewLicenseSet(), Metadata: pkg.CargoPackageMetadata{ Name: "winapi-i686-pc-windows-gnu", Version: "0.4.0", @@ -184,7 +184,7 @@ func TestParseCargoLock(t *testing.T) { Language: pkg.Rust, Type: pkg.RustPkg, MetadataType: pkg.RustCargoPackageMetadataType, - Licenses: nil, + Licenses: pkg.NewLicenseSet(), Metadata: pkg.CargoPackageMetadata{ Name: "winapi-x86_64-pc-windows-gnu", Version: "0.4.0", diff --git a/syft/pkg/cataloger/sbom/cataloger_test.go b/syft/pkg/cataloger/sbom/cataloger_test.go index 3d29bfaa3f7..7d2e4e65e24 100644 --- a/syft/pkg/cataloger/sbom/cataloger_test.go +++ b/syft/pkg/cataloger/sbom/cataloger_test.go @@ -38,7 +38,7 @@ func Test_parseSBOM(t *testing.T) { Version: "3.2.0-r23", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []pkg.License{pkg.NewLicense("GPL-2.0-only")}, + Licenses: pkg.NewLicenseSet(pkg.NewLicense("GPL-2.0-only")), FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/alpine-baselayout@3.2.0-r23?arch=x86_64&upstream=alpine-baselayout&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -55,7 +55,7 @@ func Test_parseSBOM(t *testing.T) { Version: "3.2.0-r23", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []pkg.License{pkg.NewLicense("GPL-2.0-only")}, + Licenses: pkg.NewLicenseSet(pkg.NewLicense("GPL-2.0-only")), FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/alpine-baselayout-data@3.2.0-r23?arch=x86_64&upstream=alpine-baselayout&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -76,7 +76,7 @@ func Test_parseSBOM(t *testing.T) { Version: "2.4-r1", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []pkg.License{pkg.NewLicense("MIT")}, + Licenses: pkg.NewLicenseSet(pkg.NewLicense("MIT")), FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/alpine-keys@2.4-r1?arch=x86_64&upstream=alpine-keys&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -93,7 +93,7 @@ func Test_parseSBOM(t *testing.T) { Version: "2.12.9-r3", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []pkg.License{pkg.NewLicense("GPL-2.0-only")}, + Licenses: pkg.NewLicenseSet(pkg.NewLicense("GPL-2.0-only")), FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/apk-tools@2.12.9-r3?arch=x86_64&upstream=apk-tools&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -110,7 +110,7 @@ func Test_parseSBOM(t *testing.T) { Version: "1.35.0-r17", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []pkg.License{pkg.NewLicense("GPL-2.0-only")}, + Licenses: pkg.NewLicenseSet(pkg.NewLicense("GPL-2.0-only")), FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/busybox@1.35.0-r17?arch=x86_64&upstream=busybox&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -122,10 +122,10 @@ func Test_parseSBOM(t *testing.T) { Version: "20220614-r0", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []pkg.License{ + Licenses: pkg.NewLicenseSet( pkg.NewLicense("MPL-2.0"), pkg.NewLicense("MIT"), - }, + ), FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/ca-certificates-bundle@20220614-r0?arch=x86_64&upstream=ca-certificates&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -146,10 +146,10 @@ func Test_parseSBOM(t *testing.T) { Version: "0.7.2-r3", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []pkg.License{ + Licenses: pkg.NewLicenseSet( pkg.NewLicense("BSD-2-Clause"), pkg.NewLicense("BSD-3-Clause"), - }, + ), FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/libc-utils@0.7.2-r3?arch=x86_64&upstream=libc-dev&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -166,7 +166,7 @@ func Test_parseSBOM(t *testing.T) { Version: "1.1.1s-r0", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []pkg.License{pkg.NewLicense("OpenSSL")}, // SPDX expression is not set + Licenses: pkg.NewLicenseSet(pkg.NewLicense("OpenSSL")), // SPDX expression is not set FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/libcrypto1.1@1.1.1s-r0?arch=x86_64&upstream=openssl&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -178,7 +178,7 @@ func Test_parseSBOM(t *testing.T) { Version: "1.1.1s-r0", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []pkg.License{pkg.NewLicense("OpenSSL")}, // SPDX expression is not set + Licenses: pkg.NewLicenseSet(pkg.NewLicense("OpenSSL")), // SPDX expression is not set FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/libssl1.1@1.1.1s-r0?arch=x86_64&upstream=openssl&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -190,7 +190,7 @@ func Test_parseSBOM(t *testing.T) { Version: "1.2.3-r1", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []pkg.License{pkg.NewLicense("MIT")}, // SPDX expression is not set + Licenses: pkg.NewLicenseSet(pkg.NewLicense("MIT")), // SPDX expression is not set FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/musl@1.2.3-r1?arch=x86_64&upstream=musl&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -202,11 +202,11 @@ func Test_parseSBOM(t *testing.T) { Version: "1.2.3-r1", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []pkg.License{ + Licenses: pkg.NewLicenseSet( pkg.NewLicense("MIT"), pkg.NewLicense("BSD"), pkg.NewLicense("GPL2+"), // SPDX expression is not set - }, + ), FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/musl-utils@1.2.3-r1?arch=x86_64&upstream=musl&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -223,9 +223,9 @@ func Test_parseSBOM(t *testing.T) { Version: "1.3.4-r0", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []pkg.License{ + Licenses: pkg.NewLicenseSet( pkg.NewLicense("GPL-2.0-only"), - }, + ), FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/scanelf@1.3.4-r0?arch=x86_64&upstream=pax-utils&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -237,9 +237,9 @@ func Test_parseSBOM(t *testing.T) { Version: "1.35.0-r17", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []pkg.License{ + Licenses: pkg.NewLicenseSet( pkg.NewLicense("GPL-2.0-only"), - }, + ), FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/ssl_client@1.35.0-r17?arch=x86_64&upstream=busybox&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -256,9 +256,9 @@ func Test_parseSBOM(t *testing.T) { Version: "1.2.12-r3", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []pkg.License{ + Licenses: pkg.NewLicenseSet( pkg.NewLicense("Zlib"), - }, + ), FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/zlib@1.2.12-r3?arch=x86_64&upstream=zlib&distro=alpine-3.16.3", CPEs: mustCPEs( diff --git a/syft/pkg/license.go b/syft/pkg/license.go index bfa75bc64fc..ef05d53db57 100644 --- a/syft/pkg/license.go +++ b/syft/pkg/license.go @@ -1,23 +1,32 @@ package pkg import ( + "fmt" "sort" - "github.com/mitchellh/hashstructure/v2" - + "github.com/anchore/syft/internal" "github.com/anchore/syft/internal/log" + "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/source" ) var _ sort.Interface = (*Licenses)(nil) +// License represents an SPDX Expression or license value extracted from a packages metadata +// We want to ignore URL and Location since we will always merge these fields +// a License is a unique combination of value, expression and type +// its sources are always considered merge and additions to the evidence +// of where it was found and how it was sourced. +// This is different from how we treat a package since we consider package paths to distinguish +// if packages should be kept separate, this is different for licenses since we're only looking for evidence +// of where a license was declared/concluded for a package type License struct { - Value string `json:"value"` - SPDXExpression string `json:"spdxExpression"` - Type license.Type `json:"type"` - URL string `json:"url"` // external sources - Location *source.Location `json:"location,omitempty"` // on disk declaration + Value string `json:"value"` + SPDXExpression string `json:"spdxExpression"` + Type license.Type `json:"type"` + URL internal.StringSet `hash:"ignore"` + Location source.LocationSet `hash:"ignore"` } type Licenses []License @@ -30,20 +39,13 @@ func (l Licenses) Less(i, j int) bool { if l[i].Value == l[j].Value { if l[i].SPDXExpression == l[j].SPDXExpression { if l[i].Type == l[j].Type { - if l[i].URL == l[j].URL { - if l[i].Location == nil && l[j].Location == nil { - return false - } - if l[i].Location == nil { - return true - } - if l[j].Location == nil { - return false - } - sl := source.Locations{*l[i].Location, *l[j].Location} - return sl.Less(0, 1) - } - return l[i].URL < l[j].URL + // While URL and location are not exclusive fields + // returning true here reduces the number of swaps + // while keeping a consistent sort order of + // the order that they appear in the list initially + // If users in the future have preference to sorting based + // on the slice representation of either field we can update this code + return true } return l[i].Type < l[j].Type } @@ -68,11 +70,30 @@ func NewLicense(value string) License { Value: value, SPDXExpression: spdxExpression, Type: license.Declared, + URL: internal.NewStringSet(), + Location: source.NewLocationSet(), + } +} + +func NewLicenseFromType(value string, t license.Type) License { + spdxExpression, err := license.ParseExpression(value) + if err != nil { + log.Trace("unable to parse license expression: %w", err) + } + + return License{ + Value: value, + SPDXExpression: spdxExpression, + Type: t, + URL: internal.NewStringSet(), + Location: source.NewLocationSet(), } } func NewLicensesFromValues(values ...string) (licenses []License) { for _, v := range values { + // ignore common SPDX license expression connectors + // that could be included in input if v == "" || v == "AND" { continue } @@ -86,49 +107,48 @@ func NewLicensesFromLocation(location source.Location, values ...string) (licens if v == "" { continue } - licenses = append(licenses, NewLicenseFromLocation(v, location)) + licenses = append(licenses, NewLicenseFromLocations(v, location)) } return } -func NewLicenseFromLocation(value string, location source.Location) License { +func NewLicenseFromLocations(value string, locations ...source.Location) License { l := NewLicense(value) - l.Location = &location + for _, loc := range locations { + l.Location.Add(loc) + } return l } -func NewLicensesFromURL(url string, values ...string) (licenses []License) { - for _, v := range values { - if v == "" { - continue +func LicenseFromURLs(value string, urls ...string) License { + l := NewLicense(value) + for _, u := range urls { + if u != "" { + l.URL.Add(u) } - licenses = append(licenses, NewLicenseFromURL(v, url)) } - return -} - -func NewLicenseFromURL(value string, url string) License { - l := NewLicense(value) - l.URL = url return l } // this is a bit of a hack to not infinitely recurse when hashing a license -type noLayerLicense License - -func (s License) Hash() (uint64, error) { - if s.Location != nil { - l := *s.Location - // much like the location set hash function, we should not consider the file system ID when hashing - // so that licenses found in different layers (at the same path) are not considered different. - l.FileSystemID = "" - s.Location = &l +func (s License) Merge(l License) (*License, error) { + sHash, err := artifact.IDByHash(s) + if err != nil { + return nil, err + } + lHash, err := artifact.IDByHash(l) + if err != nil { + return nil, err } + if err != nil { + return nil, err + } + if sHash != lHash { + return nil, fmt.Errorf("cannot merge licenses with different hash") + } + + s.URL.Add(l.URL.ToSlice()...) + s.Location.Add(l.Location.ToSlice()...) - return hashstructure.Hash(noLayerLicense(s), hashstructure.FormatV2, - &hashstructure.HashOptions{ - ZeroNil: true, - SlicesAsSets: true, - }, - ) + return &s, nil } diff --git a/syft/pkg/license_set.go b/syft/pkg/license_set.go index 519605dfc47..b0a701e21af 100644 --- a/syft/pkg/license_set.go +++ b/syft/pkg/license_set.go @@ -7,10 +7,11 @@ import ( "github.com/mitchellh/hashstructure/v2" "github.com/anchore/syft/internal/log" + "github.com/anchore/syft/syft/artifact" ) type LicenseSet struct { - set map[uint64]Licenses + set map[artifact.ID]License } func NewLicenseSet(licenses ...License) (s LicenseSet) { @@ -21,77 +22,41 @@ func NewLicenseSet(licenses ...License) (s LicenseSet) { return s } -func (s *LicenseSet) get(license License) (uint64, int, *License, error) { - id, err := license.Hash() +func (s *LicenseSet) addToExisting(license License) (id artifact.ID, merged bool, err error) { + id, err = artifact.IDByHash(license) if err != nil { - return 0, 0, nil, fmt.Errorf("could not get the hash for a license: %w", err) - } - licenses, ok := s.set[id] - if !ok { - return id, 0, nil, nil + return id, false, fmt.Errorf("could not get the hash for a license: %w", err) } - if license.Location == nil { - switch len(licenses) { - case 0: - return id, 0, nil, nil - case 1: - return id, 0, &licenses[0], nil - default: - log.Debugf("license set contains multiple licenses with the same hash (when there is no location): %#v returns %#v", license, licenses) - // we don't know what the right answer is - return id, 0, nil, nil - } + v, ok := s.set[id] + if !ok { + // doesn't exist safe to add + return id, false, nil } - // I'm only hitting this if the FS id is different, since that's the only reason today that you can have - // the same hash ID but different information on the license - for idx, l := range licenses { - if l.Location.FileSystemID == license.Location.FileSystemID { - return id, idx, &licenses[idx], nil - } + // we got the same id so we want to merge the URL OR Location data + // URL/Location are not considered when taking the Hash + m, err := v.Merge(license) + if err != nil { + return id, false, fmt.Errorf("could not merge license into map: %w", err) } + s.set[id] = *m - return id, 0, nil, nil + return id, true, nil } func (s *LicenseSet) Add(licenses ...License) { if s.set == nil { - s.set = make(map[uint64]Licenses) + s.set = make(map[artifact.ID]License) } for _, l := range licenses { - if id, _, v, err := s.get(l); v == nil && err == nil { + if id, merged, err := s.addToExisting(l); err == nil && !merged { // doesn't exist, add it - s.set[id] = append(s.set[id], l) + s.set[id] = l } else if err != nil { - log.Debugf("license set failed to add license %#v: %+v", l, err) - } - } -} - -func (s LicenseSet) Remove(licenses ...License) { - if s.set == nil { - return - } - for _, l := range licenses { - id, idx, v, err := s.get(l) - if err != nil { - log.Debugf("license set failed to remove license %#v: %+v", l, err) + log.Trace("license set failed to add license %#v: %+v", l, err) } - if v == nil { - continue - } - // remove the license from the specific index already found - s.set[id] = append(s.set[id][:idx], s.set[id][idx+1:]...) - } -} - -func (s LicenseSet) Contains(l License) bool { - if s.set == nil { - return false } - _, _, v, err := s.get(l) - return v != nil && err == nil } func (s LicenseSet) ToSlice() []License { @@ -100,7 +65,7 @@ func (s LicenseSet) ToSlice() []License { } var licenses []License for _, v := range s.set { - licenses = append(licenses, v...) + licenses = append(licenses, v) } sort.Sort(Licenses(licenses)) return licenses @@ -113,3 +78,7 @@ func (s LicenseSet) Hash() (uint64, error) { SlicesAsSets: true, }) } + +func (s LicenseSet) Empty() bool { + return len(s.set) < 1 +} diff --git a/syft/pkg/license_set_test.go b/syft/pkg/license_set_test.go index 347c8bfa650..a85104a8f57 100644 --- a/syft/pkg/license_set_test.go +++ b/syft/pkg/license_set_test.go @@ -5,6 +5,8 @@ import ( "github.com/stretchr/testify/assert" + "github.com/anchore/syft/internal" + "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/source" ) @@ -35,39 +37,75 @@ func TestLicenseSet_Add(t *testing.T) { }, }, { - name: "duplicate licenses with locations", + name: "deduplicate licenses with locations", licenses: []License{ - NewLicenseFromLocation("MIT", source.NewLocationFromCoordinates(source.Coordinates{RealPath: "/place", FileSystemID: "1"})), - NewLicenseFromLocation("MIT", source.NewLocationFromCoordinates(source.Coordinates{RealPath: "/place", FileSystemID: "1"})), + NewLicenseFromLocations("MIT", source.NewLocationFromCoordinates(source.Coordinates{RealPath: "/place", FileSystemID: "1"})), + NewLicenseFromLocations("MIT", source.NewLocationFromCoordinates(source.Coordinates{RealPath: "/place", FileSystemID: "1"})), + NewLicenseFromLocations("MIT", source.NewLocationFromCoordinates(source.Coordinates{RealPath: "/place", FileSystemID: "2"})), }, want: []License{ - NewLicenseFromLocation("MIT", source.NewLocationFromCoordinates(source.Coordinates{RealPath: "/place", FileSystemID: "1"})), + NewLicenseFromLocations( + "MIT", + source.NewLocationFromCoordinates(source.Coordinates{RealPath: "/place", FileSystemID: "1"}), + source.NewLocationFromCoordinates(source.Coordinates{RealPath: "/place", FileSystemID: "2"}), + ), }, }, { name: "same licenses with different locations", licenses: []License{ NewLicense("MIT"), - NewLicenseFromLocation("MIT", source.NewLocationFromCoordinates(source.Coordinates{RealPath: "/place", FileSystemID: "2"})), - NewLicenseFromLocation("MIT", source.NewLocationFromCoordinates(source.Coordinates{RealPath: "/place", FileSystemID: "1"})), + NewLicenseFromLocations("MIT", source.NewLocationFromCoordinates(source.Coordinates{RealPath: "/place", FileSystemID: "2"})), + NewLicenseFromLocations("MIT", source.NewLocationFromCoordinates(source.Coordinates{RealPath: "/place", FileSystemID: "1"})), }, want: []License{ - NewLicense("MIT"), - NewLicenseFromLocation("MIT", source.NewLocationFromCoordinates(source.Coordinates{RealPath: "/place", FileSystemID: "1"})), - NewLicenseFromLocation("MIT", source.NewLocationFromCoordinates(source.Coordinates{RealPath: "/place", FileSystemID: "2"})), + NewLicenseFromLocations( + "MIT", + source.NewLocationFromCoordinates(source.Coordinates{RealPath: "/place", FileSystemID: "1"}), + source.NewLocationFromCoordinates(source.Coordinates{RealPath: "/place", FileSystemID: "2"}), + ), }, }, { name: "same license from different sources", licenses: []License{ NewLicense("MIT"), - NewLicenseFromLocation("MIT", source.NewLocation("/place")), - NewLicenseFromURL("MIT", "https://example.com"), + NewLicenseFromLocations("MIT", source.NewLocation("/place")), + LicenseFromURLs("MIT", "https://example.com"), }, want: []License{ - NewLicense("MIT"), - NewLicenseFromLocation("MIT", source.NewLocation("/place")), - NewLicenseFromURL("MIT", "https://example.com"), + { + Value: "MIT", + SPDXExpression: "MIT", + Type: license.Declared, + URL: internal.NewStringSet("https://example.com"), + Location: source.NewLocationSet(source.NewLocation("/place")), + }, + }, + }, + { + name: "different licenses from different sources with different types constitute two licenses", + licenses: []License{ + NewLicenseFromType("MIT", license.Concluded), + NewLicenseFromType("MIT", license.Declared), + NewLicenseFromLocations("MIT", source.NewLocation("/place")), + LicenseFromURLs("MIT", "https://example.com"), + }, + want: []License{ + { + Value: "MIT", + SPDXExpression: "MIT", + Type: license.Concluded, + URL: internal.NewStringSet(), + Location: source.NewLocationSet(), + }, + { + Value: "MIT", + SPDXExpression: "MIT", + Type: license.Declared, + URL: internal.NewStringSet("https://example.com"), + Location: source.NewLocationSet(source.NewLocation("/place")), + }, }, }, } @@ -75,7 +113,8 @@ func TestLicenseSet_Add(t *testing.T) { t.Run(tt.name, func(t *testing.T) { s := NewLicenseSet() s.Add(tt.licenses...) - assert.Equal(t, tt.want, s.ToSlice()) + testMe := s.ToSlice() + assert.Equal(t, tt.want, testMe) }) } } diff --git a/syft/pkg/license_test.go b/syft/pkg/license_test.go index fbeb391840f..3b92035b742 100644 --- a/syft/pkg/license_test.go +++ b/syft/pkg/license_test.go @@ -18,8 +18,8 @@ func Test_Hash(t *testing.T) { loc2 := source.NewLocation("place!") loc2.FileSystemID = "fs2" // important! there is a different file system ID - lic1 := NewLicenseFromLocation("MIT", loc1) - lic2 := NewLicenseFromLocation("MIT", loc2) + lic1 := NewLicenseFromLocations("MIT", loc1) + lic2 := NewLicenseFromLocations("MIT", loc2) hash1, err := artifact.IDByHash(lic1) require.NoError(t, err) @@ -44,44 +44,44 @@ func Test_Sort(t *testing.T) { { name: "single", licenses: []License{ - NewLicenseFromLocation("MIT", source.NewLocation("place!")), + NewLicenseFromLocations("MIT", source.NewLocation("place!")), }, expected: []License{ - NewLicenseFromLocation("MIT", source.NewLocation("place!")), + NewLicenseFromLocations("MIT", source.NewLocation("place!")), }, }, { name: "multiple", licenses: []License{ - NewLicenseFromLocation("MIT", source.NewLocation("place!")), - NewLicenseFromURL("MIT", "https://github.com/anchore/syft/blob/main/LICENSE"), - NewLicenseFromLocation("Apache", source.NewLocation("area!")), - NewLicenseFromLocation("gpl2+", source.NewLocation("area!")), + NewLicenseFromLocations("MIT", source.NewLocation("place!")), + LicenseFromURLs("MIT", "https://github.com/anchore/syft/blob/main/LICENSE"), + NewLicenseFromLocations("Apache", source.NewLocation("area!")), + NewLicenseFromLocations("gpl2+", source.NewLocation("area!")), }, expected: Licenses{ - NewLicenseFromLocation("Apache", source.NewLocation("area!")), - NewLicenseFromLocation("MIT", source.NewLocation("place!")), - NewLicenseFromURL("MIT", "https://github.com/anchore/syft/blob/main/LICENSE"), - NewLicenseFromLocation("gpl2+", source.NewLocation("area!")), + NewLicenseFromLocations("Apache", source.NewLocation("area!")), + LicenseFromURLs("MIT", "https://github.com/anchore/syft/blob/main/LICENSE"), + NewLicenseFromLocations("MIT", source.NewLocation("place!")), + NewLicenseFromLocations("gpl2+", source.NewLocation("area!")), }, }, { name: "multiple with location variants", licenses: []License{ - NewLicenseFromLocation("MIT", source.NewLocation("place!")), - NewLicenseFromLocation("MIT", source.NewLocation("park!")), + NewLicenseFromLocations("MIT", source.NewLocation("place!")), + NewLicenseFromLocations("MIT", source.NewLocation("park!")), NewLicense("MIT"), NewLicense("AAL"), NewLicense("Adobe-2006"), - NewLicenseFromLocation("Apache", source.NewLocation("area!")), + NewLicenseFromLocations("Apache", source.NewLocation("area!")), }, expected: Licenses{ NewLicense("AAL"), NewLicense("Adobe-2006"), - NewLicenseFromLocation("Apache", source.NewLocation("area!")), + NewLicenseFromLocations("Apache", source.NewLocation("area!")), NewLicense("MIT"), - NewLicenseFromLocation("MIT", source.NewLocation("park!")), - NewLicenseFromLocation("MIT", source.NewLocation("place!")), + NewLicenseFromLocations("MIT", source.NewLocation("park!")), + NewLicenseFromLocations("MIT", source.NewLocation("place!")), }, }, } diff --git a/syft/pkg/package.go b/syft/pkg/package.go index 07b1b7d2f44..6d028f20c68 100644 --- a/syft/pkg/package.go +++ b/syft/pkg/package.go @@ -23,7 +23,7 @@ type Package struct { Version string // the version of the package FoundBy string `hash:"ignore" cyclonedx:"foundBy"` // the specific cataloger that discovered this package Locations source.LocationSet // the locations that lead to the discovery of this package (note: this is not necessarily the locations that make up this package) - Licenses []License // licenses discovered with the package metadata + Licenses LicenseSet // licenses discovered with the package metadata Language Language `hash:"ignore" cyclonedx:"language"` // the language ecosystem this package belongs to (e.g. JavaScript, Python, etc) Type Type `cyclonedx:"type"` // the package type (e.g. Npm, Yarn, Python, Rpm, Deb, etc) CPEs []cpe.CPE `hash:"ignore"` // all possible Common Platform Enumerators (note: this is NOT included in the definition of the ID since all fields on a CPE are derived from other fields) @@ -65,6 +65,7 @@ func (p *Package) merge(other Package) error { } p.Locations.Add(other.Locations.ToSlice()...) + p.Licenses.Add(other.Licenses.ToSlice()...) p.CPEs = cpe.Merge(p.CPEs, other.CPEs) diff --git a/syft/pkg/package_test.go b/syft/pkg/package_test.go index e8b4a8aae74..7c461a680b8 100644 --- a/syft/pkg/package_test.go +++ b/syft/pkg/package_test.go @@ -27,10 +27,10 @@ func TestIDUniqueness(t *testing.T) { Locations: source.NewLocationSet( originalLocation, ), - Licenses: []License{ + Licenses: NewLicenseSet( NewLicense("MIT"), NewLicense("cc0-1.0"), - }, + ), Language: "math", Type: PythonPkg, CPEs: []cpe.CPE{ @@ -82,10 +82,10 @@ func TestIDUniqueness(t *testing.T) { name: "licenses order is ignored", transform: func(pkg Package) Package { // note: same as the original package, only a different order - pkg.Licenses = []License{ + pkg.Licenses = NewLicenseSet( NewLicense("cc0-1.0"), NewLicense("MIT"), - } + ) return pkg }, expectedIDComparison: assert.Equal, @@ -111,7 +111,7 @@ func TestIDUniqueness(t *testing.T) { { name: "licenses is reflected", transform: func(pkg Package) Package { - pkg.Licenses = []License{NewLicense("new!")} + pkg.Licenses = NewLicenseSet(NewLicense("new!")) return pkg }, expectedIDComparison: assert.NotEqual, @@ -412,6 +412,24 @@ func TestPackage_Merge(t *testing.T) { return true }, ), + cmp.Comparer( + func(x, y LicenseSet) bool { + xs := x.ToSlice() + ys := y.ToSlice() + + if len(xs) != len(ys) { + return false + } + for i, xe := range xs { + ye := ys[i] + if !licenseComparer(xe, ye) { + return false + } + } + + return true + }, + ), cmp.Comparer(locationComparer), ); diff != "" { t.Errorf("unexpected result from parsing (-expected +actual)\n%s", diff) @@ -420,6 +438,10 @@ func TestPackage_Merge(t *testing.T) { } } +func licenseComparer(x, y License) bool { + return cmp.Equal(x, y, cmp.Comparer(locationComparer)) +} + func locationComparer(x, y source.Location) bool { return cmp.Equal(x.Coordinates, y.Coordinates) && cmp.Equal(x.VirtualPath, y.VirtualPath) } diff --git a/syft/source/location_set.go b/syft/source/location_set.go index 86d184c769d..aeaaf9a9761 100644 --- a/syft/source/location_set.go +++ b/syft/source/location_set.go @@ -82,6 +82,10 @@ func (s *LocationSet) CoordinateSet() CoordinateSet { return set } +func (s *LocationSet) Empty() bool { + return len(s.set) == 0 +} + func (s LocationSet) Hash() (uint64, error) { // access paths and filesystem IDs are not considered when hashing a location set, only the real paths return hashstructure.Hash(s.CoordinateSet().Paths(), hashstructure.FormatV2, &hashstructure.HashOptions{ From f045bf7317ca2aa2013716aa697297eba3ad5b66 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Thu, 11 May 2023 12:56:25 -0400 Subject: [PATCH 78/99] test: add failing tests for r cataloger licenses; update schema Signed-off-by: Christopher Phillips --- schema/json/schema-8.0.0.json | 53 +++++++++++++++++ syft/pkg/cataloger/r/package.go | 66 ++++++++++++++++++++- syft/pkg/cataloger/r/package_test.go | 85 +++++++++++++++++++++++++++- 3 files changed, 200 insertions(+), 4 deletions(-) diff --git a/schema/json/schema-8.0.0.json b/schema/json/schema-8.0.0.json index be9b9807f05..74157826c1b 100644 --- a/schema/json/schema-8.0.0.json +++ b/schema/json/schema-8.0.0.json @@ -1166,6 +1166,9 @@ { "$ref": "#/$defs/PythonRequirementsMetadata" }, + { + "$ref": "#/$defs/RDescriptionFileMetadata" + }, { "$ref": "#/$defs/RebarLockMetadata" }, @@ -1592,6 +1595,56 @@ "markers" ] }, + "RDescriptionFileMetadata": { + "properties": { + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "url": { + "items": { + "type": "string" + }, + "type": "array" + }, + "repository": { + "type": "string" + }, + "built": { + "type": "string" + }, + "needsCompilation": { + "type": "boolean" + }, + "imports": { + "items": { + "type": "string" + }, + "type": "array" + }, + "depends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "suggests": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, "RebarLockMetadata": { "properties": { "name": { diff --git a/syft/pkg/cataloger/r/package.go b/syft/pkg/cataloger/r/package.go index e0802831777..e911d568c7c 100644 --- a/syft/pkg/cataloger/r/package.go +++ b/syft/pkg/cataloger/r/package.go @@ -1,6 +1,8 @@ package r import ( + "strings" + "github.com/anchore/packageurl-go" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/source" @@ -11,11 +13,14 @@ func newPackage(pd parseData, locations ...source.Location) pkg.Package { for _, loc := range locations { locationSet.Add(loc.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)) } + + licenses := parseLicenseData(pd.License) + result := pkg.Package{ Name: pd.Package, Version: pd.Version, Locations: locationSet, - Licenses: []string{pd.License}, + Licenses: pkg.NewLicenseSet(licenses...), Language: pkg.R, Type: pkg.Rpkg, PURL: packageURL(pd), @@ -30,3 +35,62 @@ func newPackage(pd parseData, locations ...source.Location) pkg.Package { func packageURL(m parseData) string { return packageurl.NewPackageURL("cran", "", m.Package, m.Version, nil, "").ToString() } + +// https://r-pkgs.org/description.html#the-license-field +// four forms: +// 1. "GPL (>= 2)" +// 2. "GPL-2" +// 3. "MIT + file LICENSE" +// 4. "pointer to the full text of the license; file LICENSE" +// 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 ...source.Location) []pkg.License { + licenses := make([]pkg.License, 0) + // check case 1 for surrounding parens + if strings.Contains(license, "(") && strings.Contains(license, ")") { + licenseVersion := strings.Split(license, " ") + if len(licenseVersion) == 2 { + license = strings.Join([]string{licenseVersion[0], parseVersion(licenseVersion[1])}, "") + licenses = append(licenses, pkg.NewLicenseFromLocations(license, locations...)) + return licenses + } + } + + // case 3 + if strings.Contains(license, "+") && strings.Contains(license, "LICENSE") { + splitField := strings.Split(license, " ") + if len(splitField) > 0 { + licenses = append(licenses, pkg.NewLicenseFromLocations(splitField[0], locations...)) + return licenses + } + } + + // TODO: case 4 if we are able to read the location data and find the adjacent file? + + // no specific case found for the above so assume case 2 + // check if the common name in case 2 is valid SDPX + licenses = append(licenses, pkg.NewLicenseFromLocations(license, locations...)) + return licenses +} + +// attempt to make best guess at SPDX license ID from version operator in case 2 +/* +‘<’, ‘<=’, ‘>’, ‘>=’, ‘==’, or ‘!=’ +*/ +func parseVersion(version string) string { + version = strings.ReplaceAll(version, "(", "") + version = strings.ReplaceAll(version, ")", "") + operatorVersion := strings.Split(version, " ") + if len(operatorVersion) == 2 { + version = operatorVersion[1] + operator := operatorVersion[0] + switch operator { + case ">=": + return version + "+" + case "==": + return version + } + } + return version +} diff --git a/syft/pkg/cataloger/r/package_test.go b/syft/pkg/cataloger/r/package_test.go index cccebf67993..5c342b7add1 100644 --- a/syft/pkg/cataloger/r/package_test.go +++ b/syft/pkg/cataloger/r/package_test.go @@ -1,14 +1,93 @@ package r -import "testing" +import ( + "testing" -func Test_newPackage(t *testing.T) { + "github.com/anchore/syft/syft/pkg" +) + +func Test_newPackageLicenses(t *testing.T) { testCases := []struct { name string - }{} + pd parseData + want []pkg.License + }{ + { + "License field with single valid spdx", + parseData{ + Package: "Foo", + Version: "1", + License: "MIT", + }, + []pkg.License{ + pkg.NewLicense("MIT"), + }, + }, + { + "License field with version separator", + parseData{ + Package: "Bar", + Version: "2", + License: "LGPL (>= 2.0, < 3)", + }, + []pkg.License{ + pkg.NewLicense("LGPL-2.0-only"), + }, + }, + { + "License field with file reference", + parseData{ + Package: "Baz", + Version: "3", + License: "GPL-2 + file LICENSE", + }, + []pkg.License{ + pkg.NewLicense("GPL-2.0-only"), + }, + }, + { + "License field which covers no case", + parseData{ + Package: "Baz", + Version: "3", + License: "Mozilla Public License", + }, + []pkg.License{ + pkg.NewLicense("Mozilla Public License"), + }, + }, + { + "License field with multiple cases separated by |", + parseData{ + Package: "Baz", + Version: "3", + License: "GPL-2 | file LICENCE | LGPL (>= 2.0)", + }, + []pkg.License{ + pkg.NewLicense("GPL-2.0-only"), + pkg.NewLicense("LGPL-2.0-only"), + }, + }, + } for _, tt := range testCases { t.Run(tt.name, func(t *testing.T) { + got := parseLicenseData(tt.pd.License) + if len(got) != len(tt.want) { + t.Errorf("unexpected number of licenses: got=%d, want=%d", len(got), len(tt.want)) + } + + for _, wantLicense := range tt.want { + found := false + for _, gotLicense := range got { + if wantLicense.Type == gotLicense.Type && wantLicense.Value == gotLicense.Value { + found = true + } + } + if !found { + t.Errorf("could not find expected license: %+v", wantLicense) + } + } }) } } From 85f2f4db1b0ecc5466e042f5056637f9368345f9 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Thu, 11 May 2023 14:26:18 -0400 Subject: [PATCH 79/99] test: update r parse version with specific rules Signed-off-by: Christopher Phillips --- syft/pkg/cataloger/r/cataloger_test.go | 4 +- syft/pkg/cataloger/r/package.go | 91 ++++++++++++++++++-------- syft/pkg/cataloger/r/package_test.go | 33 +++++++--- 3 files changed, 87 insertions(+), 41 deletions(-) diff --git a/syft/pkg/cataloger/r/cataloger_test.go b/syft/pkg/cataloger/r/cataloger_test.go index 82e6c2b2747..1581e8dc657 100644 --- a/syft/pkg/cataloger/r/cataloger_test.go +++ b/syft/pkg/cataloger/r/cataloger_test.go @@ -16,7 +16,7 @@ func TestRPackageCataloger(t *testing.T) { Version: "4.3.0", FoundBy: "r-package-cataloger", Locations: source.NewLocationSet(source.NewLocation("base/DESCRIPTION")), - Licenses: []string{"Part of R 4.3.0"}, + Licenses: pkg.NewLicenseSet([]pkg.License{pkg.NewLicense("Part of R 4.3.0")}...), Language: pkg.R, Type: pkg.Rpkg, PURL: "pkg:cran/base@4.3.0", @@ -35,7 +35,7 @@ func TestRPackageCataloger(t *testing.T) { Version: "1.5.0.9000", FoundBy: "r-package-cataloger", Locations: source.NewLocationSet(source.NewLocation("stringr/DESCRIPTION")), - Licenses: []string{"MIT + file LICENSE"}, + Licenses: pkg.NewLicenseSet([]pkg.License{pkg.NewLicense("MIT")}...), Language: pkg.R, Type: pkg.Rpkg, PURL: "pkg:cran/stringr@1.5.0.9000", diff --git a/syft/pkg/cataloger/r/package.go b/syft/pkg/cataloger/r/package.go index e911d568c7c..b916cc9da69 100644 --- a/syft/pkg/cataloger/r/package.go +++ b/syft/pkg/cataloger/r/package.go @@ -47,50 +47,83 @@ func packageURL(m parseData) string { // https://cran.rstudio.com/doc/manuals/r-devel/R-exts.html#Licensing func parseLicenseData(license string, locations ...source.Location) []pkg.License { licenses := make([]pkg.License, 0) - // check case 1 for surrounding parens - if strings.Contains(license, "(") && strings.Contains(license, ")") { - licenseVersion := strings.Split(license, " ") - if len(licenseVersion) == 2 { - license = strings.Join([]string{licenseVersion[0], parseVersion(licenseVersion[1])}, "") - licenses = append(licenses, pkg.NewLicenseFromLocations(license, locations...)) - return licenses + + // check if multiple licenses are separated by | + splitField := strings.Split(license, "|") + for _, l := range splitField { + // check case 1 for surrounding parens + l = strings.TrimSpace(l) + if strings.Contains(l, "(") && strings.Contains(l, ")") { + 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...)) + continue + } } - } - // case 3 - if strings.Contains(license, "+") && strings.Contains(license, "LICENSE") { - splitField := strings.Split(license, " ") - if len(splitField) > 0 { - licenses = append(licenses, pkg.NewLicenseFromLocations(splitField[0], locations...)) - return licenses + // case 3 + if strings.Contains(l, "+") && strings.Contains(l, "LICENSE") { + splitField := strings.Split(l, " ") + if len(splitField) > 0 { + licenses = append(licenses, pkg.NewLicenseFromLocations(splitField[0], locations...)) + continue + } } - } - // TODO: case 4 if we are able to read the location data and find the adjacent file? + // TODO: case 4 if we are able to read the location data and find the adjacent file? + if l == "file LICENSE" { + continue + } - // no specific case found for the above so assume case 2 - // check if the common name in case 2 is valid SDPX - licenses = append(licenses, pkg.NewLicenseFromLocations(license, locations...)) + // 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...)) + continue + } return licenses } // attempt to make best guess at SPDX license ID from version operator in case 2 /* ‘<’, ‘<=’, ‘>’, ‘>=’, ‘==’, or ‘!=’ +cant be (>= 2.0) OR (>= 2.0, < 3) +since there is no way in SPDX licenses to express < some other version +we attempt to check the constraint to see if this should be + or not */ func parseVersion(version string) string { version = strings.ReplaceAll(version, "(", "") version = strings.ReplaceAll(version, ")", "") - operatorVersion := strings.Split(version, " ") - if len(operatorVersion) == 2 { - version = operatorVersion[1] - operator := operatorVersion[0] - switch operator { - case ">=": - return version + "+" - case "==": - return version + + // multiple constraints + if strings.Contains(version, ",") { + multipleConstraints := strings.Split(version, ",") + // SPDX does not support considering multiple constraints + // so we will just take the first one and attempt to form the best SPDX ID we can + for _, v := range multipleConstraints { + constraintVersion := strings.SplitN(v, " ", 2) + if len(constraintVersion) == 2 { + // switch on the operator and return the version with + or without + switch constraintVersion[0] { + case ">", ">=": + return constraintVersion[1] + "+" + default: + return constraintVersion[1] + } + } + } + } + // single constraint + singleContraint := strings.Split(version, " ") + if len(singleContraint) == 2 { + switch singleContraint[0] { + case ">", ">=": + return singleContraint[1] + "+" + default: + return singleContraint[1] } } - return version + + // could not parse version constraint so return "" + return "" } diff --git a/syft/pkg/cataloger/r/package_test.go b/syft/pkg/cataloger/r/package_test.go index 5c342b7add1..8eb06642cd5 100644 --- a/syft/pkg/cataloger/r/package_test.go +++ b/syft/pkg/cataloger/r/package_test.go @@ -6,7 +6,7 @@ import ( "github.com/anchore/syft/syft/pkg" ) -func Test_newPackageLicenses(t *testing.T) { +func Test_NewPackageLicenses(t *testing.T) { testCases := []struct { name string pd parseData @@ -24,14 +24,25 @@ func Test_newPackageLicenses(t *testing.T) { }, }, { - "License field with version separator", + "License field with single version separator no +", + parseData{ + Package: "Bar", + Version: "2", + License: "LGPL (== 2.0)", + }, + []pkg.License{ + pkg.NewLicense("LGPL2.0"), + }, + }, + { + "License field with multiple version separator", parseData{ Package: "Bar", Version: "2", License: "LGPL (>= 2.0, < 3)", }, []pkg.License{ - pkg.NewLicense("LGPL-2.0-only"), + pkg.NewLicense("LGPL2.0+"), }, }, { @@ -42,7 +53,7 @@ func Test_newPackageLicenses(t *testing.T) { License: "GPL-2 + file LICENSE", }, []pkg.License{ - pkg.NewLicense("GPL-2.0-only"), + pkg.NewLicense("GPL-2"), }, }, { @@ -57,15 +68,15 @@ func Test_newPackageLicenses(t *testing.T) { }, }, { - "License field with multiple cases separated by |", + "License field with multiple cases", parseData{ Package: "Baz", Version: "3", - License: "GPL-2 | file LICENCE | LGPL (>= 2.0)", + License: "GPL-2 | file LICENSE | LGPL (>= 2.0)", }, []pkg.License{ - pkg.NewLicense("GPL-2.0-only"), - pkg.NewLicense("LGPL-2.0-only"), + pkg.NewLicense("GPL-2"), + pkg.NewLicense("LGPL2.0+"), }, }, } @@ -80,12 +91,14 @@ func Test_newPackageLicenses(t *testing.T) { for _, wantLicense := range tt.want { found := false for _, gotLicense := range got { - if wantLicense.Type == gotLicense.Type && wantLicense.Value == gotLicense.Value { + if wantLicense.Type == gotLicense.Type && + wantLicense.SPDXExpression == gotLicense.SPDXExpression && + wantLicense.Value == gotLicense.Value { found = true } } if !found { - t.Errorf("could not find expected license: %+v", wantLicense) + t.Errorf("could not find expected license: %+v; got: %+v", wantLicense, got) } } }) From 60da8ba6a17822584564a143ab228145c4bbe894 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Thu, 11 May 2023 15:16:31 -0400 Subject: [PATCH 80/99] test: add package merge test for collection Signed-off-by: Christopher Phillips --- syft/pkg/catalog_test.go | 53 +++++++++++++++++++++++++++++++++++++ syft/pkg/license.go | 5 +++- syft/source/location_set.go | 3 +++ 3 files changed, 60 insertions(+), 1 deletion(-) diff --git a/syft/pkg/catalog_test.go b/syft/pkg/catalog_test.go index 897616ca585..1d6053a76ac 100644 --- a/syft/pkg/catalog_test.go +++ b/syft/pkg/catalog_test.go @@ -17,6 +17,59 @@ type expectedIndexes struct { byPath map[string]*strset.Set } +func TestCatalogMergePackageLicenses(t *testing.T) { + tests := []struct { + name string + pkgs []Package + expectedPkgs []Package + }{ + { + name: "merges licenses of packages with equal ID", + pkgs: []Package{ + { + id: "equal", + Licenses: NewLicenseSet( + NewLicense("foo"), + NewLicense("baq"), + NewLicense("quz"), + ), + }, + { + id: "equal", + Licenses: NewLicenseSet( + NewLicense("bar"), + NewLicense("baz"), + NewLicense("foo"), + NewLicense("qux"), + ), + }, + }, + expectedPkgs: []Package{ + { + id: "equal", + Licenses: NewLicenseSet( + NewLicense("foo"), + NewLicense("baq"), + NewLicense("quz"), + NewLicense("qux"), + NewLicense("bar"), + NewLicense("baz"), + ), + }, + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + collection := NewCollection(test.pkgs...) + for i, p := range collection.Sorted() { + assert.Equal(t, test.expectedPkgs[i].Licenses, p.Licenses) + } + }) + } +} + func TestCatalogDeleteRemovesPackages(t *testing.T) { tests := []struct { name string diff --git a/syft/pkg/license.go b/syft/pkg/license.go index ef05d53db57..f754db6a1bf 100644 --- a/syft/pkg/license.go +++ b/syft/pkg/license.go @@ -148,7 +148,10 @@ func (s License) Merge(l License) (*License, error) { } s.URL.Add(l.URL.ToSlice()...) - s.Location.Add(l.Location.ToSlice()...) + if s.Location.Empty() && l.Location.Empty() { + return &s, nil + } + s.Location.Add(l.Location.ToSlice()...) return &s, nil } diff --git a/syft/source/location_set.go b/syft/source/location_set.go index aeaaf9a9761..100bf95e5d4 100644 --- a/syft/source/location_set.go +++ b/syft/source/location_set.go @@ -83,6 +83,9 @@ func (s *LocationSet) CoordinateSet() CoordinateSet { } func (s *LocationSet) Empty() bool { + if s.set == nil { + return true + } return len(s.set) == 0 } From 0dbd3a64e0c376dd48b5b395c060e473f8cd4649 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Thu, 11 May 2023 16:55:03 -0400 Subject: [PATCH 81/99] chore: update license model to guarantee collection for location Signed-off-by: Christopher Phillips --- syft/formats/syftjson/to_format_model.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/syft/formats/syftjson/to_format_model.go b/syft/formats/syftjson/to_format_model.go index 63fea8a8982..67feccc177f 100644 --- a/syft/formats/syftjson/to_format_model.go +++ b/syft/formats/syftjson/to_format_model.go @@ -186,12 +186,17 @@ func toPackageModels(catalog *pkg.Collection) []model.Package { func toLicenseModel(pkgLicenses []pkg.License) (modelLicenses []model.License) { for _, l := range pkgLicenses { + // guarantee collection + locations := make([]source.Location, 0) + if v := l.Location.ToSlice(); v != nil { + locations = v + } modelLicenses = append(modelLicenses, model.License{ Value: l.Value, SPDXExpression: l.SPDXExpression, Type: l.Type, URL: l.URL.ToSlice(), - Location: l.Location.ToSlice(), + Location: locations, }) } return From 219056b2795f0c7418be4c696b55cc208b79a179 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Fri, 12 May 2023 11:43:19 -0400 Subject: [PATCH 82/99] test: update golden images with new location collection Signed-off-by: Christopher Phillips --- .../syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden | 2 +- .../test-fixtures/snapshot/TestEncodeFullJSONDocument.golden | 2 +- .../syftjson/test-fixtures/snapshot/TestImageEncoder.golden | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden b/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden index 96fbd86a06e..1336756c8c6 100644 --- a/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden +++ b/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden @@ -17,7 +17,7 @@ "spdxExpression": "MIT", "type": "declared", "url": [], - "locations": null + "locations": [] } ], "language": "python", diff --git a/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden b/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden index a6c1f6d696a..923ff4f517c 100644 --- a/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden +++ b/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden @@ -17,7 +17,7 @@ "spdxExpression": "MIT", "type": "declared", "url": [], - "locations": null + "locations": [] } ], "language": "python", diff --git a/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden b/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden index 9a4f2e0b944..efee678a598 100644 --- a/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden +++ b/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden @@ -18,7 +18,7 @@ "spdxExpression": "MIT", "type": "declared", "url": [], - "locations": null + "locations": [] } ], "language": "python", From 26712bd6acdec6722174fc49bdfbf7e2fa363d92 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Fri, 12 May 2023 11:51:44 -0400 Subject: [PATCH 83/99] feat: protect map from empty license value Signed-off-by: Christopher Phillips --- syft/pkg/license_set.go | 16 ++++++++++------ syft/pkg/license_set_test.go | 19 +++++++++++++++++++ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/syft/pkg/license_set.go b/syft/pkg/license_set.go index b0a701e21af..d404288a0cf 100644 --- a/syft/pkg/license_set.go +++ b/syft/pkg/license_set.go @@ -34,7 +34,7 @@ func (s *LicenseSet) addToExisting(license License) (id artifact.ID, merged bool return id, false, nil } - // we got the same id so we want to merge the URL OR Location data + // we got the same id; we want to merge the URL and Location data // URL/Location are not considered when taking the Hash m, err := v.Merge(license) if err != nil { @@ -50,11 +50,15 @@ func (s *LicenseSet) Add(licenses ...License) { s.set = make(map[artifact.ID]License) } for _, l := range licenses { - if id, merged, err := s.addToExisting(l); err == nil && !merged { - // doesn't exist, add it - s.set[id] = l - } else if err != nil { - log.Trace("license set failed to add license %#v: %+v", l, err) + // we only want to add licenses that have a value + // note, this check should be moved to the license constructor in the future + if l.Value != "" { + if id, merged, err := s.addToExisting(l); err == nil && !merged { + // doesn't exist, add it + s.set[id] = l + } else if err != nil { + log.Trace("license set failed to add license %#v: %+v", l, err) + } } } } diff --git a/syft/pkg/license_set_test.go b/syft/pkg/license_set_test.go index a85104a8f57..4d95739f27f 100644 --- a/syft/pkg/license_set_test.go +++ b/syft/pkg/license_set_test.go @@ -25,6 +25,25 @@ func TestLicenseSet_Add(t *testing.T) { NewLicense("MIT"), }, }, + { + name: "add multiple simple licenses", + licenses: []License{ + NewLicense("MIT"), + NewLicense("MIT"), + NewLicense("Apache-2.0"), + }, + want: []License{ + NewLicense("Apache-2.0"), + NewLicense("MIT"), + }, + }, + { + name: "attempt to add a license with no name", + licenses: []License{ + NewLicense(""), + }, + want: nil, + }, { name: "keep multiple licenses sorted", licenses: []License{ From 71b5c484dad0061869c03647b577b9c643a2cda3 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Fri, 12 May 2023 13:16:08 -0400 Subject: [PATCH 84/99] chore: update log to include original value Signed-off-by: Christopher Phillips --- syft/file/license.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syft/file/license.go b/syft/file/license.go index 2d5841b11db..12d487d540c 100644 --- a/syft/file/license.go +++ b/syft/file/license.go @@ -21,7 +21,7 @@ type LicenseEvidence struct { func NewLicense(value string) License { spdxExpression, err := license.ParseExpression(value) if err != nil { - log.Trace("unable to parse license expression: %w", err) + log.Trace("unable to parse license expression: %s, %w", value, err) } return License{ From fb8249489d8fa15f879ecbbd5a50ac7d32ab58bf Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Fri, 12 May 2023 13:21:20 -0400 Subject: [PATCH 85/99] chore: update test name remove 1 Signed-off-by: Christopher Phillips --- syft/formats/common/spdxhelpers/license_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syft/formats/common/spdxhelpers/license_test.go b/syft/formats/common/spdxhelpers/license_test.go index 43e5600f72d..a6e165fde27 100644 --- a/syft/formats/common/spdxhelpers/license_test.go +++ b/syft/formats/common/spdxhelpers/license_test.go @@ -111,7 +111,7 @@ func Test_License(t *testing.T) { } } -func Test_joinLicenses1(t *testing.T) { +func Test_joinLicenses(t *testing.T) { tests := []struct { name string args []string From 5a5dcebb6c9ef9102353182f83987d1a35c65d56 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Fri, 12 May 2023 13:25:55 -0400 Subject: [PATCH 86/99] chore: remove old todo Signed-off-by: Christopher Phillips --- internal/licenses/parser.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/licenses/parser.go b/internal/licenses/parser.go index 98168af37b9..b5cde28f7d0 100644 --- a/internal/licenses/parser.go +++ b/internal/licenses/parser.go @@ -29,7 +29,6 @@ func Parse(reader io.Reader, l source.Location) (licenses []pkg.License, err err } for _, m := range cov.Match { - // TODO: spdx ID validation here? lic := pkg.NewLicenseFromLocations(m.ID, l) lic.Type = license.Concluded From cbcb8b94703f4546c57ba69d3c66d6b1a5ca3dc5 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Fri, 12 May 2023 13:33:37 -0400 Subject: [PATCH 87/99] chore: update test to use declared struct Signed-off-by: Christopher Phillips --- .../common/spdxhelpers/license_test.go | 28 +++++++------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/syft/formats/common/spdxhelpers/license_test.go b/syft/formats/common/spdxhelpers/license_test.go index a6e165fde27..4f9147f9fd2 100644 --- a/syft/formats/common/spdxhelpers/license_test.go +++ b/syft/formats/common/spdxhelpers/license_test.go @@ -9,18 +9,19 @@ import ( ) func Test_License(t *testing.T) { + type expected struct { + concluded string + declared string + } tests := []struct { name string input pkg.Package - expected struct { - concluded string - declared string - } + expected expected }{ { name: "no licenses", input: pkg.Package{}, - expected: struct{ concluded, declared string }{ + expected: expected { concluded: "NOASSERTION", declared: "NOASSERTION", }, @@ -30,7 +31,7 @@ func Test_License(t *testing.T) { input: pkg.Package{ Licenses: pkg.NewLicenseSet(pkg.NewLicense("made-up")), }, - expected: struct{ concluded, declared string }{ + expected: expected{ concluded: "NOASSERTION", declared: "LicenseRef-made-up", }, @@ -56,10 +57,7 @@ func Test_License(t *testing.T) { pkg.NewLicense("GPL-3.0-only"), ), }, - expected: struct { - concluded string - declared string - }{ + expected: expected { concluded: "NOASSERTION", // because we sort licenses alphabetically GPL ends up at the start declared: "GPL-3.0-only AND MIT", @@ -74,10 +72,7 @@ func Test_License(t *testing.T) { pkg.NewLicense("MIT"), ), }, - expected: struct { - concluded string - declared string - }{ + expected: expected { concluded: "NOASSERTION", // because we separate licenses between valid SPDX and non valid, valid ID always end at the front declared: "MIT AND LicenseRef-one-thing-first AND LicenseRef-two-things----second", @@ -92,10 +87,7 @@ func Test_License(t *testing.T) { pkg.NewLicense("MIT OR APACHE-2.0"), ), }, - expected: struct { - concluded string - declared string - }{ + expected: expected { concluded: "NOASSERTION", // because we separate licenses between valid SPDX and non valid, valid ID always end at the front declared: "(MIT AND GPL-3.0-only) AND (MIT OR APACHE-2.0) AND LicenseRef-one-thing-first", From 96804f6b72a05274305b02dd3fb63dff0f1bf249 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Fri, 12 May 2023 13:38:52 -0400 Subject: [PATCH 88/99] chore: update comments with linked issue Signed-off-by: Christopher Phillips --- syft/license/license.go | 1 + 1 file changed, 1 insertion(+) diff --git a/syft/license/license.go b/syft/license/license.go index a1e6c3b2c39..e9dd93c6235 100644 --- a/syft/license/license.go +++ b/syft/license/license.go @@ -25,6 +25,7 @@ func ParseExpression(expression string) (string, error) { // If it doesn't exist initially in the SPDX list it might be a more complex expression // ignored variable is any invalid expressions // TODO: contribute to spdxexp to expose deprecated license IDs + // https://github.com/anchore/syft/issues/1814 valid, _ := spdxexp.ValidateLicenses([]string{expression}) if !valid { return "", fmt.Errorf("failed to validate spdx expression: %s", expression) From 029b9b16f0e1546ff6d1f8c69eb1245624c4e9d5 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Fri, 12 May 2023 13:43:09 -0400 Subject: [PATCH 89/99] chore: update new licenses from values Signed-off-by: Christopher Phillips --- syft/pkg/catalog_test.go | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/syft/pkg/catalog_test.go b/syft/pkg/catalog_test.go index 1d6053a76ac..5271ac262cb 100644 --- a/syft/pkg/catalog_test.go +++ b/syft/pkg/catalog_test.go @@ -29,18 +29,13 @@ func TestCatalogMergePackageLicenses(t *testing.T) { { id: "equal", Licenses: NewLicenseSet( - NewLicense("foo"), - NewLicense("baq"), - NewLicense("quz"), + NewLicensesFromValues("foo", "baq", "quz")..., ), }, { id: "equal", Licenses: NewLicenseSet( - NewLicense("bar"), - NewLicense("baz"), - NewLicense("foo"), - NewLicense("qux"), + NewLicensesFromValues("bar", "baz", "foo", "qux")..., ), }, }, @@ -48,12 +43,7 @@ func TestCatalogMergePackageLicenses(t *testing.T) { { id: "equal", Licenses: NewLicenseSet( - NewLicense("foo"), - NewLicense("baq"), - NewLicense("quz"), - NewLicense("qux"), - NewLicense("bar"), - NewLicense("baz"), + NewLicensesFromValues("foo", "baq", "quz", "qux", "bar", "baz")..., ), }, }, From 2b9115d1326b2aaadc6c3f086d21f8a0d94035d9 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Fri, 12 May 2023 13:44:28 -0400 Subject: [PATCH 90/99] chore: remove stale TODO Signed-off-by: Christopher Phillips --- syft/pkg/license.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/syft/pkg/license.go b/syft/pkg/license.go index f754db6a1bf..cb6e804810c 100644 --- a/syft/pkg/license.go +++ b/syft/pkg/license.go @@ -64,8 +64,6 @@ func NewLicense(value string) License { log.Trace("unable to parse license expression: %w", err) } - // TODO: how do we express other places where a license is declared - // EX: we got this from the go module cache at path /x/y/z on disk return License{ Value: value, SPDXExpression: spdxExpression, From 63345c3721b9a0b4138b1e5458ec642ced87f9a1 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Fri, 12 May 2023 13:56:08 -0400 Subject: [PATCH 91/99] chore: lint-fix Signed-off-by: Christopher Phillips --- syft/formats/common/spdxhelpers/license_test.go | 10 +++++----- syft/pkg/license.go | 5 ----- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/syft/formats/common/spdxhelpers/license_test.go b/syft/formats/common/spdxhelpers/license_test.go index 4f9147f9fd2..cb4e614e445 100644 --- a/syft/formats/common/spdxhelpers/license_test.go +++ b/syft/formats/common/spdxhelpers/license_test.go @@ -11,7 +11,7 @@ import ( func Test_License(t *testing.T) { type expected struct { concluded string - declared string + declared string } tests := []struct { name string @@ -21,7 +21,7 @@ func Test_License(t *testing.T) { { name: "no licenses", input: pkg.Package{}, - expected: expected { + expected: expected{ concluded: "NOASSERTION", declared: "NOASSERTION", }, @@ -57,7 +57,7 @@ func Test_License(t *testing.T) { pkg.NewLicense("GPL-3.0-only"), ), }, - expected: expected { + expected: expected{ concluded: "NOASSERTION", // because we sort licenses alphabetically GPL ends up at the start declared: "GPL-3.0-only AND MIT", @@ -72,7 +72,7 @@ func Test_License(t *testing.T) { pkg.NewLicense("MIT"), ), }, - expected: expected { + expected: expected{ concluded: "NOASSERTION", // because we separate licenses between valid SPDX and non valid, valid ID always end at the front declared: "MIT AND LicenseRef-one-thing-first AND LicenseRef-two-things----second", @@ -87,7 +87,7 @@ func Test_License(t *testing.T) { pkg.NewLicense("MIT OR APACHE-2.0"), ), }, - expected: expected { + expected: expected{ concluded: "NOASSERTION", // because we separate licenses between valid SPDX and non valid, valid ID always end at the front declared: "(MIT AND GPL-3.0-only) AND (MIT OR APACHE-2.0) AND LicenseRef-one-thing-first", diff --git a/syft/pkg/license.go b/syft/pkg/license.go index cb6e804810c..d7eba5b3075 100644 --- a/syft/pkg/license.go +++ b/syft/pkg/license.go @@ -90,11 +90,6 @@ func NewLicenseFromType(value string, t license.Type) License { func NewLicensesFromValues(values ...string) (licenses []License) { for _, v := range values { - // ignore common SPDX license expression connectors - // that could be included in input - if v == "" || v == "AND" { - continue - } licenses = append(licenses, NewLicense(v)) } return From 0a9b1b0d7df4efc2855b827f2426de798b560449 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Fri, 12 May 2023 14:04:27 -0400 Subject: [PATCH 92/99] chore: update comment for URL/License merge Signed-off-by: Christopher Phillips --- syft/pkg/license.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/syft/pkg/license.go b/syft/pkg/license.go index d7eba5b3075..4a06ebc5be7 100644 --- a/syft/pkg/license.go +++ b/syft/pkg/license.go @@ -14,13 +14,14 @@ import ( var _ sort.Interface = (*Licenses)(nil) // License represents an SPDX Expression or license value extracted from a packages metadata -// We want to ignore URL and Location since we will always merge these fields -// a License is a unique combination of value, expression and type -// its sources are always considered merge and additions to the evidence +// We want to ignore URL and Location since we merge these fields across equal licenses. +// A License is a unique combination of value, expression and type, where +// its sources are always considered merged and additions to the evidence // of where it was found and how it was sourced. -// This is different from how we treat a package since we consider package paths to distinguish -// if packages should be kept separate, this is different for licenses since we're only looking for evidence -// of where a license was declared/concluded for a package +// This is different from how we treat a package since we consider package paths +// in order to distinguish if packages should be kept separate +// this is different for licenses since we're only looking for evidence +// of where a license was declared/concluded for a given package type License struct { Value string `json:"value"` SPDXExpression string `json:"spdxExpression"` From 3dab7e3f4450963db53774a25aa624ecaaaddf18 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Fri, 12 May 2023 14:07:52 -0400 Subject: [PATCH 93/99] test: update hash test to prove URL ignored Signed-off-by: Christopher Phillips --- syft/pkg/license_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/syft/pkg/license_test.go b/syft/pkg/license_test.go index 3b92035b742..62429f234c9 100644 --- a/syft/pkg/license_test.go +++ b/syft/pkg/license_test.go @@ -21,6 +21,9 @@ func Test_Hash(t *testing.T) { lic1 := NewLicenseFromLocations("MIT", loc1) lic2 := NewLicenseFromLocations("MIT", loc2) + lic1.URL.Add("foo") + lic2.URL.Add("bar") // we also want to check the URL are ignored + hash1, err := artifact.IDByHash(lic1) require.NoError(t, err) From a0190d29660a8715bf672f1f9d5f1e3b1ec73296 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Fri, 12 May 2023 15:08:15 -0400 Subject: [PATCH 94/99] feat: simplify cyclone dx helper logic Signed-off-by: Christopher Phillips --- .../common/cyclonedxhelpers/licenses.go | 21 +++++-------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/syft/formats/common/cyclonedxhelpers/licenses.go b/syft/formats/common/cyclonedxhelpers/licenses.go index a8e812ae5bf..c2a5d814e86 100644 --- a/syft/formats/common/cyclonedxhelpers/licenses.go +++ b/syft/formats/common/cyclonedxhelpers/licenses.go @@ -91,6 +91,9 @@ func separateLicenses(p pkg.Package) (spdx, other cyclonedx.Licenses, expression for _, l := range p.Licenses.ToSlice() { // singular expression case if value, exists := spdxlicense.ID(l.SPDXExpression); exists { + if _, exists := seen[value]; exists { + continue + } // we do 1 license -> many URL in our internal model // this fans out different URL to single cyclone licenses if !l.URL.Empty() { @@ -104,17 +107,9 @@ func separateLicenses(p pkg.Package) (spdx, other cyclonedx.Licenses, expression }) continue } - spdxc = append(spdxc, cyclonedx.LicenseChoice{ - License: &cyclonedx.License{ - ID: value, - }, - }) - continue } } - if _, exists := seen[value]; exists { - continue - } + spdxc = append(spdxc, cyclonedx.LicenseChoice{ License: &cyclonedx.License{ ID: value, @@ -123,6 +118,7 @@ func separateLicenses(p pkg.Package) (spdx, other cyclonedx.Licenses, expression seen[value] = true continue } + if l.SPDXExpression != "" { // COMPLEX EXPRESSION CASE: do we instead break the spdx expression out // into individual licenses OR combine singular licenses into a single expression? @@ -140,15 +136,8 @@ func separateLicenses(p pkg.Package) (spdx, other cyclonedx.Licenses, expression URL: url, }, }) - continue } - otherc = append(otherc, cyclonedx.LicenseChoice{ - License: &cyclonedx.License{ - Name: l.Value, - }, - }) } - continue } otherc = append(otherc, cyclonedx.LicenseChoice{ License: &cyclonedx.License{ From 3f2d1677de70920bb48610447ae308c35b6c844c Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Fri, 12 May 2023 15:35:45 -0400 Subject: [PATCH 95/99] test: add new tests for cyclonedx encode behavior Signed-off-by: Christopher Phillips --- .../common/cyclonedxhelpers/licenses.go | 34 +++++++++++-------- .../common/cyclonedxhelpers/licenses_test.go | 33 ++++++++++++++++++ syft/pkg/license.go | 2 +- syft/pkg/license_set_test.go | 4 +-- syft/pkg/license_test.go | 4 +-- 5 files changed, 57 insertions(+), 20 deletions(-) diff --git a/syft/formats/common/cyclonedxhelpers/licenses.go b/syft/formats/common/cyclonedxhelpers/licenses.go index c2a5d814e86..2b006ba732f 100644 --- a/syft/formats/common/cyclonedxhelpers/licenses.go +++ b/syft/formats/common/cyclonedxhelpers/licenses.go @@ -28,16 +28,24 @@ func encodeLicenses(p pkg.Package) *cyclonedx.Licenses { return &otherc } - if len(ex) > 0 { - return &cyclonedx.Licenses{ - cyclonedx.LicenseChoice{ - Expression: mergeSPDX(ex, spdxc), - }, + if len(spdxc) > 0 { + for _, l := range ex { + spdxc = append(spdxc, cyclonedx.LicenseChoice{ + License: &cyclonedx.License{ + Name: l, + }, + }) } + return &spdxc } - if len(spdxc) > 0 { - return &spdxc + if len(ex) > 0 { + // only expressions found + var expressions cyclonedx.Licenses + expressions = append(expressions, cyclonedx.LicenseChoice{ + Expression: mergeSPDX(ex), + }) + return &expressions } return nil @@ -56,11 +64,11 @@ func decodeLicenses(c *cyclonedx.Component) []pkg.License { // these fields are mutually exclusive in the spec switch { case l.License.ID != "": - licenses = append(licenses, pkg.LicenseFromURLs(l.License.ID, l.License.URL)) + licenses = append(licenses, pkg.NewLicenseFromURLs(l.License.ID, l.License.URL)) case l.License.Name != "": - licenses = append(licenses, pkg.LicenseFromURLs(l.License.Name, l.License.URL)) + licenses = append(licenses, pkg.NewLicenseFromURLs(l.License.Name, l.License.URL)) case l.Expression != "": - licenses = append(licenses, pkg.LicenseFromURLs(l.Expression, l.License.URL)) + licenses = append(licenses, pkg.NewLicenseFromURLs(l.Expression, l.License.URL)) default: } } @@ -148,7 +156,7 @@ func separateLicenses(p pkg.Package) (spdx, other cyclonedx.Licenses, expression return spdxc, otherc, ex } -func mergeSPDX(ex []string, spdxc cyclonedx.Licenses) string { +func mergeSPDX(ex []string) string { var candidate []string for _, e := range ex { // if the expression does not have balanced parens add them @@ -158,10 +166,6 @@ func mergeSPDX(ex []string, spdxc cyclonedx.Licenses) string { } } - for _, l := range spdxc { - candidate = append(candidate, l.License.ID) - } - if len(candidate) == 1 { return reduceOuter(strings.Join(candidate, " AND ")) } diff --git a/syft/formats/common/cyclonedxhelpers/licenses_test.go b/syft/formats/common/cyclonedxhelpers/licenses_test.go index 478d5674ca6..acd62f2e3ad 100644 --- a/syft/formats/common/cyclonedxhelpers/licenses_test.go +++ b/syft/formats/common/cyclonedxhelpers/licenses_test.go @@ -85,6 +85,39 @@ 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"), + ), + }, + expected: &cyclonedx.Licenses{ + { + License: &cyclonedx.License{ + ID: "MIT", + URL: "https://opensource.org/licenses/MIT", + }, + }, + { + License: &cyclonedx.License{ + ID: "MIT", + URL: "https://spdx.org/licenses/MIT.html", + }, + }, + { + License: &cyclonedx.License{ + ID: "MIT", + }, + }, + { + License: &cyclonedx.License{ + Name: "MIT AND GPL-3.0-only", + }, + }, + }, + }, // TODO: do we drop the non SPDX ID license and do a single expression // OR do we keep the non SPDX ID license and do multiple licenses where the complex // expressions are set as the NAME field? diff --git a/syft/pkg/license.go b/syft/pkg/license.go index 4a06ebc5be7..e5cc57910cd 100644 --- a/syft/pkg/license.go +++ b/syft/pkg/license.go @@ -114,7 +114,7 @@ func NewLicenseFromLocations(value string, locations ...source.Location) License return l } -func LicenseFromURLs(value string, urls ...string) License { +func NewLicenseFromURLs(value string, urls ...string) License { l := NewLicense(value) for _, u := range urls { if u != "" { diff --git a/syft/pkg/license_set_test.go b/syft/pkg/license_set_test.go index 4d95739f27f..c6039b2c2ed 100644 --- a/syft/pkg/license_set_test.go +++ b/syft/pkg/license_set_test.go @@ -90,7 +90,7 @@ func TestLicenseSet_Add(t *testing.T) { licenses: []License{ NewLicense("MIT"), NewLicenseFromLocations("MIT", source.NewLocation("/place")), - LicenseFromURLs("MIT", "https://example.com"), + NewLicenseFromURLs("MIT", "https://example.com"), }, want: []License{ { @@ -108,7 +108,7 @@ func TestLicenseSet_Add(t *testing.T) { NewLicenseFromType("MIT", license.Concluded), NewLicenseFromType("MIT", license.Declared), NewLicenseFromLocations("MIT", source.NewLocation("/place")), - LicenseFromURLs("MIT", "https://example.com"), + NewLicenseFromURLs("MIT", "https://example.com"), }, want: []License{ { diff --git a/syft/pkg/license_test.go b/syft/pkg/license_test.go index 62429f234c9..142d18d0a86 100644 --- a/syft/pkg/license_test.go +++ b/syft/pkg/license_test.go @@ -57,13 +57,13 @@ func Test_Sort(t *testing.T) { name: "multiple", licenses: []License{ NewLicenseFromLocations("MIT", source.NewLocation("place!")), - LicenseFromURLs("MIT", "https://github.com/anchore/syft/blob/main/LICENSE"), + NewLicenseFromURLs("MIT", "https://github.com/anchore/syft/blob/main/LICENSE"), NewLicenseFromLocations("Apache", source.NewLocation("area!")), NewLicenseFromLocations("gpl2+", source.NewLocation("area!")), }, expected: Licenses{ NewLicenseFromLocations("Apache", source.NewLocation("area!")), - LicenseFromURLs("MIT", "https://github.com/anchore/syft/blob/main/LICENSE"), + NewLicenseFromURLs("MIT", "https://github.com/anchore/syft/blob/main/LICENSE"), NewLicenseFromLocations("MIT", source.NewLocation("place!")), NewLicenseFromLocations("gpl2+", source.NewLocation("area!")), }, From 6bab23bb19e4427d7326f8ac9ed260bf9ee8e465 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Mon, 15 May 2023 13:27:25 -0400 Subject: [PATCH 96/99] test: update tests with new SBOM cataloger changes Signed-off-by: Christopher Phillips --- syft/formats/common/cyclonedxhelpers/licenses.go | 5 ----- syft/pkg/cataloger/sbom/cataloger_test.go | 10 +++++----- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/syft/formats/common/cyclonedxhelpers/licenses.go b/syft/formats/common/cyclonedxhelpers/licenses.go index 2b006ba732f..d95dad4f773 100644 --- a/syft/formats/common/cyclonedxhelpers/licenses.go +++ b/syft/formats/common/cyclonedxhelpers/licenses.go @@ -95,13 +95,9 @@ func separateLicenses(p pkg.Package) (spdx, other cyclonedx.Licenses, expression */ // dedupe spdxlicenseID - seen := make(map[string]bool) for _, l := range p.Licenses.ToSlice() { // singular expression case if value, exists := spdxlicense.ID(l.SPDXExpression); exists { - if _, exists := seen[value]; exists { - continue - } // we do 1 license -> many URL in our internal model // this fans out different URL to single cyclone licenses if !l.URL.Empty() { @@ -123,7 +119,6 @@ func separateLicenses(p pkg.Package) (spdx, other cyclonedx.Licenses, expression ID: value, }, }) - seen[value] = true continue } diff --git a/syft/pkg/cataloger/sbom/cataloger_test.go b/syft/pkg/cataloger/sbom/cataloger_test.go index 48882ba6639..a2226f80e7c 100644 --- a/syft/pkg/cataloger/sbom/cataloger_test.go +++ b/syft/pkg/cataloger/sbom/cataloger_test.go @@ -281,7 +281,7 @@ func Test_parseSBOM(t *testing.T) { Version: "1.1.1s-r0", Type: "apk", Locations: apkgdbLocation, - Licenses: []string{"OpenSSL"}, + Licenses: pkg.NewLicenseSet(pkg.NewLicense("OpenSSL")), FoundBy: "apkdb-cataloger", PURL: "pkg:apk/alpine/libssl1.1@1.1.1s-r0?arch=x86_64&upstream=openssl&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -294,7 +294,7 @@ func Test_parseSBOM(t *testing.T) { Version: "1.35.0-r17", Type: "apk", Locations: apkgdbLocation, - Licenses: []string{"GPL-2.0-only"}, + Licenses: pkg.NewLicenseSet(pkg.NewLicense("GPL-2.0-only")), FoundBy: "apkdb-cataloger", PURL: "pkg:apk/alpine/ssl_client@1.35.0-r17?arch=x86_64&upstream=busybox&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -312,7 +312,7 @@ func Test_parseSBOM(t *testing.T) { Version: "3.2.0-r23", Type: "apk", Locations: apkgdbLocation, - Licenses: []string{"GPL-2.0-only"}, + Licenses: pkg.NewLicenseSet(pkg.NewLicense("GPL-2.0-only")), FoundBy: "apkdb-cataloger", PURL: "pkg:apk/alpine/alpine-baselayout@3.2.0-r23?arch=x86_64&upstream=alpine-baselayout&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -330,7 +330,7 @@ func Test_parseSBOM(t *testing.T) { Version: "1.35.0-r17", Type: "apk", Locations: apkgdbLocation, - Licenses: []string{"GPL-2.0-only"}, + Licenses: pkg.NewLicenseSet(pkg.NewLicense("GPL-2.0-only")), FoundBy: "apkdb-cataloger", PURL: "pkg:apk/alpine/busybox@1.35.0-r17?arch=x86_64&upstream=busybox&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -343,7 +343,7 @@ func Test_parseSBOM(t *testing.T) { Version: "1.2.3-r1", Type: "apk", Locations: apkgdbLocation, - Licenses: []string{"MIT"}, + Licenses: pkg.NewLicenseSet(pkg.NewLicense("MIT")), FoundBy: "apkdb-cataloger", PURL: "pkg:apk/alpine/musl@1.2.3-r1?arch=x86_64&upstream=musl&distro=alpine-3.16.3", CPEs: mustCPEs( From 667cbe28de018a29516822d475c023702dc8fb3e Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Mon, 15 May 2023 13:59:17 -0400 Subject: [PATCH 97/99] fix: cleanup the logic for seen non url Signed-off-by: Christopher Phillips --- .../common/cyclonedxhelpers/licenses.go | 26 ++++++++++++------- .../common/cyclonedxhelpers/licenses_test.go | 5 ---- test/integration/encode_decode_cycle_test.go | 5 ++++ 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/syft/formats/common/cyclonedxhelpers/licenses.go b/syft/formats/common/cyclonedxhelpers/licenses.go index d95dad4f773..7778946a649 100644 --- a/syft/formats/common/cyclonedxhelpers/licenses.go +++ b/syft/formats/common/cyclonedxhelpers/licenses.go @@ -85,7 +85,7 @@ func separateLicenses(p pkg.Package) (spdx, other cyclonedx.Licenses, expression pkg.License can be a couple of things: - Complex SPDX expression - Some other Valid license ID - - Some non standard non spdx license + - Some non-standard non spdx license To determine if an expression is a singular ID we first run it against the SPDX license list. @@ -94,12 +94,14 @@ func separateLicenses(p pkg.Package) (spdx, other cyclonedx.Licenses, expression as a license choice and the invalid expression as a license string. */ - // dedupe spdxlicenseID + seenNoURL := make(map[string]bool) for _, l := range p.Licenses.ToSlice() { // singular expression case + // only ID field here since we guarantee that the license is valid if value, exists := spdxlicense.ID(l.SPDXExpression); exists { // we do 1 license -> many URL in our internal model // this fans out different URL to single cyclone licenses + // we cannot track seen for this since we need to fan out if !l.URL.Empty() { for _, url := range l.URL.ToSlice() { if url != "" { @@ -109,16 +111,21 @@ func separateLicenses(p pkg.Package) (spdx, other cyclonedx.Licenses, expression URL: url, }, }) - continue } } + } else { + if seenNoURL[value] { + // we have already seen this license ID without a URL + // we should not add it again + continue + } + spdxc = append(spdxc, cyclonedx.LicenseChoice{ + License: &cyclonedx.License{ + ID: value, + }, + }) + seenNoURL[value] = true } - - spdxc = append(spdxc, cyclonedx.LicenseChoice{ - License: &cyclonedx.License{ - ID: value, - }, - }) continue } @@ -130,6 +137,7 @@ func separateLicenses(p pkg.Package) (spdx, other cyclonedx.Licenses, expression } // license string that are not valid spdx expressions or ids + // only Name here since we cannot guarantee that the license is a valid SPDX expression if !l.URL.Empty() { for _, url := range l.URL.ToSlice() { if url != "" { diff --git a/syft/formats/common/cyclonedxhelpers/licenses_test.go b/syft/formats/common/cyclonedxhelpers/licenses_test.go index acd62f2e3ad..2d82c9e1c61 100644 --- a/syft/formats/common/cyclonedxhelpers/licenses_test.go +++ b/syft/formats/common/cyclonedxhelpers/licenses_test.go @@ -106,11 +106,6 @@ func Test_encodeLicense(t *testing.T) { URL: "https://spdx.org/licenses/MIT.html", }, }, - { - License: &cyclonedx.License{ - ID: "MIT", - }, - }, { License: &cyclonedx.License{ Name: "MIT AND GPL-3.0-only", diff --git a/test/integration/encode_decode_cycle_test.go b/test/integration/encode_decode_cycle_test.go index 67940212bb1..c17f8e8e5bb 100644 --- a/test/integration/encode_decode_cycle_test.go +++ b/test/integration/encode_decode_cycle_test.go @@ -3,6 +3,7 @@ package integration import ( "bytes" "fmt" + "os" "regexp" "testing" @@ -81,6 +82,10 @@ func TestEncodeDecodeEncodeCycleComparison(t *testing.T) { by2, err := formats.Encode(*newSBOM, format) require.NoError(t, err) + // debug write out the bytes for inspection + os.WriteFile(fmt.Sprintf("/tmp/%s-%s1.json", image, test.formatOption), by1, 0644) + os.WriteFile(fmt.Sprintf("/tmp/%s-%s2.json", image, test.formatOption), by2, 0644) + if test.redactor != nil { by1 = test.redactor(by1) by2 = test.redactor(by2) From 235b8d29d1d2268f9d34dff021bb50c062c422b1 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Mon, 15 May 2023 14:03:32 -0400 Subject: [PATCH 98/99] test: update coverage for encode/decode cdx Signed-off-by: Christopher Phillips --- .../common/cyclonedxhelpers/licenses_test.go | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/syft/formats/common/cyclonedxhelpers/licenses_test.go b/syft/formats/common/cyclonedxhelpers/licenses_test.go index 2d82c9e1c61..a2e4a922ad0 100644 --- a/syft/formats/common/cyclonedxhelpers/licenses_test.go +++ b/syft/formats/common/cyclonedxhelpers/licenses_test.go @@ -113,6 +113,35 @@ func Test_encodeLicense(t *testing.T) { }, }, }, + { + name: "with multiple URLs and single with no URL", + 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.NewLicense("MIT"), + ), + }, + expected: &cyclonedx.Licenses{ + { + License: &cyclonedx.License{ + ID: "MIT", + URL: "https://opensource.org/licenses/MIT", + }, + }, + { + License: &cyclonedx.License{ + ID: "MIT", + URL: "https://spdx.org/licenses/MIT.html", + }, + }, + { + License: &cyclonedx.License{ + Name: "MIT AND GPL-3.0-only", + }, + }, + }, + }, // TODO: do we drop the non SPDX ID license and do a single expression // OR do we keep the non SPDX ID license and do multiple licenses where the complex // expressions are set as the NAME field? From d1d26d355e178368ada7e104acf258809cd5cca1 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Mon, 15 May 2023 16:11:15 -0400 Subject: [PATCH 99/99] fix: update license logic in cyclonedxhelpers to account for dupes Signed-off-by: Christopher Phillips --- .../common/cyclonedxhelpers/licenses.go | 83 ++++++++++--------- .../common/cyclonedxhelpers/licenses_test.go | 27 +++++- test/integration/encode_decode_cycle_test.go | 5 -- 3 files changed, 67 insertions(+), 48 deletions(-) diff --git a/syft/formats/common/cyclonedxhelpers/licenses.go b/syft/formats/common/cyclonedxhelpers/licenses.go index 7778946a649..a2e0772bffa 100644 --- a/syft/formats/common/cyclonedxhelpers/licenses.go +++ b/syft/formats/common/cyclonedxhelpers/licenses.go @@ -78,11 +78,11 @@ func decodeLicenses(c *cyclonedx.Component) []pkg.License { // nolint:funlen func separateLicenses(p pkg.Package) (spdx, other cyclonedx.Licenses, expressions []string) { + ex := make([]string, 0) spdxc := cyclonedx.Licenses{} otherc := cyclonedx.Licenses{} - ex := make([]string, 0) /* - pkg.License can be a couple of things: + pkg.License can be a couple of things: see above declarations - Complex SPDX expression - Some other Valid license ID - Some non-standard non spdx license @@ -94,61 +94,42 @@ func separateLicenses(p pkg.Package) (spdx, other cyclonedx.Licenses, expression as a license choice and the invalid expression as a license string. */ - seenNoURL := make(map[string]bool) + seen := make(map[string]bool) for _, l := range p.Licenses.ToSlice() { // singular expression case // only ID field here since we guarantee that the license is valid if value, exists := spdxlicense.ID(l.SPDXExpression); exists { - // we do 1 license -> many URL in our internal model - // this fans out different URL to single cyclone licenses - // we cannot track seen for this since we need to fan out if !l.URL.Empty() { - for _, url := range l.URL.ToSlice() { - if url != "" { - spdxc = append(spdxc, cyclonedx.LicenseChoice{ - License: &cyclonedx.License{ - ID: value, - URL: url, - }, - }) - } - } - } else { - if seenNoURL[value] { - // we have already seen this license ID without a URL - // we should not add it again - continue - } - spdxc = append(spdxc, cyclonedx.LicenseChoice{ - License: &cyclonedx.License{ - ID: value, - }, - }) - seenNoURL[value] = true + processLicenseURLs(l, value, &spdxc) + continue } + + if _, exists := seen[value]; exists { + continue + } + // try making set of license choices to avoid duplicates + // only update if the license has more information + spdxc = append(spdxc, cyclonedx.LicenseChoice{ + License: &cyclonedx.License{ + ID: value, + }, + }) + seen[value] = true + // we have added the license to the SPDX license list check next license continue } if l.SPDXExpression != "" { - // COMPLEX EXPRESSION CASE: do we instead break the spdx expression out - // into individual licenses OR combine singular licenses into a single expression? + // COMPLEX EXPRESSION CASE ex = append(ex, l.SPDXExpression) continue } // license string that are not valid spdx expressions or ids - // only Name here since we cannot guarantee that the license is a valid SPDX expression + // we only use license Name here since we cannot guarantee that the license is a valid SPDX expression if !l.URL.Empty() { - for _, url := range l.URL.ToSlice() { - if url != "" { - otherc = append(otherc, cyclonedx.LicenseChoice{ - License: &cyclonedx.License{ - Name: l.Value, - URL: url, - }, - }) - } - } + processLicenseURLs(l, "", &otherc) + continue } otherc = append(otherc, cyclonedx.LicenseChoice{ License: &cyclonedx.License{ @@ -159,6 +140,26 @@ func separateLicenses(p pkg.Package) (spdx, other cyclonedx.Licenses, expression return spdxc, otherc, ex } +func processLicenseURLs(l pkg.License, spdxID string, populate *cyclonedx.Licenses) { + for _, url := range l.URL.ToSlice() { + if spdxID == "" { + *populate = append(*populate, cyclonedx.LicenseChoice{ + License: &cyclonedx.License{ + URL: url, + Name: l.Value, + }, + }) + } else { + *populate = append(*populate, cyclonedx.LicenseChoice{ + License: &cyclonedx.License{ + ID: spdxID, + URL: url, + }, + }) + } + } +} + func mergeSPDX(ex []string) string { var candidate []string for _, e := range ex { diff --git a/syft/formats/common/cyclonedxhelpers/licenses_test.go b/syft/formats/common/cyclonedxhelpers/licenses_test.go index a2e4a922ad0..b3a390abcbb 100644 --- a/syft/formats/common/cyclonedxhelpers/licenses_test.go +++ b/syft/formats/common/cyclonedxhelpers/licenses_test.go @@ -91,9 +91,21 @@ func Test_encodeLicense(t *testing.T) { 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"), ), }, expected: &cyclonedx.Licenses{ + { + License: &cyclonedx.License{ + Name: "FakeLicense", + URL: "htts://someurl.com", + }, + }, + { + License: &cyclonedx.License{ + Name: "MIT AND GPL-3.0-only", + }, + }, { License: &cyclonedx.License{ ID: "MIT", @@ -106,9 +118,20 @@ func Test_encodeLicense(t *testing.T) { URL: "https://spdx.org/licenses/MIT.html", }, }, + }, + }, + { + name: "with multiple values licenses are deduplicated", + input: pkg.Package{ + Licenses: pkg.NewLicenseSet( + pkg.NewLicense("Apache-2"), + pkg.NewLicense("Apache-2.0"), + ), + }, + expected: &cyclonedx.Licenses{ { License: &cyclonedx.License{ - Name: "MIT AND GPL-3.0-only", + ID: "Apache-2.0", }, }, }, @@ -117,9 +140,9 @@ func Test_encodeLicense(t *testing.T) { name: "with multiple URLs and single with no URL", 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.NewLicense("MIT"), ), }, expected: &cyclonedx.Licenses{ diff --git a/test/integration/encode_decode_cycle_test.go b/test/integration/encode_decode_cycle_test.go index c17f8e8e5bb..67940212bb1 100644 --- a/test/integration/encode_decode_cycle_test.go +++ b/test/integration/encode_decode_cycle_test.go @@ -3,7 +3,6 @@ package integration import ( "bytes" "fmt" - "os" "regexp" "testing" @@ -82,10 +81,6 @@ func TestEncodeDecodeEncodeCycleComparison(t *testing.T) { by2, err := formats.Encode(*newSBOM, format) require.NoError(t, err) - // debug write out the bytes for inspection - os.WriteFile(fmt.Sprintf("/tmp/%s-%s1.json", image, test.formatOption), by1, 0644) - os.WriteFile(fmt.Sprintf("/tmp/%s-%s2.json", image, test.formatOption), by2, 0644) - if test.redactor != nil { by1 = test.redactor(by1) by2 = test.redactor(by2)