diff --git a/internal/constants.go b/internal/constants.go index 19c4a5c6019..a5b3f8dc795 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 = "6.2.1" + JSONSchemaVersion = "7.0.0" ) diff --git a/schema/json/schema-7.0.0.json b/schema/json/schema-7.0.0.json new file mode 100644 index 00000000000..63161e14d01 --- /dev/null +++ b/schema/json/schema-7.0.0.json @@ -0,0 +1,1642 @@ +{ + "$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" + } + }, + "type": "object" + }, + "Location": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + }, + "virtualPath": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "MixLockMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "pkgHashExt": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "pkgHash", + "pkgHashExt" + ] + }, + "NpmPackageJSONMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + }, + "author": { + "type": "string" + }, + "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": { + "items": { + "type": "string" + }, + "type": "array" + }, + "language": { + "type": "string" + }, + "cpes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "purl": { + "type": "string" + }, + "metadataType": { + "type": "string" + }, + "metadata": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "#/$defs/AlpmMetadata" + }, + { + "$ref": "#/$defs/ApkMetadata" + }, + { + "$ref": "#/$defs/BinaryMetadata" + }, + { + "$ref": "#/$defs/CargoPackageMetadata" + }, + { + "$ref": "#/$defs/CocoapodsMetadata" + }, + { + "$ref": "#/$defs/ConanLockMetadata" + }, + { + "$ref": "#/$defs/ConanMetadata" + }, + { + "$ref": "#/$defs/DartPubMetadata" + }, + { + "$ref": "#/$defs/DotnetDepsMetadata" + }, + { + "$ref": "#/$defs/DpkgMetadata" + }, + { + "$ref": "#/$defs/GemMetadata" + }, + { + "$ref": "#/$defs/GolangBinMetadata" + }, + { + "$ref": "#/$defs/GolangModMetadata" + }, + { + "$ref": "#/$defs/HackageMetadata" + }, + { + "$ref": "#/$defs/JavaMetadata" + }, + { + "$ref": "#/$defs/KbPackageMetadata" + }, + { + "$ref": "#/$defs/MixLockMetadata" + }, + { + "$ref": "#/$defs/NpmPackageJSONMetadata" + }, + { + "$ref": "#/$defs/NpmPackageLockJSONMetadata" + }, + { + "$ref": "#/$defs/PhpComposerJSONMetadata" + }, + { + "$ref": "#/$defs/PortageMetadata" + }, + { + "$ref": "#/$defs/PythonPackageMetadata" + }, + { + "$ref": "#/$defs/PythonPipfileLockMetadata" + }, + { + "$ref": "#/$defs/RebarLockMetadata" + }, + { + "$ref": "#/$defs/RpmMetadata" + } + ] + } + }, + "type": "object", + "required": [ + "id", + "name", + "version", + "type", + "foundBy", + "locations", + "licenses", + "language", + "cpes", + "purl" + ] + }, + "PhpComposerAuthors": { + "properties": { + "name": { + "type": "string" + }, + "email": { + "type": "string" + }, + "homepage": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name" + ] + }, + "PhpComposerExternalReference": { + "properties": { + "type": { + "type": "string" + }, + "url": { + "type": "string" + }, + "reference": { + "type": "string" + }, + "shasum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "type", + "url", + "reference" + ] + }, + "PhpComposerJSONMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "dist": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "require": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "provide": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "require-dev": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "suggest": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "type": { + "type": "string" + }, + "notification-url": { + "type": "string" + }, + "bin": { + "items": { + "type": "string" + }, + "type": "array" + }, + "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/formats/syftjson/encoder_test.go b/syft/formats/syftjson/encoder_test.go index 3c0ee8c54f4..185658f7b77 100644 --- a/syft/formats/syftjson/encoder_test.go +++ b/syft/formats/syftjson/encoder_test.go @@ -213,5 +213,6 @@ func TestEncodeFullJSONDocument(t *testing.T) { s, *updateJson, true, + schemaVersionRedactor, ) } diff --git a/syft/formats/syftjson/format.go b/syft/formats/syftjson/format.go index fe5ffca2167..5f336871bee 100644 --- a/syft/formats/syftjson/format.go +++ b/syft/formats/syftjson/format.go @@ -1,6 +1,7 @@ package syftjson import ( + "github.com/anchore/syft/internal" "github.com/anchore/syft/syft/sbom" ) @@ -8,7 +9,7 @@ const ID sbom.FormatID = "syft-json" func Format() sbom.Format { return sbom.NewFormat( - "6", + internal.JSONSchemaVersion, encoder, decoder, validator, diff --git a/syft/formats/syftjson/to_format_model_test.go b/syft/formats/syftjson/to_format_model_test.go index 2dcfae67c52..a99e66a3692 100644 --- a/syft/formats/syftjson/to_format_model_test.go +++ b/syft/formats/syftjson/to_format_model_test.go @@ -1,7 +1,6 @@ package syftjson import ( - "strings" "testing" "github.com/scylladb/go-set/strset" @@ -9,16 +8,10 @@ import ( "github.com/stretchr/testify/require" "github.com/anchore/stereoscope/pkg/file" - "github.com/anchore/syft/internal" "github.com/anchore/syft/syft/formats/syftjson/model" "github.com/anchore/syft/syft/source" ) -func Test_SyftJsonID_Compatibility(t *testing.T) { - jsonMajorVersion := strings.Split(internal.JSONSchemaVersion, ".")[0] - assert.Equal(t, jsonMajorVersion, string(Format().Version())) -} - func Test_toSourceModel(t *testing.T) { allSchemes := strset.New() for _, s := range source.AllSchemes { diff --git a/syft/pkg/binary_metadata.go b/syft/pkg/binary_metadata.go index 199c93f9c9d..de0a0a2d40b 100644 --- a/syft/pkg/binary_metadata.go +++ b/syft/pkg/binary_metadata.go @@ -1,7 +1,12 @@ package pkg +import "github.com/anchore/syft/syft/source" + type BinaryMetadata struct { - Classifier string `mapstructure:"Classifier" json:"classifier"` - RealPath string `mapstructure:"RealPath" json:"realPath"` - VirtualPath string `mapstructure:"VirtualPath" json:"virtualPath"` + Matches []ClassifierMatch `mapstructure:"Matches" json:"matches"` +} + +type ClassifierMatch struct { + Classifier string `mapstructure:"Classifier" json:"classifier"` + Location source.Location `mapstructure:"Location" json:"location"` } diff --git a/syft/pkg/cataloger/binary/cataloger.go b/syft/pkg/cataloger/binary/cataloger.go index a610de19d18..3d610f9bc17 100644 --- a/syft/pkg/cataloger/binary/cataloger.go +++ b/syft/pkg/cataloger/binary/cataloger.go @@ -33,19 +33,42 @@ func (c Cataloger) Catalog(resolver source.FileResolver) ([]pkg.Package, []artif for _, cls := range defaultClassifiers { log.WithFields("classifier", cls.Class).Trace("cataloging binaries") - pkgs, err := catalog(resolver, cls) + newPkgs, err := catalog(resolver, cls) if err != nil { log.WithFields("error", err, "classifier", cls.Class).Warn("unable to catalog binary package: %w", err) continue } - packages = append(packages, pkgs...) + newPackages: + for i := range newPkgs { + newPkg := &newPkgs[i] + for j := range packages { + p := &packages[j] + // consolidate identical packages found in different locations or by different classifiers + if packagesMatch(p, newPkg) { + mergePackages(p, newPkg) + continue newPackages + } + } + packages = append(packages, *newPkg) + } } return packages, relationships, nil } -func catalog(resolver source.FileResolver, cls classifier) ([]pkg.Package, error) { - var pkgs []pkg.Package +// mergePackages merges information from the extra package into the target package +func mergePackages(target *pkg.Package, extra *pkg.Package) { + // add the locations + target.Locations.Add(extra.Locations.ToSlice()...) + // update the metadata to indicate which classifiers were used + meta, _ := target.Metadata.(pkg.BinaryMetadata) + if m, ok := extra.Metadata.(pkg.BinaryMetadata); ok { + meta.Matches = append(meta.Matches, m.Matches...) + } + target.Metadata = meta +} + +func catalog(resolver source.FileResolver, cls classifier) (packages []pkg.Package, err error) { locations, err := resolver.FilesByGlob(cls.FileGlob) if err != nil { return nil, err @@ -56,26 +79,13 @@ func catalog(resolver source.FileResolver, cls classifier) ([]pkg.Package, error return nil, err } locationReader := source.NewLocationReadCloser(location, reader) - newPkgs, err := cls.EvidenceMatcher(cls, locationReader) + pkgs, err := cls.EvidenceMatcher(cls, locationReader) if err != nil { return nil, err } - newPackages: - for i := range newPkgs { - newPkg := &newPkgs[i] - for j := range pkgs { - p := &pkgs[j] - // consolidate identical packages found in different locations, - // but continue to track each location - if packagesMatch(p, newPkg) { - p.Locations.Add(newPkg.Locations.ToSlice()...) - continue newPackages - } - } - pkgs = append(pkgs, *newPkg) - } + packages = append(packages, pkgs...) } - return pkgs, nil + return packages, nil } // packagesMatch returns true if the binary packages "match" based on basic criteria diff --git a/syft/pkg/cataloger/binary/cataloger_test.go b/syft/pkg/cataloger/binary/cataloger_test.go index 4e2eb4d0810..a73144e5de5 100644 --- a/syft/pkg/cataloger/binary/cataloger_test.go +++ b/syft/pkg/cataloger/binary/cataloger_test.go @@ -2,6 +2,7 @@ package binary import ( "errors" + "fmt" "io" "testing" @@ -27,10 +28,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) { Version: "15beta4", Type: "binary", PURL: "pkg:generic/postgresql@15beta4", - Locations: singleLocation("postgres"), - Metadata: pkg.BinaryMetadata{ - Classifier: "postgresql-binary", - }, + Locations: locations("postgres"), + Metadata: metadata("postgresql-binary"), }, }, { @@ -41,10 +40,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) { Version: "15.1", Type: "binary", PURL: "pkg:generic/postgresql@15.1", - Locations: singleLocation("postgres"), - Metadata: pkg.BinaryMetadata{ - Classifier: "postgresql-binary", - }, + Locations: locations("postgres"), + Metadata: metadata("postgresql-binary"), }, }, { @@ -55,10 +52,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) { Version: "9.6.24", Type: "binary", PURL: "pkg:generic/postgresql@9.6.24", - Locations: singleLocation("postgres"), - Metadata: pkg.BinaryMetadata{ - Classifier: "postgresql-binary", - }, + Locations: locations("postgres"), + Metadata: metadata("postgresql-binary"), }, }, { @@ -69,9 +64,26 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) { Version: "9.5alpha1", Type: "binary", PURL: "pkg:generic/postgresql@9.5alpha1", - Locations: singleLocation("postgres"), + Locations: locations("postgres"), + Metadata: metadata("postgresql-binary"), + }, + }, + { + name: "positive-python-duplicates", + fixtureDir: "test-fixtures/classifiers/positive/python-duplicates", + expected: pkg.Package{ + Name: "python", + Version: "3.8.16", + Type: "binary", + PURL: "pkg:generic/python@3.8.16", + Locations: locations("dir/python3.8", "python3.8", "libpython3.8.so", "patchlevel.h"), Metadata: pkg.BinaryMetadata{ - Classifier: "postgresql-binary", + Matches: []pkg.ClassifierMatch{ + match("python-binary", "dir/python3.8"), + match("python-binary", "python3.8"), + match("python-binary-lib", "libpython3.8.so"), + match("cpython-source", "patchlevel.h"), + }, }, }, }, @@ -83,10 +95,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) { Version: "2.9.6", Type: "binary", PURL: "pkg:generic/traefik@2.9.6", - Locations: singleLocation("traefik"), - Metadata: pkg.BinaryMetadata{ - Classifier: "traefik-binary", - }, + Locations: locations("traefik"), + Metadata: metadata("traefik-binary"), }, }, { @@ -97,10 +107,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) { Version: "1.7.34", Type: "binary", PURL: "pkg:generic/traefik@1.7.34", - Locations: singleLocation("traefik"), - Metadata: pkg.BinaryMetadata{ - Classifier: "traefik-binary", - }, + Locations: locations("traefik"), + Metadata: metadata("traefik-binary"), }, }, { @@ -111,10 +119,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) { Version: "1.6.18", Type: "binary", PURL: "pkg:generic/memcached@1.6.18", - Locations: singleLocation("memcached"), - Metadata: pkg.BinaryMetadata{ - Classifier: "memcached-binary", - }, + Locations: locations("memcached"), + Metadata: metadata("memcached-binary"), }, }, { @@ -125,10 +131,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) { Version: "2.4.54", Type: "binary", PURL: "pkg:generic/httpd@2.4.54", - Locations: singleLocation("httpd"), - Metadata: pkg.BinaryMetadata{ - Classifier: "httpd-binary", - }, + Locations: locations("httpd"), + Metadata: metadata("httpd-binary"), }, }, { @@ -139,10 +143,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) { Version: "8.2.1", Type: "binary", PURL: "pkg:generic/php-cli@8.2.1", - Locations: singleLocation("php"), - Metadata: pkg.BinaryMetadata{ - Classifier: "php-cli-binary", - }, + Locations: locations("php"), + Metadata: metadata("php-cli-binary"), }, }, { @@ -153,10 +155,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) { Version: "8.2.1", Type: "binary", PURL: "pkg:generic/php-fpm@8.2.1", - Locations: singleLocation("php-fpm"), - Metadata: pkg.BinaryMetadata{ - Classifier: "php-fpm-binary", - }, + Locations: locations("php-fpm"), + Metadata: metadata("php-fpm-binary"), }, }, { @@ -167,10 +167,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) { Version: "8.2.1", Type: "binary", PURL: "pkg:generic/php@8.2.1", - Locations: singleLocation("libphp.so"), - Metadata: pkg.BinaryMetadata{ - Classifier: "php-apache-binary", - }, + Locations: locations("libphp.so"), + Metadata: metadata("php-apache-binary"), }, }, { @@ -223,10 +221,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) { Version: "2.8.23", Type: "binary", PURL: "pkg:generic/redis@2.8.23", - Locations: singleLocation("redis-server"), - Metadata: pkg.BinaryMetadata{ - Classifier: "redis-binary", - }, + Locations: locations("redis-server"), + Metadata: metadata("redis-binary"), }, }, { @@ -237,10 +233,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) { Version: "4.0.11", Type: "binary", PURL: "pkg:generic/redis@4.0.11", - Locations: singleLocation("redis-server"), - Metadata: pkg.BinaryMetadata{ - Classifier: "redis-binary", - }, + Locations: locations("redis-server"), + Metadata: metadata("redis-binary"), }, }, { @@ -251,10 +245,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) { Version: "5.0.0", Type: "binary", PURL: "pkg:generic/redis@5.0.0", - Locations: singleLocation("redis-server"), - Metadata: pkg.BinaryMetadata{ - Classifier: "redis-binary", - }, + Locations: locations("redis-server"), + Metadata: metadata("redis-binary"), }, }, { @@ -265,10 +257,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) { Version: "6.0.16", Type: "binary", PURL: "pkg:generic/redis@6.0.16", - Locations: singleLocation("redis-server"), - Metadata: pkg.BinaryMetadata{ - Classifier: "redis-binary", - }, + Locations: locations("redis-server"), + Metadata: metadata("redis-binary"), }, }, { @@ -279,101 +269,84 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) { Version: "7.0.0", Type: "binary", PURL: "pkg:generic/redis@7.0.0", - Locations: singleLocation("redis-server"), - Metadata: pkg.BinaryMetadata{ - Classifier: "redis-binary", - }, + Locations: locations("redis-server"), + Metadata: metadata("redis-binary"), }, }, { name: "positive-libpython3.7.so", - fixtureDir: "test-fixtures/classifiers/positive", + fixtureDir: "test-fixtures/classifiers/positive/python-binary-lib-3.7", expected: pkg.Package{ Name: "python", Version: "3.7.4a-vZ9", PURL: "pkg:generic/python@3.7.4a-vZ9", - Locations: singleLocation("libpython3.7.so"), - Metadata: pkg.BinaryMetadata{ - Classifier: "python-binary-lib", - }, + Locations: locations("libpython3.7.so"), + Metadata: metadata("python-binary-lib"), }, }, { name: "positive-python3.6", - fixtureDir: "test-fixtures/classifiers/positive", + fixtureDir: "test-fixtures/classifiers/positive/python-binary-3.6", expected: pkg.Package{ Name: "python", Version: "3.6.3a-vZ9", PURL: "pkg:generic/python@3.6.3a-vZ9", - Locations: singleLocation("python3.6"), - Metadata: pkg.BinaryMetadata{ - Classifier: "python-binary", - }, + Locations: locations("python3.6"), + Metadata: metadata("python-binary"), }, }, { name: "positive-patchlevel.h", - fixtureDir: "test-fixtures/classifiers/positive", + fixtureDir: "test-fixtures/classifiers/positive/python-source-3.9", expected: pkg.Package{ Name: "python", Version: "3.9-aZ5", PURL: "pkg:generic/python@3.9-aZ5", - Locations: singleLocation("patchlevel.h"), - Metadata: pkg.BinaryMetadata{ - Classifier: "cpython-source", - }, + Locations: locations("patchlevel.h"), + Metadata: metadata("cpython-source"), }, }, { name: "positive-go", - fixtureDir: "test-fixtures/classifiers/positive", + fixtureDir: "test-fixtures/classifiers/positive/go-1.14", expected: pkg.Package{ Name: "go", Version: "1.14", PURL: "pkg:generic/go@1.14", - Locations: singleLocation("go"), - Metadata: pkg.BinaryMetadata{ - Classifier: "go-binary", - }, + Locations: locations("go"), + Metadata: metadata("go-binary"), }, }, { name: "positive-node", - fixtureDir: "test-fixtures/classifiers/positive", + fixtureDir: "test-fixtures/classifiers/positive/node-19.2.1", expected: pkg.Package{ Name: "node", Version: "19.2.1", PURL: "pkg:generic/node@19.2.1", - Locations: singleLocation("node"), - Metadata: pkg.BinaryMetadata{ - Classifier: "nodejs-binary", - }, + Locations: locations("node"), + Metadata: metadata("nodejs-binary"), }, }, { name: "positive-go-hint", - fixtureDir: "test-fixtures/classifiers/positive", + fixtureDir: "test-fixtures/classifiers/positive/go-hint-1.15", expected: pkg.Package{ Name: "go", Version: "1.15", PURL: "pkg:generic/go@1.15", - Locations: singleLocation("VERSION"), - Metadata: pkg.BinaryMetadata{ - Classifier: "go-binary-hint", - }, + Locations: locations("VERSION"), + Metadata: metadata("go-binary-hint"), }, }, { name: "positive-busybox", - fixtureDir: "test-fixtures/classifiers/positive", + fixtureDir: "test-fixtures/classifiers/positive/busybox-3.33.3", expected: pkg.Package{ Name: "busybox", Version: "3.33.3", - Locations: singleLocation("["), // note: busybox is a link to [ - Metadata: pkg.BinaryMetadata{ - Classifier: "busybox-binary", - VirtualPath: "busybox", - }, + Locations: locations("["), // note: busybox is a link to [ + Metadata: metadata("busybox-binary", "[", "busybox"), }, }, { @@ -384,11 +357,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) { Version: "1.8.0_352-b08", Type: "binary", PURL: "pkg:generic/java@1.8.0_352-b08", - Locations: singleLocation("java"), - Metadata: pkg.BinaryMetadata{ - Classifier: "java-binary-openjdk", - VirtualPath: "java", - }, + Locations: locations("java"), + Metadata: metadata("java-binary-openjdk", "java"), }, }, { @@ -399,11 +369,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) { Version: "11.0.17+8-LTS", Type: "binary", PURL: "pkg:generic/java@11.0.17+8-LTS", - Locations: singleLocation("java"), - Metadata: pkg.BinaryMetadata{ - Classifier: "java-binary-openjdk", - VirtualPath: "java", - }, + Locations: locations("java"), + Metadata: metadata("java-binary-openjdk", "java"), }, }, { @@ -414,11 +381,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) { Version: "19.0.1+10-21", Type: "binary", PURL: "pkg:generic/java@19.0.1+10-21", - Locations: singleLocation("java"), - Metadata: pkg.BinaryMetadata{ - Classifier: "java-binary-oracle", - VirtualPath: "java", - }, + Locations: locations("java"), + Metadata: metadata("java-binary-oracle", "java"), }, }, { @@ -429,11 +393,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) { Version: "19.0.1+10-21", Type: "binary", PURL: "pkg:generic/java@19.0.1+10-21", - Locations: singleLocation("java"), - Metadata: pkg.BinaryMetadata{ - Classifier: "java-binary-oracle", - VirtualPath: "java", - }, + Locations: locations("java"), + Metadata: metadata("java-binary-oracle", "java"), }, }, { @@ -444,11 +405,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) { Version: "1.8.0-foreman_2022_09_22_15_30-b00", Type: "binary", PURL: "pkg:generic/java@1.8.0-foreman_2022_09_22_15_30-b00", - Locations: singleLocation("java"), - Metadata: pkg.BinaryMetadata{ - Classifier: "java-binary-ibm", - VirtualPath: "java", - }, + Locations: locations("java"), + Metadata: metadata("java-binary-ibm", "java"), }, }, } @@ -466,18 +424,20 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) { packages, _, err := c.Catalog(resolver) require.NoError(t, err) - ok := false for _, p := range packages { - if test.expected.Locations.ToSlice()[0].RealPath == p.Locations.ToSlice()[0].RealPath { - ok = true - assertPackagesAreEqual(t, test.expected, p) + expectedLocations := test.expected.Locations.ToSlice() + gotLocations := p.Locations.ToSlice() + require.Len(t, gotLocations, len(expectedLocations)) + + for i, expectedLocation := range expectedLocations { + gotLocation := gotLocations[i] + if expectedLocation.RealPath != gotLocation.RealPath { + t.Fatalf("locations do not match; expected: %v got: %v", expectedLocations, gotLocations) + } } - } - if !ok { - t.Fatalf("could not find test location=%q", test.expected.Locations.ToSlice()[0].RealPath) + assertPackagesAreEqual(t, test.expected, p) } - }) } } @@ -494,11 +454,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases_Image(t *testing.T) { expected: pkg.Package{ Name: "busybox", Version: "1.35.0", - Locations: singleLocation("/bin/["), - Metadata: pkg.BinaryMetadata{ - Classifier: "busybox-binary", - VirtualPath: "/bin/busybox", - }, + Locations: locations("/bin/["), + Metadata: metadata("busybox-binary", "/bin/[", "/bin/busybox"), }, }, } @@ -517,18 +474,20 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases_Image(t *testing.T) { packages, _, err := c.Catalog(resolver) require.NoError(t, err) - ok := false for _, p := range packages { - if test.expected.Locations.ToSlice()[0].RealPath == p.Locations.ToSlice()[0].RealPath { - ok = true - assertPackagesAreEqual(t, test.expected, p) + expectedLocations := test.expected.Locations.ToSlice() + gotLocations := p.Locations.ToSlice() + require.Len(t, gotLocations, len(expectedLocations)) + + for i, expectedLocation := range expectedLocations { + gotLocation := gotLocations[i] + if expectedLocation.RealPath != gotLocation.RealPath { + t.Fatalf("locations do not match; expected: %v got: %v", expectedLocations, gotLocations) + } } - } - if !ok { - t.Fatalf("could not find test location=%q", test.expected.Locations.ToSlice()[0].RealPath) + assertPackagesAreEqual(t, test.expected, p) } - }) } } @@ -547,21 +506,80 @@ func TestClassifierCataloger_DefaultClassifiers_NegativeCases(t *testing.T) { assert.Equal(t, 0, len(actualResults)) } -func singleLocation(s string) source.LocationSet { - return source.NewLocationSet(source.NewLocation(s)) +func locations(locations ...string) source.LocationSet { + var locs []source.Location + for _, s := range locations { + locs = append(locs, source.NewLocation(s)) + } + return source.NewLocationSet(locs...) +} + +// metadata paths are: realPath, virtualPath +func metadata(classifier string, paths ...string) pkg.BinaryMetadata { + return pkg.BinaryMetadata{ + Matches: []pkg.ClassifierMatch{ + match(classifier, paths...), + }, + } +} + +// match paths are: realPath, virtualPath +func match(classifier string, paths ...string) pkg.ClassifierMatch { + realPath := "" + if len(paths) > 0 { + realPath = paths[0] + } + virtualPath := "" + if len(paths) > 1 { + virtualPath = paths[1] + } + return pkg.ClassifierMatch{ + Classifier: classifier, + Location: source.Location{ + Coordinates: source.Coordinates{ + RealPath: realPath, + }, + VirtualPath: virtualPath, + }, + } } func assertPackagesAreEqual(t *testing.T, expected pkg.Package, p pkg.Package) { - meta1 := expected.Metadata.(pkg.BinaryMetadata) - meta2 := p.Metadata.(pkg.BinaryMetadata) + m1 := expected.Metadata.(pkg.BinaryMetadata).Matches + m2 := p.Metadata.(pkg.BinaryMetadata).Matches + matches := true + if len(m1) == len(m2) { + for i, m1 := range m1 { + m2 := m2[i] + if m1.Classifier != m2.Classifier { + matches = false + break + } + if m1.Location.RealPath != "" && m1.Location.RealPath != m2.Location.RealPath { + matches = false + break + } + if m1.Location.VirtualPath != "" && m1.Location.VirtualPath != m2.Location.VirtualPath { + matches = false + break + } + } + } else { + matches = false + } if expected.Name != p.Name || expected.Version != p.Version || expected.PURL != p.PURL || - meta1.Classifier != meta2.Classifier { - assert.Failf(t, "packages not equal", "%v != %v", expected, p) + !matches { + assert.Failf(t, "packages not equal", "%v != %v", stringifyPkg(expected), stringifyPkg(p)) } } +func stringifyPkg(p pkg.Package) string { + matches := p.Metadata.(pkg.BinaryMetadata).Matches + return fmt.Sprintf("(name=%s, version=%s, purl=%s, matches=%+v)", p.Name, p.Version, p.PURL, matches) +} + type panicyResolver struct { searchCalled bool } @@ -586,7 +604,7 @@ func (p *panicyResolver) FileContentsByLocation(_ source.Location) (io.ReadClose return nil, errors.New("not implemented") } -func (p *panicyResolver) HasPath(s string) bool { +func (p *panicyResolver) HasPath(_ string) bool { return true } diff --git a/syft/pkg/cataloger/binary/classifier.go b/syft/pkg/cataloger/binary/classifier.go index 4e89483233d..d625166b03f 100644 --- a/syft/pkg/cataloger/binary/classifier.go +++ b/syft/pkg/cataloger/binary/classifier.go @@ -131,9 +131,12 @@ func singlePackage(classifier classifier, reader source.LocationReadCloser, matc FoundBy: catalogerName, MetadataType: pkg.BinaryMetadataType, Metadata: pkg.BinaryMetadata{ - Classifier: classifier.Class, - RealPath: reader.RealPath, - VirtualPath: reader.VirtualPath, + Matches: []pkg.ClassifierMatch{ + { + Classifier: classifier.Class, + Location: reader.Location, + }, + }, }, } diff --git a/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/.gitignore b/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/.gitignore index 5b880180ccf..5acc24bd654 100644 --- a/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/.gitignore +++ b/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/.gitignore @@ -1,2 +1,2 @@ -!libpython3.7.so -!libphp.so \ No newline at end of file +!*.so +!VERSION \ No newline at end of file diff --git a/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/[ b/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/busybox-3.33.3/[ similarity index 100% rename from syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/[ rename to syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/busybox-3.33.3/[ diff --git a/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/busybox b/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/busybox-3.33.3/busybox similarity index 100% rename from syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/busybox rename to syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/busybox-3.33.3/busybox diff --git a/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/go b/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/go-1.14/go similarity index 100% rename from syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/go rename to syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/go-1.14/go diff --git a/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/VERSION b/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/go-hint-1.15/VERSION similarity index 100% rename from syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/VERSION rename to syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/go-hint-1.15/VERSION diff --git a/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/node b/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/node-19.2.1/node similarity index 100% rename from syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/node rename to syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/node-19.2.1/node diff --git a/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/python3.6 b/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/python-binary-3.6/python3.6 similarity index 100% rename from syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/python3.6 rename to syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/python-binary-3.6/python3.6 diff --git a/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/libpython3.7.so b/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/python-binary-lib-3.7/libpython3.7.so similarity index 100% rename from syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/libpython3.7.so rename to syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/python-binary-lib-3.7/libpython3.7.so diff --git a/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/python-duplicates/dir/python3.8 b/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/python-duplicates/dir/python3.8 new file mode 100644 index 00000000000..eee6392d5c7 --- /dev/null +++ b/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/python-duplicates/dir/python3.8 @@ -0,0 +1 @@ +3.8.16 diff --git a/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/python-duplicates/libpython3.8.so b/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/python-duplicates/libpython3.8.so new file mode 100644 index 00000000000..eee6392d5c7 --- /dev/null +++ b/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/python-duplicates/libpython3.8.so @@ -0,0 +1 @@ +3.8.16 diff --git a/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/python-duplicates/patchlevel.h b/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/python-duplicates/patchlevel.h new file mode 100644 index 00000000000..01e9908e9c2 --- /dev/null +++ b/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/python-duplicates/patchlevel.h @@ -0,0 +1 @@ +#define PY_VERSION 3.8.16 diff --git a/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/python-duplicates/python3.8 b/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/python-duplicates/python3.8 new file mode 100644 index 00000000000..eee6392d5c7 --- /dev/null +++ b/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/python-duplicates/python3.8 @@ -0,0 +1 @@ +3.8.16 diff --git a/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/patchlevel.h b/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/python-source-3.9/patchlevel.h similarity index 100% rename from syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/patchlevel.h rename to syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/python-source-3.9/patchlevel.h diff --git a/syft/source/location.go b/syft/source/location.go index a284d8dacce..b4f1b267a4a 100644 --- a/syft/source/location.go +++ b/syft/source/location.go @@ -13,8 +13,8 @@ type Location struct { Coordinates `cyclonedx:""` // Empty string here means there is no intermediate property name, e.g. syft:locations:0:path without "coordinates" // note: it is IMPORTANT to ignore anything but the coordinates for a Location when considering the ID (hash value) // since the coordinates are the minimally correct ID for a location (symlinks should not come into play) - VirtualPath string `hash:"ignore"` // The path to the file which may or may not have hardlinks / symlinks - ref file.Reference `hash:"ignore"` // The file reference relative to the stereoscope.FileCatalog that has more information about this location. + VirtualPath string `hash:"ignore" json:"virtualPath,omitempty"` // The path to the file which may or may not have hardlinks / symlinks + ref file.Reference `hash:"ignore"` // The file reference relative to the stereoscope.FileCatalog that has more information about this location. } // NewLocation creates a new Location representing a path without denoting a filesystem or FileCatalog reference.