mirror of
https://github.com/anchore/syft
synced 2024-11-10 06:14:16 +00:00
feat: Add support for new Copyrights and schema updates
- **Added**: New JSON schema version `16.0.16` with support for the new `Copyrights`. - **Modified**: Updated the `JSONSchemaVersion` parameter to use the new schema. - **Added**: New `Copyrights` field to the `Package` and `PackageBasicData` structs, similar to the existing `Licenses` field. - **Added**: New `Copyright` struct. - **Implemented**: Sorting methods for the `Copyright` struct. - **Changed**: Updated the `PackageCopyrightText` to use `helpers.GetCopyrights(p.Copyrights)`, which formats the copyright text and returns a string. Example output: "Copyright 2014-2014 Matt Zabriskie & Collaborators". - **Added**: `Copyrights` assignment to the `toSyftPackage` function. Signed-off-by: dor-hayun <dor.hayun@mend.io>
This commit is contained in:
parent
f2caf45695
commit
b75dd28a62
35 changed files with 3132 additions and 103 deletions
|
@ -9,10 +9,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func DefaultCommonOptions() []cmp.Option {
|
func DefaultCommonOptions() []cmp.Option {
|
||||||
return CommonOptions(nil, nil)
|
return CommonOptions(nil, nil, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func CommonOptions(licenseCmp LicenseComparer, locationCmp LocationComparer) []cmp.Option {
|
//nolint:funlen
|
||||||
|
func CommonOptions(licenseCmp LicenseComparer, locationCmp LocationComparer, copyrightCmp CopyrightComparer) []cmp.Option {
|
||||||
if licenseCmp == nil {
|
if licenseCmp == nil {
|
||||||
licenseCmp = DefaultLicenseComparer
|
licenseCmp = DefaultLicenseComparer
|
||||||
}
|
}
|
||||||
|
@ -21,6 +22,10 @@ func CommonOptions(licenseCmp LicenseComparer, locationCmp LocationComparer) []c
|
||||||
locationCmp = DefaultLocationComparer
|
locationCmp = DefaultLocationComparer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if copyrightCmp == nil {
|
||||||
|
copyrightCmp = DefaultCopyrightComparer
|
||||||
|
}
|
||||||
|
|
||||||
return []cmp.Option{
|
return []cmp.Option{
|
||||||
cmpopts.IgnoreFields(pkg.Package{}, "id"), // note: ID is not deterministic for test purposes
|
cmpopts.IgnoreFields(pkg.Package{}, "id"), // note: ID is not deterministic for test purposes
|
||||||
cmpopts.SortSlices(pkg.Less),
|
cmpopts.SortSlices(pkg.Less),
|
||||||
|
@ -61,11 +66,31 @@ func CommonOptions(licenseCmp LicenseComparer, locationCmp LocationComparer) []c
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
cmp.Comparer(
|
||||||
|
func(x, y pkg.CopyrightsSet) bool {
|
||||||
|
xs := x.ToSlice()
|
||||||
|
ys := y.ToSlice()
|
||||||
|
|
||||||
|
if len(xs) != len(ys) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i, xe := range xs {
|
||||||
|
ye := ys[i]
|
||||||
|
if !copyrightCmp(xe, ye) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
),
|
||||||
cmp.Comparer(
|
cmp.Comparer(
|
||||||
locationCmp,
|
locationCmp,
|
||||||
),
|
),
|
||||||
cmp.Comparer(
|
cmp.Comparer(
|
||||||
licenseCmp,
|
licenseCmp,
|
||||||
),
|
),
|
||||||
|
cmp.Comparer(
|
||||||
|
copyrightCmp,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
17
internal/cmptest/copyright.go
Normal file
17
internal/cmptest/copyright.go
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
package cmptest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/pkg"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CopyrightComparer func(x, y pkg.Copyright) bool
|
||||||
|
|
||||||
|
func DefaultCopyrightComparer(x, y pkg.Copyright) bool {
|
||||||
|
return cmp.Equal(x, y, cmp.Comparer(
|
||||||
|
func(x, y string) bool {
|
||||||
|
return x == y
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
|
@ -3,5 +3,5 @@ package internal
|
||||||
const (
|
const (
|
||||||
// JSONSchemaVersion is the current schema version output by the JSON encoder
|
// 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.
|
// 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 = "16.0.15"
|
JSONSchemaVersion = "16.0.16"
|
||||||
)
|
)
|
||||||
|
|
|
@ -351,6 +351,7 @@ func relationshipComparer(x, y []artifact.Relationship) string {
|
||||||
artifact.Relationship{},
|
artifact.Relationship{},
|
||||||
file.LocationSet{},
|
file.LocationSet{},
|
||||||
pkg.LicenseSet{},
|
pkg.LicenseSet{},
|
||||||
|
pkg.CopyrightsSet{},
|
||||||
), cmpopts.SortSlices(lessRelationships))
|
), cmpopts.SortSlices(lessRelationships))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,9 +7,9 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/anchore/syft/internal/log"
|
|
||||||
"github.com/hashicorp/go-multierror"
|
"github.com/hashicorp/go-multierror"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/internal/log"
|
||||||
"github.com/anchore/syft/internal/sbomsync"
|
"github.com/anchore/syft/internal/sbomsync"
|
||||||
"github.com/anchore/syft/syft/event/monitor"
|
"github.com/anchore/syft/syft/event/monitor"
|
||||||
"github.com/anchore/syft/syft/file"
|
"github.com/anchore/syft/syft/file"
|
||||||
|
|
2614
schema/json/schema-16.0.16.json
Normal file
2614
schema/json/schema-16.0.16.json
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||||
"$id": "anchore.io/schema/syft/json/16.0.15/document",
|
"$id": "anchore.io/schema/syft/json/16.0.16/document",
|
||||||
"$ref": "#/$defs/Document",
|
"$ref": "#/$defs/Document",
|
||||||
"$defs": {
|
"$defs": {
|
||||||
"AlpmDbEntry": {
|
"AlpmDbEntry": {
|
||||||
|
@ -370,6 +370,28 @@
|
||||||
"path"
|
"path"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"Copyright": {
|
||||||
|
"properties": {
|
||||||
|
"url": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"author": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"startYear": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"endYear": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"author",
|
||||||
|
"startYear",
|
||||||
|
"endYear"
|
||||||
|
]
|
||||||
|
},
|
||||||
"DartPubspecLockEntry": {
|
"DartPubspecLockEntry": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"name": {
|
"name": {
|
||||||
|
@ -1462,6 +1484,9 @@
|
||||||
"licenses": {
|
"licenses": {
|
||||||
"$ref": "#/$defs/licenses"
|
"$ref": "#/$defs/licenses"
|
||||||
},
|
},
|
||||||
|
"copyrights": {
|
||||||
|
"$ref": "#/$defs/copyrights"
|
||||||
|
},
|
||||||
"language": {
|
"language": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
@ -1626,6 +1651,7 @@
|
||||||
"foundBy",
|
"foundBy",
|
||||||
"locations",
|
"locations",
|
||||||
"licenses",
|
"licenses",
|
||||||
|
"copyrights",
|
||||||
"language",
|
"language",
|
||||||
"cpes",
|
"cpes",
|
||||||
"purl"
|
"purl"
|
||||||
|
@ -2566,6 +2592,12 @@
|
||||||
"pluginInstallDirectory"
|
"pluginInstallDirectory"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"copyrights": {
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/$defs/Copyright"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
},
|
||||||
"cpes": {
|
"cpes": {
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/$defs/CPE"
|
"$ref": "#/$defs/CPE"
|
||||||
|
|
|
@ -444,8 +444,8 @@ func toPackages(rels *relationship.Index, catalog *pkg.Collection, sbom sbom.SBO
|
||||||
// NOASSERTION, if
|
// NOASSERTION, if
|
||||||
// (i) the SPDX document creator has made no attempt to determine this field; or
|
// (i) the SPDX document creator has made no attempt to determine this field; or
|
||||||
// (ii) the SPDX document creator has intentionally provided no information (no meaning should be implied by doing so).
|
// (ii) the SPDX document creator has intentionally provided no information (no meaning should be implied by doing so).
|
||||||
//
|
// (iii) Get the formatted copyright text if available, otherwise return NOASSERTION
|
||||||
PackageCopyrightText: noAssertion,
|
PackageCopyrightText: helpers.GetCopyrights(p.Copyrights),
|
||||||
|
|
||||||
// 7.18: Package Summary Description
|
// 7.18: Package Summary Description
|
||||||
// Cardinality: optional, one
|
// Cardinality: optional, one
|
||||||
|
|
|
@ -595,6 +595,7 @@ func Test_convertToAndFromFormat(t *testing.T) {
|
||||||
cmpopts.IgnoreUnexported(pkg.Collection{}),
|
cmpopts.IgnoreUnexported(pkg.Collection{}),
|
||||||
cmpopts.IgnoreUnexported(pkg.Package{}),
|
cmpopts.IgnoreUnexported(pkg.Package{}),
|
||||||
cmpopts.IgnoreUnexported(pkg.LicenseSet{}),
|
cmpopts.IgnoreUnexported(pkg.LicenseSet{}),
|
||||||
|
cmpopts.IgnoreUnexported(pkg.CopyrightsSet{}),
|
||||||
cmpopts.IgnoreFields(sbom.Artifacts{}, "FileMetadata", "FileDigests"),
|
cmpopts.IgnoreFields(sbom.Artifacts{}, "FileMetadata", "FileDigests"),
|
||||||
); diff != "" {
|
); diff != "" {
|
||||||
t.Fatalf("packages do not match:\n%s", diff)
|
t.Fatalf("packages do not match:\n%s", diff)
|
||||||
|
|
|
@ -48,6 +48,7 @@ func EncodeComponent(p pkg.Package) cyclonedx.Component {
|
||||||
Version: p.Version,
|
Version: p.Version,
|
||||||
PackageURL: p.PURL,
|
PackageURL: p.PURL,
|
||||||
Licenses: encodeLicenses(p),
|
Licenses: encodeLicenses(p),
|
||||||
|
Copyright: encodeCopyrights(p),
|
||||||
CPE: encodeSingleCPE(p),
|
CPE: encodeSingleCPE(p),
|
||||||
Author: encodeAuthor(p),
|
Author: encodeAuthor(p),
|
||||||
Publisher: encodePublisher(p),
|
Publisher: encodePublisher(p),
|
||||||
|
|
|
@ -187,6 +187,7 @@ func Test_encodeCompomentType(t *testing.T) {
|
||||||
Value: "go-module",
|
Value: "go-module",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Copyright: "",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -206,6 +207,8 @@ func Test_encodeCompomentType(t *testing.T) {
|
||||||
Value: "binary",
|
Value: "binary",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Copyright: "",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,11 @@ import (
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
noAssertion = "NOASSERTION"
|
||||||
|
copyrightPrefix = "Copyright"
|
||||||
|
)
|
||||||
|
|
||||||
// This should be a function that just surfaces licenses already validated in the package struct
|
// This should be a function that just surfaces licenses already validated in the package struct
|
||||||
func encodeLicenses(p pkg.Package) *cyclonedx.Licenses {
|
func encodeLicenses(p pkg.Package) *cyclonedx.Licenses {
|
||||||
spdx, other, ex := separateLicenses(p)
|
spdx, other, ex := separateLicenses(p)
|
||||||
|
@ -195,3 +200,31 @@ func reduceOuter(expression string) string {
|
||||||
|
|
||||||
return sb.String()
|
return sb.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func encodeCopyrights(p pkg.Package) string {
|
||||||
|
if p.Copyrights.Empty() {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var strArr []string
|
||||||
|
|
||||||
|
for _, c := range p.Copyrights.ToSlice() {
|
||||||
|
var sb strings.Builder
|
||||||
|
sb.WriteString(copyrightPrefix)
|
||||||
|
|
||||||
|
// Construct the string with Start Year, End Year, and Author
|
||||||
|
if c.StartYear != "" {
|
||||||
|
sb.WriteString(" " + c.StartYear)
|
||||||
|
}
|
||||||
|
if c.EndYear != "" {
|
||||||
|
sb.WriteString("-" + c.EndYear)
|
||||||
|
}
|
||||||
|
if c.Author != "" {
|
||||||
|
sb.WriteString(" " + c.Author)
|
||||||
|
}
|
||||||
|
|
||||||
|
strArr = append(strArr, sb.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(strArr, ", ")
|
||||||
|
}
|
||||||
|
|
45
syft/format/internal/spdxutil/helpers/copyright.go
Normal file
45
syft/format/internal/spdxutil/helpers/copyright.go
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
package helpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/pkg"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
noAssertion = "NOASSERTION"
|
||||||
|
copyrightPrefix = "Copyright"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetCopyrights(copyrights pkg.CopyrightsSet) string {
|
||||||
|
result := noAssertion
|
||||||
|
|
||||||
|
for _, c := range copyrights.ToSlice() {
|
||||||
|
var sb strings.Builder
|
||||||
|
|
||||||
|
sb.WriteString(copyrightPrefix)
|
||||||
|
|
||||||
|
// Start Year
|
||||||
|
if c.StartYear != "" {
|
||||||
|
sb.WriteString(" ")
|
||||||
|
sb.WriteString(c.StartYear)
|
||||||
|
}
|
||||||
|
|
||||||
|
// End Year
|
||||||
|
if c.EndYear != "" {
|
||||||
|
sb.WriteString("-")
|
||||||
|
sb.WriteString(c.EndYear)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Author
|
||||||
|
if c.Author != "" {
|
||||||
|
sb.WriteString(" ")
|
||||||
|
sb.WriteString(c.Author)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign the formatted string to result
|
||||||
|
result = sb.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
|
@ -14,7 +14,7 @@
|
||||||
},
|
},
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"SPDXID": "SPDXRef-Package-files-analyzed-false-7d37ba9d2f7c574b",
|
"SPDXID": "SPDXRef-Package-files-analyzed-false-0950a383541717dc",
|
||||||
"copyrightText": "NOASSERTION",
|
"copyrightText": "NOASSERTION",
|
||||||
"downloadLocation": "NOASSERTION",
|
"downloadLocation": "NOASSERTION",
|
||||||
"filesAnalyzed": false,
|
"filesAnalyzed": false,
|
||||||
|
@ -27,7 +27,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "files-analyzed-true",
|
"name": "files-analyzed-true",
|
||||||
"SPDXID": "SPDXRef-Package-files-analyzed-true-035066c2086b8bb4",
|
"SPDXID": "SPDXRef-Package-files-analyzed-true-1d0a8d923f0cd238",
|
||||||
"versionInfo": "v1",
|
"versionInfo": "v1",
|
||||||
"supplier": "NOASSERTION",
|
"supplier": "NOASSERTION",
|
||||||
"downloadLocation": "NOASSERTION",
|
"downloadLocation": "NOASSERTION",
|
||||||
|
@ -77,18 +77,18 @@
|
||||||
],
|
],
|
||||||
"relationships": [
|
"relationships": [
|
||||||
{
|
{
|
||||||
"spdxElementId": "SPDXRef-Package-files-analyzed-true-035066c2086b8bb4",
|
"spdxElementId": "SPDXRef-Package-files-analyzed-true-1d0a8d923f0cd238",
|
||||||
"relatedSpdxElement": "SPDXRef-File-some-file-2c5bc344430decac",
|
"relatedSpdxElement": "SPDXRef-File-some-file-2c5bc344430decac",
|
||||||
"relationshipType": "CONTAINS"
|
"relationshipType": "CONTAINS"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"spdxElementId": "SPDXRef-DocumentRoot-Unknown-",
|
"spdxElementId": "SPDXRef-DocumentRoot-Unknown-",
|
||||||
"relatedSpdxElement": "SPDXRef-Package-files-analyzed-false-7d37ba9d2f7c574b",
|
"relatedSpdxElement": "SPDXRef-Package-files-analyzed-false-0950a383541717dc",
|
||||||
"relationshipType": "CONTAINS"
|
"relationshipType": "CONTAINS"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"spdxElementId": "SPDXRef-DocumentRoot-Unknown-",
|
"spdxElementId": "SPDXRef-DocumentRoot-Unknown-",
|
||||||
"relatedSpdxElement": "SPDXRef-Package-files-analyzed-true-035066c2086b8bb4",
|
"relatedSpdxElement": "SPDXRef-Package-files-analyzed-true-1d0a8d923f0cd238",
|
||||||
"relationshipType": "CONTAINS"
|
"relationshipType": "CONTAINS"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "package-1",
|
"name": "package-1",
|
||||||
"SPDXID": "SPDXRef-Package-python-package-1-5a2b1ae000fcb51e",
|
"SPDXID": "SPDXRef-Package-python-package-1-f7fdfcfa4ca6e742",
|
||||||
"versionInfo": "1.0.1",
|
"versionInfo": "1.0.1",
|
||||||
"supplier": "NOASSERTION",
|
"supplier": "NOASSERTION",
|
||||||
"downloadLocation": "NOASSERTION",
|
"downloadLocation": "NOASSERTION",
|
||||||
|
@ -39,7 +39,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "package-2",
|
"name": "package-2",
|
||||||
"SPDXID": "SPDXRef-Package-deb-package-2-39392bb5e270f669",
|
"SPDXID": "SPDXRef-Package-deb-package-2-062f404587213e8b",
|
||||||
"versionInfo": "2.0.1",
|
"versionInfo": "2.0.1",
|
||||||
"supplier": "NOASSERTION",
|
"supplier": "NOASSERTION",
|
||||||
"downloadLocation": "NOASSERTION",
|
"downloadLocation": "NOASSERTION",
|
||||||
|
@ -75,12 +75,12 @@
|
||||||
"relationships": [
|
"relationships": [
|
||||||
{
|
{
|
||||||
"spdxElementId": "SPDXRef-DocumentRoot-Directory-some-path",
|
"spdxElementId": "SPDXRef-DocumentRoot-Directory-some-path",
|
||||||
"relatedSpdxElement": "SPDXRef-Package-python-package-1-5a2b1ae000fcb51e",
|
"relatedSpdxElement": "SPDXRef-Package-python-package-1-f7fdfcfa4ca6e742",
|
||||||
"relationshipType": "CONTAINS"
|
"relationshipType": "CONTAINS"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"spdxElementId": "SPDXRef-DocumentRoot-Directory-some-path",
|
"spdxElementId": "SPDXRef-DocumentRoot-Directory-some-path",
|
||||||
"relatedSpdxElement": "SPDXRef-Package-deb-package-2-39392bb5e270f669",
|
"relatedSpdxElement": "SPDXRef-Package-deb-package-2-062f404587213e8b",
|
||||||
"relationshipType": "CONTAINS"
|
"relationshipType": "CONTAINS"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "package-1",
|
"name": "package-1",
|
||||||
"SPDXID": "SPDXRef-Package-python-package-1-c5cf7ac34cbca450",
|
"SPDXID": "SPDXRef-Package-python-package-1-69910a93dc37ffb4",
|
||||||
"versionInfo": "1.0.1",
|
"versionInfo": "1.0.1",
|
||||||
"supplier": "NOASSERTION",
|
"supplier": "NOASSERTION",
|
||||||
"downloadLocation": "NOASSERTION",
|
"downloadLocation": "NOASSERTION",
|
||||||
|
@ -39,7 +39,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "package-2",
|
"name": "package-2",
|
||||||
"SPDXID": "SPDXRef-Package-deb-package-2-4b756c6f6fb127a3",
|
"SPDXID": "SPDXRef-Package-deb-package-2-fe989317bb1cbb62",
|
||||||
"versionInfo": "2.0.1",
|
"versionInfo": "2.0.1",
|
||||||
"supplier": "NOASSERTION",
|
"supplier": "NOASSERTION",
|
||||||
"downloadLocation": "NOASSERTION",
|
"downloadLocation": "NOASSERTION",
|
||||||
|
@ -89,12 +89,12 @@
|
||||||
"relationships": [
|
"relationships": [
|
||||||
{
|
{
|
||||||
"spdxElementId": "SPDXRef-DocumentRoot-Image-user-image-input",
|
"spdxElementId": "SPDXRef-DocumentRoot-Image-user-image-input",
|
||||||
"relatedSpdxElement": "SPDXRef-Package-python-package-1-c5cf7ac34cbca450",
|
"relatedSpdxElement": "SPDXRef-Package-python-package-1-69910a93dc37ffb4",
|
||||||
"relationshipType": "CONTAINS"
|
"relationshipType": "CONTAINS"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"spdxElementId": "SPDXRef-DocumentRoot-Image-user-image-input",
|
"spdxElementId": "SPDXRef-DocumentRoot-Image-user-image-input",
|
||||||
"relatedSpdxElement": "SPDXRef-Package-deb-package-2-4b756c6f6fb127a3",
|
"relatedSpdxElement": "SPDXRef-Package-deb-package-2-fe989317bb1cbb62",
|
||||||
"relationshipType": "CONTAINS"
|
"relationshipType": "CONTAINS"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "package-1",
|
"name": "package-1",
|
||||||
"SPDXID": "SPDXRef-Package-python-package-1-c5cf7ac34cbca450",
|
"SPDXID": "SPDXRef-Package-python-package-1-69910a93dc37ffb4",
|
||||||
"versionInfo": "1.0.1",
|
"versionInfo": "1.0.1",
|
||||||
"supplier": "NOASSERTION",
|
"supplier": "NOASSERTION",
|
||||||
"downloadLocation": "NOASSERTION",
|
"downloadLocation": "NOASSERTION",
|
||||||
|
@ -39,7 +39,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "package-2",
|
"name": "package-2",
|
||||||
"SPDXID": "SPDXRef-Package-deb-package-2-4b756c6f6fb127a3",
|
"SPDXID": "SPDXRef-Package-deb-package-2-fe989317bb1cbb62",
|
||||||
"versionInfo": "2.0.1",
|
"versionInfo": "2.0.1",
|
||||||
"supplier": "NOASSERTION",
|
"supplier": "NOASSERTION",
|
||||||
"downloadLocation": "NOASSERTION",
|
"downloadLocation": "NOASSERTION",
|
||||||
|
@ -198,43 +198,43 @@
|
||||||
],
|
],
|
||||||
"relationships": [
|
"relationships": [
|
||||||
{
|
{
|
||||||
"spdxElementId": "SPDXRef-Package-python-package-1-c5cf7ac34cbca450",
|
"spdxElementId": "SPDXRef-Package-python-package-1-69910a93dc37ffb4",
|
||||||
"relatedSpdxElement": "SPDXRef-File-f1-5265a4dde3edbf7c",
|
"relatedSpdxElement": "SPDXRef-File-f1-5265a4dde3edbf7c",
|
||||||
"relationshipType": "CONTAINS"
|
"relationshipType": "CONTAINS"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"spdxElementId": "SPDXRef-Package-python-package-1-c5cf7ac34cbca450",
|
"spdxElementId": "SPDXRef-Package-python-package-1-69910a93dc37ffb4",
|
||||||
"relatedSpdxElement": "SPDXRef-File-z1-f5-839d99ee67d9d174",
|
"relatedSpdxElement": "SPDXRef-File-z1-f5-839d99ee67d9d174",
|
||||||
"relationshipType": "CONTAINS"
|
"relationshipType": "CONTAINS"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"spdxElementId": "SPDXRef-Package-python-package-1-c5cf7ac34cbca450",
|
"spdxElementId": "SPDXRef-Package-python-package-1-69910a93dc37ffb4",
|
||||||
"relatedSpdxElement": "SPDXRef-File-a1-f6-9c2f7510199b17f6",
|
"relatedSpdxElement": "SPDXRef-File-a1-f6-9c2f7510199b17f6",
|
||||||
"relationshipType": "CONTAINS"
|
"relationshipType": "CONTAINS"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"spdxElementId": "SPDXRef-Package-python-package-1-c5cf7ac34cbca450",
|
"spdxElementId": "SPDXRef-Package-python-package-1-69910a93dc37ffb4",
|
||||||
"relatedSpdxElement": "SPDXRef-File-d2-f4-c641caa71518099f",
|
"relatedSpdxElement": "SPDXRef-File-d2-f4-c641caa71518099f",
|
||||||
"relationshipType": "CONTAINS"
|
"relationshipType": "CONTAINS"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"spdxElementId": "SPDXRef-Package-python-package-1-c5cf7ac34cbca450",
|
"spdxElementId": "SPDXRef-Package-python-package-1-69910a93dc37ffb4",
|
||||||
"relatedSpdxElement": "SPDXRef-File-d1-f3-c6f5b29dca12661f",
|
"relatedSpdxElement": "SPDXRef-File-d1-f3-c6f5b29dca12661f",
|
||||||
"relationshipType": "CONTAINS"
|
"relationshipType": "CONTAINS"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"spdxElementId": "SPDXRef-Package-python-package-1-c5cf7ac34cbca450",
|
"spdxElementId": "SPDXRef-Package-python-package-1-69910a93dc37ffb4",
|
||||||
"relatedSpdxElement": "SPDXRef-File-f2-f9e49132a4b96ccd",
|
"relatedSpdxElement": "SPDXRef-File-f2-f9e49132a4b96ccd",
|
||||||
"relationshipType": "CONTAINS"
|
"relationshipType": "CONTAINS"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"spdxElementId": "SPDXRef-DocumentRoot-Image-user-image-input",
|
"spdxElementId": "SPDXRef-DocumentRoot-Image-user-image-input",
|
||||||
"relatedSpdxElement": "SPDXRef-Package-python-package-1-c5cf7ac34cbca450",
|
"relatedSpdxElement": "SPDXRef-Package-python-package-1-69910a93dc37ffb4",
|
||||||
"relationshipType": "CONTAINS"
|
"relationshipType": "CONTAINS"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"spdxElementId": "SPDXRef-DocumentRoot-Image-user-image-input",
|
"spdxElementId": "SPDXRef-DocumentRoot-Image-user-image-input",
|
||||||
"relatedSpdxElement": "SPDXRef-Package-deb-package-2-4b756c6f6fb127a3",
|
"relatedSpdxElement": "SPDXRef-Package-deb-package-2-fe989317bb1cbb62",
|
||||||
"relationshipType": "CONTAINS"
|
"relationshipType": "CONTAINS"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -22,7 +22,7 @@ PackageLicenseDeclared: NOASSERTION
|
||||||
##### Package: @at-sign
|
##### Package: @at-sign
|
||||||
|
|
||||||
PackageName: @at-sign
|
PackageName: @at-sign
|
||||||
SPDXID: SPDXRef-Package--at-sign-1c8c811ea5b1cd46
|
SPDXID: SPDXRef-Package--at-sign-ec109f3d122ef1db
|
||||||
PackageSupplier: NOASSERTION
|
PackageSupplier: NOASSERTION
|
||||||
PackageDownloadLocation: NOASSERTION
|
PackageDownloadLocation: NOASSERTION
|
||||||
FilesAnalyzed: false
|
FilesAnalyzed: false
|
||||||
|
@ -34,7 +34,7 @@ PackageCopyrightText: NOASSERTION
|
||||||
##### Package: some/slashes
|
##### Package: some/slashes
|
||||||
|
|
||||||
PackageName: some/slashes
|
PackageName: some/slashes
|
||||||
SPDXID: SPDXRef-Package-some-slashes-8a8e95924316c66b
|
SPDXID: SPDXRef-Package-some-slashes-8a21771e3392022f
|
||||||
PackageSupplier: NOASSERTION
|
PackageSupplier: NOASSERTION
|
||||||
PackageDownloadLocation: NOASSERTION
|
PackageDownloadLocation: NOASSERTION
|
||||||
FilesAnalyzed: false
|
FilesAnalyzed: false
|
||||||
|
@ -46,7 +46,7 @@ PackageCopyrightText: NOASSERTION
|
||||||
##### Package: under_scores
|
##### Package: under_scores
|
||||||
|
|
||||||
PackageName: under_scores
|
PackageName: under_scores
|
||||||
SPDXID: SPDXRef-Package-under-scores-883703d950ec00f3
|
SPDXID: SPDXRef-Package-under-scores-5db453bf3f332f99
|
||||||
PackageSupplier: NOASSERTION
|
PackageSupplier: NOASSERTION
|
||||||
PackageDownloadLocation: NOASSERTION
|
PackageDownloadLocation: NOASSERTION
|
||||||
FilesAnalyzed: false
|
FilesAnalyzed: false
|
||||||
|
@ -57,8 +57,8 @@ PackageCopyrightText: NOASSERTION
|
||||||
|
|
||||||
##### Relationships
|
##### Relationships
|
||||||
|
|
||||||
Relationship: SPDXRef-DocumentRoot-Directory-foobar-baz CONTAINS SPDXRef-Package--at-sign-1c8c811ea5b1cd46
|
Relationship: SPDXRef-DocumentRoot-Directory-foobar-baz CONTAINS SPDXRef-Package--at-sign-ec109f3d122ef1db
|
||||||
Relationship: SPDXRef-DocumentRoot-Directory-foobar-baz CONTAINS SPDXRef-Package-some-slashes-8a8e95924316c66b
|
Relationship: SPDXRef-DocumentRoot-Directory-foobar-baz CONTAINS SPDXRef-Package-some-slashes-8a21771e3392022f
|
||||||
Relationship: SPDXRef-DocumentRoot-Directory-foobar-baz CONTAINS SPDXRef-Package-under-scores-883703d950ec00f3
|
Relationship: SPDXRef-DocumentRoot-Directory-foobar-baz CONTAINS SPDXRef-Package-under-scores-5db453bf3f332f99
|
||||||
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-DocumentRoot-Directory-foobar-baz
|
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-DocumentRoot-Directory-foobar-baz
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,7 @@ ExternalRef: PACKAGE-MANAGER purl pkg:oci/user-image-input@sha256:2731251dc34951
|
||||||
##### Package: package-2
|
##### Package: package-2
|
||||||
|
|
||||||
PackageName: package-2
|
PackageName: package-2
|
||||||
SPDXID: SPDXRef-Package-deb-package-2-4b756c6f6fb127a3
|
SPDXID: SPDXRef-Package-deb-package-2-fe989317bb1cbb62
|
||||||
PackageVersion: 2.0.1
|
PackageVersion: 2.0.1
|
||||||
PackageSupplier: NOASSERTION
|
PackageSupplier: NOASSERTION
|
||||||
PackageDownloadLocation: NOASSERTION
|
PackageDownloadLocation: NOASSERTION
|
||||||
|
@ -84,7 +84,7 @@ ExternalRef: PACKAGE-MANAGER purl pkg:deb/debian/package-2@2.0.1
|
||||||
##### Package: package-1
|
##### Package: package-1
|
||||||
|
|
||||||
PackageName: package-1
|
PackageName: package-1
|
||||||
SPDXID: SPDXRef-Package-python-package-1-c5cf7ac34cbca450
|
SPDXID: SPDXRef-Package-python-package-1-69910a93dc37ffb4
|
||||||
PackageVersion: 1.0.1
|
PackageVersion: 1.0.1
|
||||||
PackageSupplier: NOASSERTION
|
PackageSupplier: NOASSERTION
|
||||||
PackageDownloadLocation: NOASSERTION
|
PackageDownloadLocation: NOASSERTION
|
||||||
|
@ -98,13 +98,13 @@ ExternalRef: PACKAGE-MANAGER purl a-purl-1
|
||||||
|
|
||||||
##### Relationships
|
##### Relationships
|
||||||
|
|
||||||
Relationship: SPDXRef-Package-python-package-1-c5cf7ac34cbca450 CONTAINS SPDXRef-File-f1-5265a4dde3edbf7c
|
Relationship: SPDXRef-Package-python-package-1-69910a93dc37ffb4 CONTAINS SPDXRef-File-f1-5265a4dde3edbf7c
|
||||||
Relationship: SPDXRef-Package-python-package-1-c5cf7ac34cbca450 CONTAINS SPDXRef-File-z1-f5-839d99ee67d9d174
|
Relationship: SPDXRef-Package-python-package-1-69910a93dc37ffb4 CONTAINS SPDXRef-File-z1-f5-839d99ee67d9d174
|
||||||
Relationship: SPDXRef-Package-python-package-1-c5cf7ac34cbca450 CONTAINS SPDXRef-File-a1-f6-9c2f7510199b17f6
|
Relationship: SPDXRef-Package-python-package-1-69910a93dc37ffb4 CONTAINS SPDXRef-File-a1-f6-9c2f7510199b17f6
|
||||||
Relationship: SPDXRef-Package-python-package-1-c5cf7ac34cbca450 CONTAINS SPDXRef-File-d2-f4-c641caa71518099f
|
Relationship: SPDXRef-Package-python-package-1-69910a93dc37ffb4 CONTAINS SPDXRef-File-d2-f4-c641caa71518099f
|
||||||
Relationship: SPDXRef-Package-python-package-1-c5cf7ac34cbca450 CONTAINS SPDXRef-File-d1-f3-c6f5b29dca12661f
|
Relationship: SPDXRef-Package-python-package-1-69910a93dc37ffb4 CONTAINS SPDXRef-File-d1-f3-c6f5b29dca12661f
|
||||||
Relationship: SPDXRef-Package-python-package-1-c5cf7ac34cbca450 CONTAINS SPDXRef-File-f2-f9e49132a4b96ccd
|
Relationship: SPDXRef-Package-python-package-1-69910a93dc37ffb4 CONTAINS SPDXRef-File-f2-f9e49132a4b96ccd
|
||||||
Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-python-package-1-c5cf7ac34cbca450
|
Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-python-package-1-69910a93dc37ffb4
|
||||||
Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-deb-package-2-4b756c6f6fb127a3
|
Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-deb-package-2-fe989317bb1cbb62
|
||||||
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-DocumentRoot-Image-user-image-input
|
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-DocumentRoot-Image-user-image-input
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ PackageLicenseDeclared: NOASSERTION
|
||||||
##### Package: package-2
|
##### Package: package-2
|
||||||
|
|
||||||
PackageName: package-2
|
PackageName: package-2
|
||||||
SPDXID: SPDXRef-Package-deb-package-2-39392bb5e270f669
|
SPDXID: SPDXRef-Package-deb-package-2-062f404587213e8b
|
||||||
PackageVersion: 2.0.1
|
PackageVersion: 2.0.1
|
||||||
PackageSupplier: NOASSERTION
|
PackageSupplier: NOASSERTION
|
||||||
PackageDownloadLocation: NOASSERTION
|
PackageDownloadLocation: NOASSERTION
|
||||||
|
@ -37,7 +37,7 @@ ExternalRef: PACKAGE-MANAGER purl pkg:deb/debian/package-2@2.0.1
|
||||||
##### Package: package-1
|
##### Package: package-1
|
||||||
|
|
||||||
PackageName: package-1
|
PackageName: package-1
|
||||||
SPDXID: SPDXRef-Package-python-package-1-5a2b1ae000fcb51e
|
SPDXID: SPDXRef-Package-python-package-1-f7fdfcfa4ca6e742
|
||||||
PackageVersion: 1.0.1
|
PackageVersion: 1.0.1
|
||||||
PackageSupplier: NOASSERTION
|
PackageSupplier: NOASSERTION
|
||||||
PackageDownloadLocation: NOASSERTION
|
PackageDownloadLocation: NOASSERTION
|
||||||
|
@ -51,7 +51,7 @@ ExternalRef: PACKAGE-MANAGER purl a-purl-2
|
||||||
|
|
||||||
##### Relationships
|
##### Relationships
|
||||||
|
|
||||||
Relationship: SPDXRef-DocumentRoot-Directory-some-path CONTAINS SPDXRef-Package-python-package-1-5a2b1ae000fcb51e
|
Relationship: SPDXRef-DocumentRoot-Directory-some-path CONTAINS SPDXRef-Package-python-package-1-f7fdfcfa4ca6e742
|
||||||
Relationship: SPDXRef-DocumentRoot-Directory-some-path CONTAINS SPDXRef-Package-deb-package-2-39392bb5e270f669
|
Relationship: SPDXRef-DocumentRoot-Directory-some-path CONTAINS SPDXRef-Package-deb-package-2-062f404587213e8b
|
||||||
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-DocumentRoot-Directory-some-path
|
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-DocumentRoot-Directory-some-path
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ ExternalRef: PACKAGE-MANAGER purl pkg:oci/user-image-input@sha256:2731251dc34951
|
||||||
##### Package: package-2
|
##### Package: package-2
|
||||||
|
|
||||||
PackageName: package-2
|
PackageName: package-2
|
||||||
SPDXID: SPDXRef-Package-deb-package-2-4b756c6f6fb127a3
|
SPDXID: SPDXRef-Package-deb-package-2-fe989317bb1cbb62
|
||||||
PackageVersion: 2.0.1
|
PackageVersion: 2.0.1
|
||||||
PackageSupplier: NOASSERTION
|
PackageSupplier: NOASSERTION
|
||||||
PackageDownloadLocation: NOASSERTION
|
PackageDownloadLocation: NOASSERTION
|
||||||
|
@ -40,7 +40,7 @@ ExternalRef: PACKAGE-MANAGER purl pkg:deb/debian/package-2@2.0.1
|
||||||
##### Package: package-1
|
##### Package: package-1
|
||||||
|
|
||||||
PackageName: package-1
|
PackageName: package-1
|
||||||
SPDXID: SPDXRef-Package-python-package-1-c5cf7ac34cbca450
|
SPDXID: SPDXRef-Package-python-package-1-69910a93dc37ffb4
|
||||||
PackageVersion: 1.0.1
|
PackageVersion: 1.0.1
|
||||||
PackageSupplier: NOASSERTION
|
PackageSupplier: NOASSERTION
|
||||||
PackageDownloadLocation: NOASSERTION
|
PackageDownloadLocation: NOASSERTION
|
||||||
|
@ -54,7 +54,7 @@ ExternalRef: PACKAGE-MANAGER purl a-purl-1
|
||||||
|
|
||||||
##### Relationships
|
##### Relationships
|
||||||
|
|
||||||
Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-python-package-1-c5cf7ac34cbca450
|
Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-python-package-1-69910a93dc37ffb4
|
||||||
Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-deb-package-2-4b756c6f6fb127a3
|
Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-deb-package-2-fe989317bb1cbb62
|
||||||
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-DocumentRoot-Image-user-image-input
|
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-DocumentRoot-Image-user-image-input
|
||||||
|
|
||||||
|
|
|
@ -24,16 +24,17 @@ type Package struct {
|
||||||
|
|
||||||
// PackageBasicData contains non-ambiguous values (type-wise) from pkg.Package.
|
// PackageBasicData contains non-ambiguous values (type-wise) from pkg.Package.
|
||||||
type PackageBasicData struct {
|
type PackageBasicData struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
Type pkg.Type `json:"type"`
|
Type pkg.Type `json:"type"`
|
||||||
FoundBy string `json:"foundBy"`
|
FoundBy string `json:"foundBy"`
|
||||||
Locations []file.Location `json:"locations"`
|
Locations []file.Location `json:"locations"`
|
||||||
Licenses licenses `json:"licenses"`
|
Licenses licenses `json:"licenses"`
|
||||||
Language pkg.Language `json:"language"`
|
Copyrights copyrights `json:"copyrights"`
|
||||||
CPEs cpes `json:"cpes"`
|
Language pkg.Language `json:"language"`
|
||||||
PURL string `json:"purl"`
|
CPEs cpes `json:"cpes"`
|
||||||
|
PURL string `json:"purl"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type cpes []CPE
|
type cpes []CPE
|
||||||
|
@ -53,6 +54,15 @@ type License struct {
|
||||||
Locations []file.Location `json:"locations"`
|
Locations []file.Location `json:"locations"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type copyrights []Copyright
|
||||||
|
|
||||||
|
type Copyright struct {
|
||||||
|
URL string `json:"url,omitempty"`
|
||||||
|
Author string `json:"author"`
|
||||||
|
StartYear string `json:"startYear"`
|
||||||
|
EndYear string `json:"endYear"`
|
||||||
|
}
|
||||||
|
|
||||||
func newModelLicensesFromValues(licenses []string) (ml []License) {
|
func newModelLicensesFromValues(licenses []string) (ml []License) {
|
||||||
for _, v := range licenses {
|
for _, v := range licenses {
|
||||||
expression, err := license.ParseExpression(v)
|
expression, err := license.ParseExpression(v)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"artifacts": [
|
"artifacts": [
|
||||||
{
|
{
|
||||||
"id": "5a2b1ae000fcb51e",
|
"id": "f7fdfcfa4ca6e742",
|
||||||
"name": "package-1",
|
"name": "package-1",
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"type": "python",
|
"type": "python",
|
||||||
|
@ -21,6 +21,7 @@
|
||||||
"locations": []
|
"locations": []
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"copyrights": [],
|
||||||
"language": "python",
|
"language": "python",
|
||||||
"cpes": [
|
"cpes": [
|
||||||
{
|
{
|
||||||
|
@ -44,7 +45,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "39392bb5e270f669",
|
"id": "062f404587213e8b",
|
||||||
"name": "package-2",
|
"name": "package-2",
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"type": "deb",
|
"type": "deb",
|
||||||
|
@ -56,6 +57,7 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"licenses": [],
|
"licenses": [],
|
||||||
|
"copyrights": [],
|
||||||
"language": "",
|
"language": "",
|
||||||
"cpes": [
|
"cpes": [
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"artifacts": [
|
"artifacts": [
|
||||||
{
|
{
|
||||||
"id": "ad3ecac55fe1c30f",
|
"id": "ecf423ccf313f850",
|
||||||
"name": "package-1",
|
"name": "package-1",
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"type": "python",
|
"type": "python",
|
||||||
|
@ -21,6 +21,7 @@
|
||||||
"locations": []
|
"locations": []
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"copyrights": [],
|
||||||
"language": "python",
|
"language": "python",
|
||||||
"cpes": [
|
"cpes": [
|
||||||
{
|
{
|
||||||
|
@ -40,7 +41,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "fa4ec37eccd65756",
|
"id": "b4d209e1bb8d83cb",
|
||||||
"name": "package-2",
|
"name": "package-2",
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"type": "deb",
|
"type": "deb",
|
||||||
|
@ -52,6 +53,7 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"licenses": [],
|
"licenses": [],
|
||||||
|
"copyrights": [],
|
||||||
"language": "",
|
"language": "",
|
||||||
"cpes": [
|
"cpes": [
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"artifacts": [
|
"artifacts": [
|
||||||
{
|
{
|
||||||
"id": "c5cf7ac34cbca450",
|
"id": "69910a93dc37ffb4",
|
||||||
"name": "package-1",
|
"name": "package-1",
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"type": "python",
|
"type": "python",
|
||||||
|
@ -22,6 +22,7 @@
|
||||||
"locations": []
|
"locations": []
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"copyrights": [],
|
||||||
"language": "python",
|
"language": "python",
|
||||||
"cpes": [
|
"cpes": [
|
||||||
{
|
{
|
||||||
|
@ -41,7 +42,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "4b756c6f6fb127a3",
|
"id": "fe989317bb1cbb62",
|
||||||
"name": "package-2",
|
"name": "package-2",
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"type": "deb",
|
"type": "deb",
|
||||||
|
@ -54,6 +55,7 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"licenses": [],
|
"licenses": [],
|
||||||
|
"copyrights": [],
|
||||||
"language": "",
|
"language": "",
|
||||||
"cpes": [
|
"cpes": [
|
||||||
{
|
{
|
||||||
|
|
|
@ -233,6 +233,18 @@ func toLicenseModel(pkgLicenses []pkg.License) (modelLicenses []model.License) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func toCopyrightModel(pkgCopyrights []pkg.Copyright) (modelCopyrights []model.Copyright) {
|
||||||
|
for _, l := range pkgCopyrights {
|
||||||
|
modelCopyrights = append(modelCopyrights, model.Copyright{
|
||||||
|
URL: l.URL,
|
||||||
|
Author: l.Author,
|
||||||
|
StartYear: l.StartYear,
|
||||||
|
EndYear: l.EndYear,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// toPackageModel crates a new Package from the given pkg.Package.
|
// toPackageModel crates a new Package from the given pkg.Package.
|
||||||
func toPackageModel(p pkg.Package, cfg EncoderConfig) model.Package {
|
func toPackageModel(p pkg.Package, cfg EncoderConfig) model.Package {
|
||||||
var cpes = make([]model.CPE, len(p.CPEs))
|
var cpes = make([]model.CPE, len(p.CPEs))
|
||||||
|
@ -251,18 +263,24 @@ func toPackageModel(p pkg.Package, cfg EncoderConfig) model.Package {
|
||||||
licenses = toLicenseModel(p.Licenses.ToSlice())
|
licenses = toLicenseModel(p.Licenses.ToSlice())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var copyrights = make([]model.Copyright, 0)
|
||||||
|
if !p.Copyrights.Empty() {
|
||||||
|
copyrights = toCopyrightModel(p.Copyrights.ToSlice())
|
||||||
|
}
|
||||||
|
|
||||||
return model.Package{
|
return model.Package{
|
||||||
PackageBasicData: model.PackageBasicData{
|
PackageBasicData: model.PackageBasicData{
|
||||||
ID: string(p.ID()),
|
ID: string(p.ID()),
|
||||||
Name: p.Name,
|
Name: p.Name,
|
||||||
Version: p.Version,
|
Version: p.Version,
|
||||||
Type: p.Type,
|
Type: p.Type,
|
||||||
FoundBy: p.FoundBy,
|
FoundBy: p.FoundBy,
|
||||||
Locations: p.Locations.ToSlice(),
|
Locations: p.Locations.ToSlice(),
|
||||||
Licenses: licenses,
|
Licenses: licenses,
|
||||||
Language: p.Language,
|
Copyrights: copyrights,
|
||||||
CPEs: cpes,
|
Language: p.Language,
|
||||||
PURL: p.PURL,
|
CPEs: cpes,
|
||||||
|
PURL: p.PURL,
|
||||||
},
|
},
|
||||||
PackageCustomData: model.PackageCustomData{
|
PackageCustomData: model.PackageCustomData{
|
||||||
MetadataType: metadataType(p.Metadata, cfg.Legacy),
|
MetadataType: metadataType(p.Metadata, cfg.Legacy),
|
||||||
|
|
|
@ -162,6 +162,19 @@ func toSyftLicenses(m []model.License) (p []pkg.License) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func toSyftCopyrights(m []model.Copyright) (p []pkg.Copyright) {
|
||||||
|
for _, l := range m {
|
||||||
|
p = append(p, pkg.Copyright{
|
||||||
|
URL: l.URL,
|
||||||
|
Author: l.Author,
|
||||||
|
StartYear: l.StartYear,
|
||||||
|
EndYear: l.EndYear,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func toSyftFileType(ty string) stereoscopeFile.Type {
|
func toSyftFileType(ty string) stereoscopeFile.Type {
|
||||||
switch ty {
|
switch ty {
|
||||||
case "SymbolicLink":
|
case "SymbolicLink":
|
||||||
|
@ -331,16 +344,17 @@ func toSyftPackage(p model.Package, idAliases map[string]string) pkg.Package {
|
||||||
}
|
}
|
||||||
|
|
||||||
out := pkg.Package{
|
out := pkg.Package{
|
||||||
Name: p.Name,
|
Name: p.Name,
|
||||||
Version: p.Version,
|
Version: p.Version,
|
||||||
FoundBy: p.FoundBy,
|
FoundBy: p.FoundBy,
|
||||||
Locations: file.NewLocationSet(p.Locations...),
|
Locations: file.NewLocationSet(p.Locations...),
|
||||||
Licenses: pkg.NewLicenseSet(toSyftLicenses(p.Licenses)...),
|
Licenses: pkg.NewLicenseSet(toSyftLicenses(p.Licenses)...),
|
||||||
Language: p.Language,
|
Copyrights: pkg.NewCopyrightSet(toSyftCopyrights(p.Copyrights)...),
|
||||||
Type: p.Type,
|
Language: p.Language,
|
||||||
CPEs: cpes,
|
Type: p.Type,
|
||||||
PURL: p.PURL,
|
CPEs: cpes,
|
||||||
Metadata: p.Metadata,
|
PURL: p.PURL,
|
||||||
|
Metadata: p.Metadata,
|
||||||
}
|
}
|
||||||
|
|
||||||
// we don't know if this package ID is truly unique, however, we need to trust the user input in case there are
|
// we don't know if this package ID is truly unique, however, we need to trust the user input in case there are
|
||||||
|
|
|
@ -71,6 +71,8 @@ func findMetadataDefinitionNames(paths ...string) ([]string, error) {
|
||||||
// remove known exceptions, that is, types exported in the pkg Package that are not used
|
// remove known exceptions, that is, types exported in the pkg Package that are not used
|
||||||
// in a metadata type but are not metadata types themselves.
|
// in a metadata type but are not metadata types themselves.
|
||||||
names.Remove("Licenses", "KeyValue")
|
names.Remove("Licenses", "KeyValue")
|
||||||
|
names.Remove("Copyrights", "KeyValue")
|
||||||
|
names.Remove("CopyrightsSet", "KeyValue")
|
||||||
|
|
||||||
strNames := names.List()
|
strNames := names.List()
|
||||||
sort.Strings(strNames)
|
sort.Strings(strNames)
|
||||||
|
|
|
@ -157,7 +157,7 @@ func Test_newELFPackage(t *testing.T) {
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
actual := newELFPackage(test.metadata, file.NewLocationSet())
|
actual := newELFPackage(test.metadata, file.NewLocationSet())
|
||||||
if diff := cmp.Diff(test.expected, actual, cmpopts.IgnoreFields(pkg.Package{}, "id"), cmpopts.IgnoreUnexported(pkg.Package{}, file.LocationSet{}, pkg.LicenseSet{})); diff != "" {
|
if diff := cmp.Diff(test.expected, actual, cmpopts.IgnoreFields(pkg.Package{}, "id"), cmpopts.IgnoreUnexported(pkg.Package{}, file.LocationSet{}, pkg.LicenseSet{}, pkg.CopyrightsSet{})); diff != "" {
|
||||||
t.Errorf("newELFPackage() mismatch (-want +got):\n%s", diff)
|
t.Errorf("newELFPackage() mismatch (-want +got):\n%s", diff)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -42,6 +42,7 @@ type CatalogTester struct {
|
||||||
compareOptions []cmp.Option
|
compareOptions []cmp.Option
|
||||||
locationComparer cmptest.LocationComparer
|
locationComparer cmptest.LocationComparer
|
||||||
licenseComparer cmptest.LicenseComparer
|
licenseComparer cmptest.LicenseComparer
|
||||||
|
copyrightComparer cmptest.CopyrightComparer
|
||||||
packageStringer func(pkg.Package) string
|
packageStringer func(pkg.Package) string
|
||||||
customAssertions []func(t *testing.T, pkgs []pkg.Package, relationships []artifact.Relationship)
|
customAssertions []func(t *testing.T, pkgs []pkg.Package, relationships []artifact.Relationship)
|
||||||
}
|
}
|
||||||
|
@ -267,7 +268,7 @@ func (p *CatalogTester) TestCataloger(t *testing.T, cataloger pkg.Cataloger) {
|
||||||
func (p *CatalogTester) assertPkgs(t *testing.T, pkgs []pkg.Package, relationships []artifact.Relationship) {
|
func (p *CatalogTester) assertPkgs(t *testing.T, pkgs []pkg.Package, relationships []artifact.Relationship) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
p.compareOptions = append(p.compareOptions, cmptest.CommonOptions(p.licenseComparer, p.locationComparer)...)
|
p.compareOptions = append(p.compareOptions, cmptest.CommonOptions(p.licenseComparer, p.locationComparer, p.copyrightComparer)...)
|
||||||
|
|
||||||
{
|
{
|
||||||
r := cmptest.NewDiffReporter()
|
r := cmptest.NewDiffReporter()
|
||||||
|
@ -320,6 +321,7 @@ func TestFileParserWithEnv(t *testing.T, fixturePath string, parser generic.Pars
|
||||||
NewCatalogTester().FromFile(t, fixturePath).WithEnv(env).Expects(expectedPkgs, expectedRelationships).TestParser(t, parser)
|
NewCatalogTester().FromFile(t, fixturePath).WithEnv(env).Expects(expectedPkgs, expectedRelationships).TestParser(t, parser)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//nolint:funlen
|
||||||
func AssertPackagesEqual(t *testing.T, a, b pkg.Package) {
|
func AssertPackagesEqual(t *testing.T, a, b pkg.Package) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
opts := []cmp.Option{
|
opts := []cmp.Option{
|
||||||
|
@ -360,12 +362,33 @@ func AssertPackagesEqual(t *testing.T, a, b pkg.Package) {
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
cmp.Comparer(
|
||||||
|
func(x, y pkg.CopyrightsSet) bool {
|
||||||
|
xs := x.ToSlice()
|
||||||
|
ys := y.ToSlice()
|
||||||
|
|
||||||
|
if len(xs) != len(ys) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i, xe := range xs {
|
||||||
|
ye := ys[i]
|
||||||
|
if !cmptest.DefaultCopyrightComparer(xe, ye) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
),
|
||||||
cmp.Comparer(
|
cmp.Comparer(
|
||||||
cmptest.DefaultLocationComparer,
|
cmptest.DefaultLocationComparer,
|
||||||
),
|
),
|
||||||
cmp.Comparer(
|
cmp.Comparer(
|
||||||
cmptest.DefaultLicenseComparer,
|
cmptest.DefaultLicenseComparer,
|
||||||
),
|
),
|
||||||
|
cmp.Comparer(
|
||||||
|
cmptest.DefaultCopyrightComparer,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
if diff := cmp.Diff(a, b, opts...); diff != "" {
|
if diff := cmp.Diff(a, b, opts...); diff != "" {
|
||||||
|
|
70
syft/pkg/copyright.go
Normal file
70
syft/pkg/copyright.go
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
package pkg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/scylladb/go-set/strset"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Copyright struct {
|
||||||
|
URL string `json:"url,omitempty"`
|
||||||
|
Author string `json:"author"`
|
||||||
|
StartYear string `json:"startYear"`
|
||||||
|
EndYear string `json:"endYear"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Copyrights []Copyright
|
||||||
|
|
||||||
|
func (c Copyrights) Len() int {
|
||||||
|
return len(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Copyrights) Swap(i, j int) {
|
||||||
|
c[i], c[j] = c[j], c[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Copyrights) Less(i, j int) bool {
|
||||||
|
return c[i].Author < c[j].Author
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge attempts to merge two Copyright instances. It merges URLs if the Author,
|
||||||
|
// StartYear, and EndYear are the same or compatible.
|
||||||
|
func (s Copyright) Merge(c Copyright) (*Copyright, error) {
|
||||||
|
// Check if the Author is the same
|
||||||
|
if s.Author != c.Author {
|
||||||
|
return nil, fmt.Errorf("cannot merge copyrights with different authors: %s vs %s", s.Author, c.Author)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the StartYear and EndYear are compatible
|
||||||
|
if s.StartYear != c.StartYear || s.EndYear != c.EndYear {
|
||||||
|
return nil, fmt.Errorf("cannot merge copyrights with different years: %s-%s vs %s-%s", s.StartYear, s.EndYear, c.StartYear, c.EndYear)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge URLs
|
||||||
|
if c.URL != "" {
|
||||||
|
s.URL = mergeURLs(s.URL, c.URL)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// mergeURLs merges two URL strings, deduplicates, and sorts them.
|
||||||
|
func mergeURLs(sURL, cURL string) string {
|
||||||
|
var urls []string
|
||||||
|
if sURL != "" {
|
||||||
|
urls = append(urls, sURL)
|
||||||
|
}
|
||||||
|
if cURL != "" {
|
||||||
|
urls = append(urls, cURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(urls) > 0 {
|
||||||
|
// Deduplicate and sort URLs
|
||||||
|
urlsSet := strset.New(urls...)
|
||||||
|
sortedURLs := urlsSet.List()
|
||||||
|
sort.Strings(sortedURLs)
|
||||||
|
return sortedURLs[0] // Assuming we return the first one or join them into a single string
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
89
syft/pkg/copyright_set.go
Normal file
89
syft/pkg/copyright_set.go
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
//nolint:dupl
|
||||||
|
package pkg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/mitchellh/hashstructure/v2"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/internal/log"
|
||||||
|
"github.com/anchore/syft/syft/artifact"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CopyrightsSet struct {
|
||||||
|
set map[artifact.ID]Copyright
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCopyrightSet(copyrights ...Copyright) (c CopyrightsSet) {
|
||||||
|
for _, l := range copyrights {
|
||||||
|
c.Add(l)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CopyrightsSet) addToExisting(copyright Copyright) (id artifact.ID, merged bool, err error) {
|
||||||
|
id, err = artifact.IDByHash(copyright)
|
||||||
|
if err != nil {
|
||||||
|
return id, false, fmt.Errorf("could not get the hash for a copyright: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
v, ok := c.set[id]
|
||||||
|
if !ok {
|
||||||
|
// doesn't exist safe to add
|
||||||
|
return id, false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// we got the same id; we want to merge the URLs and Location data
|
||||||
|
// URLs/Location are not considered when taking the Hash
|
||||||
|
m, err := v.Merge(copyright)
|
||||||
|
if err != nil {
|
||||||
|
return id, false, fmt.Errorf("could not merge license into map: %w", err)
|
||||||
|
}
|
||||||
|
c.set[id] = *m
|
||||||
|
|
||||||
|
return id, true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CopyrightsSet) Add(copyrights ...Copyright) {
|
||||||
|
if c.set == nil {
|
||||||
|
c.set = make(map[artifact.ID]Copyright)
|
||||||
|
}
|
||||||
|
for _, l := range copyrights {
|
||||||
|
// we only want to add copyrights that have a value
|
||||||
|
// note, this check should be moved to the license constructor in the future
|
||||||
|
if l.Author != "" {
|
||||||
|
if id, merged, err := c.addToExisting(l); err == nil && !merged {
|
||||||
|
// doesn't exist, add it
|
||||||
|
c.set[id] = l
|
||||||
|
} else if err != nil {
|
||||||
|
log.Trace("copyright set failed to add copyright %#v: %+v", l, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c CopyrightsSet) ToSlice() []Copyright {
|
||||||
|
if c.set == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var copyrights []Copyright
|
||||||
|
for _, v := range c.set {
|
||||||
|
copyrights = append(copyrights, v)
|
||||||
|
}
|
||||||
|
sort.Sort(Copyrights(copyrights))
|
||||||
|
return copyrights
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c CopyrightsSet) Hash() (uint64, error) {
|
||||||
|
// access paths and filesystem IDs are not considered when hashing a copyright set, only the real paths
|
||||||
|
return hashstructure.Hash(c.ToSlice(), hashstructure.FormatV2, &hashstructure.HashOptions{
|
||||||
|
ZeroNil: true,
|
||||||
|
SlicesAsSets: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c CopyrightsSet) Empty() bool {
|
||||||
|
return len(c.set) < 1
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
//nolint:dupl
|
||||||
package pkg
|
package pkg
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -17,17 +17,19 @@ import (
|
||||||
// Package represents an application or library that has been bundled into a distributable format.
|
// 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: if we ignore FoundBy for ID generation should we merge the field to show it was found in two places?
|
||||||
type Package struct {
|
type Package struct {
|
||||||
id artifact.ID `hash:"ignore"`
|
id artifact.ID `hash:"ignore"`
|
||||||
Name string // the package name
|
Name string // the package name
|
||||||
Version string // the version of the package
|
Version string // the version of the package
|
||||||
FoundBy string `hash:"ignore" cyclonedx:"foundBy"` // the specific cataloger that discovered this package
|
FoundBy string `hash:"ignore" cyclonedx:"foundBy"` // the specific cataloger that discovered this package
|
||||||
Locations file.LocationSet // the locations that lead to the discovery of this package (note: this is not necessarily the locations that make up this package)
|
Locations file.LocationSet // the locations that lead to the discovery of this package (note: this is not necessarily the locations that make up this package)
|
||||||
Licenses LicenseSet // 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)
|
Copyrights CopyrightsSet // copyrights discovered with the package metadata
|
||||||
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)
|
Language Language `hash:"ignore" cyclonedx:"language"` // the language ecosystem this package belongs to (e.g. JavaScript, Python, etc)
|
||||||
PURL string `hash:"ignore"` // the Package URL (see https://github.com/package-url/purl-spec)
|
Type Type `cyclonedx:"type"` // the package type (e.g. Npm, Yarn, Python, Rpm, Deb, etc)
|
||||||
Metadata interface{} // additional data found while parsing the package source
|
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)
|
||||||
|
PURL string `hash:"ignore"` // the Package URL (see https://github.com/package-url/purl-spec)
|
||||||
|
Metadata interface{} // additional data found while parsing the package source
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Package) OverrideID(id artifact.ID) {
|
func (p *Package) OverrideID(id artifact.ID) {
|
||||||
|
|
|
@ -416,6 +416,24 @@ func TestPackage_Merge(t *testing.T) {
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
cmp.Comparer(
|
||||||
|
func(x, y CopyrightsSet) bool {
|
||||||
|
xs := x.ToSlice()
|
||||||
|
ys := y.ToSlice()
|
||||||
|
|
||||||
|
if len(xs) != len(ys) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i, xe := range xs {
|
||||||
|
ye := ys[i]
|
||||||
|
if !copyrightComparer(xe, ye) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
),
|
||||||
cmp.Comparer(locationComparer),
|
cmp.Comparer(locationComparer),
|
||||||
); diff != "" {
|
); diff != "" {
|
||||||
t.Errorf("unexpected result from parsing (-expected +actual)\n%s", diff)
|
t.Errorf("unexpected result from parsing (-expected +actual)\n%s", diff)
|
||||||
|
@ -428,6 +446,10 @@ func licenseComparer(x, y License) bool {
|
||||||
return cmp.Equal(x, y, cmp.Comparer(locationComparer))
|
return cmp.Equal(x, y, cmp.Comparer(locationComparer))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func copyrightComparer(x, y Copyright) bool {
|
||||||
|
return cmp.Equal(x, y, cmp.Comparer(copyrightComparer))
|
||||||
|
}
|
||||||
|
|
||||||
func locationComparer(x, y file.Location) bool {
|
func locationComparer(x, y file.Location) bool {
|
||||||
return cmp.Equal(x.Coordinates, y.Coordinates) && cmp.Equal(x.AccessPath, y.AccessPath)
|
return cmp.Equal(x.Coordinates, y.Coordinates) && cmp.Equal(x.AccessPath, y.AccessPath)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue