diff --git a/internal/cmptest/common_options.go b/internal/cmptest/common_options.go
index ecfb54edfded..ce23f71839b8 100644
--- a/internal/cmptest/common_options.go
+++ b/internal/cmptest/common_options.go
@@ -9,10 +9,11 @@ import (
)
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 {
licenseCmp = DefaultLicenseComparer
}
@@ -21,6 +22,10 @@ func CommonOptions(licenseCmp LicenseComparer, locationCmp LocationComparer) []c
locationCmp = DefaultLocationComparer
}
+ if copyrightCmp == nil {
+ copyrightCmp = DefaultCopyrightComparer
+ }
+
return []cmp.Option{
cmpopts.IgnoreFields(pkg.Package{}, "id"), // note: ID is not deterministic for test purposes
cmpopts.SortSlices(pkg.Less),
@@ -61,11 +66,31 @@ func CommonOptions(licenseCmp LicenseComparer, locationCmp LocationComparer) []c
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(
locationCmp,
),
cmp.Comparer(
licenseCmp,
),
+ cmp.Comparer(
+ copyrightCmp,
+ ),
}
}
diff --git a/internal/cmptest/copyright.go b/internal/cmptest/copyright.go
new file mode 100644
index 000000000000..789db8c2afca
--- /dev/null
+++ b/internal/cmptest/copyright.go
@@ -0,0 +1,16 @@
+package cmptest
+
+import (
+ "github.com/anchore/syft/syft/pkg"
+ "github.com/google/go-cmp/cmp"
+)
+
+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
+ },
+ ))
+}
diff --git a/internal/constants.go b/internal/constants.go
index 6d1dd197439c..93b0093ab6a2 100644
--- a/internal/constants.go
+++ b/internal/constants.go
@@ -3,5 +3,5 @@ package internal
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 = "16.0.15"
+ JSONSchemaVersion = "16.0.16"
)
diff --git a/internal/relationship/binary/binary_dependencies_test.go b/internal/relationship/binary/binary_dependencies_test.go
index ea524fa1f7cf..602139e9ad56 100644
--- a/internal/relationship/binary/binary_dependencies_test.go
+++ b/internal/relationship/binary/binary_dependencies_test.go
@@ -351,6 +351,7 @@ func relationshipComparer(x, y []artifact.Relationship) string {
artifact.Relationship{},
file.LocationSet{},
pkg.LicenseSet{},
+ pkg.CopyrightsSet{},
), cmpopts.SortSlices(lessRelationships))
}
diff --git a/schema/json/schema-16.0.16.json b/schema/json/schema-16.0.16.json
new file mode 100644
index 000000000000..413117ee2f7e
--- /dev/null
+++ b/schema/json/schema-16.0.16.json
@@ -0,0 +1,2615 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "anchore.io/schema/syft/json/16.0.15/document",
+ "$ref": "#/$defs/Document",
+ "$defs": {
+ "AlpmDbEntry": {
+ "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"
+ },
+ "provides": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "depends": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ }
+ },
+ "type": "object",
+ "required": [
+ "basepackage",
+ "package",
+ "version",
+ "description",
+ "architecture",
+ "size",
+ "packager",
+ "url",
+ "validation",
+ "reason",
+ "files",
+ "backup"
+ ]
+ },
+ "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"
+ },
+ "ApkDbEntry": {
+ "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"
+ ]
+ },
+ "ApkFileRecord": {
+ "properties": {
+ "path": {
+ "type": "string"
+ },
+ "ownerUid": {
+ "type": "string"
+ },
+ "ownerGid": {
+ "type": "string"
+ },
+ "permissions": {
+ "type": "string"
+ },
+ "digest": {
+ "$ref": "#/$defs/Digest"
+ }
+ },
+ "type": "object",
+ "required": [
+ "path"
+ ]
+ },
+ "BinarySignature": {
+ "properties": {
+ "matches": {
+ "items": {
+ "$ref": "#/$defs/ClassifierMatch"
+ },
+ "type": "array"
+ }
+ },
+ "type": "object",
+ "required": [
+ "matches"
+ ]
+ },
+ "CConanFileEntry": {
+ "properties": {
+ "ref": {
+ "type": "string"
+ }
+ },
+ "type": "object",
+ "required": [
+ "ref"
+ ]
+ },
+ "CConanInfoEntry": {
+ "properties": {
+ "ref": {
+ "type": "string"
+ },
+ "package_id": {
+ "type": "string"
+ }
+ },
+ "type": "object",
+ "required": [
+ "ref"
+ ]
+ },
+ "CConanLockEntry": {
+ "properties": {
+ "ref": {
+ "type": "string"
+ },
+ "package_id": {
+ "type": "string"
+ },
+ "prev": {
+ "type": "string"
+ },
+ "requires": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "build_requires": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "py_requires": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "options": {
+ "$ref": "#/$defs/KeyValues"
+ },
+ "path": {
+ "type": "string"
+ },
+ "context": {
+ "type": "string"
+ }
+ },
+ "type": "object",
+ "required": [
+ "ref"
+ ]
+ },
+ "CConanLockV2Entry": {
+ "properties": {
+ "ref": {
+ "type": "string"
+ },
+ "packageID": {
+ "type": "string"
+ },
+ "username": {
+ "type": "string"
+ },
+ "channel": {
+ "type": "string"
+ },
+ "recipeRevision": {
+ "type": "string"
+ },
+ "packageRevision": {
+ "type": "string"
+ },
+ "timestamp": {
+ "type": "string"
+ }
+ },
+ "type": "object",
+ "required": [
+ "ref"
+ ]
+ },
+ "CPE": {
+ "properties": {
+ "cpe": {
+ "type": "string"
+ },
+ "source": {
+ "type": "string"
+ }
+ },
+ "type": "object",
+ "required": [
+ "cpe"
+ ]
+ },
+ "ClassifierMatch": {
+ "properties": {
+ "classifier": {
+ "type": "string"
+ },
+ "location": {
+ "$ref": "#/$defs/Location"
+ }
+ },
+ "type": "object",
+ "required": [
+ "classifier",
+ "location"
+ ]
+ },
+ "CocoaPodfileLockEntry": {
+ "properties": {
+ "checksum": {
+ "type": "string"
+ }
+ },
+ "type": "object",
+ "required": [
+ "checksum"
+ ]
+ },
+ "Coordinates": {
+ "properties": {
+ "path": {
+ "type": "string"
+ },
+ "layerID": {
+ "type": "string"
+ }
+ },
+ "type": "object",
+ "required": [
+ "path"
+ ]
+ },
+ "DartPubspecLockEntry": {
+ "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"
+ },
+ "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"
+ ]
+ },
+ "DotnetDepsEntry": {
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "version": {
+ "type": "string"
+ },
+ "path": {
+ "type": "string"
+ },
+ "sha512": {
+ "type": "string"
+ },
+ "hashPath": {
+ "type": "string"
+ }
+ },
+ "type": "object",
+ "required": [
+ "name",
+ "version",
+ "path",
+ "sha512",
+ "hashPath"
+ ]
+ },
+ "DotnetPortableExecutableEntry": {
+ "properties": {
+ "assemblyVersion": {
+ "type": "string"
+ },
+ "legalCopyright": {
+ "type": "string"
+ },
+ "comments": {
+ "type": "string"
+ },
+ "internalName": {
+ "type": "string"
+ },
+ "companyName": {
+ "type": "string"
+ },
+ "productName": {
+ "type": "string"
+ },
+ "productVersion": {
+ "type": "string"
+ }
+ },
+ "type": "object",
+ "required": [
+ "assemblyVersion",
+ "legalCopyright",
+ "companyName",
+ "productName",
+ "productVersion"
+ ]
+ },
+ "DpkgDbEntry": {
+ "properties": {
+ "package": {
+ "type": "string"
+ },
+ "source": {
+ "type": "string"
+ },
+ "version": {
+ "type": "string"
+ },
+ "sourceVersion": {
+ "type": "string"
+ },
+ "architecture": {
+ "type": "string"
+ },
+ "maintainer": {
+ "type": "string"
+ },
+ "installedSize": {
+ "type": "integer"
+ },
+ "provides": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "depends": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "preDepends": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "files": {
+ "items": {
+ "$ref": "#/$defs/DpkgFileRecord"
+ },
+ "type": "array"
+ }
+ },
+ "type": "object",
+ "required": [
+ "package",
+ "source",
+ "version",
+ "sourceVersion",
+ "architecture",
+ "maintainer",
+ "installedSize",
+ "files"
+ ]
+ },
+ "DpkgFileRecord": {
+ "properties": {
+ "path": {
+ "type": "string"
+ },
+ "digest": {
+ "$ref": "#/$defs/Digest"
+ },
+ "isConfigFile": {
+ "type": "boolean"
+ }
+ },
+ "type": "object",
+ "required": [
+ "path",
+ "isConfigFile"
+ ]
+ },
+ "ELFSecurityFeatures": {
+ "properties": {
+ "symbolTableStripped": {
+ "type": "boolean"
+ },
+ "stackCanary": {
+ "type": "boolean"
+ },
+ "nx": {
+ "type": "boolean"
+ },
+ "relRO": {
+ "type": "string"
+ },
+ "pie": {
+ "type": "boolean"
+ },
+ "dso": {
+ "type": "boolean"
+ },
+ "safeStack": {
+ "type": "boolean"
+ },
+ "cfi": {
+ "type": "boolean"
+ },
+ "fortify": {
+ "type": "boolean"
+ }
+ },
+ "type": "object",
+ "required": [
+ "symbolTableStripped",
+ "nx",
+ "relRO",
+ "pie",
+ "dso"
+ ]
+ },
+ "ElfBinaryPackageNoteJsonPayload": {
+ "properties": {
+ "type": {
+ "type": "string"
+ },
+ "architecture": {
+ "type": "string"
+ },
+ "osCPE": {
+ "type": "string"
+ },
+ "os": {
+ "type": "string"
+ },
+ "osVersion": {
+ "type": "string"
+ },
+ "system": {
+ "type": "string"
+ },
+ "vendor": {
+ "type": "string"
+ },
+ "sourceRepo": {
+ "type": "string"
+ },
+ "commit": {
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
+ "ElixirMixLockEntry": {
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "version": {
+ "type": "string"
+ },
+ "pkgHash": {
+ "type": "string"
+ },
+ "pkgHashExt": {
+ "type": "string"
+ }
+ },
+ "type": "object",
+ "required": [
+ "name",
+ "version",
+ "pkgHash",
+ "pkgHashExt"
+ ]
+ },
+ "ErlangRebarLockEntry": {
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "version": {
+ "type": "string"
+ },
+ "pkgHash": {
+ "type": "string"
+ },
+ "pkgHashExt": {
+ "type": "string"
+ }
+ },
+ "type": "object",
+ "required": [
+ "name",
+ "version",
+ "pkgHash",
+ "pkgHashExt"
+ ]
+ },
+ "Executable": {
+ "properties": {
+ "format": {
+ "type": "string"
+ },
+ "hasExports": {
+ "type": "boolean"
+ },
+ "hasEntrypoint": {
+ "type": "boolean"
+ },
+ "importedLibraries": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "elfSecurityFeatures": {
+ "$ref": "#/$defs/ELFSecurityFeatures"
+ }
+ },
+ "type": "object",
+ "required": [
+ "format",
+ "hasExports",
+ "hasEntrypoint",
+ "importedLibraries"
+ ]
+ },
+ "File": {
+ "properties": {
+ "id": {
+ "type": "string"
+ },
+ "location": {
+ "$ref": "#/$defs/Coordinates"
+ },
+ "metadata": {
+ "$ref": "#/$defs/FileMetadataEntry"
+ },
+ "contents": {
+ "type": "string"
+ },
+ "digests": {
+ "items": {
+ "$ref": "#/$defs/Digest"
+ },
+ "type": "array"
+ },
+ "licenses": {
+ "items": {
+ "$ref": "#/$defs/FileLicense"
+ },
+ "type": "array"
+ },
+ "executable": {
+ "$ref": "#/$defs/Executable"
+ }
+ },
+ "type": "object",
+ "required": [
+ "id",
+ "location"
+ ]
+ },
+ "FileLicense": {
+ "properties": {
+ "value": {
+ "type": "string"
+ },
+ "spdxExpression": {
+ "type": "string"
+ },
+ "type": {
+ "type": "string"
+ },
+ "evidence": {
+ "$ref": "#/$defs/FileLicenseEvidence"
+ }
+ },
+ "type": "object",
+ "required": [
+ "value",
+ "spdxExpression",
+ "type"
+ ]
+ },
+ "FileLicenseEvidence": {
+ "properties": {
+ "confidence": {
+ "type": "integer"
+ },
+ "offset": {
+ "type": "integer"
+ },
+ "extent": {
+ "type": "integer"
+ }
+ },
+ "type": "object",
+ "required": [
+ "confidence",
+ "offset",
+ "extent"
+ ]
+ },
+ "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"
+ ]
+ },
+ "GoModuleBuildinfoEntry": {
+ "properties": {
+ "goBuildSettings": {
+ "$ref": "#/$defs/KeyValues"
+ },
+ "goCompiledVersion": {
+ "type": "string"
+ },
+ "architecture": {
+ "type": "string"
+ },
+ "h1Digest": {
+ "type": "string"
+ },
+ "mainModule": {
+ "type": "string"
+ },
+ "goCryptoSettings": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "goExperiments": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ }
+ },
+ "type": "object",
+ "required": [
+ "goCompiledVersion",
+ "architecture"
+ ]
+ },
+ "GoModuleEntry": {
+ "properties": {
+ "h1Digest": {
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
+ "HaskellHackageStackEntry": {
+ "properties": {
+ "pkgHash": {
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
+ "HaskellHackageStackLockEntry": {
+ "properties": {
+ "pkgHash": {
+ "type": "string"
+ },
+ "snapshotURL": {
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
+ "IDLikes": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "JavaArchive": {
+ "properties": {
+ "virtualPath": {
+ "type": "string"
+ },
+ "manifest": {
+ "$ref": "#/$defs/JavaManifest"
+ },
+ "pomProperties": {
+ "$ref": "#/$defs/JavaPomProperties"
+ },
+ "pomProject": {
+ "$ref": "#/$defs/JavaPomProject"
+ },
+ "digest": {
+ "items": {
+ "$ref": "#/$defs/Digest"
+ },
+ "type": "array"
+ }
+ },
+ "type": "object",
+ "required": [
+ "virtualPath"
+ ]
+ },
+ "JavaManifest": {
+ "properties": {
+ "main": {
+ "$ref": "#/$defs/KeyValues"
+ },
+ "sections": {
+ "items": {
+ "$ref": "#/$defs/KeyValues"
+ },
+ "type": "array"
+ }
+ },
+ "type": "object"
+ },
+ "JavaPomParent": {
+ "properties": {
+ "groupId": {
+ "type": "string"
+ },
+ "artifactId": {
+ "type": "string"
+ },
+ "version": {
+ "type": "string"
+ }
+ },
+ "type": "object",
+ "required": [
+ "groupId",
+ "artifactId",
+ "version"
+ ]
+ },
+ "JavaPomProject": {
+ "properties": {
+ "path": {
+ "type": "string"
+ },
+ "parent": {
+ "$ref": "#/$defs/JavaPomParent"
+ },
+ "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"
+ ]
+ },
+ "JavaPomProperties": {
+ "properties": {
+ "path": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "groupId": {
+ "type": "string"
+ },
+ "artifactId": {
+ "type": "string"
+ },
+ "version": {
+ "type": "string"
+ },
+ "scope": {
+ "type": "string"
+ },
+ "extraFields": {
+ "patternProperties": {
+ ".*": {
+ "type": "string"
+ }
+ },
+ "type": "object"
+ }
+ },
+ "type": "object",
+ "required": [
+ "path",
+ "name",
+ "groupId",
+ "artifactId",
+ "version"
+ ]
+ },
+ "JavascriptNpmPackage": {
+ "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"
+ ]
+ },
+ "JavascriptNpmPackageLockEntry": {
+ "properties": {
+ "resolved": {
+ "type": "string"
+ },
+ "integrity": {
+ "type": "string"
+ }
+ },
+ "type": "object",
+ "required": [
+ "resolved",
+ "integrity"
+ ]
+ },
+ "JavascriptYarnLockEntry": {
+ "properties": {
+ "resolved": {
+ "type": "string"
+ },
+ "integrity": {
+ "type": "string"
+ }
+ },
+ "type": "object",
+ "required": [
+ "resolved",
+ "integrity"
+ ]
+ },
+ "KeyValue": {
+ "properties": {
+ "key": {
+ "type": "string"
+ },
+ "value": {
+ "type": "string"
+ }
+ },
+ "type": "object",
+ "required": [
+ "key",
+ "value"
+ ]
+ },
+ "KeyValues": {
+ "items": {
+ "$ref": "#/$defs/KeyValue"
+ },
+ "type": "array"
+ },
+ "License": {
+ "properties": {
+ "value": {
+ "type": "string"
+ },
+ "spdxExpression": {
+ "type": "string"
+ },
+ "type": {
+ "type": "string"
+ },
+ "urls": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "locations": {
+ "items": {
+ "$ref": "#/$defs/Location"
+ },
+ "type": "array"
+ }
+ },
+ "type": "object",
+ "required": [
+ "value",
+ "spdxExpression",
+ "type",
+ "urls",
+ "locations"
+ ]
+ },
+ "Copyright": {
+ "properties": {
+ "author": {
+ "type": "string"
+ },
+ "startYear": {
+ "type": "string"
+ },
+ "endYear": {
+ "type": "string"
+ },
+ "url": {
+ "type": "string"
+ }
+ },
+ "type": "object",
+ "required": [
+ "author",
+ "startYear",
+ "endYear",
+ "url"
+ ]
+ },
+ "LinuxKernelArchive": {
+ "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"
+ ]
+ },
+ "LinuxKernelModule": {
+ "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"
+ },
+ "accessPath": {
+ "type": "string"
+ },
+ "annotations": {
+ "patternProperties": {
+ ".*": {
+ "type": "string"
+ }
+ },
+ "type": "object"
+ }
+ },
+ "type": "object",
+ "required": [
+ "path",
+ "accessPath"
+ ]
+ },
+ "LuarocksPackage": {
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "version": {
+ "type": "string"
+ },
+ "license": {
+ "type": "string"
+ },
+ "homepage": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ },
+ "url": {
+ "type": "string"
+ },
+ "dependencies": {
+ "patternProperties": {
+ ".*": {
+ "type": "string"
+ }
+ },
+ "type": "object"
+ }
+ },
+ "type": "object",
+ "required": [
+ "name",
+ "version",
+ "license",
+ "homepage",
+ "description",
+ "url",
+ "dependencies"
+ ]
+ },
+ "MicrosoftKbPatch": {
+ "properties": {
+ "product_id": {
+ "type": "string"
+ },
+ "kb": {
+ "type": "string"
+ }
+ },
+ "type": "object",
+ "required": [
+ "product_id",
+ "kb"
+ ]
+ },
+ "NixStoreEntry": {
+ "properties": {
+ "outputHash": {
+ "type": "string"
+ },
+ "output": {
+ "type": "string"
+ },
+ "files": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ }
+ },
+ "type": "object",
+ "required": [
+ "outputHash",
+ "files"
+ ]
+ },
+ "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": {
+ "$ref": "#/$defs/licenses"
+ },
+ "copyrights": {
+ "$ref": "#/$defs/copyrights"
+ },
+
+ "language": {
+ "type": "string"
+ },
+ "cpes": {
+ "$ref": "#/$defs/cpes"
+ },
+ "purl": {
+ "type": "string"
+ },
+ "metadataType": {
+ "type": "string"
+ },
+ "metadata": {
+ "anyOf": [
+ {
+ "type": "null"
+ },
+ {
+ "$ref": "#/$defs/AlpmDbEntry"
+ },
+ {
+ "$ref": "#/$defs/ApkDbEntry"
+ },
+ {
+ "$ref": "#/$defs/BinarySignature"
+ },
+ {
+ "$ref": "#/$defs/CConanFileEntry"
+ },
+ {
+ "$ref": "#/$defs/CConanInfoEntry"
+ },
+ {
+ "$ref": "#/$defs/CConanLockEntry"
+ },
+ {
+ "$ref": "#/$defs/CConanLockV2Entry"
+ },
+ {
+ "$ref": "#/$defs/CocoaPodfileLockEntry"
+ },
+ {
+ "$ref": "#/$defs/DartPubspecLockEntry"
+ },
+ {
+ "$ref": "#/$defs/DotnetDepsEntry"
+ },
+ {
+ "$ref": "#/$defs/DotnetPortableExecutableEntry"
+ },
+ {
+ "$ref": "#/$defs/DpkgDbEntry"
+ },
+ {
+ "$ref": "#/$defs/ElfBinaryPackageNoteJsonPayload"
+ },
+ {
+ "$ref": "#/$defs/ElixirMixLockEntry"
+ },
+ {
+ "$ref": "#/$defs/ErlangRebarLockEntry"
+ },
+ {
+ "$ref": "#/$defs/GoModuleBuildinfoEntry"
+ },
+ {
+ "$ref": "#/$defs/GoModuleEntry"
+ },
+ {
+ "$ref": "#/$defs/HaskellHackageStackEntry"
+ },
+ {
+ "$ref": "#/$defs/HaskellHackageStackLockEntry"
+ },
+ {
+ "$ref": "#/$defs/JavaArchive"
+ },
+ {
+ "$ref": "#/$defs/JavascriptNpmPackage"
+ },
+ {
+ "$ref": "#/$defs/JavascriptNpmPackageLockEntry"
+ },
+ {
+ "$ref": "#/$defs/JavascriptYarnLockEntry"
+ },
+ {
+ "$ref": "#/$defs/LinuxKernelArchive"
+ },
+ {
+ "$ref": "#/$defs/LinuxKernelModule"
+ },
+ {
+ "$ref": "#/$defs/LuarocksPackage"
+ },
+ {
+ "$ref": "#/$defs/MicrosoftKbPatch"
+ },
+ {
+ "$ref": "#/$defs/NixStoreEntry"
+ },
+ {
+ "$ref": "#/$defs/PhpComposerInstalledEntry"
+ },
+ {
+ "$ref": "#/$defs/PhpComposerLockEntry"
+ },
+ {
+ "$ref": "#/$defs/PhpPeclEntry"
+ },
+ {
+ "$ref": "#/$defs/PortageDbEntry"
+ },
+ {
+ "$ref": "#/$defs/PythonPackage"
+ },
+ {
+ "$ref": "#/$defs/PythonPipRequirementsEntry"
+ },
+ {
+ "$ref": "#/$defs/PythonPipfileLockEntry"
+ },
+ {
+ "$ref": "#/$defs/PythonPoetryLockEntry"
+ },
+ {
+ "$ref": "#/$defs/RDescription"
+ },
+ {
+ "$ref": "#/$defs/RpmArchive"
+ },
+ {
+ "$ref": "#/$defs/RpmDbEntry"
+ },
+ {
+ "$ref": "#/$defs/RubyGemspec"
+ },
+ {
+ "$ref": "#/$defs/RustCargoAuditEntry"
+ },
+ {
+ "$ref": "#/$defs/RustCargoLockEntry"
+ },
+ {
+ "$ref": "#/$defs/SwiftPackageManagerLockEntry"
+ },
+ {
+ "$ref": "#/$defs/SwiplpackPackage"
+ },
+ {
+ "$ref": "#/$defs/WordpressPluginEntry"
+ }
+ ]
+ }
+ },
+ "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"
+ ]
+ },
+ "PhpComposerInstalledEntry": {
+ "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"
+ ]
+ },
+ "PhpComposerLockEntry": {
+ "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"
+ ]
+ },
+ "PhpPeclEntry": {
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "version": {
+ "type": "string"
+ },
+ "license": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ }
+ },
+ "type": "object",
+ "required": [
+ "name",
+ "version"
+ ]
+ },
+ "PortageDbEntry": {
+ "properties": {
+ "installedSize": {
+ "type": "integer"
+ },
+ "files": {
+ "items": {
+ "$ref": "#/$defs/PortageFileRecord"
+ },
+ "type": "array"
+ }
+ },
+ "type": "object",
+ "required": [
+ "installedSize",
+ "files"
+ ]
+ },
+ "PortageFileRecord": {
+ "properties": {
+ "path": {
+ "type": "string"
+ },
+ "digest": {
+ "$ref": "#/$defs/Digest"
+ }
+ },
+ "type": "object",
+ "required": [
+ "path"
+ ]
+ },
+ "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"
+ ]
+ },
+ "PythonPackage": {
+ "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"
+ },
+ "requiresPython": {
+ "type": "string"
+ },
+ "requiresDist": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "providesExtra": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ }
+ },
+ "type": "object",
+ "required": [
+ "name",
+ "version",
+ "author",
+ "authorEmail",
+ "platform",
+ "sitePackagesRootPath"
+ ]
+ },
+ "PythonPipRequirementsEntry": {
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "extras": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "versionConstraint": {
+ "type": "string"
+ },
+ "url": {
+ "type": "string"
+ },
+ "markers": {
+ "type": "string"
+ }
+ },
+ "type": "object",
+ "required": [
+ "name",
+ "versionConstraint"
+ ]
+ },
+ "PythonPipfileLockEntry": {
+ "properties": {
+ "hashes": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "index": {
+ "type": "string"
+ }
+ },
+ "type": "object",
+ "required": [
+ "hashes",
+ "index"
+ ]
+ },
+ "PythonPoetryLockDependencyEntry": {
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "version": {
+ "type": "string"
+ },
+ "optional": {
+ "type": "boolean"
+ },
+ "markers": {
+ "type": "string"
+ },
+ "extras": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ }
+ },
+ "type": "object",
+ "required": [
+ "name",
+ "version",
+ "optional"
+ ]
+ },
+ "PythonPoetryLockEntry": {
+ "properties": {
+ "index": {
+ "type": "string"
+ },
+ "dependencies": {
+ "items": {
+ "$ref": "#/$defs/PythonPoetryLockDependencyEntry"
+ },
+ "type": "array"
+ },
+ "extras": {
+ "items": {
+ "$ref": "#/$defs/PythonPoetryLockExtraEntry"
+ },
+ "type": "array"
+ }
+ },
+ "type": "object",
+ "required": [
+ "index",
+ "dependencies"
+ ]
+ },
+ "PythonPoetryLockExtraEntry": {
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "dependencies": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ }
+ },
+ "type": "object",
+ "required": [
+ "name",
+ "dependencies"
+ ]
+ },
+ "RDescription": {
+ "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"
+ },
+ "Relationship": {
+ "properties": {
+ "parent": {
+ "type": "string"
+ },
+ "child": {
+ "type": "string"
+ },
+ "type": {
+ "type": "string"
+ },
+ "metadata": true
+ },
+ "type": "object",
+ "required": [
+ "parent",
+ "child",
+ "type"
+ ]
+ },
+ "RpmArchive": {
+ "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"
+ },
+ "provides": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "requires": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "files": {
+ "items": {
+ "$ref": "#/$defs/RpmFileRecord"
+ },
+ "type": "array"
+ }
+ },
+ "type": "object",
+ "required": [
+ "name",
+ "version",
+ "epoch",
+ "architecture",
+ "release",
+ "sourceRpm",
+ "size",
+ "vendor",
+ "files"
+ ]
+ },
+ "RpmDbEntry": {
+ "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"
+ },
+ "provides": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "requires": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "files": {
+ "items": {
+ "$ref": "#/$defs/RpmFileRecord"
+ },
+ "type": "array"
+ }
+ },
+ "type": "object",
+ "required": [
+ "name",
+ "version",
+ "epoch",
+ "architecture",
+ "release",
+ "sourceRpm",
+ "size",
+ "vendor",
+ "files"
+ ]
+ },
+ "RpmFileRecord": {
+ "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"
+ ]
+ },
+ "RubyGemspec": {
+ "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"
+ ]
+ },
+ "RustCargoAuditEntry": {
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "version": {
+ "type": "string"
+ },
+ "source": {
+ "type": "string"
+ }
+ },
+ "type": "object",
+ "required": [
+ "name",
+ "version",
+ "source"
+ ]
+ },
+ "RustCargoLockEntry": {
+ "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"
+ ]
+ },
+ "Schema": {
+ "properties": {
+ "version": {
+ "type": "string"
+ },
+ "url": {
+ "type": "string"
+ }
+ },
+ "type": "object",
+ "required": [
+ "version",
+ "url"
+ ]
+ },
+ "Source": {
+ "properties": {
+ "id": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "version": {
+ "type": "string"
+ },
+ "type": {
+ "type": "string"
+ },
+ "metadata": true
+ },
+ "type": "object",
+ "required": [
+ "id",
+ "name",
+ "version",
+ "type",
+ "metadata"
+ ]
+ },
+ "SwiftPackageManagerLockEntry": {
+ "properties": {
+ "revision": {
+ "type": "string"
+ }
+ },
+ "type": "object",
+ "required": [
+ "revision"
+ ]
+ },
+ "SwiplpackPackage": {
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "version": {
+ "type": "string"
+ },
+ "author": {
+ "type": "string"
+ },
+ "authorEmail": {
+ "type": "string"
+ },
+ "packager": {
+ "type": "string"
+ },
+ "packagerEmail": {
+ "type": "string"
+ },
+ "homepage": {
+ "type": "string"
+ },
+ "dependencies": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ }
+ },
+ "type": "object",
+ "required": [
+ "name",
+ "version",
+ "author",
+ "authorEmail",
+ "packager",
+ "packagerEmail",
+ "homepage",
+ "dependencies"
+ ]
+ },
+ "WordpressPluginEntry": {
+ "properties": {
+ "pluginInstallDirectory": {
+ "type": "string"
+ },
+ "author": {
+ "type": "string"
+ },
+ "authorUri": {
+ "type": "string"
+ }
+ },
+ "type": "object",
+ "required": [
+ "pluginInstallDirectory"
+ ]
+ },
+ "cpes": {
+ "items": {
+ "$ref": "#/$defs/CPE"
+ },
+ "type": "array"
+ },
+ "licenses": {
+ "items": {
+ "$ref": "#/$defs/License"
+ },
+ "type": "array"
+ },
+ "copyrights": {
+ "items": {
+ "$ref": "#/$defs/Copyright"
+ },
+ "type": "array"
+ }
+ }
+}
diff --git a/syft/format/common/spdxhelpers/to_format_model.go b/syft/format/common/spdxhelpers/to_format_model.go
index 3fd136e84df7..6e03d3fad544 100644
--- a/syft/format/common/spdxhelpers/to_format_model.go
+++ b/syft/format/common/spdxhelpers/to_format_model.go
@@ -444,8 +444,8 @@ func toPackages(rels *relationship.Index, catalog *pkg.Collection, sbom sbom.SBO
// NOASSERTION, if
// (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).
- //
- PackageCopyrightText: noAssertion,
+ // (iii) Get the formatted copyright text if available, otherwise return NOASSERTION
+ PackageCopyrightText: helpers.GetCopyrights(p.Copyrights),
// 7.18: Package Summary Description
// Cardinality: optional, one
diff --git a/syft/format/common/spdxhelpers/to_syft_model_test.go b/syft/format/common/spdxhelpers/to_syft_model_test.go
index e8526bab5885..897fe2abba15 100644
--- a/syft/format/common/spdxhelpers/to_syft_model_test.go
+++ b/syft/format/common/spdxhelpers/to_syft_model_test.go
@@ -595,6 +595,7 @@ func Test_convertToAndFromFormat(t *testing.T) {
cmpopts.IgnoreUnexported(pkg.Collection{}),
cmpopts.IgnoreUnexported(pkg.Package{}),
cmpopts.IgnoreUnexported(pkg.LicenseSet{}),
+ cmpopts.IgnoreUnexported(pkg.CopyrightsSet{}),
cmpopts.IgnoreFields(sbom.Artifacts{}, "FileMetadata", "FileDigests"),
); diff != "" {
t.Fatalf("packages do not match:\n%s", diff)
diff --git a/syft/format/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden b/syft/format/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden
index e4e7bd7521b7..8311adb2857a 100644
--- a/syft/format/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden
+++ b/syft/format/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden
@@ -35,6 +35,7 @@
}
}
],
+ "copyright":"NOASSERTION",
"cpe": "cpe:2.3:*:some:package:2:*:*:*:*:*:*:*",
"purl": "a-purl-2",
"properties": [
@@ -62,6 +63,7 @@
},
{
"bom-ref":"redacted",
+ "copyright":"NOASSERTION",
"type": "library",
"name": "package-2",
"version": "2.0.1",
diff --git a/syft/format/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden b/syft/format/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden
index 62750e9e68a5..3f1d5f844a89 100644
--- a/syft/format/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden
+++ b/syft/format/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden
@@ -36,6 +36,7 @@
}
}
],
+ "copyright":"NOASSERTION",
"cpe": "cpe:2.3:*:some:package:1:*:*:*:*:*:*:*",
"purl": "a-purl-1",
"properties": [
@@ -67,6 +68,7 @@
},
{
"bom-ref":"redacted",
+ "copyright":"NOASSERTION",
"type": "library",
"name": "package-2",
"version": "2.0.1",
diff --git a/syft/format/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden b/syft/format/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden
index 9a9f7bce8a65..1752c19a14a9 100644
--- a/syft/format/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden
+++ b/syft/format/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden
@@ -24,6 +24,7 @@
MIT
+ NOASSERTION
cpe:2.3:*:some:package:2:*:*:*:*:*:*:*
a-purl-2
@@ -37,6 +38,7 @@
package-2
2.0.1
+ NOASSERTION
cpe:2.3:*:some:package:2:*:*:*:*:*:*:*
pkg:deb/debian/package-2@2.0.1
diff --git a/syft/format/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden b/syft/format/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden
index 12c99a5fa5d3..793fc2c885e2 100644
--- a/syft/format/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden
+++ b/syft/format/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden
@@ -25,6 +25,7 @@
MIT
+ NOASSERTION
cpe:2.3:*:some:package:1:*:*:*:*:*:*:*
a-purl-1
@@ -39,6 +40,7 @@
package-2
2.0.1
+ NOASSERTION
cpe:2.3:*:some:package:2:*:*:*:*:*:*:*
pkg:deb/debian/package-2@2.0.1
diff --git a/syft/format/internal/cyclonedxutil/helpers/component.go b/syft/format/internal/cyclonedxutil/helpers/component.go
index 526094ac5c30..d64a91cfa4ad 100644
--- a/syft/format/internal/cyclonedxutil/helpers/component.go
+++ b/syft/format/internal/cyclonedxutil/helpers/component.go
@@ -48,6 +48,7 @@ func EncodeComponent(p pkg.Package) cyclonedx.Component {
Version: p.Version,
PackageURL: p.PURL,
Licenses: encodeLicenses(p),
+ Copyright: encodeCopyrights(p),
CPE: encodeSingleCPE(p),
Author: encodeAuthor(p),
Publisher: encodePublisher(p),
diff --git a/syft/format/internal/cyclonedxutil/helpers/component_test.go b/syft/format/internal/cyclonedxutil/helpers/component_test.go
index 7f2b9d245250..7de350d4160b 100644
--- a/syft/format/internal/cyclonedxutil/helpers/component_test.go
+++ b/syft/format/internal/cyclonedxutil/helpers/component_test.go
@@ -187,6 +187,7 @@ func Test_encodeCompomentType(t *testing.T) {
Value: "go-module",
},
},
+ Copyright: noAssertion,
},
},
{
@@ -206,6 +207,8 @@ func Test_encodeCompomentType(t *testing.T) {
Value: "binary",
},
},
+
+ Copyright: noAssertion,
},
},
}
diff --git a/syft/format/internal/cyclonedxutil/helpers/licenses.go b/syft/format/internal/cyclonedxutil/helpers/licenses.go
index a092d3abdf3b..3ac7ba0135fc 100644
--- a/syft/format/internal/cyclonedxutil/helpers/licenses.go
+++ b/syft/format/internal/cyclonedxutil/helpers/licenses.go
@@ -5,11 +5,15 @@ import (
"strings"
"github.com/CycloneDX/cyclonedx-go"
-
"github.com/anchore/syft/internal/spdxlicense"
"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
func encodeLicenses(p pkg.Package) *cyclonedx.Licenses {
spdx, other, ex := separateLicenses(p)
@@ -198,3 +202,31 @@ func reduceOuter(expression string) string {
return sb.String()
}
+
+func encodeCopyrights(p pkg.Package) string {
+ if p.Copyrights.Empty() {
+ return noAssertion
+ }
+
+ 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, ", ")
+}
diff --git a/syft/format/internal/spdxutil/helpers/copyright.go b/syft/format/internal/spdxutil/helpers/copyright.go
new file mode 100644
index 000000000000..f269e4218537
--- /dev/null
+++ b/syft/format/internal/spdxutil/helpers/copyright.go
@@ -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
+}
diff --git a/syft/format/spdxjson/test-fixtures/snapshot/TestSPDX22JSONRequredProperties.golden b/syft/format/spdxjson/test-fixtures/snapshot/TestSPDX22JSONRequredProperties.golden
index 118247b1dc09..0ca78b8b9da7 100644
--- a/syft/format/spdxjson/test-fixtures/snapshot/TestSPDX22JSONRequredProperties.golden
+++ b/syft/format/spdxjson/test-fixtures/snapshot/TestSPDX22JSONRequredProperties.golden
@@ -14,7 +14,7 @@
},
"packages": [
{
- "SPDXID": "SPDXRef-Package-files-analyzed-false-7d37ba9d2f7c574b",
+ "SPDXID": "SPDXRef-Package-files-analyzed-false-0950a383541717dc",
"copyrightText": "NOASSERTION",
"downloadLocation": "NOASSERTION",
"filesAnalyzed": false,
@@ -27,7 +27,7 @@
},
{
"name": "files-analyzed-true",
- "SPDXID": "SPDXRef-Package-files-analyzed-true-035066c2086b8bb4",
+ "SPDXID": "SPDXRef-Package-files-analyzed-true-1d0a8d923f0cd238",
"versionInfo": "v1",
"supplier": "NOASSERTION",
"downloadLocation": "NOASSERTION",
@@ -77,18 +77,18 @@
],
"relationships": [
{
- "spdxElementId": "SPDXRef-Package-files-analyzed-true-035066c2086b8bb4",
+ "spdxElementId": "SPDXRef-Package-files-analyzed-true-1d0a8d923f0cd238",
"relatedSpdxElement": "SPDXRef-File-some-file-2c5bc344430decac",
"relationshipType": "CONTAINS"
},
{
"spdxElementId": "SPDXRef-DocumentRoot-Unknown-",
- "relatedSpdxElement": "SPDXRef-Package-files-analyzed-false-7d37ba9d2f7c574b",
+ "relatedSpdxElement": "SPDXRef-Package-files-analyzed-false-0950a383541717dc",
"relationshipType": "CONTAINS"
},
{
"spdxElementId": "SPDXRef-DocumentRoot-Unknown-",
- "relatedSpdxElement": "SPDXRef-Package-files-analyzed-true-035066c2086b8bb4",
+ "relatedSpdxElement": "SPDXRef-Package-files-analyzed-true-1d0a8d923f0cd238",
"relationshipType": "CONTAINS"
},
{
diff --git a/syft/format/spdxjson/test-fixtures/snapshot/TestSPDXJSONDirectoryEncoder.golden b/syft/format/spdxjson/test-fixtures/snapshot/TestSPDXJSONDirectoryEncoder.golden
index 35433f6f547b..6d0237dc5e0e 100644
--- a/syft/format/spdxjson/test-fixtures/snapshot/TestSPDXJSONDirectoryEncoder.golden
+++ b/syft/format/spdxjson/test-fixtures/snapshot/TestSPDXJSONDirectoryEncoder.golden
@@ -15,7 +15,7 @@
"packages": [
{
"name": "package-1",
- "SPDXID": "SPDXRef-Package-python-package-1-5a2b1ae000fcb51e",
+ "SPDXID": "SPDXRef-Package-python-package-1-f7fdfcfa4ca6e742",
"versionInfo": "1.0.1",
"supplier": "NOASSERTION",
"downloadLocation": "NOASSERTION",
@@ -39,7 +39,7 @@
},
{
"name": "package-2",
- "SPDXID": "SPDXRef-Package-deb-package-2-39392bb5e270f669",
+ "SPDXID": "SPDXRef-Package-deb-package-2-062f404587213e8b",
"versionInfo": "2.0.1",
"supplier": "NOASSERTION",
"downloadLocation": "NOASSERTION",
@@ -75,12 +75,12 @@
"relationships": [
{
"spdxElementId": "SPDXRef-DocumentRoot-Directory-some-path",
- "relatedSpdxElement": "SPDXRef-Package-python-package-1-5a2b1ae000fcb51e",
+ "relatedSpdxElement": "SPDXRef-Package-python-package-1-f7fdfcfa4ca6e742",
"relationshipType": "CONTAINS"
},
{
"spdxElementId": "SPDXRef-DocumentRoot-Directory-some-path",
- "relatedSpdxElement": "SPDXRef-Package-deb-package-2-39392bb5e270f669",
+ "relatedSpdxElement": "SPDXRef-Package-deb-package-2-062f404587213e8b",
"relationshipType": "CONTAINS"
},
{
diff --git a/syft/format/spdxjson/test-fixtures/snapshot/TestSPDXJSONImageEncoder.golden b/syft/format/spdxjson/test-fixtures/snapshot/TestSPDXJSONImageEncoder.golden
index 737aed46893a..857ab51628c3 100644
--- a/syft/format/spdxjson/test-fixtures/snapshot/TestSPDXJSONImageEncoder.golden
+++ b/syft/format/spdxjson/test-fixtures/snapshot/TestSPDXJSONImageEncoder.golden
@@ -15,7 +15,7 @@
"packages": [
{
"name": "package-1",
- "SPDXID": "SPDXRef-Package-python-package-1-c5cf7ac34cbca450",
+ "SPDXID": "SPDXRef-Package-python-package-1-69910a93dc37ffb4",
"versionInfo": "1.0.1",
"supplier": "NOASSERTION",
"downloadLocation": "NOASSERTION",
@@ -39,7 +39,7 @@
},
{
"name": "package-2",
- "SPDXID": "SPDXRef-Package-deb-package-2-4b756c6f6fb127a3",
+ "SPDXID": "SPDXRef-Package-deb-package-2-fe989317bb1cbb62",
"versionInfo": "2.0.1",
"supplier": "NOASSERTION",
"downloadLocation": "NOASSERTION",
@@ -89,12 +89,12 @@
"relationships": [
{
"spdxElementId": "SPDXRef-DocumentRoot-Image-user-image-input",
- "relatedSpdxElement": "SPDXRef-Package-python-package-1-c5cf7ac34cbca450",
+ "relatedSpdxElement": "SPDXRef-Package-python-package-1-69910a93dc37ffb4",
"relationshipType": "CONTAINS"
},
{
"spdxElementId": "SPDXRef-DocumentRoot-Image-user-image-input",
- "relatedSpdxElement": "SPDXRef-Package-deb-package-2-4b756c6f6fb127a3",
+ "relatedSpdxElement": "SPDXRef-Package-deb-package-2-fe989317bb1cbb62",
"relationshipType": "CONTAINS"
},
{
diff --git a/syft/format/spdxjson/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden b/syft/format/spdxjson/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden
index 54533ae4ccfd..ebf1ff55bad3 100644
--- a/syft/format/spdxjson/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden
+++ b/syft/format/spdxjson/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden
@@ -15,7 +15,7 @@
"packages": [
{
"name": "package-1",
- "SPDXID": "SPDXRef-Package-python-package-1-c5cf7ac34cbca450",
+ "SPDXID": "SPDXRef-Package-python-package-1-69910a93dc37ffb4",
"versionInfo": "1.0.1",
"supplier": "NOASSERTION",
"downloadLocation": "NOASSERTION",
@@ -39,7 +39,7 @@
},
{
"name": "package-2",
- "SPDXID": "SPDXRef-Package-deb-package-2-4b756c6f6fb127a3",
+ "SPDXID": "SPDXRef-Package-deb-package-2-fe989317bb1cbb62",
"versionInfo": "2.0.1",
"supplier": "NOASSERTION",
"downloadLocation": "NOASSERTION",
@@ -198,43 +198,43 @@
],
"relationships": [
{
- "spdxElementId": "SPDXRef-Package-python-package-1-c5cf7ac34cbca450",
+ "spdxElementId": "SPDXRef-Package-python-package-1-69910a93dc37ffb4",
"relatedSpdxElement": "SPDXRef-File-f1-5265a4dde3edbf7c",
"relationshipType": "CONTAINS"
},
{
- "spdxElementId": "SPDXRef-Package-python-package-1-c5cf7ac34cbca450",
+ "spdxElementId": "SPDXRef-Package-python-package-1-69910a93dc37ffb4",
"relatedSpdxElement": "SPDXRef-File-z1-f5-839d99ee67d9d174",
"relationshipType": "CONTAINS"
},
{
- "spdxElementId": "SPDXRef-Package-python-package-1-c5cf7ac34cbca450",
+ "spdxElementId": "SPDXRef-Package-python-package-1-69910a93dc37ffb4",
"relatedSpdxElement": "SPDXRef-File-a1-f6-9c2f7510199b17f6",
"relationshipType": "CONTAINS"
},
{
- "spdxElementId": "SPDXRef-Package-python-package-1-c5cf7ac34cbca450",
+ "spdxElementId": "SPDXRef-Package-python-package-1-69910a93dc37ffb4",
"relatedSpdxElement": "SPDXRef-File-d2-f4-c641caa71518099f",
"relationshipType": "CONTAINS"
},
{
- "spdxElementId": "SPDXRef-Package-python-package-1-c5cf7ac34cbca450",
+ "spdxElementId": "SPDXRef-Package-python-package-1-69910a93dc37ffb4",
"relatedSpdxElement": "SPDXRef-File-d1-f3-c6f5b29dca12661f",
"relationshipType": "CONTAINS"
},
{
- "spdxElementId": "SPDXRef-Package-python-package-1-c5cf7ac34cbca450",
+ "spdxElementId": "SPDXRef-Package-python-package-1-69910a93dc37ffb4",
"relatedSpdxElement": "SPDXRef-File-f2-f9e49132a4b96ccd",
"relationshipType": "CONTAINS"
},
{
"spdxElementId": "SPDXRef-DocumentRoot-Image-user-image-input",
- "relatedSpdxElement": "SPDXRef-Package-python-package-1-c5cf7ac34cbca450",
+ "relatedSpdxElement": "SPDXRef-Package-python-package-1-69910a93dc37ffb4",
"relationshipType": "CONTAINS"
},
{
"spdxElementId": "SPDXRef-DocumentRoot-Image-user-image-input",
- "relatedSpdxElement": "SPDXRef-Package-deb-package-2-4b756c6f6fb127a3",
+ "relatedSpdxElement": "SPDXRef-Package-deb-package-2-fe989317bb1cbb62",
"relationshipType": "CONTAINS"
},
{
diff --git a/syft/format/spdxtagvalue/test-fixtures/snapshot/TestSPDXJSONSPDXIDs.golden b/syft/format/spdxtagvalue/test-fixtures/snapshot/TestSPDXJSONSPDXIDs.golden
index dd946aa235b1..4a6cd08c23e1 100644
--- a/syft/format/spdxtagvalue/test-fixtures/snapshot/TestSPDXJSONSPDXIDs.golden
+++ b/syft/format/spdxtagvalue/test-fixtures/snapshot/TestSPDXJSONSPDXIDs.golden
@@ -22,7 +22,7 @@ PackageLicenseDeclared: NOASSERTION
##### Package: @at-sign
PackageName: @at-sign
-SPDXID: SPDXRef-Package--at-sign-1c8c811ea5b1cd46
+SPDXID: SPDXRef-Package--at-sign-ec109f3d122ef1db
PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION
FilesAnalyzed: false
@@ -34,7 +34,7 @@ PackageCopyrightText: NOASSERTION
##### Package: some/slashes
PackageName: some/slashes
-SPDXID: SPDXRef-Package-some-slashes-8a8e95924316c66b
+SPDXID: SPDXRef-Package-some-slashes-8a21771e3392022f
PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION
FilesAnalyzed: false
@@ -46,7 +46,7 @@ PackageCopyrightText: NOASSERTION
##### Package: under_scores
PackageName: under_scores
-SPDXID: SPDXRef-Package-under-scores-883703d950ec00f3
+SPDXID: SPDXRef-Package-under-scores-5db453bf3f332f99
PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION
FilesAnalyzed: false
@@ -57,8 +57,8 @@ PackageCopyrightText: NOASSERTION
##### Relationships
-Relationship: SPDXRef-DocumentRoot-Directory-foobar-baz CONTAINS SPDXRef-Package--at-sign-1c8c811ea5b1cd46
-Relationship: SPDXRef-DocumentRoot-Directory-foobar-baz CONTAINS SPDXRef-Package-some-slashes-8a8e95924316c66b
-Relationship: SPDXRef-DocumentRoot-Directory-foobar-baz CONTAINS SPDXRef-Package-under-scores-883703d950ec00f3
+Relationship: SPDXRef-DocumentRoot-Directory-foobar-baz CONTAINS SPDXRef-Package--at-sign-ec109f3d122ef1db
+Relationship: SPDXRef-DocumentRoot-Directory-foobar-baz CONTAINS SPDXRef-Package-some-slashes-8a21771e3392022f
+Relationship: SPDXRef-DocumentRoot-Directory-foobar-baz CONTAINS SPDXRef-Package-under-scores-5db453bf3f332f99
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-DocumentRoot-Directory-foobar-baz
diff --git a/syft/format/spdxtagvalue/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden b/syft/format/spdxtagvalue/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden
index 75cab71eb796..168c12624741 100644
--- a/syft/format/spdxtagvalue/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden
+++ b/syft/format/spdxtagvalue/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden
@@ -69,7 +69,7 @@ ExternalRef: PACKAGE-MANAGER purl pkg:oci/user-image-input@sha256:2731251dc34951
##### Package: package-2
PackageName: package-2
-SPDXID: SPDXRef-Package-deb-package-2-4b756c6f6fb127a3
+SPDXID: SPDXRef-Package-deb-package-2-fe989317bb1cbb62
PackageVersion: 2.0.1
PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION
@@ -84,7 +84,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-c5cf7ac34cbca450
+SPDXID: SPDXRef-Package-python-package-1-69910a93dc37ffb4
PackageVersion: 1.0.1
PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION
@@ -98,13 +98,13 @@ ExternalRef: PACKAGE-MANAGER purl a-purl-1
##### Relationships
-Relationship: SPDXRef-Package-python-package-1-c5cf7ac34cbca450 CONTAINS SPDXRef-File-f1-5265a4dde3edbf7c
-Relationship: SPDXRef-Package-python-package-1-c5cf7ac34cbca450 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-c5cf7ac34cbca450 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-c5cf7ac34cbca450 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-deb-package-2-4b756c6f6fb127a3
+Relationship: SPDXRef-Package-python-package-1-69910a93dc37ffb4 CONTAINS SPDXRef-File-f1-5265a4dde3edbf7c
+Relationship: SPDXRef-Package-python-package-1-69910a93dc37ffb4 CONTAINS SPDXRef-File-z1-f5-839d99ee67d9d174
+Relationship: SPDXRef-Package-python-package-1-69910a93dc37ffb4 CONTAINS SPDXRef-File-a1-f6-9c2f7510199b17f6
+Relationship: SPDXRef-Package-python-package-1-69910a93dc37ffb4 CONTAINS SPDXRef-File-d2-f4-c641caa71518099f
+Relationship: SPDXRef-Package-python-package-1-69910a93dc37ffb4 CONTAINS SPDXRef-File-d1-f3-c6f5b29dca12661f
+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-69910a93dc37ffb4
+Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-deb-package-2-fe989317bb1cbb62
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-DocumentRoot-Image-user-image-input
diff --git a/syft/format/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueDirectoryEncoder.golden b/syft/format/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueDirectoryEncoder.golden
index bccd8acc0f2d..9bbc6473d27b 100644
--- a/syft/format/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueDirectoryEncoder.golden
+++ b/syft/format/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueDirectoryEncoder.golden
@@ -22,7 +22,7 @@ PackageLicenseDeclared: NOASSERTION
##### Package: package-2
PackageName: package-2
-SPDXID: SPDXRef-Package-deb-package-2-39392bb5e270f669
+SPDXID: SPDXRef-Package-deb-package-2-062f404587213e8b
PackageVersion: 2.0.1
PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION
@@ -37,7 +37,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-5a2b1ae000fcb51e
+SPDXID: SPDXRef-Package-python-package-1-f7fdfcfa4ca6e742
PackageVersion: 1.0.1
PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION
@@ -51,7 +51,7 @@ ExternalRef: PACKAGE-MANAGER purl a-purl-2
##### Relationships
-Relationship: SPDXRef-DocumentRoot-Directory-some-path CONTAINS SPDXRef-Package-python-package-1-5a2b1ae000fcb51e
-Relationship: SPDXRef-DocumentRoot-Directory-some-path CONTAINS SPDXRef-Package-deb-package-2-39392bb5e270f669
+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-062f404587213e8b
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-DocumentRoot-Directory-some-path
diff --git a/syft/format/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueImageEncoder.golden b/syft/format/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueImageEncoder.golden
index c93fb63298fe..6eff49f246f3 100644
--- a/syft/format/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueImageEncoder.golden
+++ b/syft/format/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueImageEncoder.golden
@@ -25,7 +25,7 @@ ExternalRef: PACKAGE-MANAGER purl pkg:oci/user-image-input@sha256:2731251dc34951
##### Package: package-2
PackageName: package-2
-SPDXID: SPDXRef-Package-deb-package-2-4b756c6f6fb127a3
+SPDXID: SPDXRef-Package-deb-package-2-fe989317bb1cbb62
PackageVersion: 2.0.1
PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION
@@ -40,7 +40,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-c5cf7ac34cbca450
+SPDXID: SPDXRef-Package-python-package-1-69910a93dc37ffb4
PackageVersion: 1.0.1
PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION
@@ -54,7 +54,7 @@ ExternalRef: PACKAGE-MANAGER purl a-purl-1
##### 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-deb-package-2-4b756c6f6fb127a3
+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-fe989317bb1cbb62
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-DocumentRoot-Image-user-image-input
diff --git a/syft/format/syftjson/model/package.go b/syft/format/syftjson/model/package.go
index 503709d1f16b..ca252b00a1bc 100644
--- a/syft/format/syftjson/model/package.go
+++ b/syft/format/syftjson/model/package.go
@@ -24,16 +24,17 @@ type Package struct {
// PackageBasicData contains non-ambiguous values (type-wise) from pkg.Package.
type PackageBasicData struct {
- ID string `json:"id"`
- Name string `json:"name"`
- Version string `json:"version"`
- Type pkg.Type `json:"type"`
- FoundBy string `json:"foundBy"`
- Locations []file.Location `json:"locations"`
- Licenses licenses `json:"licenses"`
- Language pkg.Language `json:"language"`
- CPEs cpes `json:"cpes"`
- PURL string `json:"purl"`
+ ID string `json:"id"`
+ Name string `json:"name"`
+ Version string `json:"version"`
+ Type pkg.Type `json:"type"`
+ FoundBy string `json:"foundBy"`
+ Locations []file.Location `json:"locations"`
+ Licenses licenses `json:"licenses"`
+ Copyrights copyrights `json:"copyrights"`
+ Language pkg.Language `json:"language"`
+ CPEs cpes `json:"cpes"`
+ PURL string `json:"purl"`
}
type cpes []CPE
@@ -53,6 +54,15 @@ type License struct {
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) {
for _, v := range licenses {
expression, err := license.ParseExpression(v)
diff --git a/syft/format/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden b/syft/format/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden
index 0ae05a3aa5fa..a778afff863b 100644
--- a/syft/format/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden
+++ b/syft/format/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden
@@ -1,7 +1,7 @@
{
"artifacts": [
{
- "id": "5a2b1ae000fcb51e",
+ "id": "f7fdfcfa4ca6e742",
"name": "package-1",
"version": "1.0.1",
"type": "python",
@@ -21,6 +21,7 @@
"locations": []
}
],
+ "copyrights": [],
"language": "python",
"cpes": [
{
@@ -44,7 +45,7 @@
}
},
{
- "id": "39392bb5e270f669",
+ "id": "062f404587213e8b",
"name": "package-2",
"version": "2.0.1",
"type": "deb",
@@ -56,6 +57,7 @@
}
],
"licenses": [],
+ "copyrights": [],
"language": "",
"cpes": [
{
diff --git a/syft/format/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden b/syft/format/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden
index 6fc9041e9f13..ca774d826b9c 100644
--- a/syft/format/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden
+++ b/syft/format/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden
@@ -1,7 +1,7 @@
{
"artifacts": [
{
- "id": "ad3ecac55fe1c30f",
+ "id": "ecf423ccf313f850",
"name": "package-1",
"version": "1.0.1",
"type": "python",
@@ -21,6 +21,7 @@
"locations": []
}
],
+ "copyrights": [],
"language": "python",
"cpes": [
{
@@ -40,7 +41,7 @@
}
},
{
- "id": "fa4ec37eccd65756",
+ "id": "b4d209e1bb8d83cb",
"name": "package-2",
"version": "2.0.1",
"type": "deb",
@@ -52,6 +53,7 @@
}
],
"licenses": [],
+ "copyrights": [],
"language": "",
"cpes": [
{
diff --git a/syft/format/syftjson/test-fixtures/snapshot/TestImageEncoder.golden b/syft/format/syftjson/test-fixtures/snapshot/TestImageEncoder.golden
index f013f2026a05..517eebab2089 100644
--- a/syft/format/syftjson/test-fixtures/snapshot/TestImageEncoder.golden
+++ b/syft/format/syftjson/test-fixtures/snapshot/TestImageEncoder.golden
@@ -1,7 +1,7 @@
{
"artifacts": [
{
- "id": "c5cf7ac34cbca450",
+ "id": "69910a93dc37ffb4",
"name": "package-1",
"version": "1.0.1",
"type": "python",
@@ -22,6 +22,7 @@
"locations": []
}
],
+ "copyrights": [],
"language": "python",
"cpes": [
{
@@ -41,7 +42,7 @@
}
},
{
- "id": "4b756c6f6fb127a3",
+ "id": "fe989317bb1cbb62",
"name": "package-2",
"version": "2.0.1",
"type": "deb",
@@ -54,6 +55,7 @@
}
],
"licenses": [],
+ "copyrights": [],
"language": "",
"cpes": [
{
diff --git a/syft/format/syftjson/to_format_model.go b/syft/format/syftjson/to_format_model.go
index 42ec48f77d2c..7b3ef8247d7b 100644
--- a/syft/format/syftjson/to_format_model.go
+++ b/syft/format/syftjson/to_format_model.go
@@ -233,6 +233,18 @@ func toLicenseModel(pkgLicenses []pkg.License) (modelLicenses []model.License) {
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.
func toPackageModel(p pkg.Package, cfg EncoderConfig) model.Package {
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())
}
+ var copyrights = make([]model.Copyright, 0)
+ if !p.Copyrights.Empty() {
+ copyrights = toCopyrightModel(p.Copyrights.ToSlice())
+ }
+
return model.Package{
PackageBasicData: model.PackageBasicData{
- ID: string(p.ID()),
- Name: p.Name,
- Version: p.Version,
- Type: p.Type,
- FoundBy: p.FoundBy,
- Locations: p.Locations.ToSlice(),
- Licenses: licenses,
- Language: p.Language,
- CPEs: cpes,
- PURL: p.PURL,
+ ID: string(p.ID()),
+ Name: p.Name,
+ Version: p.Version,
+ Type: p.Type,
+ FoundBy: p.FoundBy,
+ Locations: p.Locations.ToSlice(),
+ Licenses: licenses,
+ Copyrights: copyrights,
+ Language: p.Language,
+ CPEs: cpes,
+ PURL: p.PURL,
},
PackageCustomData: model.PackageCustomData{
MetadataType: metadataType(p.Metadata, cfg.Legacy),
diff --git a/syft/format/syftjson/to_syft_model.go b/syft/format/syftjson/to_syft_model.go
index 289e91fda527..241a4a159792 100644
--- a/syft/format/syftjson/to_syft_model.go
+++ b/syft/format/syftjson/to_syft_model.go
@@ -162,6 +162,19 @@ func toSyftLicenses(m []model.License) (p []pkg.License) {
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 {
switch ty {
case "SymbolicLink":
@@ -331,16 +344,17 @@ func toSyftPackage(p model.Package, idAliases map[string]string) pkg.Package {
}
out := pkg.Package{
- Name: p.Name,
- Version: p.Version,
- FoundBy: p.FoundBy,
- Locations: file.NewLocationSet(p.Locations...),
- Licenses: pkg.NewLicenseSet(toSyftLicenses(p.Licenses)...),
- Language: p.Language,
- Type: p.Type,
- CPEs: cpes,
- PURL: p.PURL,
- Metadata: p.Metadata,
+ Name: p.Name,
+ Version: p.Version,
+ FoundBy: p.FoundBy,
+ Locations: file.NewLocationSet(p.Locations...),
+ Licenses: pkg.NewLicenseSet(toSyftLicenses(p.Licenses)...),
+ Copyrights: pkg.NewCopyrightSet(toSyftCopyrights(p.Copyrights)...),
+ Language: p.Language,
+ Type: p.Type,
+ CPEs: cpes,
+ 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
diff --git a/syft/internal/packagemetadata/discover_type_names.go b/syft/internal/packagemetadata/discover_type_names.go
index 03f8a4cc62b7..ed8c4701c89a 100644
--- a/syft/internal/packagemetadata/discover_type_names.go
+++ b/syft/internal/packagemetadata/discover_type_names.go
@@ -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
// in a metadata type but are not metadata types themselves.
names.Remove("Licenses", "KeyValue")
+ names.Remove("Copyrights", "KeyValue")
+ names.Remove("CopyrightsSet", "KeyValue")
strNames := names.List()
sort.Strings(strNames)
diff --git a/syft/pkg/cataloger/binary/elf_package_test.go b/syft/pkg/cataloger/binary/elf_package_test.go
index 85fa422211f2..93e16343b1ba 100644
--- a/syft/pkg/cataloger/binary/elf_package_test.go
+++ b/syft/pkg/cataloger/binary/elf_package_test.go
@@ -157,7 +157,7 @@ func Test_newELFPackage(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
actual := newELFPackage(test.metadata, file.NewLocationSet())
- 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)
}
})
diff --git a/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go b/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go
index ae26bb6d13b0..ce743bec5d63 100644
--- a/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go
+++ b/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go
@@ -42,6 +42,7 @@ type CatalogTester struct {
compareOptions []cmp.Option
locationComparer cmptest.LocationComparer
licenseComparer cmptest.LicenseComparer
+ copyrightComparer cmptest.CopyrightComparer
packageStringer func(pkg.Package) string
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) {
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()
@@ -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)
}
+//nolint:funlen
func AssertPackagesEqual(t *testing.T, a, b pkg.Package) {
t.Helper()
opts := []cmp.Option{
@@ -360,12 +362,33 @@ func AssertPackagesEqual(t *testing.T, a, b pkg.Package) {
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(
cmptest.DefaultLocationComparer,
),
cmp.Comparer(
cmptest.DefaultLicenseComparer,
),
+ cmp.Comparer(
+ cmptest.DefaultCopyrightComparer,
+ ),
}
if diff := cmp.Diff(a, b, opts...); diff != "" {
diff --git a/syft/pkg/copyright.go b/syft/pkg/copyright.go
new file mode 100644
index 000000000000..a81fa617a0a7
--- /dev/null
+++ b/syft/pkg/copyright.go
@@ -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 ""
+}
diff --git a/syft/pkg/copyright_set.go b/syft/pkg/copyright_set.go
new file mode 100644
index 000000000000..8c4c4ab69ffe
--- /dev/null
+++ b/syft/pkg/copyright_set.go
@@ -0,0 +1,88 @@
+//nolint:dupl
+package pkg
+
+import (
+ "fmt"
+ "sort"
+
+ "github.com/anchore/syft/internal/log"
+ "github.com/anchore/syft/syft/artifact"
+ "github.com/mitchellh/hashstructure/v2"
+)
+
+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
+}
diff --git a/syft/pkg/license_set.go b/syft/pkg/license_set.go
index 99593fae2a25..fdf104f4da88 100644
--- a/syft/pkg/license_set.go
+++ b/syft/pkg/license_set.go
@@ -1,13 +1,13 @@
+//nolint:dupl
package pkg
import (
"fmt"
"sort"
- "github.com/mitchellh/hashstructure/v2"
-
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/artifact"
+ "github.com/mitchellh/hashstructure/v2"
)
type LicenseSet struct {
diff --git a/syft/pkg/package.go b/syft/pkg/package.go
index 8ee8d969fae7..8b52cfc93b66 100644
--- a/syft/pkg/package.go
+++ b/syft/pkg/package.go
@@ -17,17 +17,19 @@ 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?
type Package struct {
- id artifact.ID `hash:"ignore"`
- Name string // the package name
- Version string // the version of the 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)
- 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)
- 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
+ id artifact.ID `hash:"ignore"`
+ Name string // the package name
+ Version string // the version of the 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)
+ Licenses LicenseSet // licenses discovered with the package metadata
+ Copyrights CopyrightsSet // copyrights 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)
+ 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) {
diff --git a/syft/pkg/package_test.go b/syft/pkg/package_test.go
index 94896d652ed4..c3e5397ea248 100644
--- a/syft/pkg/package_test.go
+++ b/syft/pkg/package_test.go
@@ -416,6 +416,24 @@ func TestPackage_Merge(t *testing.T) {
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),
); 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))
}
+func copyrightComparer(x, y Copyright) bool {
+ return cmp.Equal(x, y, cmp.Comparer(copyrightComparer))
+}
+
func locationComparer(x, y file.Location) bool {
return cmp.Equal(x.Coordinates, y.Coordinates) && cmp.Equal(x.AccessPath, y.AccessPath)
}