From 33a98e6b52d52231f94d85c8468b50886cb56393 Mon Sep 17 00:00:00 2001 From: mikey strauss Date: Fri, 2 Feb 2024 23:58:00 +0200 Subject: [PATCH 1/2] Add support for conan lock v2 (#2461) * conan lock 2.x requires field support Signed-off-by: houdini91 * PR review, struct renaming Signed-off-by: houdini91 --------- Signed-off-by: houdini91 --- syft/internal/packagemetadata/generated.go | 2 +- syft/internal/packagemetadata/names.go | 2 +- syft/internal/packagemetadata/names_test.go | 4 +- syft/pkg/cataloger/cpp/package.go | 6 +- syft/pkg/cataloger/cpp/parse_conanlock.go | 82 ++++++++++++++++++- .../pkg/cataloger/cpp/parse_conanlock_test.go | 12 +-- syft/pkg/conan.go | 34 +++++--- 7 files changed, 117 insertions(+), 25 deletions(-) diff --git a/syft/internal/packagemetadata/generated.go b/syft/internal/packagemetadata/generated.go index ddee53a5b6c..82fea4b84cd 100644 --- a/syft/internal/packagemetadata/generated.go +++ b/syft/internal/packagemetadata/generated.go @@ -11,7 +11,7 @@ func AllTypes() []any { pkg.ApkDBEntry{}, pkg.BinarySignature{}, pkg.CocoaPodfileLockEntry{}, - pkg.ConanLockEntry{}, + pkg.ConanV1LockEntry{}, pkg.ConanfileEntry{}, pkg.ConaninfoEntry{}, pkg.DartPubspecLockEntry{}, diff --git a/syft/internal/packagemetadata/names.go b/syft/internal/packagemetadata/names.go index c997069dc1c..0d5151f0e48 100644 --- a/syft/internal/packagemetadata/names.go +++ b/syft/internal/packagemetadata/names.go @@ -66,7 +66,7 @@ var jsonTypes = makeJSONTypes( jsonNames(pkg.ApkDBEntry{}, "apk-db-entry", "ApkMetadata"), jsonNames(pkg.BinarySignature{}, "binary-signature", "BinaryMetadata"), jsonNames(pkg.CocoaPodfileLockEntry{}, "cocoa-podfile-lock-entry", "CocoapodsMetadataType"), - jsonNames(pkg.ConanLockEntry{}, "c-conan-lock-entry", "ConanLockMetadataType"), + jsonNames(pkg.ConanV1LockEntry{}, "c-conan-lock-entry", "ConanLockMetadataType"), jsonNames(pkg.ConanfileEntry{}, "c-conan-file-entry", "ConanMetadataType"), jsonNames(pkg.ConaninfoEntry{}, "c-conan-info-entry"), jsonNames(pkg.DartPubspecLockEntry{}, "dart-pubspec-lock-entry", "DartPubMetadata"), diff --git a/syft/internal/packagemetadata/names_test.go b/syft/internal/packagemetadata/names_test.go index 21ee46ff734..752cb7c82e0 100644 --- a/syft/internal/packagemetadata/names_test.go +++ b/syft/internal/packagemetadata/names_test.go @@ -103,7 +103,7 @@ func TestReflectTypeFromJSONName_LegacyValues(t *testing.T) { { name: "map pkg.ConanLockEntry struct type", input: "ConanLockMetadataType", - expected: reflect.TypeOf(pkg.ConanLockEntry{}), + expected: reflect.TypeOf(pkg.ConanV1LockEntry{}), }, { name: "map pkg.ConanfileEntry struct type", @@ -290,7 +290,7 @@ func Test_JSONName_JSONLegacyName(t *testing.T) { }, { name: "ConanLockMetadata", - metadata: pkg.ConanLockEntry{}, + metadata: pkg.ConanV1LockEntry{}, expectedJSONName: "c-conan-lock-entry", expectedLegacyName: "ConanLockMetadataType", }, diff --git a/syft/pkg/cataloger/cpp/package.go b/syft/pkg/cataloger/cpp/package.go index 9a0b4be1b45..1b29329afa6 100644 --- a/syft/pkg/cataloger/cpp/package.go +++ b/syft/pkg/cataloger/cpp/package.go @@ -70,7 +70,11 @@ func newConanfilePackage(m pkg.ConanfileEntry, locations ...file.Location) *pkg. return newConanPackage(m.Ref, m, locations...) } -func newConanlockPackage(m pkg.ConanLockEntry, locations ...file.Location) *pkg.Package { +func newConanlockPackage(m pkg.ConanV1LockEntry, locations ...file.Location) *pkg.Package { + return newConanPackage(m.Ref, m, locations...) +} + +func newConanRefrencePackage(m pkg.ConanV2LockEntry, locations ...file.Location) *pkg.Package { return newConanPackage(m.Ref, m, locations...) } diff --git a/syft/pkg/cataloger/cpp/parse_conanlock.go b/syft/pkg/cataloger/cpp/parse_conanlock.go index ad2499c1d2e..847996b9c1c 100644 --- a/syft/pkg/cataloger/cpp/parse_conanlock.go +++ b/syft/pkg/cataloger/cpp/parse_conanlock.go @@ -26,8 +26,13 @@ type conanLock struct { Path string `json:"path"` } `json:"nodes"` } `json:"graph_lock"` - Version string `json:"version"` - ProfileHost string `json:"profile_host"` + Version string `json:"version"` + ProfileHost string `json:"profile_host"` + ProfileBuild string `json:"profile_build,omitempty"` + // conan v0.5+ lockfiles use "requires", "build_requires" and "python_requires" + Requires []string `json:"requires,omitempty"` + BuildRequires []string `json:"build_requires,omitempty"` + PythonRequires []string `json:"python_requires,omitempty"` } // parseConanlock is a parser function for conan.lock contents, returning all packages discovered. @@ -42,13 +47,33 @@ func parseConanlock(_ context.Context, _ file.Resolver, _ *generic.Environment, // in a second iteration var indexToPkgMap = map[string]pkg.Package{} + // Support for conan lock 2.x requires field + for _, ref := range cl.Requires { + reference := parseConanV2Reference(ref) + if reference.Name == "" { + continue + } + + p := newConanRefrencePackage( + reference, + reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), + ) + + if p != nil { + pk := *p + pkgs = append(pkgs, pk) + indexToPkgMap[reference.Name] = pk + } + + } + // we do not want to store the index list requires in the conan metadata, because it is not useful to have it in // the SBOM. Instead, we will store it in a map and then use it to build the relationships // maps pkg.ID to a list of indices var parsedPkgRequires = map[artifact.ID][]string{} for idx, node := range cl.GraphLock.Nodes { - metadata := pkg.ConanLockEntry{ + metadata := pkg.ConanV1LockEntry{ Ref: node.Ref, Options: parseOptions(node.Options), Path: node.Path, @@ -106,3 +131,54 @@ func parseOptions(options string) []pkg.KeyValue { return o } + +func parseConanV2Reference(ref string) pkg.ConanV2LockEntry { + // very flexible format name/version[@username[/channel]][#rrev][:pkgid[#prev]][%timestamp] + reference := pkg.ConanV2LockEntry{Ref: ref} + + parts := strings.SplitN(ref, "%", 2) + if len(parts) == 2 { + ref = parts[0] + reference.TimeStamp = parts[1] + } + + parts = strings.SplitN(ref, ":", 2) + if len(parts) == 2 { + ref = parts[0] + parts = strings.SplitN(parts[1], "#", 2) + reference.PackageID = parts[0] + if len(parts) == 2 { + reference.PackageRevision = parts[1] + } + } + + parts = strings.SplitN(ref, "#", 2) + if len(parts) == 2 { + ref = parts[0] + reference.RecipeRevision = parts[1] + } + + parts = strings.SplitN(ref, "@", 2) + if len(parts) == 2 { + ref = parts[0] + UsernameChannel := parts[1] + + parts = strings.SplitN(UsernameChannel, "/", 2) + reference.Username = parts[0] + if len(parts) == 2 { + reference.Channel = parts[1] + } + } + + parts = strings.SplitN(ref, "/", 2) + if len(parts) == 2 { + reference.Name = parts[0] + reference.Version = parts[1] + } else { + // consumer conanfile.txt or conanfile.py might not have a name + reference.Name = "" + reference.Version = ref + } + + return reference +} diff --git a/syft/pkg/cataloger/cpp/parse_conanlock_test.go b/syft/pkg/cataloger/cpp/parse_conanlock_test.go index c972f3e18ca..ef3d85f76e9 100644 --- a/syft/pkg/cataloger/cpp/parse_conanlock_test.go +++ b/syft/pkg/cataloger/cpp/parse_conanlock_test.go @@ -19,7 +19,7 @@ func TestParseConanlock(t *testing.T) { Locations: file.NewLocationSet(file.NewLocation(fixture)), Language: pkg.CPP, Type: pkg.ConanPkg, - Metadata: pkg.ConanLockEntry{ + Metadata: pkg.ConanV1LockEntry{ Ref: "mfast/1.2.2@my_user/my_channel#c6f6387c9b99780f0ee05e25f99d0f39", Options: pkg.KeyValues{ {Key: "fPIC", Value: "True"}, @@ -110,7 +110,7 @@ func TestParseConanlock(t *testing.T) { Locations: file.NewLocationSet(file.NewLocation(fixture)), Language: pkg.CPP, Type: pkg.ConanPkg, - Metadata: pkg.ConanLockEntry{ + Metadata: pkg.ConanV1LockEntry{ Ref: "boost/1.75.0#a9c318f067216f900900e044e7af4ab1", Options: pkg.KeyValues{ {Key: "addr2line_location", Value: "/usr/bin/addr2line"}, @@ -196,7 +196,7 @@ func TestParseConanlock(t *testing.T) { Locations: file.NewLocationSet(file.NewLocation(fixture)), Language: pkg.CPP, Type: pkg.ConanPkg, - Metadata: pkg.ConanLockEntry{ + Metadata: pkg.ConanV1LockEntry{ Ref: "zlib/1.2.12#c67ce17f2e96b972d42393ce50a76a1a", Options: pkg.KeyValues{ { @@ -220,7 +220,7 @@ func TestParseConanlock(t *testing.T) { Locations: file.NewLocationSet(file.NewLocation(fixture)), Language: pkg.CPP, Type: pkg.ConanPkg, - Metadata: pkg.ConanLockEntry{ + Metadata: pkg.ConanV1LockEntry{ Ref: "bzip2/1.0.8#62a8031289639043797cf53fa876d0ef", Options: []pkg.KeyValue{ { @@ -248,7 +248,7 @@ func TestParseConanlock(t *testing.T) { Locations: file.NewLocationSet(file.NewLocation(fixture)), Language: pkg.CPP, Type: pkg.ConanPkg, - Metadata: pkg.ConanLockEntry{ + Metadata: pkg.ConanV1LockEntry{ Ref: "libbacktrace/cci.20210118#76e40b760e0bcd602d46db56b22820ab", Options: []pkg.KeyValue{ { @@ -272,7 +272,7 @@ func TestParseConanlock(t *testing.T) { Locations: file.NewLocationSet(file.NewLocation(fixture)), Language: pkg.CPP, Type: pkg.ConanPkg, - Metadata: pkg.ConanLockEntry{ + Metadata: pkg.ConanV1LockEntry{ Ref: "tinyxml2/9.0.0#9f13a36ebfc222cd55fe531a0a8d94d1", Options: []pkg.KeyValue{ { diff --git a/syft/pkg/conan.go b/syft/pkg/conan.go index 14cf830e00b..23f1e3c3815 100644 --- a/syft/pkg/conan.go +++ b/syft/pkg/conan.go @@ -1,16 +1,28 @@ package pkg -// ConanLockEntry represents a single "node" entry from a conan.lock file. -type ConanLockEntry struct { - Ref string `json:"ref"` - PackageID string `json:"package_id,omitempty"` - Prev string `json:"prev,omitempty"` - Requires []string `json:"requires,omitempty"` - BuildRequires []string `json:"build_requires,omitempty"` - PythonRequires []string `json:"py_requires,omitempty"` - Options KeyValues `json:"options,omitempty"` - Path string `json:"path,omitempty"` - Context string `json:"context,omitempty"` +type ConanV2LockEntry struct { + Name string `json:"name,omitempty"` + Version string `json:"version,omitempty"` + Username string `json:"username,omitempty"` + Channel string `json:"channel,omitempty"` + RecipeRevision string `json:"recipe_revision,omitempty"` + PackageID string `json:"package_id,omitempty"` + PackageRevision string `json:"package_revision,omitempty"` + TimeStamp string `json:"timestamp,omitempty"` + Ref string `json:"ref"` +} + +// ConanV1LockEntry represents a single "node" entry from a conan.lock file. +type ConanV1LockEntry struct { + Ref string `json:"ref"` + PackageID string `json:"package_id,omitempty"` + Prev string `json:"prev,omitempty"` + Requires []string `json:"requires,omitempty"` + BuildRequires []string `json:"build_requires,omitempty"` + PythonRequires []string `json:"py_requires,omitempty"` + Options map[string]string `json:"options,omitempty"` + Path string `json:"path,omitempty"` + Context string `json:"context,omitempty"` } // ConanfileEntry represents a single "Requires" entry from a conanfile.txt. From 1c461e998565516592b36b0c1a3cb2ca3befb045 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Fri, 2 Feb 2024 17:45:38 -0500 Subject: [PATCH 2/2] decompose conanlock parser + add tests Signed-off-by: Alex Goodman --- internal/constants.go | 2 +- schema/json/schema-16.0.3.json | 2284 +++++++++++++++++ schema/json/schema-latest.json | 34 +- syft/internal/packagemetadata/generated.go | 1 + syft/internal/packagemetadata/names.go | 1 + syft/pkg/cataloger/cpp/cataloger.go | 2 +- syft/pkg/cataloger/cpp/package.go | 2 +- syft/pkg/cataloger/cpp/parse_conanlock.go | 95 +- .../pkg/cataloger/cpp/parse_conanlock_test.go | 45 +- .../cpp/test-fixtures/conanlock-v2/conan.lock | 9 + syft/pkg/conan.go | 37 +- 11 files changed, 2446 insertions(+), 66 deletions(-) create mode 100644 schema/json/schema-16.0.3.json create mode 100644 syft/pkg/cataloger/cpp/test-fixtures/conanlock-v2/conan.lock diff --git a/internal/constants.go b/internal/constants.go index ce391e1640e..cc69cc97dc3 100644 --- a/internal/constants.go +++ b/internal/constants.go @@ -3,5 +3,5 @@ package internal const ( // JSONSchemaVersion is the current schema version output by the JSON encoder // This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment. - JSONSchemaVersion = "16.0.2" + JSONSchemaVersion = "16.0.3" ) diff --git a/schema/json/schema-16.0.3.json b/schema/json/schema-16.0.3.json new file mode 100644 index 00000000000..e846be24170 --- /dev/null +++ b/schema/json/schema-16.0.3.json @@ -0,0 +1,2284 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "anchore.io/schema/syft/json/16.0.3/document", + "$ref": "#/$defs/Document", + "$defs": { + "AlpmDbEntry": { + "properties": { + "basepackage": { + "type": "string" + }, + "package": { + "type": "string" + }, + "version": { + "type": "string" + }, + "description": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "packager": { + "type": "string" + }, + "url": { + "type": "string" + }, + "validation": { + "type": "string" + }, + "reason": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/AlpmFileRecord" + }, + "type": "array" + }, + "backup": { + "items": { + "$ref": "#/$defs/AlpmFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "basepackage", + "package", + "version", + "description", + "architecture", + "size", + "packager", + "url", + "validation", + "reason", + "files", + "backup" + ] + }, + "AlpmFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "type": { + "type": "string" + }, + "uid": { + "type": "string" + }, + "gid": { + "type": "string" + }, + "time": { + "type": "string", + "format": "date-time" + }, + "size": { + "type": "string" + }, + "link": { + "type": "string" + }, + "digest": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object" + }, + "ApkDbEntry": { + "properties": { + "package": { + "type": "string" + }, + "originPackage": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "version": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "url": { + "type": "string" + }, + "description": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "installedSize": { + "type": "integer" + }, + "pullDependencies": { + "items": { + "type": "string" + }, + "type": "array" + }, + "provides": { + "items": { + "type": "string" + }, + "type": "array" + }, + "pullChecksum": { + "type": "string" + }, + "gitCommitOfApkPort": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/ApkFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "originPackage", + "maintainer", + "version", + "architecture", + "url", + "description", + "size", + "installedSize", + "pullDependencies", + "provides", + "pullChecksum", + "gitCommitOfApkPort", + "files" + ] + }, + "ApkFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "ownerUid": { + "type": "string" + }, + "ownerGid": { + "type": "string" + }, + "permissions": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "BinarySignature": { + "properties": { + "matches": { + "items": { + "$ref": "#/$defs/ClassifierMatch" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "matches" + ] + }, + "CConanFileEntry": { + "properties": { + "ref": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "CConanInfoEntry": { + "properties": { + "ref": { + "type": "string" + }, + "package_id": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "CConanLockEntry": { + "properties": { + "ref": { + "type": "string" + }, + "package_id": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "build_requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "py_requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "options": { + "$ref": "#/$defs/KeyValues" + }, + "path": { + "type": "string" + }, + "context": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "CConanLockV2Entry": { + "properties": { + "ref": { + "type": "string" + }, + "packageID": { + "type": "string" + }, + "username": { + "type": "string" + }, + "channel": { + "type": "string" + }, + "recipeRevision": { + "type": "string" + }, + "packageRevision": { + "type": "string" + }, + "timestamp": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "CPE": { + "properties": { + "cpe": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "type": "object", + "required": [ + "cpe" + ] + }, + "ClassifierMatch": { + "properties": { + "classifier": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Location" + } + }, + "type": "object", + "required": [ + "classifier", + "location" + ] + }, + "CocoaPodfileLockEntry": { + "properties": { + "checksum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "checksum" + ] + }, + "Coordinates": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "DartPubspecLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "hosted_url": { + "type": "string" + }, + "vcs_url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Descriptor": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "configuration": true + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Digest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algorithm", + "value" + ] + }, + "Document": { + "properties": { + "artifacts": { + "items": { + "$ref": "#/$defs/Package" + }, + "type": "array" + }, + "artifactRelationships": { + "items": { + "$ref": "#/$defs/Relationship" + }, + "type": "array" + }, + "files": { + "items": { + "$ref": "#/$defs/File" + }, + "type": "array" + }, + "source": { + "$ref": "#/$defs/Source" + }, + "distro": { + "$ref": "#/$defs/LinuxRelease" + }, + "descriptor": { + "$ref": "#/$defs/Descriptor" + }, + "schema": { + "$ref": "#/$defs/Schema" + } + }, + "type": "object", + "required": [ + "artifacts", + "artifactRelationships", + "source", + "distro", + "descriptor", + "schema" + ] + }, + "DotnetDepsEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "path": { + "type": "string" + }, + "sha512": { + "type": "string" + }, + "hashPath": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "path", + "sha512", + "hashPath" + ] + }, + "DotnetPortableExecutableEntry": { + "properties": { + "assemblyVersion": { + "type": "string" + }, + "legalCopyright": { + "type": "string" + }, + "comments": { + "type": "string" + }, + "internalName": { + "type": "string" + }, + "companyName": { + "type": "string" + }, + "productName": { + "type": "string" + }, + "productVersion": { + "type": "string" + } + }, + "type": "object", + "required": [ + "assemblyVersion", + "legalCopyright", + "companyName", + "productName", + "productVersion" + ] + }, + "DpkgDbEntry": { + "properties": { + "package": { + "type": "string" + }, + "source": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "installedSize": { + "type": "integer" + }, + "provides": { + "items": { + "type": "string" + }, + "type": "array" + }, + "depends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "preDepends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "files": { + "items": { + "$ref": "#/$defs/DpkgFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "source", + "version", + "sourceVersion", + "architecture", + "maintainer", + "installedSize", + "files" + ] + }, + "DpkgFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "isConfigFile": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "path", + "isConfigFile" + ] + }, + "ELFSecurityFeatures": { + "properties": { + "symbolTableStripped": { + "type": "boolean" + }, + "stackCanary": { + "type": "boolean" + }, + "nx": { + "type": "boolean" + }, + "relRO": { + "type": "string" + }, + "pie": { + "type": "boolean" + }, + "dso": { + "type": "boolean" + }, + "safeStack": { + "type": "boolean" + }, + "cfi": { + "type": "boolean" + }, + "fortify": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "symbolTableStripped", + "nx", + "relRO", + "pie", + "dso" + ] + }, + "ElixirMixLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "pkgHashExt": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "pkgHash", + "pkgHashExt" + ] + }, + "ErlangRebarLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "pkgHashExt": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "pkgHash", + "pkgHashExt" + ] + }, + "Executable": { + "properties": { + "format": { + "type": "string" + }, + "elfSecurityFeatures": { + "$ref": "#/$defs/ELFSecurityFeatures" + } + }, + "type": "object", + "required": [ + "format" + ] + }, + "File": { + "properties": { + "id": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Coordinates" + }, + "metadata": { + "$ref": "#/$defs/FileMetadataEntry" + }, + "contents": { + "type": "string" + }, + "digests": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + }, + "licenses": { + "items": { + "$ref": "#/$defs/FileLicense" + }, + "type": "array" + }, + "executable": { + "$ref": "#/$defs/Executable" + } + }, + "type": "object", + "required": [ + "id", + "location" + ] + }, + "FileLicense": { + "properties": { + "value": { + "type": "string" + }, + "spdxExpression": { + "type": "string" + }, + "type": { + "type": "string" + }, + "evidence": { + "$ref": "#/$defs/FileLicenseEvidence" + } + }, + "type": "object", + "required": [ + "value", + "spdxExpression", + "type" + ] + }, + "FileLicenseEvidence": { + "properties": { + "confidence": { + "type": "integer" + }, + "offset": { + "type": "integer" + }, + "extent": { + "type": "integer" + } + }, + "type": "object", + "required": [ + "confidence", + "offset", + "extent" + ] + }, + "FileMetadataEntry": { + "properties": { + "mode": { + "type": "integer" + }, + "type": { + "type": "string" + }, + "linkDestination": { + "type": "string" + }, + "userID": { + "type": "integer" + }, + "groupID": { + "type": "integer" + }, + "mimeType": { + "type": "string" + }, + "size": { + "type": "integer" + } + }, + "type": "object", + "required": [ + "mode", + "type", + "userID", + "groupID", + "mimeType", + "size" + ] + }, + "GoModuleBuildinfoEntry": { + "properties": { + "goBuildSettings": { + "$ref": "#/$defs/KeyValues" + }, + "goCompiledVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "h1Digest": { + "type": "string" + }, + "mainModule": { + "type": "string" + }, + "goCryptoSettings": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "goCompiledVersion", + "architecture" + ] + }, + "GoModuleEntry": { + "properties": { + "h1Digest": { + "type": "string" + } + }, + "type": "object" + }, + "HaskellHackageStackEntry": { + "properties": { + "pkgHash": { + "type": "string" + } + }, + "type": "object" + }, + "HaskellHackageStackLockEntry": { + "properties": { + "pkgHash": { + "type": "string" + }, + "snapshotURL": { + "type": "string" + } + }, + "type": "object" + }, + "IDLikes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "JavaArchive": { + "properties": { + "virtualPath": { + "type": "string" + }, + "manifest": { + "$ref": "#/$defs/JavaManifest" + }, + "pomProperties": { + "$ref": "#/$defs/JavaPomProperties" + }, + "pomProject": { + "$ref": "#/$defs/JavaPomProject" + }, + "digest": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "virtualPath" + ] + }, + "JavaManifest": { + "properties": { + "main": { + "$ref": "#/$defs/KeyValues" + }, + "sections": { + "items": { + "$ref": "#/$defs/KeyValues" + }, + "type": "array" + } + }, + "type": "object" + }, + "JavaPomParent": { + "properties": { + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "type": "object", + "required": [ + "groupId", + "artifactId", + "version" + ] + }, + "JavaPomProject": { + "properties": { + "path": { + "type": "string" + }, + "parent": { + "$ref": "#/$defs/JavaPomParent" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "groupId", + "artifactId", + "version", + "name" + ] + }, + "JavaPomProperties": { + "properties": { + "path": { + "type": "string" + }, + "name": { + "type": "string" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "scope": { + "type": "string" + }, + "extraFields": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path", + "name", + "groupId", + "artifactId", + "version" + ] + }, + "JavascriptNpmPackage": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "author": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + }, + "private": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "homepage", + "description", + "url", + "private" + ] + }, + "JavascriptNpmPackageLockEntry": { + "properties": { + "resolved": { + "type": "string" + }, + "integrity": { + "type": "string" + } + }, + "type": "object", + "required": [ + "resolved", + "integrity" + ] + }, + "JavascriptYarnLockEntry": { + "properties": { + "resolved": { + "type": "string" + }, + "integrity": { + "type": "string" + } + }, + "type": "object", + "required": [ + "resolved", + "integrity" + ] + }, + "KeyValue": { + "properties": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "key", + "value" + ] + }, + "KeyValues": { + "items": { + "$ref": "#/$defs/KeyValue" + }, + "type": "array" + }, + "License": { + "properties": { + "value": { + "type": "string" + }, + "spdxExpression": { + "type": "string" + }, + "type": { + "type": "string" + }, + "urls": { + "items": { + "type": "string" + }, + "type": "array" + }, + "locations": { + "items": { + "$ref": "#/$defs/Location" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "value", + "spdxExpression", + "type", + "urls", + "locations" + ] + }, + "LinuxKernelArchive": { + "properties": { + "name": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "version": { + "type": "string" + }, + "extendedVersion": { + "type": "string" + }, + "buildTime": { + "type": "string" + }, + "author": { + "type": "string" + }, + "format": { + "type": "string" + }, + "rwRootFS": { + "type": "boolean" + }, + "swapDevice": { + "type": "integer" + }, + "rootDevice": { + "type": "integer" + }, + "videoMode": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "architecture", + "version" + ] + }, + "LinuxKernelModule": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "path": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "license": { + "type": "string" + }, + "kernelVersion": { + "type": "string" + }, + "versionMagic": { + "type": "string" + }, + "parameters": { + "patternProperties": { + ".*": { + "$ref": "#/$defs/LinuxKernelModuleParameter" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "LinuxKernelModuleParameter": { + "properties": { + "type": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "type": "object" + }, + "LinuxRelease": { + "properties": { + "prettyName": { + "type": "string" + }, + "name": { + "type": "string" + }, + "id": { + "type": "string" + }, + "idLike": { + "$ref": "#/$defs/IDLikes" + }, + "version": { + "type": "string" + }, + "versionID": { + "type": "string" + }, + "versionCodename": { + "type": "string" + }, + "buildID": { + "type": "string" + }, + "imageID": { + "type": "string" + }, + "imageVersion": { + "type": "string" + }, + "variant": { + "type": "string" + }, + "variantID": { + "type": "string" + }, + "homeURL": { + "type": "string" + }, + "supportURL": { + "type": "string" + }, + "bugReportURL": { + "type": "string" + }, + "privacyPolicyURL": { + "type": "string" + }, + "cpeName": { + "type": "string" + }, + "supportEnd": { + "type": "string" + } + }, + "type": "object" + }, + "Location": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + }, + "accessPath": { + "type": "string" + }, + "annotations": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path", + "accessPath" + ] + }, + "MicrosoftKbPatch": { + "properties": { + "product_id": { + "type": "string" + }, + "kb": { + "type": "string" + } + }, + "type": "object", + "required": [ + "product_id", + "kb" + ] + }, + "NixStoreEntry": { + "properties": { + "outputHash": { + "type": "string" + }, + "output": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "outputHash", + "files" + ] + }, + "Package": { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "type": { + "type": "string" + }, + "foundBy": { + "type": "string" + }, + "locations": { + "items": { + "$ref": "#/$defs/Location" + }, + "type": "array" + }, + "licenses": { + "$ref": "#/$defs/licenses" + }, + "language": { + "type": "string" + }, + "cpes": { + "$ref": "#/$defs/cpes" + }, + "purl": { + "type": "string" + }, + "metadataType": { + "type": "string" + }, + "metadata": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "#/$defs/AlpmDbEntry" + }, + { + "$ref": "#/$defs/ApkDbEntry" + }, + { + "$ref": "#/$defs/BinarySignature" + }, + { + "$ref": "#/$defs/CConanFileEntry" + }, + { + "$ref": "#/$defs/CConanInfoEntry" + }, + { + "$ref": "#/$defs/CConanLockEntry" + }, + { + "$ref": "#/$defs/CConanLockV2Entry" + }, + { + "$ref": "#/$defs/CocoaPodfileLockEntry" + }, + { + "$ref": "#/$defs/DartPubspecLockEntry" + }, + { + "$ref": "#/$defs/DotnetDepsEntry" + }, + { + "$ref": "#/$defs/DotnetPortableExecutableEntry" + }, + { + "$ref": "#/$defs/DpkgDbEntry" + }, + { + "$ref": "#/$defs/ElixirMixLockEntry" + }, + { + "$ref": "#/$defs/ErlangRebarLockEntry" + }, + { + "$ref": "#/$defs/GoModuleBuildinfoEntry" + }, + { + "$ref": "#/$defs/GoModuleEntry" + }, + { + "$ref": "#/$defs/HaskellHackageStackEntry" + }, + { + "$ref": "#/$defs/HaskellHackageStackLockEntry" + }, + { + "$ref": "#/$defs/JavaArchive" + }, + { + "$ref": "#/$defs/JavascriptNpmPackage" + }, + { + "$ref": "#/$defs/JavascriptNpmPackageLockEntry" + }, + { + "$ref": "#/$defs/JavascriptYarnLockEntry" + }, + { + "$ref": "#/$defs/LinuxKernelArchive" + }, + { + "$ref": "#/$defs/LinuxKernelModule" + }, + { + "$ref": "#/$defs/MicrosoftKbPatch" + }, + { + "$ref": "#/$defs/NixStoreEntry" + }, + { + "$ref": "#/$defs/PhpComposerInstalledEntry" + }, + { + "$ref": "#/$defs/PhpComposerLockEntry" + }, + { + "$ref": "#/$defs/PortageDbEntry" + }, + { + "$ref": "#/$defs/PythonPackage" + }, + { + "$ref": "#/$defs/PythonPipRequirementsEntry" + }, + { + "$ref": "#/$defs/PythonPipfileLockEntry" + }, + { + "$ref": "#/$defs/PythonPoetryLockEntry" + }, + { + "$ref": "#/$defs/RDescription" + }, + { + "$ref": "#/$defs/RpmArchive" + }, + { + "$ref": "#/$defs/RpmDbEntry" + }, + { + "$ref": "#/$defs/RubyGemspec" + }, + { + "$ref": "#/$defs/RustCargoAuditEntry" + }, + { + "$ref": "#/$defs/RustCargoLockEntry" + }, + { + "$ref": "#/$defs/SwiftPackageManagerLockEntry" + } + ] + } + }, + "type": "object", + "required": [ + "id", + "name", + "version", + "type", + "foundBy", + "locations", + "licenses", + "language", + "cpes", + "purl" + ] + }, + "PhpComposerAuthors": { + "properties": { + "name": { + "type": "string" + }, + "email": { + "type": "string" + }, + "homepage": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name" + ] + }, + "PhpComposerExternalReference": { + "properties": { + "type": { + "type": "string" + }, + "url": { + "type": "string" + }, + "reference": { + "type": "string" + }, + "shasum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "type", + "url", + "reference" + ] + }, + "PhpComposerInstalledEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "dist": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "require": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "provide": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "require-dev": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "suggest": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "license": { + "items": { + "type": "string" + }, + "type": "array" + }, + "type": { + "type": "string" + }, + "notification-url": { + "type": "string" + }, + "bin": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "$ref": "#/$defs/PhpComposerAuthors" + }, + "type": "array" + }, + "description": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "keywords": { + "items": { + "type": "string" + }, + "type": "array" + }, + "time": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "dist" + ] + }, + "PhpComposerLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "dist": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "require": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "provide": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "require-dev": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "suggest": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "license": { + "items": { + "type": "string" + }, + "type": "array" + }, + "type": { + "type": "string" + }, + "notification-url": { + "type": "string" + }, + "bin": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "$ref": "#/$defs/PhpComposerAuthors" + }, + "type": "array" + }, + "description": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "keywords": { + "items": { + "type": "string" + }, + "type": "array" + }, + "time": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "dist" + ] + }, + "PortageDbEntry": { + "properties": { + "installedSize": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/PortageFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "installedSize", + "files" + ] + }, + "PortageFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "PythonDirectURLOriginInfo": { + "properties": { + "url": { + "type": "string" + }, + "commitId": { + "type": "string" + }, + "vcs": { + "type": "string" + } + }, + "type": "object", + "required": [ + "url" + ] + }, + "PythonFileDigest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algorithm", + "value" + ] + }, + "PythonFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/PythonFileDigest" + }, + "size": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "PythonPackage": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "author": { + "type": "string" + }, + "authorEmail": { + "type": "string" + }, + "platform": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/PythonFileRecord" + }, + "type": "array" + }, + "sitePackagesRootPath": { + "type": "string" + }, + "topLevelPackages": { + "items": { + "type": "string" + }, + "type": "array" + }, + "directUrlOrigin": { + "$ref": "#/$defs/PythonDirectURLOriginInfo" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "authorEmail", + "platform", + "sitePackagesRootPath" + ] + }, + "PythonPipRequirementsEntry": { + "properties": { + "name": { + "type": "string" + }, + "extras": { + "items": { + "type": "string" + }, + "type": "array" + }, + "versionConstraint": { + "type": "string" + }, + "url": { + "type": "string" + }, + "markers": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "versionConstraint" + ] + }, + "PythonPipfileLockEntry": { + "properties": { + "hashes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "index": { + "type": "string" + } + }, + "type": "object", + "required": [ + "hashes", + "index" + ] + }, + "PythonPoetryLockEntry": { + "properties": { + "index": { + "type": "string" + } + }, + "type": "object", + "required": [ + "index" + ] + }, + "RDescription": { + "properties": { + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "url": { + "items": { + "type": "string" + }, + "type": "array" + }, + "repository": { + "type": "string" + }, + "built": { + "type": "string" + }, + "needsCompilation": { + "type": "boolean" + }, + "imports": { + "items": { + "type": "string" + }, + "type": "array" + }, + "depends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "suggests": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "Relationship": { + "properties": { + "parent": { + "type": "string" + }, + "child": { + "type": "string" + }, + "type": { + "type": "string" + }, + "metadata": true + }, + "type": "object", + "required": [ + "parent", + "child", + "type" + ] + }, + "RpmArchive": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "epoch": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "architecture": { + "type": "string" + }, + "release": { + "type": "string" + }, + "sourceRpm": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "vendor": { + "type": "string" + }, + "modularityLabel": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/RpmFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "epoch", + "architecture", + "release", + "sourceRpm", + "size", + "vendor", + "files" + ] + }, + "RpmDbEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "epoch": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "architecture": { + "type": "string" + }, + "release": { + "type": "string" + }, + "sourceRpm": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "vendor": { + "type": "string" + }, + "modularityLabel": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/RpmFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "epoch", + "architecture", + "release", + "sourceRpm", + "size", + "vendor", + "files" + ] + }, + "RpmFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "mode": { + "type": "integer" + }, + "size": { + "type": "integer" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "userName": { + "type": "string" + }, + "groupName": { + "type": "string" + }, + "flags": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "mode", + "size", + "digest", + "userName", + "groupName", + "flags" + ] + }, + "RubyGemspec": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "type": "string" + }, + "type": "array" + }, + "homepage": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "RustCargoAuditEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source" + ] + }, + "RustCargoLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "type": "string" + }, + "checksum": { + "type": "string" + }, + "dependencies": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "checksum", + "dependencies" + ] + }, + "Schema": { + "properties": { + "version": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "version", + "url" + ] + }, + "Source": { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "type": { + "type": "string" + }, + "metadata": true + }, + "type": "object", + "required": [ + "id", + "name", + "version", + "type", + "metadata" + ] + }, + "SwiftPackageManagerLockEntry": { + "properties": { + "revision": { + "type": "string" + } + }, + "type": "object", + "required": [ + "revision" + ] + }, + "cpes": { + "items": { + "$ref": "#/$defs/CPE" + }, + "type": "array" + }, + "licenses": { + "items": { + "$ref": "#/$defs/License" + }, + "type": "array" + } + } +} diff --git a/schema/json/schema-latest.json b/schema/json/schema-latest.json index c18e688d5ae..e846be24170 100644 --- a/schema/json/schema-latest.json +++ b/schema/json/schema-latest.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "anchore.io/schema/syft/json/16.0.2/document", + "$id": "anchore.io/schema/syft/json/16.0.3/document", "$ref": "#/$defs/Document", "$defs": { "AlpmDbEntry": { @@ -275,6 +275,35 @@ "ref" ] }, + "CConanLockV2Entry": { + "properties": { + "ref": { + "type": "string" + }, + "packageID": { + "type": "string" + }, + "username": { + "type": "string" + }, + "channel": { + "type": "string" + }, + "recipeRevision": { + "type": "string" + }, + "packageRevision": { + "type": "string" + }, + "timestamp": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, "CPE": { "properties": { "cpe": { @@ -1363,6 +1392,9 @@ { "$ref": "#/$defs/CConanLockEntry" }, + { + "$ref": "#/$defs/CConanLockV2Entry" + }, { "$ref": "#/$defs/CocoaPodfileLockEntry" }, diff --git a/syft/internal/packagemetadata/generated.go b/syft/internal/packagemetadata/generated.go index 82fea4b84cd..080e4d3e5e9 100644 --- a/syft/internal/packagemetadata/generated.go +++ b/syft/internal/packagemetadata/generated.go @@ -12,6 +12,7 @@ func AllTypes() []any { pkg.BinarySignature{}, pkg.CocoaPodfileLockEntry{}, pkg.ConanV1LockEntry{}, + pkg.ConanV2LockEntry{}, pkg.ConanfileEntry{}, pkg.ConaninfoEntry{}, pkg.DartPubspecLockEntry{}, diff --git a/syft/internal/packagemetadata/names.go b/syft/internal/packagemetadata/names.go index 0d5151f0e48..146145808b1 100644 --- a/syft/internal/packagemetadata/names.go +++ b/syft/internal/packagemetadata/names.go @@ -67,6 +67,7 @@ var jsonTypes = makeJSONTypes( jsonNames(pkg.BinarySignature{}, "binary-signature", "BinaryMetadata"), jsonNames(pkg.CocoaPodfileLockEntry{}, "cocoa-podfile-lock-entry", "CocoapodsMetadataType"), jsonNames(pkg.ConanV1LockEntry{}, "c-conan-lock-entry", "ConanLockMetadataType"), + jsonNames(pkg.ConanV2LockEntry{}, "c-conan-lock-v2-entry"), jsonNames(pkg.ConanfileEntry{}, "c-conan-file-entry", "ConanMetadataType"), jsonNames(pkg.ConaninfoEntry{}, "c-conan-info-entry"), jsonNames(pkg.DartPubspecLockEntry{}, "dart-pubspec-lock-entry", "DartPubMetadata"), diff --git a/syft/pkg/cataloger/cpp/cataloger.go b/syft/pkg/cataloger/cpp/cataloger.go index 1d9cde8c78a..aa031204e69 100644 --- a/syft/pkg/cataloger/cpp/cataloger.go +++ b/syft/pkg/cataloger/cpp/cataloger.go @@ -12,7 +12,7 @@ import ( func NewConanCataloger() pkg.Cataloger { return generic.NewCataloger("conan-cataloger"). WithParserByGlobs(parseConanfile, "**/conanfile.txt"). - WithParserByGlobs(parseConanlock, "**/conan.lock") + WithParserByGlobs(parseConanLock, "**/conan.lock") } // NewConanInfoCataloger returns a new C/C++ conaninfo.txt cataloger object. diff --git a/syft/pkg/cataloger/cpp/package.go b/syft/pkg/cataloger/cpp/package.go index 1b29329afa6..bc003ea7f5f 100644 --- a/syft/pkg/cataloger/cpp/package.go +++ b/syft/pkg/cataloger/cpp/package.go @@ -74,7 +74,7 @@ func newConanlockPackage(m pkg.ConanV1LockEntry, locations ...file.Location) *pk return newConanPackage(m.Ref, m, locations...) } -func newConanRefrencePackage(m pkg.ConanV2LockEntry, locations ...file.Location) *pkg.Package { +func newConanReferencePackage(m pkg.ConanV2LockEntry, locations ...file.Location) *pkg.Package { return newConanPackage(m.Ref, m, locations...) } diff --git a/syft/pkg/cataloger/cpp/parse_conanlock.go b/syft/pkg/cataloger/cpp/parse_conanlock.go index 847996b9c1c..efdfcf08d42 100644 --- a/syft/pkg/cataloger/cpp/parse_conanlock.go +++ b/syft/pkg/cataloger/cpp/parse_conanlock.go @@ -11,7 +11,7 @@ import ( "github.com/anchore/syft/syft/pkg/cataloger/generic" ) -var _ generic.Parser = parseConanlock +var _ generic.Parser = parseConanLock type conanLock struct { GraphLock struct { @@ -35,9 +35,8 @@ type conanLock struct { PythonRequires []string `json:"python_requires,omitempty"` } -// parseConanlock is a parser function for conan.lock contents, returning all packages discovered. -func parseConanlock(_ context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { - var pkgs []pkg.Package +// parseConanLock is a parser function for conan.lock (v1 and V2) contents, returning all packages discovered. +func parseConanLock(_ context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { var cl conanLock if err := json.NewDecoder(reader).Decode(&cl); err != nil { return nil, nil, err @@ -47,31 +46,38 @@ func parseConanlock(_ context.Context, _ file.Resolver, _ *generic.Environment, // in a second iteration var indexToPkgMap = map[string]pkg.Package{} - // Support for conan lock 2.x requires field - for _, ref := range cl.Requires { - reference := parseConanV2Reference(ref) - if reference.Name == "" { - continue - } - - p := newConanRefrencePackage( - reference, - reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), - ) - - if p != nil { - pk := *p - pkgs = append(pkgs, pk) - indexToPkgMap[reference.Name] = pk - } - - } + v1Pkgs := handleConanLockV2(cl, reader, indexToPkgMap) // we do not want to store the index list requires in the conan metadata, because it is not useful to have it in // the SBOM. Instead, we will store it in a map and then use it to build the relationships // maps pkg.ID to a list of indices var parsedPkgRequires = map[artifact.ID][]string{} + v2Pkgs := handleConanLockV1(cl, reader, parsedPkgRequires, indexToPkgMap) + + var relationships []artifact.Relationship + var pkgs []pkg.Package + pkgs = append(pkgs, v1Pkgs...) + pkgs = append(pkgs, v2Pkgs...) + + for _, p := range pkgs { + requires := parsedPkgRequires[p.ID()] + for _, r := range requires { + // this is a pkg that package "p" depends on... make a relationship + relationships = append(relationships, artifact.Relationship{ + From: indexToPkgMap[r], + To: p, + Type: artifact.DependencyOfRelationship, + }) + } + } + + return pkgs, relationships, nil +} + +// handleConanLockV1 handles the parsing of conan lock v1 files (aka v0.4) +func handleConanLockV1(cl conanLock, reader file.LocationReadCloser, parsedPkgRequires map[artifact.ID][]string, indexToPkgMap map[string]pkg.Package) []pkg.Package { + var pkgs []pkg.Package for idx, node := range cl.GraphLock.Nodes { metadata := pkg.ConanV1LockEntry{ Ref: node.Ref, @@ -94,22 +100,30 @@ func parseConanlock(_ context.Context, _ file.Resolver, _ *generic.Environment, indexToPkgMap[idx] = pk } } + return pkgs +} - var relationships []artifact.Relationship +// handleConanLockV2 handles the parsing of conan lock v2 files (aka v0.5) +func handleConanLockV2(cl conanLock, reader file.LocationReadCloser, indexToPkgMap map[string]pkg.Package) []pkg.Package { + var pkgs []pkg.Package + for _, ref := range cl.Requires { + reference, name := parseConanV2Reference(ref) + if name == "" { + continue + } - for _, p := range pkgs { - requires := parsedPkgRequires[p.ID()] - for _, r := range requires { - // this is a pkg that package "p" depends on... make a relationship - relationships = append(relationships, artifact.Relationship{ - From: indexToPkgMap[r], - To: p, - Type: artifact.DependencyOfRelationship, - }) + p := newConanReferencePackage( + reference, + reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), + ) + + if p != nil { + pk := *p + pkgs = append(pkgs, pk) + indexToPkgMap[name] = pk } } - - return pkgs, relationships, nil + return pkgs } func parseOptions(options string) []pkg.KeyValue { @@ -132,7 +146,7 @@ func parseOptions(options string) []pkg.KeyValue { return o } -func parseConanV2Reference(ref string) pkg.ConanV2LockEntry { +func parseConanV2Reference(ref string) (pkg.ConanV2LockEntry, string) { // very flexible format name/version[@username[/channel]][#rrev][:pkgid[#prev]][%timestamp] reference := pkg.ConanV2LockEntry{Ref: ref} @@ -171,14 +185,13 @@ func parseConanV2Reference(ref string) pkg.ConanV2LockEntry { } parts = strings.SplitN(ref, "/", 2) + var name string if len(parts) == 2 { - reference.Name = parts[0] - reference.Version = parts[1] + name = parts[0] } else { // consumer conanfile.txt or conanfile.py might not have a name - reference.Name = "" - reference.Version = ref + name = "" } - return reference + return reference, name } diff --git a/syft/pkg/cataloger/cpp/parse_conanlock_test.go b/syft/pkg/cataloger/cpp/parse_conanlock_test.go index ef3d85f76e9..da143fe51b3 100644 --- a/syft/pkg/cataloger/cpp/parse_conanlock_test.go +++ b/syft/pkg/cataloger/cpp/parse_conanlock_test.go @@ -9,7 +9,7 @@ import ( "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" ) -func TestParseConanlock(t *testing.T) { +func TestParseConanLock(t *testing.T) { fixture := "test-fixtures/conan.lock" expected := []pkg.Package{ { @@ -330,5 +330,46 @@ func TestParseConanlock(t *testing.T) { }, } - pkgtest.TestFileParser(t, fixture, parseConanlock, expected, expectedRelationships) + pkgtest.TestFileParser(t, fixture, parseConanLock, expected, expectedRelationships) +} + +func TestParseConanLockV2(t *testing.T) { + fixture := "test-fixtures/conanlock-v2/conan.lock" + expected := []pkg.Package{ + { + Name: "matrix", + Version: "1.1", + PURL: "pkg:conan/matrix@1.1", + Locations: file.NewLocationSet(file.NewLocation(fixture)), + Language: pkg.CPP, + Type: pkg.ConanPkg, + Metadata: pkg.ConanV2LockEntry{ + Ref: "matrix/1.1#905c3f0babc520684c84127378fefdd0%1675278901.7527816", + RecipeRevision: "905c3f0babc520684c84127378fefdd0", + TimeStamp: "1675278901.7527816", + }, + }, + { + Name: "sound32", + Version: "1.0", + PURL: "pkg:conan/sound32@1.0", + Locations: file.NewLocationSet(file.NewLocation(fixture)), + Language: pkg.CPP, + Type: pkg.ConanPkg, + Metadata: pkg.ConanV2LockEntry{ + Ref: "sound32/1.0#83d4b7bf607b3b60a6546f8b58b5cdd7%1675278904.0791488", + RecipeRevision: "83d4b7bf607b3b60a6546f8b58b5cdd7", + TimeStamp: "1675278904.0791488", + }, + }, + } + + // relationships require IDs to be set to be sorted similarly + for i := range expected { + expected[i].SetID() + } + + var expectedRelationships []artifact.Relationship + + pkgtest.TestFileParser(t, fixture, parseConanLock, expected, expectedRelationships) } diff --git a/syft/pkg/cataloger/cpp/test-fixtures/conanlock-v2/conan.lock b/syft/pkg/cataloger/cpp/test-fixtures/conanlock-v2/conan.lock new file mode 100644 index 00000000000..ed6092555b8 --- /dev/null +++ b/syft/pkg/cataloger/cpp/test-fixtures/conanlock-v2/conan.lock @@ -0,0 +1,9 @@ +{ + "version": "0.5", + "requires": [ + "sound32/1.0#83d4b7bf607b3b60a6546f8b58b5cdd7%1675278904.0791488", + "matrix/1.1#905c3f0babc520684c84127378fefdd0%1675278901.7527816" + ], + "build_requires": [], + "python_requires": [] +} diff --git a/syft/pkg/conan.go b/syft/pkg/conan.go index 23f1e3c3815..87cdb9c9d52 100644 --- a/syft/pkg/conan.go +++ b/syft/pkg/conan.go @@ -1,28 +1,27 @@ package pkg +// ConanV1LockEntry represents a single "node" entry from a conan.lock V1 file. +type ConanV1LockEntry struct { + Ref string `json:"ref"` + PackageID string `json:"package_id,omitempty"` + Prev string `json:"prev,omitempty"` + Requires []string `json:"requires,omitempty"` + BuildRequires []string `json:"build_requires,omitempty"` + PythonRequires []string `json:"py_requires,omitempty"` + Options KeyValues `json:"options,omitempty"` + Path string `json:"path,omitempty"` + Context string `json:"context,omitempty"` +} + +// ConanV2LockEntry represents a single "node" entry from a conan.lock V2 file. type ConanV2LockEntry struct { - Name string `json:"name,omitempty"` - Version string `json:"version,omitempty"` + Ref string `json:"ref"` + PackageID string `json:"packageID,omitempty"` Username string `json:"username,omitempty"` Channel string `json:"channel,omitempty"` - RecipeRevision string `json:"recipe_revision,omitempty"` - PackageID string `json:"package_id,omitempty"` - PackageRevision string `json:"package_revision,omitempty"` + RecipeRevision string `json:"recipeRevision,omitempty"` + PackageRevision string `json:"packageRevision,omitempty"` TimeStamp string `json:"timestamp,omitempty"` - Ref string `json:"ref"` -} - -// ConanV1LockEntry represents a single "node" entry from a conan.lock file. -type ConanV1LockEntry struct { - Ref string `json:"ref"` - PackageID string `json:"package_id,omitempty"` - Prev string `json:"prev,omitempty"` - Requires []string `json:"requires,omitempty"` - BuildRequires []string `json:"build_requires,omitempty"` - PythonRequires []string `json:"py_requires,omitempty"` - Options map[string]string `json:"options,omitempty"` - Path string `json:"path,omitempty"` - Context string `json:"context,omitempty"` } // ConanfileEntry represents a single "Requires" entry from a conanfile.txt.