diff --git a/internal/constants.go b/internal/constants.go index 05e847cafd0..acf759eb146 100644 --- a/internal/constants.go +++ b/internal/constants.go @@ -6,5 +6,5 @@ const ( // JSONSchemaVersion is the current schema version output by the JSON encoder // This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment. - JSONSchemaVersion = "7.0.1" + JSONSchemaVersion = "7.0.2" ) diff --git a/internal/logicalstrings.go b/internal/logicalstrings.go new file mode 100644 index 00000000000..0620d2fd5d5 --- /dev/null +++ b/internal/logicalstrings.go @@ -0,0 +1,153 @@ +package internal + +import ( + "fmt" + "strings" + + "github.com/invopop/jsonschema" +) + +type Joiner string + +const ( + AND Joiner = "AND" + OR Joiner = "OR" +) + +// LogicalStrings is a helper type for building logical expressions of strings, which can be combined +// in complex compound ways, with logical AND and OR. If no Joiner is provided, the default is AND. +type LogicalStrings struct { + Compound []LogicalStrings + Simple []string + Joiner +} + +func (l LogicalStrings) Size() int { + return len(l.Compound) + len(l.Simple) +} + +func (l LogicalStrings) String() string { + size := l.Size() + if size == 0 { + return "" + } + var parts []string + // first get the simple + parts = append(parts, l.Simple...) + // them get the complex + for _, e := range l.Compound { + s := e.String() + if e.Size() > 1 { + s = "(" + s + ")" + } + parts = append(parts, s) + } + joiner := l.Joiner + if joiner == "" { + joiner = AND + } + return strings.Join(parts, fmt.Sprintf(" %s ", joiner)) +} + +func (l LogicalStrings) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, l.String())), nil +} + +func (l *LogicalStrings) UnmarshalJSON(data []byte) error { + raw := strings.Trim(string(data), `"`) + ls, err := ParseLogicalStrings(raw) + if err != nil { + return err + } + *l = ls + return nil +} + +// Process processes each simple element inside the LogicalStrings through a provided function, +// returning a new LogicalStrings with the fields replaced. +func (l LogicalStrings) Process(f func(string) string) LogicalStrings { + var new LogicalStrings + for _, e := range l.Simple { + new.Simple = append(new.Simple, f(e)) + } + for _, e := range l.Compound { + new.Compound = append(new.Compound, e.Process(f)) + } + return new +} + +// Elements returns all the elements of the LogicalStrings, the simple elements at the base of every compound. +func (l LogicalStrings) Elements() []string { + var elements []string + elements = append(elements, l.Simple...) + for _, e := range l.Compound { + elements = append(elements, e.Elements()...) + } + return elements +} + +func (l LogicalStrings) JSONSchema() *jsonschema.Schema { + return &jsonschema.Schema{ + Type: "string", + Title: "Logical Strings", + Description: "strings with simple or complex logical combinations", + } +} + +// ParseLogicalStrings parse strings joined by AND or OR, as well as compounded by ( and ), into a LogicalStrings struct +func ParseLogicalStrings(s string) (LogicalStrings, error) { + var ( + currentExpression string + expressionStack []string + currentLS LogicalStrings + lsStack []LogicalStrings + ) + + for _, c := range s { + switch c { + case '(': + expressionStack = append(expressionStack, currentExpression) + currentExpression = "" + lsStack = append(lsStack, currentLS) + currentLS = LogicalStrings{} + case ')': + simple, joiner := parseSimpleExpression(currentExpression) + currentLS.Simple = append(currentLS.Simple, simple...) + currentLS.Joiner = joiner + if len(expressionStack) == 0 { + return LogicalStrings{}, fmt.Errorf("unbalanced parentheses") + } + currentExpression = expressionStack[len(expressionStack)-1] + expressionStack = expressionStack[:len(expressionStack)-1] + lastLS := lsStack[len(lsStack)-1] + lastLS.Compound = append(lastLS.Compound, currentLS) + lsStack = lsStack[:len(lsStack)-1] + currentLS = lastLS + default: + currentExpression += string(c) + } + } + if currentExpression != "" { + simple, joiner := parseSimpleExpression(currentExpression) + if len(simple) > 0 { + currentLS.Simple = append(currentLS.Simple, simple...) + } + currentLS.Joiner = joiner + } + return currentLS, nil +} + +func parseSimpleExpression(s string) ([]string, Joiner) { + var ( + elements []string + joiner Joiner + ) + for _, e := range strings.Fields(s) { + if e == "AND" || e == "OR" { + joiner = Joiner(e) + } else { + elements = append(elements, e) + } + } + return elements, joiner +} diff --git a/internal/logicalstrings_test.go b/internal/logicalstrings_test.go new file mode 100644 index 00000000000..8176ef67a7a --- /dev/null +++ b/internal/logicalstrings_test.go @@ -0,0 +1,62 @@ +package internal + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestLogicalStrings(t *testing.T) { + tests := []struct { + elm LogicalStrings + expected string + }{ + {LogicalStrings{Simple: []string{"a"}}, "a"}, + {LogicalStrings{Simple: []string{"a", "b"}}, "a AND b"}, + {LogicalStrings{Simple: []string{"a", "b"}, Joiner: AND}, "a AND b"}, + {LogicalStrings{Simple: []string{"a", "b", "c"}, Joiner: OR}, "a OR b OR c"}, + {LogicalStrings{ + Compound: []LogicalStrings{ + {Simple: []string{"a", "b"}, Joiner: OR}, + {Simple: []string{"c", "d"}, Joiner: OR}, + }, + Joiner: AND, + }, "(a OR b) AND (c OR d)"}, + } + for _, test := range tests { + t.Run(test.expected, func(t *testing.T) { + assert.Equal(t, test.expected, test.elm.String()) + }) + } +} + +func TestParseLogicalStrings(t *testing.T) { + tests := []struct { + input string + expected LogicalStrings + }{ + {"a", LogicalStrings{Simple: []string{"a"}}}, + {"a AND b", LogicalStrings{Simple: []string{"a", "b"}, Joiner: AND}}, + {"a OR b", LogicalStrings{Simple: []string{"a", "b"}, Joiner: OR}}, + {"a AND (b OR c)", LogicalStrings{Simple: []string{"a"}, Joiner: AND, Compound: []LogicalStrings{ + {Simple: []string{"b", "c"}, Joiner: OR}, + }}}, + {"(a AND b) OR (c AND d)", LogicalStrings{Joiner: OR, Compound: []LogicalStrings{ + {Simple: []string{"a", "b"}, Joiner: AND}, + {Simple: []string{"c", "d"}, Joiner: AND}, + }}}, + {"(a AND b) OR (c AND (d OR e))", LogicalStrings{Joiner: OR, Compound: []LogicalStrings{ + {Simple: []string{"a", "b"}, Joiner: AND}, + {Simple: []string{"c"}, Compound: []LogicalStrings{ + {Simple: []string{"d", "e"}, Joiner: OR}, + }, Joiner: AND}, + }}}, + } + for _, test := range tests { + t.Run(test.input, func(t *testing.T) { + actual, err := ParseLogicalStrings(test.input) + assert.NoError(t, err) + assert.Equal(t, test.expected, actual) + }) + } +} diff --git a/internal/spdxlicense/license.go b/internal/spdxlicense/license.go index 1f83034df41..3ef47fd794e 100644 --- a/internal/spdxlicense/license.go +++ b/internal/spdxlicense/license.go @@ -21,7 +21,7 @@ const ( func ID(id string) (value, other string, exists bool) { id = strings.TrimSpace(id) // ignore blank strings or the joiner - if id == "" || id == "AND" { + if id == "" || id == "AND" || id == "OR" { return "", "", false } // first look for a canonical license diff --git a/internal/spdxlicense/license_test.go b/internal/spdxlicense/license_test.go index 23a54a87cf3..19eee6df4fd 100644 --- a/internal/spdxlicense/license_test.go +++ b/internal/spdxlicense/license_test.go @@ -86,6 +86,12 @@ func TestIDParse(t *testing.T) { "", false, }, + { + "OR", + "", + "", + false, + }, } for _, test := range tests { diff --git a/schema/json/schema-7.0.0.json b/schema/json/schema-7.0.0.json index 63161e14d01..575f51e5355 100644 --- a/schema/json/schema-7.0.0.json +++ b/schema/json/schema-7.0.0.json @@ -202,16 +202,21 @@ }, "BinaryMetadata": { "properties": { - "matches": { - "items": { - "$ref": "#/$defs/ClassifierMatch" - }, - "type": "array" + "classifier": { + "type": "string" + }, + "realPath": { + "type": "string" + }, + "virtualPath": { + "type": "string" } }, "type": "object", "required": [ - "matches" + "classifier", + "realPath", + "virtualPath" ] }, "CargoPackageMetadata": { @@ -244,21 +249,6 @@ "dependencies" ] }, - "ClassifierMatch": { - "properties": { - "classifier": { - "type": "string" - }, - "location": { - "$ref": "#/$defs/Location" - } - }, - "type": "object", - "required": [ - "classifier", - "location" - ] - }, "CocoapodsMetadata": { "properties": { "checksum": { @@ -803,22 +793,10 @@ }, "type": "object" }, - "Location": { - "properties": { - "path": { - "type": "string" - }, - "layerID": { - "type": "string" - }, - "virtualPath": { - "type": "string" - } - }, - "type": "object", - "required": [ - "path" - ] + "LogicalStrings": { + "type": "string", + "title": "Logical Strings", + "description": "strings with simple or complex logical combinations" }, "MixLockMetadata": { "properties": { @@ -930,10 +908,7 @@ "type": "array" }, "licenses": { - "items": { - "type": "string" - }, - "type": "array" + "$ref": "#/$defs/LogicalStrings" }, "language": { "type": "string" diff --git a/schema/json/schema-7.0.2.json b/schema/json/schema-7.0.2.json new file mode 100644 index 00000000000..ee2a95b4906 --- /dev/null +++ b/schema/json/schema-7.0.2.json @@ -0,0 +1,1647 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/anchore/syft/syft/formats/syftjson/model/document", + "$ref": "#/$defs/Document", + "$defs": { + "AlpmFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "type": { + "type": "string" + }, + "uid": { + "type": "string" + }, + "gid": { + "type": "string" + }, + "time": { + "type": "string", + "format": "date-time" + }, + "size": { + "type": "string" + }, + "link": { + "type": "string" + }, + "digest": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object" + }, + "AlpmMetadata": { + "properties": { + "basepackage": { + "type": "string" + }, + "package": { + "type": "string" + }, + "version": { + "type": "string" + }, + "description": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "packager": { + "type": "string" + }, + "license": { + "type": "string" + }, + "url": { + "type": "string" + }, + "validation": { + "type": "string" + }, + "reason": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/AlpmFileRecord" + }, + "type": "array" + }, + "backup": { + "items": { + "$ref": "#/$defs/AlpmFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "basepackage", + "package", + "version", + "description", + "architecture", + "size", + "packager", + "license", + "url", + "validation", + "reason", + "files", + "backup" + ] + }, + "ApkFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "ownerUid": { + "type": "string" + }, + "ownerGid": { + "type": "string" + }, + "permissions": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "ApkMetadata": { + "properties": { + "package": { + "type": "string" + }, + "originPackage": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "version": { + "type": "string" + }, + "license": { + "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", + "license", + "architecture", + "url", + "description", + "size", + "installedSize", + "pullDependencies", + "provides", + "pullChecksum", + "gitCommitOfApkPort", + "files" + ] + }, + "BinaryMetadata": { + "properties": { + "matches": { + "items": { + "$ref": "#/$defs/ClassifierMatch" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "matches" + ] + }, + "CargoPackageMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "type": "string" + }, + "checksum": { + "type": "string" + }, + "dependencies": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "checksum", + "dependencies" + ] + }, + "ClassifierMatch": { + "properties": { + "classifier": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Location" + } + }, + "type": "object", + "required": [ + "classifier", + "location" + ] + }, + "CocoapodsMetadata": { + "properties": { + "checksum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "checksum" + ] + }, + "ConanLockMetadata": { + "properties": { + "ref": { + "type": "string" + }, + "package_id": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "requires": { + "type": "string" + }, + "build_requires": { + "type": "string" + }, + "py_requires": { + "type": "string" + }, + "options": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "path": { + "type": "string" + }, + "context": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "ConanMetadata": { + "properties": { + "ref": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "Coordinates": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "DartPubMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "hosted_url": { + "type": "string" + }, + "vcs_url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Descriptor": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "configuration": true + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Digest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algorithm", + "value" + ] + }, + "Document": { + "properties": { + "artifacts": { + "items": { + "$ref": "#/$defs/Package" + }, + "type": "array" + }, + "artifactRelationships": { + "items": { + "$ref": "#/$defs/Relationship" + }, + "type": "array" + }, + "files": { + "items": { + "$ref": "#/$defs/File" + }, + "type": "array" + }, + "secrets": { + "items": { + "$ref": "#/$defs/Secrets" + }, + "type": "array" + }, + "source": { + "$ref": "#/$defs/Source" + }, + "distro": { + "$ref": "#/$defs/LinuxRelease" + }, + "descriptor": { + "$ref": "#/$defs/Descriptor" + }, + "schema": { + "$ref": "#/$defs/Schema" + } + }, + "type": "object", + "required": [ + "artifacts", + "artifactRelationships", + "source", + "distro", + "descriptor", + "schema" + ] + }, + "DotnetDepsMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "path": { + "type": "string" + }, + "sha512": { + "type": "string" + }, + "hashPath": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "path", + "sha512", + "hashPath" + ] + }, + "DpkgFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "isConfigFile": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "path", + "isConfigFile" + ] + }, + "DpkgMetadata": { + "properties": { + "package": { + "type": "string" + }, + "source": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "installedSize": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/DpkgFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "source", + "version", + "sourceVersion", + "architecture", + "maintainer", + "installedSize", + "files" + ] + }, + "File": { + "properties": { + "id": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Coordinates" + }, + "metadata": { + "$ref": "#/$defs/FileMetadataEntry" + }, + "contents": { + "type": "string" + }, + "digests": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "id", + "location" + ] + }, + "FileMetadataEntry": { + "properties": { + "mode": { + "type": "integer" + }, + "type": { + "type": "string" + }, + "linkDestination": { + "type": "string" + }, + "userID": { + "type": "integer" + }, + "groupID": { + "type": "integer" + }, + "mimeType": { + "type": "string" + } + }, + "type": "object", + "required": [ + "mode", + "type", + "userID", + "groupID", + "mimeType" + ] + }, + "GemMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "type": "string" + }, + "type": "array" + }, + "licenses": { + "items": { + "type": "string" + }, + "type": "array" + }, + "homepage": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "GolangBinMetadata": { + "properties": { + "goBuildSettings": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "goCompiledVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "h1Digest": { + "type": "string" + }, + "mainModule": { + "type": "string" + } + }, + "type": "object", + "required": [ + "goCompiledVersion", + "architecture" + ] + }, + "GolangModMetadata": { + "properties": { + "h1Digest": { + "type": "string" + } + }, + "type": "object" + }, + "HackageMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "snapshotURL": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "IDLikes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "JavaManifest": { + "properties": { + "main": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "namedSections": { + "patternProperties": { + ".*": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "JavaMetadata": { + "properties": { + "virtualPath": { + "type": "string" + }, + "manifest": { + "$ref": "#/$defs/JavaManifest" + }, + "pomProperties": { + "$ref": "#/$defs/PomProperties" + }, + "pomProject": { + "$ref": "#/$defs/PomProject" + }, + "digest": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "virtualPath" + ] + }, + "KbPackageMetadata": { + "properties": { + "product_id": { + "type": "string" + }, + "kb": { + "type": "string" + } + }, + "type": "object", + "required": [ + "product_id", + "kb" + ] + }, + "LinuxRelease": { + "properties": { + "prettyName": { + "type": "string" + }, + "name": { + "type": "string" + }, + "id": { + "type": "string" + }, + "idLike": { + "$ref": "#/$defs/IDLikes" + }, + "version": { + "type": "string" + }, + "versionID": { + "type": "string" + }, + "versionCodename": { + "type": "string" + }, + "buildID": { + "type": "string" + }, + "imageID": { + "type": "string" + }, + "imageVersion": { + "type": "string" + }, + "variant": { + "type": "string" + }, + "variantID": { + "type": "string" + }, + "homeURL": { + "type": "string" + }, + "supportURL": { + "type": "string" + }, + "bugReportURL": { + "type": "string" + }, + "privacyPolicyURL": { + "type": "string" + }, + "cpeName": { + "type": "string" + }, + "supportEnd": { + "type": "string" + } + }, + "type": "object" + }, + "Location": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + }, + "virtualPath": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "LogicalStrings": { + "type": "string", + "title": "Logical Strings", + "description": "strings with simple or complex logical combinations" + }, + "MixLockMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "pkgHashExt": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "pkgHash", + "pkgHashExt" + ] + }, + "NpmPackageJSONMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + }, + "author": { + "type": "string" + }, + "licenses": { + "items": { + "type": "string" + }, + "type": "array" + }, + "homepage": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + }, + "private": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "licenses", + "homepage", + "description", + "url", + "private" + ] + }, + "NpmPackageLockJSONMetadata": { + "properties": { + "resolved": { + "type": "string" + }, + "integrity": { + "type": "string" + } + }, + "type": "object", + "required": [ + "resolved", + "integrity" + ] + }, + "Package": { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "type": { + "type": "string" + }, + "foundBy": { + "type": "string" + }, + "locations": { + "items": { + "$ref": "#/$defs/Coordinates" + }, + "type": "array" + }, + "licenses": { + "$ref": "#/$defs/LogicalStrings" + }, + "language": { + "type": "string" + }, + "cpes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "purl": { + "type": "string" + }, + "metadataType": { + "type": "string" + }, + "metadata": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "#/$defs/AlpmMetadata" + }, + { + "$ref": "#/$defs/ApkMetadata" + }, + { + "$ref": "#/$defs/BinaryMetadata" + }, + { + "$ref": "#/$defs/CargoPackageMetadata" + }, + { + "$ref": "#/$defs/CocoapodsMetadata" + }, + { + "$ref": "#/$defs/ConanLockMetadata" + }, + { + "$ref": "#/$defs/ConanMetadata" + }, + { + "$ref": "#/$defs/DartPubMetadata" + }, + { + "$ref": "#/$defs/DotnetDepsMetadata" + }, + { + "$ref": "#/$defs/DpkgMetadata" + }, + { + "$ref": "#/$defs/GemMetadata" + }, + { + "$ref": "#/$defs/GolangBinMetadata" + }, + { + "$ref": "#/$defs/GolangModMetadata" + }, + { + "$ref": "#/$defs/HackageMetadata" + }, + { + "$ref": "#/$defs/JavaMetadata" + }, + { + "$ref": "#/$defs/KbPackageMetadata" + }, + { + "$ref": "#/$defs/MixLockMetadata" + }, + { + "$ref": "#/$defs/NpmPackageJSONMetadata" + }, + { + "$ref": "#/$defs/NpmPackageLockJSONMetadata" + }, + { + "$ref": "#/$defs/PhpComposerJSONMetadata" + }, + { + "$ref": "#/$defs/PortageMetadata" + }, + { + "$ref": "#/$defs/PythonPackageMetadata" + }, + { + "$ref": "#/$defs/PythonPipfileLockMetadata" + }, + { + "$ref": "#/$defs/RebarLockMetadata" + }, + { + "$ref": "#/$defs/RpmMetadata" + } + ] + } + }, + "type": "object", + "required": [ + "id", + "name", + "version", + "type", + "foundBy", + "locations", + "licenses", + "language", + "cpes", + "purl" + ] + }, + "PhpComposerAuthors": { + "properties": { + "name": { + "type": "string" + }, + "email": { + "type": "string" + }, + "homepage": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name" + ] + }, + "PhpComposerExternalReference": { + "properties": { + "type": { + "type": "string" + }, + "url": { + "type": "string" + }, + "reference": { + "type": "string" + }, + "shasum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "type", + "url", + "reference" + ] + }, + "PhpComposerJSONMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "dist": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "require": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "provide": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "require-dev": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "suggest": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "type": { + "type": "string" + }, + "notification-url": { + "type": "string" + }, + "bin": { + "items": { + "type": "string" + }, + "type": "array" + }, + "license": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "$ref": "#/$defs/PhpComposerAuthors" + }, + "type": "array" + }, + "description": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "keywords": { + "items": { + "type": "string" + }, + "type": "array" + }, + "time": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "dist" + ] + }, + "PomParent": { + "properties": { + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "type": "object", + "required": [ + "groupId", + "artifactId", + "version" + ] + }, + "PomProject": { + "properties": { + "path": { + "type": "string" + }, + "parent": { + "$ref": "#/$defs/PomParent" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "groupId", + "artifactId", + "version", + "name" + ] + }, + "PomProperties": { + "properties": { + "path": { + "type": "string" + }, + "name": { + "type": "string" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "extraFields": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path", + "name", + "groupId", + "artifactId", + "version" + ] + }, + "PortageFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "PortageMetadata": { + "properties": { + "installedSize": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/PortageFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "installedSize", + "files" + ] + }, + "PythonDirectURLOriginInfo": { + "properties": { + "url": { + "type": "string" + }, + "commitId": { + "type": "string" + }, + "vcs": { + "type": "string" + } + }, + "type": "object", + "required": [ + "url" + ] + }, + "PythonFileDigest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algorithm", + "value" + ] + }, + "PythonFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/PythonFileDigest" + }, + "size": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "PythonPackageMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "license": { + "type": "string" + }, + "author": { + "type": "string" + }, + "authorEmail": { + "type": "string" + }, + "platform": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/PythonFileRecord" + }, + "type": "array" + }, + "sitePackagesRootPath": { + "type": "string" + }, + "topLevelPackages": { + "items": { + "type": "string" + }, + "type": "array" + }, + "directUrlOrigin": { + "$ref": "#/$defs/PythonDirectURLOriginInfo" + } + }, + "type": "object", + "required": [ + "name", + "version", + "license", + "author", + "authorEmail", + "platform", + "sitePackagesRootPath" + ] + }, + "PythonPipfileLockMetadata": { + "properties": { + "hashes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "index": { + "type": "string" + } + }, + "type": "object", + "required": [ + "hashes", + "index" + ] + }, + "RebarLockMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "pkgHashExt": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "pkgHash", + "pkgHashExt" + ] + }, + "Relationship": { + "properties": { + "parent": { + "type": "string" + }, + "child": { + "type": "string" + }, + "type": { + "type": "string" + }, + "metadata": true + }, + "type": "object", + "required": [ + "parent", + "child", + "type" + ] + }, + "RpmMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "epoch": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "architecture": { + "type": "string" + }, + "release": { + "type": "string" + }, + "sourceRpm": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "license": { + "type": "string" + }, + "vendor": { + "type": "string" + }, + "modularityLabel": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/RpmdbFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "epoch", + "architecture", + "release", + "sourceRpm", + "size", + "license", + "vendor", + "modularityLabel", + "files" + ] + }, + "RpmdbFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "mode": { + "type": "integer" + }, + "size": { + "type": "integer" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "userName": { + "type": "string" + }, + "groupName": { + "type": "string" + }, + "flags": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "mode", + "size", + "digest", + "userName", + "groupName", + "flags" + ] + }, + "Schema": { + "properties": { + "version": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "version", + "url" + ] + }, + "SearchResult": { + "properties": { + "classification": { + "type": "string" + }, + "lineNumber": { + "type": "integer" + }, + "lineOffset": { + "type": "integer" + }, + "seekPosition": { + "type": "integer" + }, + "length": { + "type": "integer" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "classification", + "lineNumber", + "lineOffset", + "seekPosition", + "length" + ] + }, + "Secrets": { + "properties": { + "location": { + "$ref": "#/$defs/Coordinates" + }, + "secrets": { + "items": { + "$ref": "#/$defs/SearchResult" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "location", + "secrets" + ] + }, + "Source": { + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string" + }, + "target": true + }, + "type": "object", + "required": [ + "id", + "type", + "target" + ] + } + } +} diff --git a/syft/file/test-fixtures/snapshot/stereoscope-fixture-image-file-type-mix.golden b/syft/file/test-fixtures/snapshot/stereoscope-fixture-image-file-type-mix.golden index e85036214d9..632a1f2d995 100644 Binary files a/syft/file/test-fixtures/snapshot/stereoscope-fixture-image-file-type-mix.golden and b/syft/file/test-fixtures/snapshot/stereoscope-fixture-image-file-type-mix.golden differ diff --git a/syft/formats/common/cyclonedxhelpers/decoder_test.go b/syft/formats/common/cyclonedxhelpers/decoder_test.go index 70f648e78a2..ffb0504dd86 100644 --- a/syft/formats/common/cyclonedxhelpers/decoder_test.go +++ b/syft/formats/common/cyclonedxhelpers/decoder_test.go @@ -280,7 +280,7 @@ func Test_missingDataDecode(t *testing.T) { }, }) - assert.Len(t, pkg.Licenses, 0) + assert.Equal(t, pkg.Licenses.Size(), 0) } func Test_missingComponentsDecode(t *testing.T) { diff --git a/syft/formats/common/cyclonedxhelpers/external_references_test.go b/syft/formats/common/cyclonedxhelpers/external_references_test.go index 0dd8795574e..d5aaa3125c5 100644 --- a/syft/formats/common/cyclonedxhelpers/external_references_test.go +++ b/syft/formats/common/cyclonedxhelpers/external_references_test.go @@ -6,6 +6,7 @@ import ( "github.com/CycloneDX/cyclonedx-go" "github.com/stretchr/testify/assert" + "github.com/anchore/syft/internal" "github.com/anchore/syft/syft/pkg" ) @@ -50,7 +51,7 @@ func Test_encodeExternalReferences(t *testing.T) { Language: pkg.Rust, Type: pkg.RustPkg, MetadataType: pkg.RustCargoPackageMetadataType, - Licenses: nil, + Licenses: internal.LogicalStrings{}, Metadata: pkg.CargoPackageMetadata{ Name: "ansi_term", Version: "0.12.1", diff --git a/syft/formats/common/cyclonedxhelpers/licenses.go b/syft/formats/common/cyclonedxhelpers/licenses.go index 7518b0976b1..dfd5834d45d 100644 --- a/syft/formats/common/cyclonedxhelpers/licenses.go +++ b/syft/formats/common/cyclonedxhelpers/licenses.go @@ -3,13 +3,14 @@ package cyclonedxhelpers import ( "github.com/CycloneDX/cyclonedx-go" + "github.com/anchore/syft/internal" "github.com/anchore/syft/internal/spdxlicense" "github.com/anchore/syft/syft/pkg" ) func encodeLicenses(p pkg.Package) *cyclonedx.Licenses { lc := cyclonedx.Licenses{} - for _, licenseName := range p.Licenses { + for _, licenseName := range p.Licenses.Elements() { if value, other, exists := spdxlicense.ID(licenseName); exists { lc = append(lc, cyclonedx.LicenseChoice{ License: &cyclonedx.License{ @@ -25,7 +26,8 @@ func encodeLicenses(p pkg.Package) *cyclonedx.Licenses { return nil } -func decodeLicenses(c *cyclonedx.Component) (out []string) { +func decodeLicenses(c *cyclonedx.Component) (ls internal.LogicalStrings) { + var out []string if c.Licenses != nil { for _, l := range *c.Licenses { if l.License != nil { @@ -41,6 +43,8 @@ func decodeLicenses(c *cyclonedx.Component) (out []string) { out = append(out, lic) } } + ls.Simple = out + ls.Joiner = internal.AND } return } diff --git a/syft/formats/common/cyclonedxhelpers/licenses_test.go b/syft/formats/common/cyclonedxhelpers/licenses_test.go index 64999e60cfc..cd8d4a4a340 100644 --- a/syft/formats/common/cyclonedxhelpers/licenses_test.go +++ b/syft/formats/common/cyclonedxhelpers/licenses_test.go @@ -6,6 +6,7 @@ import ( "github.com/CycloneDX/cyclonedx-go" "github.com/stretchr/testify/assert" + "github.com/anchore/syft/internal" "github.com/anchore/syft/syft/pkg" ) @@ -23,8 +24,10 @@ func Test_encodeLicense(t *testing.T) { { name: "no SPDX licenses", input: pkg.Package{ - Licenses: []string{ - "made-up", + Licenses: internal.LogicalStrings{ + Simple: []string{ + "made-up", + }, }, }, expected: &cyclonedx.Licenses{ @@ -34,8 +37,10 @@ func Test_encodeLicense(t *testing.T) { { name: "with SPDX license", input: pkg.Package{ - Licenses: []string{ - "MIT", + Licenses: internal.LogicalStrings{ + Simple: []string{ + "MIT", + }, }, }, expected: &cyclonedx.Licenses{ @@ -45,9 +50,11 @@ func Test_encodeLicense(t *testing.T) { { name: "with SPDX license expression", input: pkg.Package{ - Licenses: []string{ - "MIT", - "GPL-3.0", + Licenses: internal.LogicalStrings{ + Simple: []string{ + "MIT", + "GPL-3.0", + }, }, }, expected: &cyclonedx.Licenses{ @@ -58,8 +65,10 @@ func Test_encodeLicense(t *testing.T) { { name: "cap insensitive", input: pkg.Package{ - Licenses: []string{ - "gpl-3.0", + Licenses: internal.LogicalStrings{ + Simple: []string{ + "gpl-3.0", + }, }, }, expected: &cyclonedx.Licenses{ @@ -69,8 +78,10 @@ func Test_encodeLicense(t *testing.T) { { name: "debian to spdx conversion", input: pkg.Package{ - Licenses: []string{ - "GPL-2", + Licenses: internal.LogicalStrings{ + Simple: []string{ + "GPL-2", + }, }, }, expected: &cyclonedx.Licenses{ diff --git a/syft/formats/common/spdxhelpers/license.go b/syft/formats/common/spdxhelpers/license.go index 68220183831..460f4b0dc99 100644 --- a/syft/formats/common/spdxhelpers/license.go +++ b/syft/formats/common/spdxhelpers/license.go @@ -1,12 +1,16 @@ package spdxhelpers import ( - "strings" + "regexp" "github.com/anchore/syft/internal/spdxlicense" "github.com/anchore/syft/syft/pkg" ) +var ( + invalidLicenseReg = regexp.MustCompile(`[^0-9A-Za-z\.-]`) +) + func License(p pkg.Package) string { // source: https://spdx.github.io/spdx-spec/3-package-information/#313-concluded-license // The options to populate this field are limited to: @@ -17,35 +21,26 @@ func License(p pkg.Package) string { // (ii) the SPDX file creator has made no attempt to determine this field; or // (iii) the SPDX file creator has intentionally provided no information (no meaning should be implied by doing so). - if len(p.Licenses) == 0 { + if p.Licenses.Size() == 0 { return NONE } // take all licenses and assume an AND expression; for information about license expressions see https://spdx.github.io/spdx-spec/appendix-IV-SPDX-license-expressions/ - parsedLicenses := parseLicenses(p.Licenses) - - for i, v := range parsedLicenses { - if strings.HasPrefix(v, spdxlicense.LicenseRefPrefix) { - parsedLicenses[i] = SanitizeElementID(v) - } - } - - if len(parsedLicenses) == 0 { - return NOASSERTION - } - - return strings.Join(parsedLicenses, " AND ") -} - -func parseLicenses(raw []string) (parsedLicenses []string) { - for _, l := range raw { - if value, other, exists := spdxlicense.ID(l); exists { + parsedLicenses := p.Licenses.Process(func(s string) string { + if value, other, exists := spdxlicense.ID(s); exists { parsed := value if other != "" { - parsed = spdxlicense.LicenseRefPrefix + other + parsed = spdxlicense.LicenseRefPrefix + licenseScrubber(other) } - parsedLicenses = append(parsedLicenses, parsed) + return parsed } - } - return + return "" + }) + + return parsedLicenses.String() +} + +func licenseScrubber(s string) string { + // replace any characters that are *not* valid: alphanumeric, . - + return invalidLicenseReg.ReplaceAllString(s, "-") } diff --git a/syft/formats/common/spdxhelpers/license_test.go b/syft/formats/common/spdxhelpers/license_test.go index 980c0f0dd23..2cd6746d4f9 100644 --- a/syft/formats/common/spdxhelpers/license_test.go +++ b/syft/formats/common/spdxhelpers/license_test.go @@ -5,6 +5,7 @@ import ( "github.com/stretchr/testify/assert" + "github.com/anchore/syft/internal" "github.com/anchore/syft/syft/pkg" ) @@ -22,8 +23,10 @@ func Test_License(t *testing.T) { { name: "no SPDX licenses", input: pkg.Package{ - Licenses: []string{ - "made-up", + Licenses: internal.LogicalStrings{ + Simple: []string{ + "made-up", + }, }, }, expected: "LicenseRef-made-up", @@ -31,8 +34,10 @@ func Test_License(t *testing.T) { { name: "with SPDX license", input: pkg.Package{ - Licenses: []string{ - "MIT", + Licenses: internal.LogicalStrings{ + Simple: []string{ + "MIT", + }, }, }, expected: "MIT", @@ -40,9 +45,11 @@ func Test_License(t *testing.T) { { name: "with SPDX license expression", input: pkg.Package{ - Licenses: []string{ - "MIT", - "GPL-3.0", + Licenses: internal.LogicalStrings{ + Simple: []string{ + "MIT", + "GPL-3.0-only", + }, }, }, expected: "MIT AND GPL-3.0-only", @@ -50,8 +57,10 @@ func Test_License(t *testing.T) { { name: "cap insensitive", input: pkg.Package{ - Licenses: []string{ - "gpl-3.0", + Licenses: internal.LogicalStrings{ + Simple: []string{ + "gpl-3.0", + }, }, }, expected: "GPL-3.0-only", @@ -59,8 +68,10 @@ func Test_License(t *testing.T) { { name: "debian to spdx conversion", input: pkg.Package{ - Licenses: []string{ - "GPL-2", + Licenses: internal.LogicalStrings{ + Simple: []string{ + "GPL-2", + }, }, }, expected: "GPL-2.0-only", @@ -83,3 +94,18 @@ func Test_License(t *testing.T) { }) } } + +func TestLicenseScrubber(t *testing.T) { + tests := []struct { + in string + out string + }{ + {"A", "A"}, + {"A-B", "A-B"}, + {"A.B", "A.B"}, + {"A.B:foo", "A.B-foo"}, + } + for _, test := range tests { + assert.Equal(t, test.out, licenseScrubber(test.in)) + } +} diff --git a/syft/formats/common/spdxhelpers/to_format_model.go b/syft/formats/common/spdxhelpers/to_format_model.go index a6f3f8d4379..a2d1406fc7a 100644 --- a/syft/formats/common/spdxhelpers/to_format_model.go +++ b/syft/formats/common/spdxhelpers/to_format_model.go @@ -514,11 +514,14 @@ func toFileTypes(metadata *source.FileMetadata) (ty []string) { func toOtherLicenses(catalog *pkg.Catalog) []*spdx.OtherLicense { licenses := map[string]bool{} - for _, p := range catalog.Sorted() { - for _, license := range parseLicenses(p.Licenses) { - if strings.HasPrefix(license, spdxlicense.LicenseRefPrefix) { - licenses[license] = true + for _, pkg := range catalog.Sorted() { + for _, license := range pkg.Licenses.Elements() { + value, other, exists := spdxlicense.ID(license) + if !exists || value != "" || other == "" { + continue } + + licenses[other] = true } } var result []*spdx.OtherLicense diff --git a/syft/formats/common/spdxhelpers/to_syft_model.go b/syft/formats/common/spdxhelpers/to_syft_model.go index 4993eea7e87..6e32a4337ca 100644 --- a/syft/formats/common/spdxhelpers/to_syft_model.go +++ b/syft/formats/common/spdxhelpers/to_syft_model.go @@ -9,6 +9,7 @@ import ( "github.com/spdx/tools-golang/spdx" "github.com/anchore/packageurl-go" + "github.com/anchore/syft/internal" "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/cpe" @@ -269,12 +270,17 @@ func extractPkgInfo(p *spdx.Package) pkgInfo { func toSyftPackage(p *spdx.Package) *pkg.Package { info := extractPkgInfo(p) + licenses, err := parseLicense(p.PackageLicenseDeclared) + if err != nil { + log.Warnf("unable to parse license for package %s: %s", p.PackageName, err) + return nil + } metadataType, metadata := extractMetadata(p, info) sP := pkg.Package{ Type: info.typ, Name: p.PackageName, Version: p.PackageVersion, - Licenses: parseLicense(p.PackageLicenseDeclared), + Licenses: licenses, CPEs: extractCPEs(p), PURL: info.purl.String(), Language: info.lang, @@ -396,9 +402,9 @@ func extractCPEs(p *spdx.Package) (cpes []cpe.CPE) { return cpes } -func parseLicense(l string) []string { +func parseLicense(l string) (internal.LogicalStrings, error) { if l == NOASSERTION || l == NONE { - return nil + return internal.LogicalStrings{}, nil } - return strings.Split(l, " AND ") + return internal.ParseLogicalStrings(l) } diff --git a/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden b/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden index 7e7a3cdfe14..6e8f3b98e51 100644 --- a/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden +++ b/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden @@ -1,10 +1,10 @@ { "bomFormat": "CycloneDX", "specVersion": "1.4", - "serialNumber": "urn:uuid:0b628da1-274e-4c24-821c-f9452f37db54", + "serialNumber": "urn:uuid:11f78f35-2ea9-4785-90da-7e073c02ecfc", "version": 1, "metadata": { - "timestamp": "2022-12-22T18:33:51-05:00", + "timestamp": "2023-02-13T10:59:09-08:00", "tools": [ { "vendor": "anchore", @@ -20,7 +20,7 @@ }, "components": [ { - "bom-ref": "1b1d0be59ac59d2c", + "bom-ref": "9f56614c62f539e9", "type": "library", "name": "package-1", "version": "1.0.1", @@ -57,7 +57,7 @@ ] }, { - "bom-ref": "pkg:deb/debian/package-2@2.0.1?package-id=db4abfe497c180d3", + "bom-ref": "pkg:deb/debian/package-2@2.0.1?package-id=342b18b33e30ed0", "type": "library", "name": "package-2", "version": "2.0.1", diff --git a/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden b/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden index de3018170ed..1b92f61df4e 100644 --- a/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden +++ b/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden @@ -1,10 +1,10 @@ { "bomFormat": "CycloneDX", "specVersion": "1.4", - "serialNumber": "urn:uuid:542fc1a1-81ac-4b76-b9e2-8e6b9d8c840a", + "serialNumber": "urn:uuid:d383da54-a35f-4bd5-a85d-688b35e92c31", "version": 1, "metadata": { - "timestamp": "2022-12-22T18:33:51-05:00", + "timestamp": "2023-02-13T10:59:09-08:00", "tools": [ { "vendor": "anchore", @@ -13,7 +13,7 @@ } ], "component": { - "bom-ref": "ffd645a093c0fe70", + "bom-ref": "b83149da0a7fd6c5", "type": "container", "name": "user-image-input", "version": "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368" @@ -21,7 +21,7 @@ }, "components": [ { - "bom-ref": "66ba429119b8bec6", + "bom-ref": "de34d3845e98e85f", "type": "library", "name": "package-1", "version": "1.0.1", @@ -53,7 +53,7 @@ }, { "name": "syft:location:0:layerID", - "value": "sha256:62058900d4ce269c900160b8dd255fe310c3a459dda236d041102fa070f84406" + "value": "sha256:626044c9c04b634ddb1659b81debbeebf312c5e3f2e6e8a693813866aa38a381" }, { "name": "syft:location:0:path", @@ -62,7 +62,7 @@ ] }, { - "bom-ref": "pkg:deb/debian/package-2@2.0.1?package-id=958443e2d9304af4", + "bom-ref": "pkg:deb/debian/package-2@2.0.1?package-id=bad230a934158ee5", "type": "library", "name": "package-2", "version": "2.0.1", @@ -83,7 +83,7 @@ }, { "name": "syft:location:0:layerID", - "value": "sha256:623ad97366f39ae279f1925673cdacb4851ddf2e3266f04e63010ec080a098c1" + "value": "sha256:2e0a2ae163675ae98403ca109fdee95bed2fea62f94e7d013146ac710d77de49" }, { "name": "syft:location:0:path", diff --git a/syft/formats/cyclonedxjson/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden b/syft/formats/cyclonedxjson/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden index 11a1958c893..fc4e17e4f51 100644 Binary files a/syft/formats/cyclonedxjson/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden and b/syft/formats/cyclonedxjson/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden differ diff --git a/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden b/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden index 095d2c3b88a..13f3b6834ac 100644 --- a/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden +++ b/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden @@ -1,7 +1,7 @@ - + - 2022-11-07T09:11:06-05:00 + 2023-02-13T10:59:09-08:00 anchore @@ -14,7 +14,7 @@ - + package-1 1.0.1 @@ -32,7 +32,7 @@ /some/path/pkg1 - + package-2 2.0.1 cpe:2.3:*:some:package:2:*:*:*:*:*:*:* diff --git a/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden b/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden index 814962bedb3..ae516010815 100644 --- a/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden +++ b/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden @@ -1,7 +1,7 @@ - + - 2022-11-07T09:11:06-05:00 + 2023-02-13T10:59:09-08:00 anchore @@ -9,13 +9,13 @@ v0.42.0-bogus - + user-image-input sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368 - + package-1 1.0.1 @@ -30,11 +30,11 @@ python PythonPackageMetadata python - sha256:fb6beecb75b39f4bb813dbf177e501edd5ddb3e69bb45cedeb78c676ee1b7a59 + sha256:626044c9c04b634ddb1659b81debbeebf312c5e3f2e6e8a693813866aa38a381 /somefile-1.txt - + package-2 2.0.1 cpe:2.3:*:some:package:2:*:*:*:*:*:*:* @@ -43,7 +43,7 @@ the-cataloger-2 DpkgMetadata deb - sha256:319b588ce64253a87b533c8ed01cf0025e0eac98e7b516e12532957e1244fdec + sha256:2e0a2ae163675ae98403ca109fdee95bed2fea62f94e7d013146ac710d77de49 /somefile-2.txt 0 diff --git a/syft/formats/cyclonedxxml/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden b/syft/formats/cyclonedxxml/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden index 11a1958c893..fc4e17e4f51 100644 Binary files a/syft/formats/cyclonedxxml/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden and b/syft/formats/cyclonedxxml/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden differ diff --git a/syft/formats/internal/testutils/utils.go b/syft/formats/internal/testutils/utils.go index f214c4f07f3..976eb45952d 100644 --- a/syft/formats/internal/testutils/utils.go +++ b/syft/formats/internal/testutils/utils.go @@ -15,6 +15,7 @@ import ( "github.com/anchore/stereoscope/pkg/filetree" "github.com/anchore/stereoscope/pkg/image" "github.com/anchore/stereoscope/pkg/imagetest" + "github.com/anchore/syft/internal" "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/cpe" "github.com/anchore/syft/syft/linux" @@ -174,7 +175,7 @@ func populateImageCatalog(catalog *pkg.Catalog, img *image.Image) { FoundBy: "the-cataloger-1", Language: pkg.Python, MetadataType: pkg.PythonPackageMetadataType, - Licenses: []string{"MIT"}, + Licenses: internal.LogicalStrings{Simple: []string{"MIT"}}, Metadata: pkg.PythonPackageMetadata{ Name: "package-1", Version: "1.0.1", @@ -249,7 +250,7 @@ func newDirectoryCatalog() *pkg.Catalog { ), Language: pkg.Python, MetadataType: pkg.PythonPackageMetadataType, - Licenses: []string{"MIT"}, + Licenses: internal.LogicalStrings{Simple: []string{"MIT"}}, Metadata: pkg.PythonPackageMetadata{ Name: "package-1", Version: "1.0.1", diff --git a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONDirectoryEncoder.golden b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONDirectoryEncoder.golden index 4ef14120dfe..851eeb73099 100644 --- a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONDirectoryEncoder.golden +++ b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONDirectoryEncoder.golden @@ -3,19 +3,19 @@ "dataLicense": "CC0-1.0", "SPDXID": "SPDXRef-DOCUMENT", "name": "/some/path", - "documentNamespace": "https://anchore.com/syft/dir/some/path-1fe34646-a616-48c7-974b-3d1e27d406e3", + "documentNamespace": "https://anchore.com/syft/dir/some/path-c5a351d0-f803-453a-a599-1b388bb321cf", "creationInfo": { "licenseListVersion": "3.19", "creators": [ "Organization: Anchore, Inc", "Tool: syft-v0.42.0-bogus" ], - "created": "2023-01-20T21:41:03Z" + "created": "2023-02-13T18:59:09Z" }, "packages": [ { "name": "package-1", - "SPDXID": "SPDXRef-Package-python-package-1-1b1d0be59ac59d2c", + "SPDXID": "SPDXRef-Package-python-package-1-9f56614c62f539e9", "versionInfo": "1.0.1", "downloadLocation": "NOASSERTION", "sourceInfo": "acquired package info from installed python package manifest file: /some/path/pkg1", @@ -37,7 +37,7 @@ }, { "name": "package-2", - "SPDXID": "SPDXRef-Package-deb-package-2-db4abfe497c180d3", + "SPDXID": "SPDXRef-Package-deb-package-2-342b18b33e30ed0", "versionInfo": "2.0.1", "downloadLocation": "NOASSERTION", "sourceInfo": "acquired package info from DPKG DB: /some/path/pkg1", diff --git a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONImageEncoder.golden b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONImageEncoder.golden index 8a5214f293f..fa96ae4af07 100644 --- a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONImageEncoder.golden +++ b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONImageEncoder.golden @@ -3,19 +3,19 @@ "dataLicense": "CC0-1.0", "SPDXID": "SPDXRef-DOCUMENT", "name": "user-image-input", - "documentNamespace": "https://anchore.com/syft/image/user-image-input-33759ac3-6006-4f2c-bdc4-f40b9287a7f0", + "documentNamespace": "https://anchore.com/syft/image/user-image-input-2ddd36be-7a79-4bdb-9619-8cf35e0e223d", "creationInfo": { "licenseListVersion": "3.19", "creators": [ "Organization: Anchore, Inc", "Tool: syft-v0.42.0-bogus" ], - "created": "2023-01-20T21:41:03Z" + "created": "2023-02-13T18:59:09Z" }, "packages": [ { "name": "package-1", - "SPDXID": "SPDXRef-Package-python-package-1-66ba429119b8bec6", + "SPDXID": "SPDXRef-Package-python-package-1-de34d3845e98e85f", "versionInfo": "1.0.1", "downloadLocation": "NOASSERTION", "sourceInfo": "acquired package info from installed python package manifest file: /somefile-1.txt", @@ -37,7 +37,7 @@ }, { "name": "package-2", - "SPDXID": "SPDXRef-Package-deb-package-2-958443e2d9304af4", + "SPDXID": "SPDXRef-Package-deb-package-2-bad230a934158ee5", "versionInfo": "2.0.1", "downloadLocation": "NOASSERTION", "sourceInfo": "acquired package info from DPKG DB: /somefile-2.txt", diff --git a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden index 9e8e1453cc1..f7c80e70b78 100644 --- a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden +++ b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden @@ -3,19 +3,19 @@ "dataLicense": "CC0-1.0", "SPDXID": "SPDXRef-DOCUMENT", "name": "user-image-input", - "documentNamespace": "https://anchore.com/syft/image/user-image-input-ce98f51f-b483-4e93-9a15-5a8a16d35de6", + "documentNamespace": "https://anchore.com/syft/image/user-image-input-f006ebea-b878-40d7-8733-4d2c3e0f6e34", "creationInfo": { "licenseListVersion": "3.19", "creators": [ "Organization: Anchore, Inc", "Tool: syft-v0.42.0-bogus" ], - "created": "2023-01-20T21:41:03Z" + "created": "2023-02-13T18:59:10Z" }, "packages": [ { "name": "package-1", - "SPDXID": "SPDXRef-Package-python-package-1-66ba429119b8bec6", + "SPDXID": "SPDXRef-Package-python-package-1-de34d3845e98e85f", "versionInfo": "1.0.1", "downloadLocation": "NOASSERTION", "sourceInfo": "acquired package info from installed python package manifest file: /somefile-1.txt", @@ -37,7 +37,7 @@ }, { "name": "package-2", - "SPDXID": "SPDXRef-Package-deb-package-2-958443e2d9304af4", + "SPDXID": "SPDXRef-Package-deb-package-2-bad230a934158ee5", "versionInfo": "2.0.1", "downloadLocation": "NOASSERTION", "sourceInfo": "acquired package info from DPKG DB: /somefile-2.txt", @@ -152,32 +152,32 @@ ], "relationships": [ { - "spdxElementId": "SPDXRef-Package-python-package-1-66ba429119b8bec6", + "spdxElementId": "SPDXRef-Package-python-package-1-de34d3845e98e85f", "relatedSpdxElement": "SPDXRef-5265a4dde3edbf7c", "relationshipType": "CONTAINS" }, { - "spdxElementId": "SPDXRef-Package-python-package-1-66ba429119b8bec6", + "spdxElementId": "SPDXRef-Package-python-package-1-de34d3845e98e85f", "relatedSpdxElement": "SPDXRef-839d99ee67d9d174", "relationshipType": "CONTAINS" }, { - "spdxElementId": "SPDXRef-Package-python-package-1-66ba429119b8bec6", + "spdxElementId": "SPDXRef-Package-python-package-1-de34d3845e98e85f", "relatedSpdxElement": "SPDXRef-9c2f7510199b17f6", "relationshipType": "CONTAINS" }, { - "spdxElementId": "SPDXRef-Package-python-package-1-66ba429119b8bec6", + "spdxElementId": "SPDXRef-Package-python-package-1-de34d3845e98e85f", "relatedSpdxElement": "SPDXRef-c641caa71518099f", "relationshipType": "CONTAINS" }, { - "spdxElementId": "SPDXRef-Package-python-package-1-66ba429119b8bec6", + "spdxElementId": "SPDXRef-Package-python-package-1-de34d3845e98e85f", "relatedSpdxElement": "SPDXRef-c6f5b29dca12661f", "relationshipType": "CONTAINS" }, { - "spdxElementId": "SPDXRef-Package-python-package-1-66ba429119b8bec6", + "spdxElementId": "SPDXRef-Package-python-package-1-de34d3845e98e85f", "relatedSpdxElement": "SPDXRef-f9e49132a4b96ccd", "relationshipType": "CONTAINS" }, diff --git a/syft/formats/spdxjson/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden b/syft/formats/spdxjson/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden index b9ddf6e3339..fc4e17e4f51 100644 Binary files a/syft/formats/spdxjson/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden and b/syft/formats/spdxjson/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden differ diff --git a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXJSONSPDXIDs.golden b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXJSONSPDXIDs.golden index c017916c28f..97ba72f334f 100644 --- a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXJSONSPDXIDs.golden +++ b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXJSONSPDXIDs.golden @@ -2,16 +2,16 @@ SPDXVersion: SPDX-2.3 DataLicense: CC0-1.0 SPDXID: SPDXRef-DOCUMENT DocumentName: foobar/baz -DocumentNamespace: https://anchore.com/syft/dir/foobar/baz-478e410d-7fad-472c-b4e9-a4068ef28160 +DocumentNamespace: https://anchore.com/syft/dir/foobar/baz-0cf4b09e-0f7b-486f-a84d-d0d91620476b LicenseListVersion: 3.19 Creator: Organization: Anchore, Inc Creator: Tool: syft-v0.42.0-bogus -Created: 2022-12-21T03:39:05Z +Created: 2023-02-13T18:59:10Z ##### Package: @at-sign PackageName: @at-sign -SPDXID: SPDXRef-Package---at-sign-3732f7a5679bdec4 +SPDXID: SPDXRef-Package---at-sign-14c1788ed9aa0270 PackageDownloadLocation: NOASSERTION FilesAnalyzed: false PackageSourceInfo: acquired package info from the following paths: @@ -22,7 +22,7 @@ PackageCopyrightText: NOASSERTION ##### Package: some/slashes PackageName: some/slashes -SPDXID: SPDXRef-Package--some-slashes-1345166d4801153b +SPDXID: SPDXRef-Package--some-slashes-5b91aeb05a1fea5e PackageDownloadLocation: NOASSERTION FilesAnalyzed: false PackageSourceInfo: acquired package info from the following paths: @@ -33,7 +33,7 @@ PackageCopyrightText: NOASSERTION ##### Package: under_scores PackageName: under_scores -SPDXID: SPDXRef-Package--under-scores-290d5c77210978c1 +SPDXID: SPDXRef-Package--under-scores-111cadb6afff78d4 PackageDownloadLocation: NOASSERTION FilesAnalyzed: false PackageSourceInfo: acquired package info from the following paths: diff --git a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden index 94cd399de23..ad391e928c4 100644 --- a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden +++ b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden @@ -2,11 +2,11 @@ SPDXVersion: SPDX-2.3 DataLicense: CC0-1.0 SPDXID: SPDXRef-DOCUMENT DocumentName: user-image-input -DocumentNamespace: https://anchore.com/syft/image/user-image-input-73433e8c-364f-42b6-b5b7-9a4da8799868 +DocumentNamespace: https://anchore.com/syft/image/user-image-input-279e1d70-8051-447c-a64f-bbf2f291e9e3 LicenseListVersion: 3.19 Creator: Organization: Anchore, Inc Creator: Tool: syft-v0.42.0-bogus -Created: 2022-12-21T03:39:05Z +Created: 2023-02-13T18:59:10Z ##### Unpackaged files @@ -49,7 +49,7 @@ LicenseConcluded: NOASSERTION ##### Package: package-2 PackageName: package-2 -SPDXID: SPDXRef-Package-deb-package-2-958443e2d9304af4 +SPDXID: SPDXRef-Package-deb-package-2-bad230a934158ee5 PackageVersion: 2.0.1 PackageDownloadLocation: NOASSERTION FilesAnalyzed: false @@ -63,7 +63,7 @@ ExternalRef: PACKAGE-MANAGER purl pkg:deb/debian/package-2@2.0.1 ##### Package: package-1 PackageName: package-1 -SPDXID: SPDXRef-Package-python-package-1-66ba429119b8bec6 +SPDXID: SPDXRef-Package-python-package-1-de34d3845e98e85f PackageVersion: 1.0.1 PackageDownloadLocation: NOASSERTION FilesAnalyzed: false @@ -76,11 +76,11 @@ ExternalRef: PACKAGE-MANAGER purl a-purl-1 ##### Relationships -Relationship: SPDXRef-Package-python-package-1-66ba429119b8bec6 CONTAINS SPDXRef-5265a4dde3edbf7c -Relationship: SPDXRef-Package-python-package-1-66ba429119b8bec6 CONTAINS SPDXRef-839d99ee67d9d174 -Relationship: SPDXRef-Package-python-package-1-66ba429119b8bec6 CONTAINS SPDXRef-9c2f7510199b17f6 -Relationship: SPDXRef-Package-python-package-1-66ba429119b8bec6 CONTAINS SPDXRef-c641caa71518099f -Relationship: SPDXRef-Package-python-package-1-66ba429119b8bec6 CONTAINS SPDXRef-c6f5b29dca12661f -Relationship: SPDXRef-Package-python-package-1-66ba429119b8bec6 CONTAINS SPDXRef-f9e49132a4b96ccd +Relationship: SPDXRef-Package-python-package-1-de34d3845e98e85f CONTAINS SPDXRef-5265a4dde3edbf7c +Relationship: SPDXRef-Package-python-package-1-de34d3845e98e85f CONTAINS SPDXRef-839d99ee67d9d174 +Relationship: SPDXRef-Package-python-package-1-de34d3845e98e85f CONTAINS SPDXRef-9c2f7510199b17f6 +Relationship: SPDXRef-Package-python-package-1-de34d3845e98e85f CONTAINS SPDXRef-c641caa71518099f +Relationship: SPDXRef-Package-python-package-1-de34d3845e98e85f CONTAINS SPDXRef-c6f5b29dca12661f +Relationship: SPDXRef-Package-python-package-1-de34d3845e98e85f CONTAINS SPDXRef-f9e49132a4b96ccd Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-DOCUMENT diff --git a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueDirectoryEncoder.golden b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueDirectoryEncoder.golden index 7bd71f05f05..d0c6e12313e 100644 --- a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueDirectoryEncoder.golden +++ b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueDirectoryEncoder.golden @@ -2,16 +2,16 @@ SPDXVersion: SPDX-2.3 DataLicense: CC0-1.0 SPDXID: SPDXRef-DOCUMENT DocumentName: /some/path -DocumentNamespace: https://anchore.com/syft/dir/some/path-1d303762-46d2-47b5-9c81-defa91387275 +DocumentNamespace: https://anchore.com/syft/dir/some/path-63e04b9b-c306-41a2-b2fd-1e5d376abf88 LicenseListVersion: 3.19 Creator: Organization: Anchore, Inc Creator: Tool: syft-v0.42.0-bogus -Created: 2022-12-21T03:39:05Z +Created: 2023-02-13T18:59:10Z ##### Package: package-2 PackageName: package-2 -SPDXID: SPDXRef-Package-deb-package-2-db4abfe497c180d3 +SPDXID: SPDXRef-Package-deb-package-2-342b18b33e30ed0 PackageVersion: 2.0.1 PackageDownloadLocation: NOASSERTION FilesAnalyzed: false @@ -25,7 +25,7 @@ ExternalRef: PACKAGE-MANAGER purl pkg:deb/debian/package-2@2.0.1 ##### Package: package-1 PackageName: package-1 -SPDXID: SPDXRef-Package-python-package-1-1b1d0be59ac59d2c +SPDXID: SPDXRef-Package-python-package-1-9f56614c62f539e9 PackageVersion: 1.0.1 PackageDownloadLocation: NOASSERTION FilesAnalyzed: false diff --git a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueImageEncoder.golden b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueImageEncoder.golden index df1cb1467d3..5c9bdee6bec 100644 --- a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueImageEncoder.golden +++ b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueImageEncoder.golden @@ -2,16 +2,16 @@ SPDXVersion: SPDX-2.3 DataLicense: CC0-1.0 SPDXID: SPDXRef-DOCUMENT DocumentName: user-image-input -DocumentNamespace: https://anchore.com/syft/image/user-image-input-559af225-63af-4bc0-94fb-bce94913bcfa +DocumentNamespace: https://anchore.com/syft/image/user-image-input-972cb3d7-7347-4acc-afcc-dff755139b4e LicenseListVersion: 3.19 Creator: Organization: Anchore, Inc Creator: Tool: syft-v0.42.0-bogus -Created: 2022-12-21T03:39:05Z +Created: 2023-02-13T18:59:10Z ##### Package: package-2 PackageName: package-2 -SPDXID: SPDXRef-Package-deb-package-2-958443e2d9304af4 +SPDXID: SPDXRef-Package-deb-package-2-bad230a934158ee5 PackageVersion: 2.0.1 PackageDownloadLocation: NOASSERTION FilesAnalyzed: false @@ -25,7 +25,7 @@ ExternalRef: PACKAGE-MANAGER purl pkg:deb/debian/package-2@2.0.1 ##### Package: package-1 PackageName: package-1 -SPDXID: SPDXRef-Package-python-package-1-66ba429119b8bec6 +SPDXID: SPDXRef-Package-python-package-1-de34d3845e98e85f PackageVersion: 1.0.1 PackageDownloadLocation: NOASSERTION FilesAnalyzed: false diff --git a/syft/formats/spdxtagvalue/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden b/syft/formats/spdxtagvalue/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden index b9ddf6e3339..fc4e17e4f51 100644 Binary files a/syft/formats/spdxtagvalue/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden and b/syft/formats/spdxtagvalue/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden differ diff --git a/syft/formats/syftjson/encoder_test.go b/syft/formats/syftjson/encoder_test.go index 185658f7b77..4ae21fc59a1 100644 --- a/syft/formats/syftjson/encoder_test.go +++ b/syft/formats/syftjson/encoder_test.go @@ -6,6 +6,7 @@ import ( "testing" stereoFile "github.com/anchore/stereoscope/pkg/file" + "github.com/anchore/syft/internal" "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/cpe" "github.com/anchore/syft/syft/file" @@ -63,7 +64,7 @@ func TestEncodeFullJSONDocument(t *testing.T) { FoundBy: "the-cataloger-1", Language: pkg.Python, MetadataType: pkg.PythonPackageMetadataType, - Licenses: []string{"MIT"}, + Licenses: internal.LogicalStrings{Simple: []string{"MIT"}}, Metadata: pkg.PythonPackageMetadata{ Name: "package-1", Version: "1.0.1", diff --git a/syft/formats/syftjson/model/package.go b/syft/formats/syftjson/model/package.go index 4567a139aeb..708a0f4dcdf 100644 --- a/syft/formats/syftjson/model/package.go +++ b/syft/formats/syftjson/model/package.go @@ -6,6 +6,7 @@ import ( "fmt" "reflect" + "github.com/anchore/syft/internal" "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/source" @@ -21,16 +22,16 @@ 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 []source.Coordinates `json:"locations"` - Licenses []string `json:"licenses"` - Language pkg.Language `json:"language"` - CPEs []string `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 []source.Coordinates `json:"locations"` + Licenses internal.LogicalStrings `json:"licenses"` + Language pkg.Language `json:"language"` + CPEs []string `json:"cpes"` + PURL string `json:"purl"` } // PackageCustomData contains ambiguous values (type-wise) from pkg.Package. diff --git a/syft/formats/syftjson/model/package_test.go b/syft/formats/syftjson/model/package_test.go index 2aa2cda0515..32f1958c01e 100644 --- a/syft/formats/syftjson/model/package_test.go +++ b/syft/formats/syftjson/model/package_test.go @@ -30,7 +30,7 @@ func TestUnmarshalPackageGolang(t *testing.T) { "path": "/Users/hal/go/bin/syft" } ], - "licenses": [], + "licenses": "", "language": "go", "cpes": [], "purl": "pkg:golang/gopkg.in/square/go-jose.v2@v2.6.0", @@ -61,7 +61,7 @@ func TestUnmarshalPackageGolang(t *testing.T) { "path": "/Users/hal/go/bin/syft" } ], - "licenses": [], + "licenses": "", "language": "go", "cpes": [], "purl": "pkg:golang/gopkg.in/square/go-jose.v2@v2.6.0" @@ -105,7 +105,7 @@ func Test_unpackMetadata(t *testing.T) { "path": "/Users/hal/go/bin/syft" } ], - "licenses": [], + "licenses": "", "language": "go", "cpes": [], "purl": "pkg:golang/gopkg.in/square/go-jose.v2@v2.6.0", @@ -131,7 +131,7 @@ func Test_unpackMetadata(t *testing.T) { "path": "/Users/hal/go/bin/syft" } ], - "licenses": [], + "licenses": "", "language": "go", "cpes": [], "purl": "pkg:golang/gopkg.in/square/go-jose.v2@v2.6.0" @@ -152,9 +152,7 @@ func Test_unpackMetadata(t *testing.T) { "layerID": "sha256:74ddd0ec08fa43d09f32636ba91a0a3053b02cb4627c35051aff89f853606b59" } ], - "licenses": [ - "GPLv2+" - ], + "licenses": "GPLv2+", "language": "", "cpes": [ "cpe:2.3:a:centos:acl:2.2.53-1.el8:*:*:*:*:*:*:*", @@ -191,7 +189,7 @@ func Test_unpackMetadata(t *testing.T) { "path": "/Users/hal/go/bin/syft" } ], - "licenses": [], + "licenses": "", "language": "go", "cpes": [], "purl": "pkg:golang/gopkg.in/square/go-jose.v2@v2.6.0", diff --git a/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden b/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden index 6a051331a0e..9d3273b861a 100644 --- a/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden +++ b/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden @@ -1,7 +1,7 @@ { "artifacts": [ { - "id": "1b1d0be59ac59d2c", + "id": "9f56614c62f539e9", "name": "package-1", "version": "1.0.1", "type": "python", @@ -11,9 +11,7 @@ "path": "/some/path/pkg1" } ], - "licenses": [ - "MIT" - ], + "licenses": "MIT", "language": "python", "cpes": [ "cpe:2.3:*:some:package:2:*:*:*:*:*:*:*" @@ -36,7 +34,7 @@ } }, { - "id": "db4abfe497c180d3", + "id": "342b18b33e30ed0", "name": "package-2", "version": "2.0.1", "type": "deb", @@ -46,7 +44,7 @@ "path": "/some/path/pkg1" } ], - "licenses": [], + "licenses": "", "language": "", "cpes": [ "cpe:2.3:*:some:package:2:*:*:*:*:*:*:*" @@ -89,7 +87,7 @@ } }, "schema": { - "version": "6.2.0", - "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-6.2.0.json" + "version": "7.0.0", + "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-7.0.0.json" } } diff --git a/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden b/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden index 3b638f94106..554966ade29 100644 --- a/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden +++ b/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden @@ -1,7 +1,7 @@ { "artifacts": [ { - "id": "304a5a8e5958a49d", + "id": "63a4be05cd4c2eec", "name": "package-1", "version": "1.0.1", "type": "python", @@ -11,9 +11,7 @@ "path": "/a/place/a" } ], - "licenses": [ - "MIT" - ], + "licenses": "MIT", "language": "python", "cpes": [ "cpe:2.3:*:some:package:1:*:*:*:*:*:*:*" @@ -31,7 +29,7 @@ } }, { - "id": "9fd0b9f41034991d", + "id": "72e7b9a70006ab6b", "name": "package-2", "version": "2.0.1", "type": "deb", @@ -41,7 +39,7 @@ "path": "/b/place/b" } ], - "licenses": [], + "licenses": "", "language": "", "cpes": [ "cpe:2.3:*:some:package:2:*:*:*:*:*:*:*" @@ -185,7 +183,7 @@ } }, "schema": { - "version": "6.2.0", - "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-6.2.0.json" + "version": "7.0.0", + "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-7.0.0.json" } } diff --git a/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden b/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden index a72adb10c60..c178bf43f7f 100644 --- a/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden +++ b/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden @@ -1,7 +1,7 @@ { "artifacts": [ { - "id": "66ba429119b8bec6", + "id": "de34d3845e98e85f", "name": "package-1", "version": "1.0.1", "type": "python", @@ -12,9 +12,7 @@ "layerID": "sha256:fb6beecb75b39f4bb813dbf177e501edd5ddb3e69bb45cedeb78c676ee1b7a59" } ], - "licenses": [ - "MIT" - ], + "licenses": "MIT", "language": "python", "cpes": [ "cpe:2.3:*:some:package:1:*:*:*:*:*:*:*" @@ -32,7 +30,7 @@ } }, { - "id": "958443e2d9304af4", + "id": "bad230a934158ee5", "name": "package-2", "version": "2.0.1", "type": "deb", @@ -43,7 +41,7 @@ "layerID": "sha256:319b588ce64253a87b533c8ed01cf0025e0eac98e7b516e12532957e1244fdec" } ], - "licenses": [], + "licenses": "", "language": "", "cpes": [ "cpe:2.3:*:some:package:2:*:*:*:*:*:*:*" @@ -112,7 +110,7 @@ } }, "schema": { - "version": "6.2.0", - "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-6.2.0.json" + "version": "7.0.0", + "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-7.0.0.json" } } diff --git a/syft/formats/syftjson/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden b/syft/formats/syftjson/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden index 11a1958c893..fc4e17e4f51 100644 Binary files a/syft/formats/syftjson/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden and b/syft/formats/syftjson/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden differ diff --git a/syft/formats/syftjson/to_format_model.go b/syft/formats/syftjson/to_format_model.go index b5067d6aef6..e266c13f06c 100644 --- a/syft/formats/syftjson/to_format_model.go +++ b/syft/formats/syftjson/to_format_model.go @@ -190,11 +190,6 @@ func toPackageModel(p pkg.Package) model.Package { cpes[i] = cpe.String(c) } - var licenses = make([]string, 0) - if p.Licenses != nil { - licenses = p.Licenses - } - locations := p.Locations.ToSlice() var coordinates = make([]source.Coordinates, len(locations)) for i, l := range locations { @@ -209,7 +204,7 @@ func toPackageModel(p pkg.Package) model.Package { Type: p.Type, FoundBy: p.FoundBy, Locations: coordinates, - Licenses: licenses, + Licenses: p.Licenses, Language: p.Language, CPEs: cpes, PURL: p.PURL, diff --git a/syft/formats/test-fixtures/alpine-syft.json b/syft/formats/test-fixtures/alpine-syft.json index ba8e0269349..1af906cb9ac 100644 --- a/syft/formats/test-fixtures/alpine-syft.json +++ b/syft/formats/test-fixtures/alpine-syft.json @@ -12,9 +12,7 @@ "layerID": "sha256:e2eb06d8af8218cfec8210147357a68b7e13f7c485b991c288c2d01dc228bb68" } ], - "licenses": [ - "GPL-2.0-only" - ], + "licenses": "GPL-2.0-only", "language": "", "cpes": [ "cpe:2.3:a:alpine-baselayout:alpine-baselayout:3.2.0-r16:*:*:*:*:*:*:*", @@ -492,9 +490,7 @@ "layerID": "sha256:e2eb06d8af8218cfec8210147357a68b7e13f7c485b991c288c2d01dc228bb68" } ], - "licenses": [ - "MIT" - ], + "licenses": "MIT", "language": "", "cpes": [ "cpe:2.3:a:alpine-keys:alpine-keys:2.3-r1:*:*:*:*:*:*:*", @@ -778,9 +774,7 @@ "layerID": "sha256:e2eb06d8af8218cfec8210147357a68b7e13f7c485b991c288c2d01dc228bb68" } ], - "licenses": [ - "GPL-2.0-only" - ], + "licenses": "GPL-2.0-only", "language": "", "cpes": [ "cpe:2.3:a:apk-tools:apk-tools:2.12.7-r0:*:*:*:*:*:*:*", @@ -875,9 +869,7 @@ "layerID": "sha256:e2eb06d8af8218cfec8210147357a68b7e13f7c485b991c288c2d01dc228bb68" } ], - "licenses": [ - "GPL-2.0-only" - ], + "licenses": "GPL-2.0-only", "language": "", "cpes": [ "cpe:2.3:a:busybox:busybox:1.33.1-r3:*:*:*:*:*:*:*" @@ -1041,11 +1033,7 @@ "layerID": "sha256:e2eb06d8af8218cfec8210147357a68b7e13f7c485b991c288c2d01dc228bb68" } ], - "licenses": [ - "MPL-2.0", - "AND", - "MIT" - ], + "licenses": "MPL-2.0 AND MIT", "language": "", "cpes": [ "cpe:2.3:a:ca-certificates-bundle:ca-certificates-bundle:20191127-r5:*:*:*:*:*:*:*", @@ -1117,11 +1105,7 @@ "layerID": "sha256:e2eb06d8af8218cfec8210147357a68b7e13f7c485b991c288c2d01dc228bb68" } ], - "licenses": [ - "BSD-2-Clause", - "AND", - "BSD-3-Clause" - ], + "licenses": "BSD-2-Clause AND BSD-3-Clause", "language": "", "cpes": [ "cpe:2.3:a:libc-utils:libc-utils:0.7.2-r3:*:*:*:*:*:*:*", @@ -1162,9 +1146,7 @@ "layerID": "sha256:e2eb06d8af8218cfec8210147357a68b7e13f7c485b991c288c2d01dc228bb68" } ], - "licenses": [ - "OpenSSL" - ], + "licenses": "OpenSSL", "language": "", "cpes": [ "cpe:2.3:a:libcrypto1.1:libcrypto1.1:1.1.1l-r0:*:*:*:*:*:*:*" @@ -1336,13 +1318,7 @@ "layerID": "sha256:e2eb06d8af8218cfec8210147357a68b7e13f7c485b991c288c2d01dc228bb68" } ], - "licenses": [ - "ISC", - "AND", - "(BSD-3-Clause", - "OR", - "MIT)" - ], + "licenses": "ISC AND (BSD-3-Clause OR MIT)", "language": "", "cpes": [ "cpe:2.3:a:libretls:libretls:3.3.3p1-r2:*:*:*:*:*:*:*" @@ -1405,9 +1381,7 @@ "layerID": "sha256:e2eb06d8af8218cfec8210147357a68b7e13f7c485b991c288c2d01dc228bb68" } ], - "licenses": [ - "OpenSSL" - ], + "licenses": "OpenSSL", "language": "", "cpes": [ "cpe:2.3:a:libssl1.1:libssl1.1:1.1.1l-r0:*:*:*:*:*:*:*" @@ -1473,9 +1447,7 @@ "layerID": "sha256:e2eb06d8af8218cfec8210147357a68b7e13f7c485b991c288c2d01dc228bb68" } ], - "licenses": [ - "MIT" - ], + "licenses": "MIT", "language": "", "cpes": [ "cpe:2.3:a:musl:musl:1.2.2-r3:*:*:*:*:*:*:*" @@ -1535,11 +1507,7 @@ "layerID": "sha256:e2eb06d8af8218cfec8210147357a68b7e13f7c485b991c288c2d01dc228bb68" } ], - "licenses": [ - "MIT", - "BSD", - "GPL2+" - ], + "licenses": "MIT AND BSD AND GPL2+", "language": "", "cpes": [ "cpe:2.3:a:musl-utils:musl-utils:1.2.2-r3:*:*:*:*:*:*:*", @@ -1640,9 +1608,7 @@ "layerID": "sha256:e2eb06d8af8218cfec8210147357a68b7e13f7c485b991c288c2d01dc228bb68" } ], - "licenses": [ - "GPL-2.0-only" - ], + "licenses": "GPL-2.0-only", "language": "", "cpes": [ "cpe:2.3:a:scanelf:scanelf:1.3.2-r0:*:*:*:*:*:*:*" @@ -1695,9 +1661,7 @@ "layerID": "sha256:e2eb06d8af8218cfec8210147357a68b7e13f7c485b991c288c2d01dc228bb68" } ], - "licenses": [ - "GPL-2.0-only" - ], + "licenses": "GPL-2.0-only", "language": "", "cpes": [ "cpe:2.3:a:ssl-client:ssl-client:1.33.1-r3:*:*:*:*:*:*:*", @@ -1755,9 +1719,7 @@ "layerID": "sha256:e2eb06d8af8218cfec8210147357a68b7e13f7c485b991c288c2d01dc228bb68" } ], - "licenses": [ - "Zlib" - ], + "licenses": "Zlib", "language": "", "cpes": [ "cpe:2.3:a:zlib:zlib:1.2.11-r3:*:*:*:*:*:*:*" diff --git a/syft/formats/text/test-fixtures/snapshot/TestTextImageEncoder.golden b/syft/formats/text/test-fixtures/snapshot/TestTextImageEncoder.golden index 4ab3a446e0c..ff810403935 100644 --- a/syft/formats/text/test-fixtures/snapshot/TestTextImageEncoder.golden +++ b/syft/formats/text/test-fixtures/snapshot/TestTextImageEncoder.golden @@ -1,11 +1,11 @@ [Image] Layer: 0 - Digest: sha256:fb6beecb75b39f4bb813dbf177e501edd5ddb3e69bb45cedeb78c676ee1b7a59 + Digest: sha256:626044c9c04b634ddb1659b81debbeebf312c5e3f2e6e8a693813866aa38a381 Size: 22 MediaType: application/vnd.docker.image.rootfs.diff.tar.gzip Layer: 1 - Digest: sha256:319b588ce64253a87b533c8ed01cf0025e0eac98e7b516e12532957e1244fdec + Digest: sha256:2e0a2ae163675ae98403ca109fdee95bed2fea62f94e7d013146ac710d77de49 Size: 16 MediaType: application/vnd.docker.image.rootfs.diff.tar.gzip diff --git a/syft/formats/text/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden b/syft/formats/text/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden index c1b1d2b797e..fc4e17e4f51 100644 Binary files a/syft/formats/text/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden and b/syft/formats/text/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden differ diff --git a/syft/pkg/cataloger/alpm/cataloger_test.go b/syft/pkg/cataloger/alpm/cataloger_test.go index 65447d66d8b..661a6c93dac 100644 --- a/syft/pkg/cataloger/alpm/cataloger_test.go +++ b/syft/pkg/cataloger/alpm/cataloger_test.go @@ -5,6 +5,7 @@ import ( "github.com/google/go-cmp/cmp/cmpopts" + "github.com/anchore/syft/internal" "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/pkg" @@ -20,7 +21,7 @@ func TestAlpmCataloger(t *testing.T) { Version: "6.2.1-2", Type: pkg.AlpmPkg, FoundBy: "alpmdb-cataloger", - Licenses: []string{"LGPL3", "GPL"}, + Licenses: internal.LogicalStrings{Simple: []string{"LGPL3", "GPL"}}, Locations: source.NewLocationSet(source.NewLocation("var/lib/pacman/local/gmp-6.2.1-2/desc")), CPEs: nil, PURL: "", diff --git a/syft/pkg/cataloger/alpm/package.go b/syft/pkg/cataloger/alpm/package.go index 1a5bdf13135..b1f525bbe4e 100644 --- a/syft/pkg/cataloger/alpm/package.go +++ b/syft/pkg/cataloger/alpm/package.go @@ -1,6 +1,8 @@ package alpm import ( + "strings" + "github.com/anchore/packageurl-go" "github.com/anchore/syft/internal" "github.com/anchore/syft/syft/linux" @@ -14,7 +16,7 @@ func newPackage(m pkg.AlpmMetadata, release *linux.Release, locations ...source. Version: m.Version, Locations: source.NewLocationSet(locations...), Type: pkg.AlpmPkg, - Licenses: internal.SplitAny(m.License, " \n"), + Licenses: internal.LogicalStrings{Simple: strings.Split(m.License, " ")}, PURL: packageURL(m, release), MetadataType: pkg.AlpmMetadataType, Metadata: m, diff --git a/syft/pkg/cataloger/apkdb/package.go b/syft/pkg/cataloger/apkdb/package.go index bad434d558d..e6ca9d1f9f2 100644 --- a/syft/pkg/cataloger/apkdb/package.go +++ b/syft/pkg/cataloger/apkdb/package.go @@ -4,17 +4,22 @@ import ( "strings" "github.com/anchore/packageurl-go" + "github.com/anchore/syft/internal" "github.com/anchore/syft/syft/linux" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/source" ) func newPackage(d pkg.ApkMetadata, release *linux.Release, locations ...source.Location) pkg.Package { + license, err := internal.ParseLogicalStrings(d.License) + if err != nil { + return pkg.Package{} + } p := pkg.Package{ Name: d.Package, Version: d.Version, Locations: source.NewLocationSet(locations...), - Licenses: strings.Split(d.License, " "), + Licenses: license, PURL: packageURL(d, release), Type: pkg.ApkPkg, MetadataType: pkg.ApkMetadataType, diff --git a/syft/pkg/cataloger/apkdb/parse_apk_db_test.go b/syft/pkg/cataloger/apkdb/parse_apk_db_test.go index 60f40c98a58..45f29a19301 100644 --- a/syft/pkg/cataloger/apkdb/parse_apk_db_test.go +++ b/syft/pkg/cataloger/apkdb/parse_apk_db_test.go @@ -12,6 +12,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/anchore/syft/internal" "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/linux" @@ -674,7 +675,7 @@ func TestMultiplePackages(t *testing.T) { { Name: "libc-utils", Version: "0.7.2-r0", - Licenses: []string{"BSD"}, + Licenses: internal.LogicalStrings{Simple: []string{"BSD"}}, Type: pkg.ApkPkg, PURL: "pkg:apk/alpine/libc-utils@0.7.2-r0?arch=x86_64&upstream=libc-dev&distro=alpine-3.12", Locations: fixtureLocationSet, @@ -700,7 +701,7 @@ func TestMultiplePackages(t *testing.T) { { Name: "musl-utils", Version: "1.1.24-r2", - Licenses: []string{"MIT", "BSD", "GPL2+"}, + Licenses: internal.LogicalStrings{Simple: []string{"MIT", "BSD", "GPL2+"}}, Type: pkg.ApkPkg, PURL: "pkg:apk/alpine/musl-utils@1.1.24-r2?arch=x86_64&upstream=musl&distro=alpine-3.12", Locations: fixtureLocationSet, diff --git a/syft/pkg/cataloger/deb/cataloger_test.go b/syft/pkg/cataloger/deb/cataloger_test.go index 8fac459c613..91c6baedc39 100644 --- a/syft/pkg/cataloger/deb/cataloger_test.go +++ b/syft/pkg/cataloger/deb/cataloger_test.go @@ -3,6 +3,7 @@ package deb import ( "testing" + "github.com/anchore/syft/internal" "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" @@ -15,7 +16,7 @@ func TestDpkgCataloger(t *testing.T) { Name: "libpam-runtime", Version: "1.1.8-3.6", FoundBy: "dpkgdb-cataloger", - Licenses: []string{"GPL-1", "GPL-2", "LGPL-2.1"}, + Licenses: internal.LogicalStrings{Simple: []string{"GPL-1", "GPL-2", "LGPL-2.1"}}, Locations: source.NewLocationSet( source.NewVirtualLocation("/var/lib/dpkg/status", "/var/lib/dpkg/status"), source.NewVirtualLocation("/var/lib/dpkg/info/libpam-runtime.md5sums", "/var/lib/dpkg/info/libpam-runtime.md5sums"), diff --git a/syft/pkg/cataloger/deb/package.go b/syft/pkg/cataloger/deb/package.go index 3f10dbe3fe1..35877e6ebde 100644 --- a/syft/pkg/cataloger/deb/package.go +++ b/syft/pkg/cataloger/deb/package.go @@ -93,7 +93,7 @@ func addLicenses(resolver source.FileResolver, dbLocation source.Location, p *pk if copyrightReader != nil && copyrightLocation != nil { defer internal.CloseAndLogError(copyrightReader, copyrightLocation.VirtualPath) // attach the licenses - p.Licenses = parseLicensesFromCopyright(copyrightReader) + p.Licenses = internal.LogicalStrings{Simple: parseLicensesFromCopyright(copyrightReader)} // keep a record of the file where this was discovered p.Locations.Add(*copyrightLocation) diff --git a/syft/pkg/cataloger/java/archive_parser.go b/syft/pkg/cataloger/java/archive_parser.go index 3150351c8cc..2b39cd3c93d 100644 --- a/syft/pkg/cataloger/java/archive_parser.go +++ b/syft/pkg/cataloger/java/archive_parser.go @@ -7,6 +7,7 @@ import ( "path" "strings" + "github.com/anchore/syft/internal" "github.com/anchore/syft/internal/file" "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/artifact" @@ -184,10 +185,14 @@ func (j *archiveParser) discoverMainPackage() (*pkg.Package, error) { log.Warnf("failed to create digest for file=%q: %+v", j.archivePath, err) } + licenses := selectLicense(manifest) + if len(licenses) == 0 { + licenses = nil + } return &pkg.Package{ Name: selectName(manifest, j.fileInfo), Version: selectVersion(manifest, j.fileInfo), - Licenses: selectLicense(manifest), + Licenses: internal.LogicalStrings{Simple: licenses}, Language: pkg.Java, Locations: source.NewLocationSet(j.location), Type: j.fileInfo.pkgType(), diff --git a/syft/pkg/cataloger/java/archive_parser_test.go b/syft/pkg/cataloger/java/archive_parser_test.go index cd2affb50ce..ed63d4aca44 100644 --- a/syft/pkg/cataloger/java/archive_parser_test.go +++ b/syft/pkg/cataloger/java/archive_parser_test.go @@ -99,7 +99,7 @@ func TestParseJar(t *testing.T) { Name: "example-jenkins-plugin", Version: "1.0-SNAPSHOT", PURL: "pkg:maven/io.jenkins.plugins/example-jenkins-plugin@1.0-SNAPSHOT", - Licenses: []string{"MIT License"}, + Licenses: internal.LogicalStrings{Simple: []string{"MIT License"}}, Language: pkg.Java, Type: pkg.JenkinsPluginPkg, MetadataType: pkg.JavaMetadataType, @@ -150,7 +150,7 @@ func TestParseJar(t *testing.T) { Name: "example-java-app-gradle", Version: "0.1.0", PURL: "pkg:maven/example-java-app-gradle/example-java-app-gradle@0.1.0", - Licenses: []string{}, + Licenses: internal.LogicalStrings{}, Language: pkg.Java, Type: pkg.JavaPkg, MetadataType: pkg.JavaMetadataType, @@ -176,7 +176,7 @@ func TestParseJar(t *testing.T) { Name: "example-java-app-maven", Version: "0.1.0", PURL: "pkg:maven/org.anchore/example-java-app-maven@0.1.0", - Licenses: []string{}, + Licenses: internal.LogicalStrings{}, Language: pkg.Java, Type: pkg.JavaPkg, MetadataType: pkg.JavaMetadataType, diff --git a/syft/pkg/cataloger/javascript/cataloger_test.go b/syft/pkg/cataloger/javascript/cataloger_test.go index 6e0ba16b613..a23760ea2a7 100644 --- a/syft/pkg/cataloger/javascript/cataloger_test.go +++ b/syft/pkg/cataloger/javascript/cataloger_test.go @@ -3,6 +3,7 @@ package javascript import ( "testing" + "github.com/anchore/syft/internal" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" "github.com/anchore/syft/syft/source" @@ -19,7 +20,7 @@ func Test_JavascriptCataloger(t *testing.T) { Locations: locationSet, Language: pkg.JavaScript, Type: pkg.NpmPkg, - Licenses: []string{"MIT"}, + Licenses: internal.LogicalStrings{Simple: []string{"MIT"}}, MetadataType: pkg.NpmPackageLockJSONMetadataType, Metadata: pkg.NpmPackageLockJSONMetadata{Resolved: "https://registry.npmjs.org/@actions/core/-/core-1.6.0.tgz", Integrity: "sha512-NB1UAZomZlCV/LmJqkLhNTqtKfFXJZAUPcfl/zqG7EfsQdeUJtaWO98SGbuQ3pydJ3fHl2CvI/51OKYlCYYcaw=="}, }, @@ -42,7 +43,7 @@ func Test_JavascriptCataloger(t *testing.T) { Locations: locationSet, Language: pkg.JavaScript, Type: pkg.NpmPkg, - Licenses: []string{"MIT"}, + Licenses: internal.LogicalStrings{Simple: []string{"MIT"}}, MetadataType: pkg.NpmPackageLockJSONMetadataType, Metadata: pkg.NpmPackageLockJSONMetadata{Resolved: "https://registry.npmjs.org/cowsay/-/cowsay-1.4.0.tgz", Integrity: "sha512-rdg5k5PsHFVJheO/pmE3aDg2rUDDTfPJau6yYkZYlHFktUz+UxbE+IgnUAEyyCyv4noL5ltxXD0gZzmHPCy/9g=="}, }, diff --git a/syft/pkg/cataloger/javascript/package.go b/syft/pkg/cataloger/javascript/package.go index 34729e4215b..b65b0eb3013 100644 --- a/syft/pkg/cataloger/javascript/package.go +++ b/syft/pkg/cataloger/javascript/package.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/anchore/packageurl-go" + "github.com/anchore/syft/internal" "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/source" @@ -17,11 +18,15 @@ func newPackageJSONPackage(u packageJSON, locations ...source.Location) pkg.Pack if err != nil { log.Warnf("unable to extract licenses from javascript package.json: %+v", err) } + licenseStructure := internal.LogicalStrings{} + if len(licenses) > 0 { + licenseStructure.Simple = licenses + } p := pkg.Package{ Name: u.Name, Version: u.Version, - Licenses: licenses, + Licenses: licenseStructure, PURL: packageURL(u.Name, u.Version), Locations: source.NewLocationSet(locations...), Language: pkg.JavaScript, @@ -92,7 +97,7 @@ func newPackageLockV2Package(resolver source.FileResolver, location source.Locat PURL: packageURL(name, u.Version), Language: pkg.JavaScript, Type: pkg.NpmPkg, - Licenses: licenses, + Licenses: internal.LogicalStrings{Simple: licenses}, MetadataType: pkg.NpmPackageLockJSONMetadataType, Metadata: pkg.NpmPackageLockJSONMetadata{Resolved: u.Resolved, Integrity: u.Integrity}, }, @@ -130,7 +135,7 @@ func newYarnLockPackage(resolver source.FileResolver, location source.Location, } func finalizeLockPkg(resolver source.FileResolver, location source.Location, p pkg.Package) pkg.Package { - p.Licenses = append(p.Licenses, addLicenses(p.Name, resolver, location)...) + p.Licenses.Simple = append(p.Licenses.Simple, addLicenses(p.Name, resolver, location)...) p.SetID() return p } diff --git a/syft/pkg/cataloger/javascript/parse_package_json_test.go b/syft/pkg/cataloger/javascript/parse_package_json_test.go index 9fe3722331c..23bb7280adf 100644 --- a/syft/pkg/cataloger/javascript/parse_package_json_test.go +++ b/syft/pkg/cataloger/javascript/parse_package_json_test.go @@ -5,6 +5,7 @@ import ( "github.com/stretchr/testify/assert" + "github.com/anchore/syft/internal" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" "github.com/anchore/syft/syft/source" @@ -22,7 +23,7 @@ func TestParsePackageJSON(t *testing.T) { Version: "6.14.6", PURL: "pkg:npm/npm@6.14.6", Type: pkg.NpmPkg, - Licenses: []string{"Artistic-2.0"}, + Licenses: internal.LogicalStrings{Simple: []string{"Artistic-2.0"}}, Language: pkg.JavaScript, MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ @@ -42,7 +43,7 @@ func TestParsePackageJSON(t *testing.T) { Version: "6.14.6", PURL: "pkg:npm/npm@6.14.6", Type: pkg.NpmPkg, - Licenses: []string{"ISC"}, + Licenses: internal.LogicalStrings{Simple: []string{"ISC"}}, Language: pkg.JavaScript, MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ @@ -62,7 +63,7 @@ func TestParsePackageJSON(t *testing.T) { Version: "6.14.6", PURL: "pkg:npm/npm@6.14.6", Type: pkg.NpmPkg, - Licenses: []string{"MIT", "Apache-2.0"}, + Licenses: internal.LogicalStrings{Simple: []string{"MIT", "Apache-2.0"}}, Language: pkg.JavaScript, MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ @@ -82,7 +83,6 @@ func TestParsePackageJSON(t *testing.T) { Version: "6.14.6", PURL: "pkg:npm/npm@6.14.6", Type: pkg.NpmPkg, - Licenses: nil, Language: pkg.JavaScript, MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ @@ -102,7 +102,6 @@ func TestParsePackageJSON(t *testing.T) { Version: "6.14.6", PURL: "pkg:npm/npm@6.14.6", Type: pkg.NpmPkg, - Licenses: []string{}, Language: pkg.JavaScript, MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ @@ -122,7 +121,7 @@ func TestParsePackageJSON(t *testing.T) { Version: "6.14.6", PURL: "pkg:npm/npm@6.14.6", Type: pkg.NpmPkg, - Licenses: []string{"Artistic-2.0"}, + Licenses: internal.LogicalStrings{Simple: []string{"Artistic-2.0"}}, Language: pkg.JavaScript, MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ @@ -142,7 +141,7 @@ func TestParsePackageJSON(t *testing.T) { Version: "1.1.1", PURL: "pkg:npm/function-bind@1.1.1", Type: pkg.NpmPkg, - Licenses: []string{"MIT"}, + Licenses: internal.LogicalStrings{Simple: []string{"MIT"}}, Language: pkg.JavaScript, MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ @@ -162,7 +161,7 @@ func TestParsePackageJSON(t *testing.T) { Version: "6.14.6", PURL: "pkg:npm/npm@6.14.6", Type: pkg.NpmPkg, - Licenses: []string{"Artistic-2.0"}, + Licenses: internal.LogicalStrings{Simple: []string{"Artistic-2.0"}}, Language: pkg.JavaScript, MetadataType: pkg.NpmPackageJSONMetadataType, Metadata: pkg.NpmPackageJSONMetadata{ diff --git a/syft/pkg/cataloger/javascript/parse_package_lock_test.go b/syft/pkg/cataloger/javascript/parse_package_lock_test.go index 75601d43712..318ac2b62fa 100644 --- a/syft/pkg/cataloger/javascript/parse_package_lock_test.go +++ b/syft/pkg/cataloger/javascript/parse_package_lock_test.go @@ -3,6 +3,7 @@ package javascript import ( "testing" + "github.com/anchore/syft/internal" "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" @@ -139,7 +140,7 @@ func TestParsePackageLockV2(t *testing.T) { PURL: "pkg:npm/%40types/prop-types@15.7.5", Language: pkg.JavaScript, Type: pkg.NpmPkg, - Licenses: []string{"MIT"}, + Licenses: internal.LogicalStrings{Simple: []string{"MIT"}}, MetadataType: "NpmPackageLockJsonMetadata", Metadata: pkg.NpmPackageLockJSONMetadata{Resolved: "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", Integrity: "sha1-XxnSuFqY6VWANvajysyIGUIPBc8="}, }, @@ -149,7 +150,7 @@ func TestParsePackageLockV2(t *testing.T) { PURL: "pkg:npm/%40types/react@18.0.17", Language: pkg.JavaScript, Type: pkg.NpmPkg, - Licenses: []string{"MIT"}, + Licenses: internal.LogicalStrings{Simple: []string{"MIT"}}, MetadataType: "NpmPackageLockJsonMetadata", Metadata: pkg.NpmPackageLockJSONMetadata{Resolved: "https://registry.npmjs.org/@types/react/-/react-18.0.17.tgz", Integrity: "sha1-RYPZwyLWfv5LOak10iPtzHBQzPQ="}, }, @@ -159,7 +160,7 @@ func TestParsePackageLockV2(t *testing.T) { PURL: "pkg:npm/%40types/scheduler@0.16.2", Language: pkg.JavaScript, Type: pkg.NpmPkg, - Licenses: []string{"MIT"}, + Licenses: internal.LogicalStrings{Simple: []string{"MIT"}}, MetadataType: "NpmPackageLockJsonMetadata", Metadata: pkg.NpmPackageLockJSONMetadata{Resolved: "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", Integrity: "sha1-GmL4lSVyPd4kuhsBsJK/XfitTTk="}, }, @@ -169,7 +170,7 @@ func TestParsePackageLockV2(t *testing.T) { PURL: "pkg:npm/csstype@3.1.0", Language: pkg.JavaScript, Type: pkg.NpmPkg, - Licenses: []string{"MIT"}, + Licenses: internal.LogicalStrings{Simple: []string{"MIT"}}, MetadataType: "NpmPackageLockJsonMetadata", Metadata: pkg.NpmPackageLockJSONMetadata{Resolved: "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz", Integrity: "sha1-TdysNxjXh8+d8NG30VAzklyPKfI="}, }, @@ -274,7 +275,7 @@ func TestParsePackageLockAlias(t *testing.T) { PURL: "pkg:npm/alias-check@1.0.0", Language: pkg.JavaScript, Type: pkg.NpmPkg, - Licenses: []string{"ISC"}, + Licenses: internal.LogicalStrings{Simple: []string{"ISC"}}, MetadataType: "NpmPackageLockJsonMetadata", Metadata: pkg.NpmPackageLockJSONMetadata{}, } @@ -305,7 +306,7 @@ func TestParsePackageLockLicenseWithArray(t *testing.T) { { Name: "tmp", Version: "1.0.0", - Licenses: []string{"ISC"}, + Licenses: internal.LogicalStrings{Simple: []string{"ISC"}}, Language: pkg.JavaScript, Type: pkg.NpmPkg, PURL: "pkg:npm/tmp@1.0.0", @@ -315,7 +316,7 @@ func TestParsePackageLockLicenseWithArray(t *testing.T) { { Name: "pause-stream", Version: "0.0.11", - Licenses: []string{"MIT", "Apache2"}, + Licenses: internal.LogicalStrings{Simple: []string{"MIT", "Apache2"}}, Language: pkg.JavaScript, Type: pkg.NpmPkg, PURL: "pkg:npm/pause-stream@0.0.11", @@ -325,7 +326,7 @@ func TestParsePackageLockLicenseWithArray(t *testing.T) { { Name: "through", Version: "2.3.8", - Licenses: []string{"MIT"}, + Licenses: internal.LogicalStrings{Simple: []string{"MIT"}}, Language: pkg.JavaScript, Type: pkg.NpmPkg, PURL: "pkg:npm/through@2.3.8", diff --git a/syft/pkg/cataloger/portage/cataloger_test.go b/syft/pkg/cataloger/portage/cataloger_test.go index 3f4494b29c1..e61437dd279 100644 --- a/syft/pkg/cataloger/portage/cataloger_test.go +++ b/syft/pkg/cataloger/portage/cataloger_test.go @@ -3,6 +3,7 @@ package portage import ( "testing" + "github.com/anchore/syft/internal" "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/pkg" @@ -23,7 +24,7 @@ func TestPortageCataloger(t *testing.T) { source.NewLocation("var/db/pkg/app-containers/skopeo-1.5.1/LICENSE"), source.NewLocation("var/db/pkg/app-containers/skopeo-1.5.1/SIZE"), ), - Licenses: []string{"Apache-2.0", "BSD", "BSD-2", "CC-BY-SA-4.0", "ISC", "MIT"}, + Licenses: internal.LogicalStrings{Simple: []string{"Apache-2.0", "BSD", "BSD-2", "CC-BY-SA-4.0", "ISC", "MIT"}}, Type: pkg.PortagePkg, MetadataType: pkg.PortageMetadataType, Metadata: pkg.PortageMetadata{ diff --git a/syft/pkg/cataloger/portage/parse_portage_contents.go b/syft/pkg/cataloger/portage/parse_portage_contents.go index dba65ace0dd..7870648b81f 100644 --- a/syft/pkg/cataloger/portage/parse_portage_contents.go +++ b/syft/pkg/cataloger/portage/parse_portage_contents.go @@ -116,7 +116,7 @@ func addLicenses(resolver source.FileResolver, dbLocation source.Location, p *pk } licenses := findings.ToSlice() sort.Strings(licenses) - p.Licenses = licenses + p.Licenses = internal.LogicalStrings{Simple: licenses} p.Locations.Add(*location) } diff --git a/syft/pkg/cataloger/python/cataloger_test.go b/syft/pkg/cataloger/python/cataloger_test.go index 8515daf4b41..bf62a2ce889 100644 --- a/syft/pkg/cataloger/python/cataloger_test.go +++ b/syft/pkg/cataloger/python/cataloger_test.go @@ -5,6 +5,7 @@ import ( "github.com/stretchr/testify/require" + "github.com/anchore/syft/internal" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" "github.com/anchore/syft/syft/source" @@ -45,7 +46,7 @@ func Test_PackageCataloger(t *testing.T) { PURL: "pkg:pypi/requests@2.22.0", Type: pkg.PythonPkg, Language: pkg.Python, - Licenses: []string{"Apache 2.0"}, + Licenses: internal.LogicalStrings{Simple: []string{"Apache 2.0"}}, FoundBy: "python-package-cataloger", MetadataType: pkg.PythonPackageMetadataType, Metadata: pkg.PythonPackageMetadata{ @@ -82,7 +83,7 @@ func Test_PackageCataloger(t *testing.T) { PURL: "pkg:pypi/Pygments@2.6.1?vcs_url=git+https://github.com/python-test/test.git%40aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", Type: pkg.PythonPkg, Language: pkg.Python, - Licenses: []string{"BSD License"}, + Licenses: internal.LogicalStrings{Simple: []string{"BSD License"}}, FoundBy: "python-package-cataloger", MetadataType: pkg.PythonPackageMetadataType, Metadata: pkg.PythonPackageMetadata{ @@ -119,7 +120,7 @@ func Test_PackageCataloger(t *testing.T) { PURL: "pkg:pypi/Pygments@2.6.1", Type: pkg.PythonPkg, Language: pkg.Python, - Licenses: []string{"BSD License"}, + Licenses: internal.LogicalStrings{Simple: []string{"BSD License"}}, FoundBy: "python-package-cataloger", MetadataType: pkg.PythonPackageMetadataType, Metadata: pkg.PythonPackageMetadata{ @@ -150,7 +151,7 @@ func Test_PackageCataloger(t *testing.T) { PURL: "pkg:pypi/Pygments@2.6.1", Type: pkg.PythonPkg, Language: pkg.Python, - Licenses: []string{"BSD License"}, + Licenses: internal.LogicalStrings{Simple: []string{"BSD License"}}, FoundBy: "python-package-cataloger", MetadataType: pkg.PythonPackageMetadataType, Metadata: pkg.PythonPackageMetadata{ @@ -173,7 +174,7 @@ func Test_PackageCataloger(t *testing.T) { PURL: "pkg:pypi/requests@2.22.0", Type: pkg.PythonPkg, Language: pkg.Python, - Licenses: []string{"Apache 2.0"}, + Licenses: internal.LogicalStrings{Simple: []string{"Apache 2.0"}}, FoundBy: "python-package-cataloger", MetadataType: pkg.PythonPackageMetadataType, Metadata: pkg.PythonPackageMetadata{ diff --git a/syft/pkg/cataloger/python/package.go b/syft/pkg/cataloger/python/package.go index 0cb1e3f71ab..628704d2aaf 100644 --- a/syft/pkg/cataloger/python/package.go +++ b/syft/pkg/cataloger/python/package.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/anchore/packageurl-go" + "github.com/anchore/syft/internal" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/source" ) @@ -51,7 +52,7 @@ func newPackageForPackage(m pkg.PythonPackageMetadata, sources ...source.Locatio Version: m.Version, PURL: packageURL(m.Name, m.Version, &m), Locations: source.NewLocationSet(sources...), - Licenses: licenses, + Licenses: internal.LogicalStrings{Simple: licenses}, Language: pkg.Python, Type: pkg.PythonPkg, MetadataType: pkg.PythonPackageMetadataType, diff --git a/syft/pkg/cataloger/rpm/package.go b/syft/pkg/cataloger/rpm/package.go index cf277e7804b..9c91173b223 100644 --- a/syft/pkg/cataloger/rpm/package.go +++ b/syft/pkg/cataloger/rpm/package.go @@ -25,7 +25,7 @@ func newPackage(dbLocation source.Location, metadata pkg.RpmMetadata, distro *li } if metadata.License != "" { - p.Licenses = append(p.Licenses, metadata.License) + p.Licenses.Simple = append(p.Licenses.Simple, metadata.License) } p.SetID() diff --git a/syft/pkg/cataloger/rpm/parse_rpm_db_test.go b/syft/pkg/cataloger/rpm/parse_rpm_db_test.go index 111195fd34b..e9543bdac96 100644 --- a/syft/pkg/cataloger/rpm/parse_rpm_db_test.go +++ b/syft/pkg/cataloger/rpm/parse_rpm_db_test.go @@ -7,6 +7,7 @@ import ( "github.com/stretchr/testify/assert" + "github.com/anchore/syft/internal" "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" @@ -97,7 +98,7 @@ func TestParseRpmDB(t *testing.T) { Locations: source.NewLocationSet(source.NewLocation("test-fixtures/Packages")), Type: pkg.RpmPkg, MetadataType: pkg.RpmMetadataType, - Licenses: []string{"MIT"}, + Licenses: internal.LogicalStrings{Simple: []string{"MIT"}}, Metadata: pkg.RpmMetadata{ Name: "dive", Epoch: nil, @@ -125,7 +126,7 @@ func TestParseRpmDB(t *testing.T) { Locations: source.NewLocationSet(source.NewLocation("test-fixtures/Packages")), Type: pkg.RpmPkg, MetadataType: pkg.RpmMetadataType, - Licenses: []string{"MIT"}, + Licenses: internal.LogicalStrings{Simple: []string{"MIT"}}, Metadata: pkg.RpmMetadata{ Name: "dive", Epoch: nil, diff --git a/syft/pkg/cataloger/rpm/parse_rpm_test.go b/syft/pkg/cataloger/rpm/parse_rpm_test.go index 1d082e69392..322f7487339 100644 --- a/syft/pkg/cataloger/rpm/parse_rpm_test.go +++ b/syft/pkg/cataloger/rpm/parse_rpm_test.go @@ -3,6 +3,7 @@ package rpm import ( "testing" + "github.com/anchore/syft/internal" "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" @@ -25,7 +26,7 @@ func TestParseRpmFiles(t *testing.T) { FoundBy: "rpm-file-cataloger", Type: pkg.RpmPkg, MetadataType: pkg.RpmMetadataType, - Licenses: []string{"MIT"}, + Licenses: internal.LogicalStrings{Simple: []string{"MIT"}}, Metadata: pkg.RpmMetadata{ Name: "abc", Epoch: intRef(0), @@ -53,7 +54,7 @@ func TestParseRpmFiles(t *testing.T) { FoundBy: "rpm-file-cataloger", Type: pkg.RpmPkg, MetadataType: pkg.RpmMetadataType, - Licenses: []string{"Public Domain"}, + Licenses: internal.LogicalStrings{Simple: []string{"Public Domain"}}, Metadata: pkg.RpmMetadata{ Name: "zork", Epoch: intRef(0), diff --git a/syft/pkg/cataloger/ruby/package.go b/syft/pkg/cataloger/ruby/package.go index 408653c2518..f11eaa6f3f1 100644 --- a/syft/pkg/cataloger/ruby/package.go +++ b/syft/pkg/cataloger/ruby/package.go @@ -2,6 +2,7 @@ package ruby import ( "github.com/anchore/packageurl-go" + "github.com/anchore/syft/internal" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/source" ) @@ -27,7 +28,7 @@ func newGemspecPackage(m pkg.GemMetadata, locations ...source.Location) pkg.Pack Version: m.Version, Locations: source.NewLocationSet(locations...), PURL: packageURL(m.Name, m.Version), - Licenses: m.Licenses, + Licenses: internal.LogicalStrings{Simple: m.Licenses}, Language: pkg.Ruby, Type: pkg.GemPkg, MetadataType: pkg.GemMetadataType, diff --git a/syft/pkg/cataloger/ruby/parse_gemspec_test.go b/syft/pkg/cataloger/ruby/parse_gemspec_test.go index 6b378ce351c..8ddf2688045 100644 --- a/syft/pkg/cataloger/ruby/parse_gemspec_test.go +++ b/syft/pkg/cataloger/ruby/parse_gemspec_test.go @@ -3,6 +3,7 @@ package ruby import ( "testing" + "github.com/anchore/syft/internal" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" "github.com/anchore/syft/syft/source" @@ -19,7 +20,7 @@ func TestParseGemspec(t *testing.T) { PURL: "pkg:gem/bundler@2.1.4", Locations: locations, Type: pkg.GemPkg, - Licenses: []string{"MIT"}, + Licenses: internal.LogicalStrings{Simple: []string{"MIT"}}, Language: pkg.Ruby, MetadataType: pkg.GemMetadataType, Metadata: pkg.GemMetadata{ diff --git a/syft/pkg/cataloger/rust/parse_cargo_lock_test.go b/syft/pkg/cataloger/rust/parse_cargo_lock_test.go index 25fba560efd..3417c89daa5 100644 --- a/syft/pkg/cataloger/rust/parse_cargo_lock_test.go +++ b/syft/pkg/cataloger/rust/parse_cargo_lock_test.go @@ -21,7 +21,6 @@ func TestParseCargoLock(t *testing.T) { Language: pkg.Rust, Type: pkg.RustPkg, MetadataType: pkg.RustCargoPackageMetadataType, - Licenses: nil, Metadata: pkg.CargoPackageMetadata{ Name: "ansi_term", Version: "0.12.1", @@ -40,7 +39,6 @@ func TestParseCargoLock(t *testing.T) { Language: pkg.Rust, Type: pkg.RustPkg, MetadataType: pkg.RustCargoPackageMetadataType, - Licenses: nil, Metadata: pkg.CargoPackageMetadata{ Name: "matches", Version: "0.1.8", @@ -57,7 +55,6 @@ func TestParseCargoLock(t *testing.T) { Language: pkg.Rust, Type: pkg.RustPkg, MetadataType: pkg.RustCargoPackageMetadataType, - Licenses: nil, Metadata: pkg.CargoPackageMetadata{ Name: "memchr", Version: "2.3.3", @@ -74,7 +71,6 @@ func TestParseCargoLock(t *testing.T) { Language: pkg.Rust, Type: pkg.RustPkg, MetadataType: pkg.RustCargoPackageMetadataType, - Licenses: nil, Metadata: pkg.CargoPackageMetadata{ Name: "natord", Version: "1.0.9", @@ -91,7 +87,6 @@ func TestParseCargoLock(t *testing.T) { Language: pkg.Rust, Type: pkg.RustPkg, MetadataType: pkg.RustCargoPackageMetadataType, - Licenses: nil, Metadata: pkg.CargoPackageMetadata{ Name: "nom", Version: "4.2.3", @@ -111,7 +106,6 @@ func TestParseCargoLock(t *testing.T) { Language: pkg.Rust, Type: pkg.RustPkg, MetadataType: pkg.RustCargoPackageMetadataType, - Licenses: nil, Metadata: pkg.CargoPackageMetadata{ Name: "unicode-bidi", Version: "0.3.4", @@ -130,7 +124,6 @@ func TestParseCargoLock(t *testing.T) { Language: pkg.Rust, Type: pkg.RustPkg, MetadataType: pkg.RustCargoPackageMetadataType, - Licenses: nil, Metadata: pkg.CargoPackageMetadata{ Name: "version_check", Version: "0.1.5", @@ -147,7 +140,6 @@ func TestParseCargoLock(t *testing.T) { Language: pkg.Rust, Type: pkg.RustPkg, MetadataType: pkg.RustCargoPackageMetadataType, - Licenses: nil, Metadata: pkg.CargoPackageMetadata{ Name: "winapi", Version: "0.3.9", @@ -167,7 +159,6 @@ func TestParseCargoLock(t *testing.T) { Language: pkg.Rust, Type: pkg.RustPkg, MetadataType: pkg.RustCargoPackageMetadataType, - Licenses: nil, Metadata: pkg.CargoPackageMetadata{ Name: "winapi-i686-pc-windows-gnu", Version: "0.4.0", @@ -184,7 +175,6 @@ func TestParseCargoLock(t *testing.T) { Language: pkg.Rust, Type: pkg.RustPkg, MetadataType: pkg.RustCargoPackageMetadataType, - Licenses: nil, Metadata: pkg.CargoPackageMetadata{ Name: "winapi-x86_64-pc-windows-gnu", Version: "0.4.0", diff --git a/syft/pkg/cataloger/sbom/cataloger_test.go b/syft/pkg/cataloger/sbom/cataloger_test.go index ea372d50ccc..7966fc71364 100644 --- a/syft/pkg/cataloger/sbom/cataloger_test.go +++ b/syft/pkg/cataloger/sbom/cataloger_test.go @@ -5,6 +5,7 @@ import ( "github.com/stretchr/testify/require" + "github.com/anchore/syft/internal" "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/cpe" "github.com/anchore/syft/syft/formats/syftjson" @@ -39,7 +40,7 @@ func Test_parseSBOM(t *testing.T) { Version: "3.2.0-r23", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []string{"GPL-2.0-only"}, + Licenses: internal.LogicalStrings{Simple: []string{"GPL-2.0-only"}}, FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/alpine-baselayout@3.2.0-r23?arch=x86_64&upstream=alpine-baselayout&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -56,7 +57,7 @@ func Test_parseSBOM(t *testing.T) { Version: "3.2.0-r23", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []string{"GPL-2.0-only"}, + Licenses: internal.LogicalStrings{Simple: []string{"GPL-2.0-only"}}, FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/alpine-baselayout-data@3.2.0-r23?arch=x86_64&upstream=alpine-baselayout&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -77,7 +78,7 @@ func Test_parseSBOM(t *testing.T) { Version: "2.4-r1", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []string{"MIT"}, + Licenses: internal.LogicalStrings{Simple: []string{"MIT"}}, FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/alpine-keys@2.4-r1?arch=x86_64&upstream=alpine-keys&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -94,7 +95,7 @@ func Test_parseSBOM(t *testing.T) { Version: "2.12.9-r3", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []string{"GPL-2.0-only"}, + Licenses: internal.LogicalStrings{Simple: []string{"GPL-2.0-only"}}, FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/apk-tools@2.12.9-r3?arch=x86_64&upstream=apk-tools&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -111,7 +112,7 @@ func Test_parseSBOM(t *testing.T) { Version: "1.35.0-r17", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []string{"GPL-2.0-only"}, + Licenses: internal.LogicalStrings{Simple: []string{"GPL-2.0-only"}}, FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/busybox@1.35.0-r17?arch=x86_64&upstream=busybox&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -123,7 +124,7 @@ func Test_parseSBOM(t *testing.T) { Version: "20220614-r0", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []string{"MPL-2.0", "AND", "MIT"}, + Licenses: internal.LogicalStrings{Simple: []string{"MPL-2.0", "MIT"}, Joiner: internal.AND}, FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/ca-certificates-bundle@20220614-r0?arch=x86_64&upstream=ca-certificates&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -144,7 +145,7 @@ func Test_parseSBOM(t *testing.T) { Version: "0.7.2-r3", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []string{"BSD-2-Clause", "AND", "BSD-3-Clause"}, + Licenses: internal.LogicalStrings{Simple: []string{"BSD-2-Clause", "BSD-3-Clause"}, Joiner: internal.AND}, FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/libc-utils@0.7.2-r3?arch=x86_64&upstream=libc-dev&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -161,7 +162,7 @@ func Test_parseSBOM(t *testing.T) { Version: "1.1.1s-r0", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []string{"OpenSSL"}, + Licenses: internal.LogicalStrings{Simple: []string{"OpenSSL"}}, FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/libcrypto1.1@1.1.1s-r0?arch=x86_64&upstream=openssl&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -173,7 +174,7 @@ func Test_parseSBOM(t *testing.T) { Version: "1.1.1s-r0", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []string{"OpenSSL"}, + Licenses: internal.LogicalStrings{Simple: []string{"OpenSSL"}}, FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/libssl1.1@1.1.1s-r0?arch=x86_64&upstream=openssl&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -185,7 +186,7 @@ func Test_parseSBOM(t *testing.T) { Version: "1.2.3-r1", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []string{"MIT"}, + Licenses: internal.LogicalStrings{Simple: []string{"MIT"}}, FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/musl@1.2.3-r1?arch=x86_64&upstream=musl&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -197,7 +198,7 @@ func Test_parseSBOM(t *testing.T) { Version: "1.2.3-r1", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []string{"MIT", "BSD", "GPL2+"}, + Licenses: internal.LogicalStrings{Simple: []string{"MIT", "BSD", "GPL2+"}, Joiner: internal.OR}, FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/musl-utils@1.2.3-r1?arch=x86_64&upstream=musl&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -214,7 +215,7 @@ func Test_parseSBOM(t *testing.T) { Version: "1.3.4-r0", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []string{"GPL-2.0-only"}, + Licenses: internal.LogicalStrings{Simple: []string{"GPL-2.0-only"}}, FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/scanelf@1.3.4-r0?arch=x86_64&upstream=pax-utils&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -226,7 +227,7 @@ func Test_parseSBOM(t *testing.T) { Version: "1.35.0-r17", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []string{"GPL-2.0-only"}, + Licenses: internal.LogicalStrings{Simple: []string{"GPL-2.0-only"}}, FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/ssl_client@1.35.0-r17?arch=x86_64&upstream=busybox&distro=alpine-3.16.3", CPEs: mustCPEs( @@ -243,7 +244,7 @@ func Test_parseSBOM(t *testing.T) { Version: "1.2.12-r3", Type: "apk", Locations: source.NewLocationSet(source.NewLocation("sbom.syft.json")), - Licenses: []string{"Zlib"}, + Licenses: internal.LogicalStrings{Simple: []string{"Zlib"}}, FoundBy: "sbom-cataloger", PURL: "pkg:apk/alpine/zlib@1.2.12-r3?arch=x86_64&upstream=zlib&distro=alpine-3.16.3", CPEs: mustCPEs( diff --git a/syft/pkg/cataloger/sbom/test-fixtures/alpine/syft-json/sbom.syft.json b/syft/pkg/cataloger/sbom/test-fixtures/alpine/syft-json/sbom.syft.json index c9e83d12045..ebe6d6a07cd 100644 --- a/syft/pkg/cataloger/sbom/test-fixtures/alpine/syft-json/sbom.syft.json +++ b/syft/pkg/cataloger/sbom/test-fixtures/alpine/syft-json/sbom.syft.json @@ -12,9 +12,7 @@ "layerID": "sha256:e5e13b0c77cbb769548077189c3da2f0a764ceca06af49d8d558e759f5c232bd" } ], - "licenses": [ - "GPL-2.0-only" - ], + "licenses": "GPL-2.0-only", "language": "", "cpes": [ "cpe:2.3:a:alpine-baselayout:alpine-baselayout:3.2.0-r23:*:*:*:*:*:*:*", @@ -395,9 +393,7 @@ "layerID": "sha256:e5e13b0c77cbb769548077189c3da2f0a764ceca06af49d8d558e759f5c232bd" } ], - "licenses": [ - "GPL-2.0-only" - ], + "licenses": "GPL-2.0-only", "language": "", "cpes": [ "cpe:2.3:a:alpine-baselayout-data:alpine-baselayout-data:3.2.0-r23:*:*:*:*:*:*:*", @@ -558,9 +554,7 @@ "layerID": "sha256:e5e13b0c77cbb769548077189c3da2f0a764ceca06af49d8d558e759f5c232bd" } ], - "licenses": [ - "MIT" - ], + "licenses": "MIT", "language": "", "cpes": [ "cpe:2.3:a:alpine-keys:alpine-keys:2.4-r1:*:*:*:*:*:*:*", @@ -995,9 +989,7 @@ "layerID": "sha256:e5e13b0c77cbb769548077189c3da2f0a764ceca06af49d8d558e759f5c232bd" } ], - "licenses": [ - "GPL-2.0-only" - ], + "licenses": "GPL-2.0-only", "language": "", "cpes": [ "cpe:2.3:a:apk-tools:apk-tools:2.12.9-r3:*:*:*:*:*:*:*", @@ -1103,9 +1095,7 @@ "layerID": "sha256:e5e13b0c77cbb769548077189c3da2f0a764ceca06af49d8d558e759f5c232bd" } ], - "licenses": [ - "GPL-2.0-only" - ], + "licenses": "GPL-2.0-only", "language": "", "cpes": [ "cpe:2.3:a:busybox:busybox:1.35.0-r17:*:*:*:*:*:*:*" @@ -1276,11 +1266,7 @@ "layerID": "sha256:e5e13b0c77cbb769548077189c3da2f0a764ceca06af49d8d558e759f5c232bd" } ], - "licenses": [ - "MPL-2.0", - "AND", - "MIT" - ], + "licenses": "MPL-2.0 AND MIT", "language": "", "cpes": [ "cpe:2.3:a:ca-certificates-bundle:ca-certificates-bundle:20220614-r0:*:*:*:*:*:*:*", @@ -1355,11 +1341,7 @@ "layerID": "sha256:e5e13b0c77cbb769548077189c3da2f0a764ceca06af49d8d558e759f5c232bd" } ], - "licenses": [ - "BSD-2-Clause", - "AND", - "BSD-3-Clause" - ], + "licenses": "BSD-2-Clause AND BSD-3-Clause", "language": "", "cpes": [ "cpe:2.3:a:libc-utils:libc-utils:0.7.2-r3:*:*:*:*:*:*:*", @@ -1403,9 +1385,7 @@ "layerID": "sha256:e5e13b0c77cbb769548077189c3da2f0a764ceca06af49d8d558e759f5c232bd" } ], - "licenses": [ - "OpenSSL" - ], + "licenses": "OpenSSL", "language": "", "cpes": [ "cpe:2.3:a:libcrypto1.1:libcrypto1.1:1.1.1s-r0:*:*:*:*:*:*:*" @@ -1582,9 +1562,7 @@ "layerID": "sha256:e5e13b0c77cbb769548077189c3da2f0a764ceca06af49d8d558e759f5c232bd" } ], - "licenses": [ - "OpenSSL" - ], + "licenses": "OpenSSL", "language": "", "cpes": [ "cpe:2.3:a:libssl1.1:libssl1.1:1.1.1s-r0:*:*:*:*:*:*:*" @@ -1656,9 +1634,7 @@ "layerID": "sha256:e5e13b0c77cbb769548077189c3da2f0a764ceca06af49d8d558e759f5c232bd" } ], - "licenses": [ - "MIT" - ], + "licenses": "MIT", "language": "", "cpes": [ "cpe:2.3:a:musl:musl:1.2.3-r1:*:*:*:*:*:*:*" @@ -1721,11 +1697,7 @@ "layerID": "sha256:e5e13b0c77cbb769548077189c3da2f0a764ceca06af49d8d558e759f5c232bd" } ], - "licenses": [ - "MIT", - "BSD", - "GPL2+" - ], + "licenses": "MIT OR BSD OR GPL2+", "language": "", "cpes": [ "cpe:2.3:a:musl-utils:musl-utils:1.2.3-r1:*:*:*:*:*:*:*", @@ -1836,9 +1808,7 @@ "layerID": "sha256:e5e13b0c77cbb769548077189c3da2f0a764ceca06af49d8d558e759f5c232bd" } ], - "licenses": [ - "GPL-2.0-only" - ], + "licenses": "GPL-2.0-only", "language": "", "cpes": [ "cpe:2.3:a:scanelf:scanelf:1.3.4-r0:*:*:*:*:*:*:*" @@ -1896,9 +1866,7 @@ "layerID": "sha256:e5e13b0c77cbb769548077189c3da2f0a764ceca06af49d8d558e759f5c232bd" } ], - "licenses": [ - "GPL-2.0-only" - ], + "licenses": "GPL-2.0-only", "language": "", "cpes": [ "cpe:2.3:a:ssl-client:ssl-client:1.35.0-r17:*:*:*:*:*:*:*", @@ -1963,9 +1931,7 @@ "layerID": "sha256:e5e13b0c77cbb769548077189c3da2f0a764ceca06af49d8d558e759f5c232bd" } ], - "licenses": [ - "Zlib" - ], + "licenses": "Zlib", "language": "", "cpes": [ "cpe:2.3:a:zlib:zlib:1.2.12-r3:*:*:*:*:*:*:*" diff --git a/syft/pkg/package.go b/syft/pkg/package.go index 88351f2174d..42ef1f4f7af 100644 --- a/syft/pkg/package.go +++ b/syft/pkg/package.go @@ -8,6 +8,7 @@ import ( "sort" "strings" + "github.com/anchore/syft/internal" "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/cpe" @@ -17,18 +18,18 @@ 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 source.LocationSet // the locations that lead to the discovery of this package (note: this is not necessarily the locations that make up this package) - Licenses []string // licenses discovered with the package metadata - 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) - MetadataType MetadataType `cyclonedx:"metadataType"` // the shape of the additional data in the "metadata" field - 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 source.LocationSet // the locations that lead to the discovery of this package (note: this is not necessarily the locations that make up this package) + Licenses internal.LogicalStrings // 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) + MetadataType MetadataType `cyclonedx:"metadataType"` // the shape of the additional data in the "metadata" field + 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 51854c061ed..258a310af2d 100644 --- a/syft/pkg/package_test.go +++ b/syft/pkg/package_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/require" "github.com/anchore/stereoscope/pkg/file" + "github.com/anchore/syft/internal" "github.com/anchore/syft/syft/cpe" "github.com/anchore/syft/syft/source" ) @@ -27,9 +28,11 @@ func TestIDUniqueness(t *testing.T) { Locations: source.NewLocationSet( originalLocation, ), - Licenses: []string{ - "cc0-1.0", - "MIT", + Licenses: internal.LogicalStrings{ + Simple: []string{ + "cc0-1.0", + "MIT", + }, }, Language: "math", Type: PythonPkg, @@ -84,9 +87,11 @@ func TestIDUniqueness(t *testing.T) { name: "licenses order is ignored", transform: func(pkg Package) Package { // note: same as the original package, only a different order - pkg.Licenses = []string{ - "MIT", - "cc0-1.0", + pkg.Licenses = internal.LogicalStrings{ + Simple: []string{ + "MIT", + "cc0-1.0", + }, } return pkg }, @@ -146,7 +151,7 @@ func TestIDUniqueness(t *testing.T) { { name: "licenses is reflected", transform: func(pkg Package) Package { - pkg.Licenses = []string{"new!"} + pkg.Licenses = internal.LogicalStrings{Simple: []string{"new!"}} return pkg }, expectedIDComparison: assert.NotEqual, @@ -264,9 +269,11 @@ func TestPackage_Merge(t *testing.T) { Locations: source.NewLocationSet( originalLocation, ), - Licenses: []string{ - "cc0-1.0", - "MIT", + Licenses: internal.LogicalStrings{ + Simple: []string{ + "cc0-1.0", + "MIT", + }, }, Language: "math", Type: PythonPkg, @@ -292,9 +299,11 @@ func TestPackage_Merge(t *testing.T) { Locations: source.NewLocationSet( similarLocation, // NOTE: difference; we have a different layer but the same path ), - Licenses: []string{ - "cc0-1.0", - "MIT", + Licenses: internal.LogicalStrings{ + Simple: []string{ + "cc0-1.0", + "MIT", + }, }, Language: "math", Type: PythonPkg, @@ -321,9 +330,11 @@ func TestPackage_Merge(t *testing.T) { originalLocation, similarLocation, // NOTE: merge! ), - Licenses: []string{ - "cc0-1.0", - "MIT", + Licenses: internal.LogicalStrings{ + Simple: []string{ + "cc0-1.0", + "MIT", + }, }, Language: "math", Type: PythonPkg, @@ -353,9 +364,11 @@ func TestPackage_Merge(t *testing.T) { Locations: source.NewLocationSet( originalLocation, ), - Licenses: []string{ - "cc0-1.0", - "MIT", + Licenses: internal.LogicalStrings{ + Simple: []string{ + "cc0-1.0", + "MIT", + }, }, Language: "math", Type: PythonPkg, @@ -381,9 +394,11 @@ func TestPackage_Merge(t *testing.T) { Locations: source.NewLocationSet( originalLocation, ), - Licenses: []string{ - "cc0-1.0", - "MIT", + Licenses: internal.LogicalStrings{ + Simple: []string{ + "cc0-1.0", + "MIT", + }, }, Language: "math", Type: PythonPkg,