diff --git a/internal/constants.go b/internal/constants.go index a11eb7e46..8fc123235 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 = "4.1.0" + JSONSchemaVersion = "5.0.0" ) diff --git a/schema/json/schema-5.0.0.json b/schema/json/schema-5.0.0.json new file mode 100644 index 000000000..3b4c19135 --- /dev/null +++ b/schema/json/schema-5.0.0.json @@ -0,0 +1,1574 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/Document", + "definitions": { + "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": "#/definitions/Digest" + }, + "type": "array" + } + }, + "additionalProperties": true, + "type": "object" + }, + "AlpmMetadata": { + "required": [ + "basepackage", + "package", + "version", + "description", + "architecture", + "size", + "packager", + "license", + "url", + "validation", + "reason", + "files", + "backup" + ], + "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": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/AlpmFileRecord" + }, + "type": "array" + }, + "backup": { + "items": { + "$ref": "#/definitions/AlpmFileRecord" + }, + "type": "array" + } + }, + "additionalProperties": true, + "type": "object" + }, + "ApkFileRecord": { + "required": [ + "path" + ], + "properties": { + "path": { + "type": "string" + }, + "ownerUid": { + "type": "string" + }, + "ownerGid": { + "type": "string" + }, + "permissions": { + "type": "string" + }, + "digest": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/Digest" + } + }, + "additionalProperties": true, + "type": "object" + }, + "ApkMetadata": { + "required": [ + "package", + "originPackage", + "maintainer", + "version", + "license", + "architecture", + "url", + "description", + "size", + "installedSize", + "pullDependencies", + "pullChecksum", + "gitCommitOfApkPort", + "files" + ], + "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": { + "type": "string" + }, + "pullChecksum": { + "type": "string" + }, + "gitCommitOfApkPort": { + "type": "string" + }, + "files": { + "items": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/ApkFileRecord" + }, + "type": "array" + } + }, + "additionalProperties": true, + "type": "object" + }, + "CargoPackageMetadata": { + "required": [ + "name", + "version", + "source", + "checksum", + "dependencies" + ], + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "type": "string" + }, + "checksum": { + "type": "string" + }, + "dependencies": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "additionalProperties": true, + "type": "object" + }, + "Classification": { + "required": [ + "class", + "virtual_path", + "metadata" + ], + "properties": { + "class": { + "type": "string" + }, + "virtual_path": { + "type": "string" + }, + "metadata": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "additionalProperties": true, + "type": "object" + }, + "ConanLockMetadata": { + "required": [ + "ref" + ], + "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" + } + }, + "additionalProperties": true, + "type": "object" + }, + "ConanMetadata": { + "required": [ + "ref" + ], + "properties": { + "ref": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "Coordinates": { + "required": [ + "path" + ], + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "DartPubMetadata": { + "required": [ + "name", + "version" + ], + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "hosted_url": { + "type": "string" + }, + "vcs_url": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "Descriptor": { + "required": [ + "name", + "version" + ], + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "configuration": { + "additionalProperties": true + } + }, + "additionalProperties": true, + "type": "object" + }, + "Digest": { + "required": [ + "algorithm", + "value" + ], + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "Document": { + "required": [ + "artifacts", + "artifactRelationships", + "source", + "distro", + "descriptor", + "schema" + ], + "properties": { + "artifacts": { + "items": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/Package" + }, + "type": "array" + }, + "artifactRelationships": { + "items": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/Relationship" + }, + "type": "array" + }, + "files": { + "items": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/File" + }, + "type": "array" + }, + "secrets": { + "items": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/Secrets" + }, + "type": "array" + }, + "source": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/Source" + }, + "distro": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/LinuxRelease" + }, + "descriptor": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/Descriptor" + }, + "schema": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/Schema" + } + }, + "additionalProperties": true, + "type": "object" + }, + "DotnetDepsMetadata": { + "required": [ + "name", + "version", + "path", + "sha512", + "hashPath" + ], + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "path": { + "type": "string" + }, + "sha512": { + "type": "string" + }, + "hashPath": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "DpkgFileRecord": { + "required": [ + "path", + "isConfigFile" + ], + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/definitions/Digest" + }, + "isConfigFile": { + "type": "boolean" + } + }, + "additionalProperties": true, + "type": "object" + }, + "DpkgMetadata": { + "required": [ + "package", + "source", + "version", + "sourceVersion", + "architecture", + "maintainer", + "installedSize", + "files" + ], + "properties": { + "package": { + "type": "string" + }, + "source": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "installedSize": { + "type": "integer" + }, + "files": { + "items": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/DpkgFileRecord" + }, + "type": "array" + } + }, + "additionalProperties": true, + "type": "object" + }, + "File": { + "required": [ + "id", + "location" + ], + "properties": { + "id": { + "type": "string" + }, + "location": { + "$ref": "#/definitions/Coordinates" + }, + "metadata": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/FileMetadataEntry" + }, + "contents": { + "type": "string" + }, + "digests": { + "items": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/Digest" + }, + "type": "array" + }, + "classifications": { + "items": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/Classification" + }, + "type": "array" + } + }, + "additionalProperties": true, + "type": "object" + }, + "FileMetadataEntry": { + "required": [ + "mode", + "type", + "userID", + "groupID", + "mimeType" + ], + "properties": { + "mode": { + "type": "integer" + }, + "type": { + "type": "string" + }, + "linkDestination": { + "type": "string" + }, + "userID": { + "type": "integer" + }, + "groupID": { + "type": "integer" + }, + "mimeType": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "GemMetadata": { + "required": [ + "name", + "version" + ], + "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" + } + }, + "additionalProperties": true, + "type": "object" + }, + "GolangBinMetadata": { + "required": [ + "goCompiledVersion", + "architecture" + ], + "properties": { + "goBuildSettings": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "goCompiledVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "h1Digest": { + "type": "string" + }, + "mainModule": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "HackageMetadata": { + "required": [ + "name", + "version" + ], + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "snapshotURL": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "JavaManifest": { + "properties": { + "main": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "namedSections": { + "patternProperties": { + ".*": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object" + } + }, + "additionalProperties": true, + "type": "object" + }, + "JavaMetadata": { + "required": [ + "virtualPath" + ], + "properties": { + "virtualPath": { + "type": "string" + }, + "manifest": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/JavaManifest" + }, + "pomProperties": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/PomProperties" + }, + "pomProject": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/PomProject" + }, + "digest": { + "items": { + "$ref": "#/definitions/Digest" + }, + "type": "array" + } + }, + "additionalProperties": true, + "type": "object" + }, + "KbPackageMetadata": { + "required": [ + "product_id", + "kb" + ], + "properties": { + "product_id": { + "type": "string" + }, + "kb": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "LinuxRelease": { + "properties": { + "prettyName": { + "type": "string" + }, + "name": { + "type": "string" + }, + "id": { + "type": "string" + }, + "idLike": { + "items": { + "type": "string" + }, + "type": "array" + }, + "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" + } + }, + "additionalProperties": true, + "type": "object" + }, + "NpmPackageJSONMetadata": { + "required": [ + "name", + "version", + "author", + "licenses", + "homepage", + "description", + "url", + "private" + ], + "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" + } + }, + "additionalProperties": true, + "type": "object" + }, + "Package": { + "required": [ + "id", + "name", + "version", + "type", + "foundBy", + "locations", + "licenses", + "language", + "cpes", + "purl" + ], + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "type": { + "type": "string" + }, + "foundBy": { + "type": "string" + }, + "locations": { + "items": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/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": "#/definitions/AlpmMetadata" + }, + { + "$ref": "#/definitions/ApkMetadata" + }, + { + "$ref": "#/definitions/CargoPackageMetadata" + }, + { + "$ref": "#/definitions/ConanLockMetadata" + }, + { + "$ref": "#/definitions/ConanMetadata" + }, + { + "$ref": "#/definitions/DartPubMetadata" + }, + { + "$ref": "#/definitions/DotnetDepsMetadata" + }, + { + "$ref": "#/definitions/DpkgMetadata" + }, + { + "$ref": "#/definitions/GemMetadata" + }, + { + "$ref": "#/definitions/GolangBinMetadata" + }, + { + "$ref": "#/definitions/HackageMetadata" + }, + { + "$ref": "#/definitions/JavaMetadata" + }, + { + "$ref": "#/definitions/KbPackageMetadata" + }, + { + "$ref": "#/definitions/NpmPackageJSONMetadata" + }, + { + "$ref": "#/definitions/PhpComposerJSONMetadata" + }, + { + "$ref": "#/definitions/PortageMetadata" + }, + { + "$ref": "#/definitions/PythonPackageMetadata" + }, + { + "$ref": "#/definitions/RpmMetadata" + } + ] + } + }, + "additionalProperties": true, + "type": "object" + }, + "PhpComposerAuthors": { + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string" + }, + "email": { + "type": "string" + }, + "homepage": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "PhpComposerExternalReference": { + "required": [ + "type", + "url", + "reference" + ], + "properties": { + "type": { + "type": "string" + }, + "url": { + "type": "string" + }, + "reference": { + "type": "string" + }, + "shasum": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "PhpComposerJSONMetadata": { + "required": [ + "name", + "version", + "source", + "dist" + ], + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/PhpComposerExternalReference" + }, + "dist": { + "$ref": "#/definitions/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": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/PhpComposerAuthors" + }, + "type": "array" + }, + "description": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "keywords": { + "items": { + "type": "string" + }, + "type": "array" + }, + "time": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "PomParent": { + "required": [ + "groupId", + "artifactId", + "version" + ], + "properties": { + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "PomProject": { + "required": [ + "path", + "groupId", + "artifactId", + "version", + "name" + ], + "properties": { + "path": { + "type": "string" + }, + "parent": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/PomParent" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "PomProperties": { + "required": [ + "path", + "name", + "groupId", + "artifactId", + "version", + "extraFields" + ], + "properties": { + "path": { + "type": "string" + }, + "name": { + "type": "string" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "extraFields": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "additionalProperties": true, + "type": "object" + }, + "PortageFileRecord": { + "required": [ + "path" + ], + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/definitions/Digest" + } + }, + "additionalProperties": true, + "type": "object" + }, + "PortageMetadata": { + "required": [ + "installedSize", + "files" + ], + "properties": { + "installedSize": { + "type": "integer" + }, + "files": { + "items": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/PortageFileRecord" + }, + "type": "array" + } + }, + "additionalProperties": true, + "type": "object" + }, + "PythonDirectURLOriginInfo": { + "required": [ + "url" + ], + "properties": { + "url": { + "type": "string" + }, + "commitId": { + "type": "string" + }, + "vcs": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "PythonFileDigest": { + "required": [ + "algorithm", + "value" + ], + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "PythonFileRecord": { + "required": [ + "path" + ], + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/PythonFileDigest" + }, + "size": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "PythonPackageMetadata": { + "required": [ + "name", + "version", + "license", + "author", + "authorEmail", + "platform", + "sitePackagesRootPath" + ], + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "license": { + "type": "string" + }, + "author": { + "type": "string" + }, + "authorEmail": { + "type": "string" + }, + "platform": { + "type": "string" + }, + "files": { + "items": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/PythonFileRecord" + }, + "type": "array" + }, + "sitePackagesRootPath": { + "type": "string" + }, + "topLevelPackages": { + "items": { + "type": "string" + }, + "type": "array" + }, + "directUrlOrigin": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/PythonDirectURLOriginInfo" + } + }, + "additionalProperties": true, + "type": "object" + }, + "Relationship": { + "required": [ + "parent", + "child", + "type" + ], + "properties": { + "parent": { + "type": "string" + }, + "child": { + "type": "string" + }, + "type": { + "type": "string" + }, + "metadata": { + "additionalProperties": true + } + }, + "additionalProperties": true, + "type": "object" + }, + "RpmMetadata": { + "required": [ + "name", + "version", + "epoch", + "architecture", + "release", + "sourceRpm", + "size", + "license", + "vendor", + "modularityLabel", + "files" + ], + "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": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/RpmdbFileRecord" + }, + "type": "array" + } + }, + "additionalProperties": true, + "type": "object" + }, + "RpmdbFileRecord": { + "required": [ + "path", + "mode", + "size", + "digest", + "userName", + "groupName", + "flags" + ], + "properties": { + "path": { + "type": "string" + }, + "mode": { + "type": "integer" + }, + "size": { + "type": "integer" + }, + "digest": { + "$ref": "#/definitions/Digest" + }, + "userName": { + "type": "string" + }, + "groupName": { + "type": "string" + }, + "flags": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "Schema": { + "required": [ + "version", + "url" + ], + "properties": { + "version": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "SearchResult": { + "required": [ + "classification", + "lineNumber", + "lineOffset", + "seekPosition", + "length" + ], + "properties": { + "classification": { + "type": "string" + }, + "lineNumber": { + "type": "integer" + }, + "lineOffset": { + "type": "integer" + }, + "seekPosition": { + "type": "integer" + }, + "length": { + "type": "integer" + }, + "value": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "Secrets": { + "required": [ + "location", + "secrets" + ], + "properties": { + "location": { + "$ref": "#/definitions/Coordinates" + }, + "secrets": { + "items": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/SearchResult" + }, + "type": "array" + } + }, + "additionalProperties": true, + "type": "object" + }, + "Source": { + "required": [ + "id", + "type", + "target" + ], + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string" + }, + "target": { + "additionalProperties": true + } + }, + "additionalProperties": true, + "type": "object" + } + } +} diff --git a/syft/formats/syftjson/format.go b/syft/formats/syftjson/format.go index 3c22344da..c2af37ad6 100644 --- a/syft/formats/syftjson/format.go +++ b/syft/formats/syftjson/format.go @@ -4,7 +4,7 @@ import ( "github.com/anchore/syft/syft/sbom" ) -const ID sbom.FormatID = "syft-4-json" +const ID sbom.FormatID = "syft-5-json" func Format() sbom.Format { return sbom.NewFormat( diff --git a/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden b/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden index 235e3dfde..522f4b946 100644 --- a/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden +++ b/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden @@ -89,7 +89,7 @@ } }, "schema": { - "version": "4.1.0", - "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-4.1.0.json" + "version": "5.0.0", + "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-5.0.0.json" } } diff --git a/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden b/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden index cb8c86461..d1a493146 100644 --- a/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden +++ b/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden @@ -185,7 +185,7 @@ } }, "schema": { - "version": "4.1.0", - "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-4.1.0.json" + "version": "5.0.0", + "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-5.0.0.json" } } diff --git a/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden b/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden index 1cfd1cc2e..8b4d657a4 100644 --- a/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden +++ b/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden @@ -112,7 +112,7 @@ } }, "schema": { - "version": "4.1.0", - "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-4.1.0.json" + "version": "5.0.0", + "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-5.0.0.json" } } diff --git a/syft/pkg/cataloger/portage/cataloger.go b/syft/pkg/cataloger/portage/cataloger.go index a8cd5b796..fb29c024e 100644 --- a/syft/pkg/cataloger/portage/cataloger.go +++ b/syft/pkg/cataloger/portage/cataloger.go @@ -18,71 +18,63 @@ import ( "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/pkg/cataloger/generic" "github.com/anchore/syft/syft/source" ) var ( - cpvRe = regexp.MustCompile(`/([^/]*/[\w+][\w+-]*)-((\d+)((\.\d+)*)([a-z]?)((_(pre|p|beta|alpha|rc)\d*)*)(-r\d+)?)/CONTENTS$`) + cpvRe = regexp.MustCompile(`/([^/]*/[\w+][\w+-]*)-((\d+)((\.\d+)*)([a-z]?)((_(pre|p|beta|alpha|rc)\d*)*)(-r\d+)?)/CONTENTS$`) + _ generic.Parser = parsePortageContents ) -type Cataloger struct{} - -// NewPortageCataloger returns a new Portage package cataloger object. -func NewPortageCataloger() *Cataloger { - return &Cataloger{} +func NewPortageCataloger() *generic.Cataloger { + return generic.NewCataloger("portage-cataloger"). + WithParserByGlobs(parsePortageContents, "**/var/db/pkg/*/*/CONTENTS") } -// Name returns a string that uniquely describes a cataloger -func (c *Cataloger) Name() string { - return "portage-cataloger" -} - -// Catalog is given an object to resolve file references and content, this function returns any discovered Packages after analyzing portage support files. -func (c *Cataloger) Catalog(resolver source.FileResolver) ([]pkg.Package, []artifact.Relationship, error) { - dbFileMatches, err := resolver.FilesByGlob(pkg.PortageDBGlob) - if err != nil { - return nil, nil, fmt.Errorf("failed to find portage files by glob: %w", err) +func parsePortageContents(resolver source.FileResolver, _ *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { + cpvMatch := cpvRe.FindStringSubmatch(reader.Location.RealPath) + if cpvMatch == nil { + return nil, nil, fmt.Errorf("failed to match package and version in %s", reader.Location.RealPath) } - var allPackages []pkg.Package - for _, dbLocation := range dbFileMatches { - cpvMatch := cpvRe.FindStringSubmatch(dbLocation.RealPath) - if cpvMatch == nil { - return nil, nil, fmt.Errorf("failed to match package and version in %s", dbLocation.RealPath) - } - entry := pkg.PortageMetadata{ + + name, version := cpvMatch[1], cpvMatch[2] + if name == "" || version == "" { + log.WithFields("path", reader.Location.RealPath).Warnf("failed to parse portage name and version") + return nil, nil, nil + } + + p := pkg.Package{ + Name: name, + Version: version, + PURL: packageURL(name, version), + Locations: source.NewLocationSet(), + Type: pkg.PortagePkg, + MetadataType: pkg.PortageMetadataType, + Metadata: pkg.PortageMetadata{ // ensure the default value for a collection is never nil since this may be shown as JSON - Files: make([]pkg.PortageFileRecord, 0), - Package: cpvMatch[1], - Version: cpvMatch[2], - } - - err = addFiles(resolver, dbLocation, &entry) - if err != nil { - return nil, nil, err - } - - addSize(resolver, dbLocation, &entry) - - p := pkg.Package{ - Name: entry.Package, - Version: entry.Version, - Type: pkg.PortagePkg, - MetadataType: pkg.PortageMetadataType, - Metadata: entry, - } - addLicenses(resolver, dbLocation, &p) - p.FoundBy = c.Name() - p.Locations.Add(dbLocation) - p.SetID() - allPackages = append(allPackages, p) + Files: make([]pkg.PortageFileRecord, 0), + }, } - return allPackages, nil, nil + addLicenses(resolver, reader.Location, &p) + addSize(resolver, reader.Location, &p) + addFiles(resolver, reader.Location, &p) + + p.SetID() + + return []pkg.Package{p}, nil, nil } -func addFiles(resolver source.FileResolver, dbLocation source.Location, entry *pkg.PortageMetadata) error { +func addFiles(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) { contentsReader, err := resolver.FileContentsByLocation(dbLocation) if err != nil { - return err + log.WithFields("path", dbLocation.RealPath).Warnf("failed to fetch portage contents (package=%s): %+v", p.Name, err) + return + } + + entry, ok := p.Metadata.(pkg.PortageMetadata) + if !ok { + return } scanner := bufio.NewScanner(contentsReader) @@ -101,7 +93,9 @@ func addFiles(resolver source.FileResolver, dbLocation source.Location, entry *p entry.Files = append(entry.Files, record) } } - return nil + + p.Metadata = entry + p.Locations.Add(dbLocation) } func addLicenses(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) { @@ -109,43 +103,60 @@ func addLicenses(resolver source.FileResolver, dbLocation source.Location, p *pk location := resolver.RelativeFileByPath(dbLocation, path.Join(parentPath, "LICENSE")) - if location != nil { - licenseReader, err := resolver.FileContentsByLocation(*location) - if err == nil { - findings := internal.NewStringSet() - scanner := bufio.NewScanner(licenseReader) - scanner.Split(bufio.ScanWords) - for scanner.Scan() { - token := scanner.Text() - if token != "||" && token != "(" && token != ")" { - findings.Add(token) - } - } - p.Licenses = findings.ToSlice() + if location == nil { + return + } - sort.Strings(p.Licenses) + licenseReader, err := resolver.FileContentsByLocation(*location) + if err != nil { + log.WithFields("path", dbLocation.RealPath).Warnf("failed to fetch portage LICENSE: %+v", err) + return + } + + findings := internal.NewStringSet() + scanner := bufio.NewScanner(licenseReader) + scanner.Split(bufio.ScanWords) + for scanner.Scan() { + token := scanner.Text() + if token != "||" && token != "(" && token != ")" { + findings.Add(token) } } + licenses := findings.ToSlice() + sort.Strings(licenses) + p.Licenses = licenses + p.Locations.Add(*location) } -func addSize(resolver source.FileResolver, dbLocation source.Location, entry *pkg.PortageMetadata) { +func addSize(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) { parentPath := filepath.Dir(dbLocation.RealPath) location := resolver.RelativeFileByPath(dbLocation, path.Join(parentPath, "SIZE")) - if location != nil { - sizeReader, err := resolver.FileContentsByLocation(*location) - if err != nil { - log.Warnf("failed to fetch portage SIZE (package=%s): %+v", entry.Package, err) - } else { - scanner := bufio.NewScanner(sizeReader) - for scanner.Scan() { - line := strings.Trim(scanner.Text(), "\n") - size, err := strconv.Atoi(line) - if err == nil { - entry.InstalledSize = size - } - } + if location == nil { + return + } + + entry, ok := p.Metadata.(pkg.PortageMetadata) + if !ok { + return + } + + sizeReader, err := resolver.FileContentsByLocation(*location) + if err != nil { + log.WithFields("name", p.Name).Warnf("failed to fetch portage SIZE: %+v", err) + return + } + + scanner := bufio.NewScanner(sizeReader) + for scanner.Scan() { + line := strings.Trim(scanner.Text(), "\n") + size, err := strconv.Atoi(line) + if err == nil { + entry.InstalledSize = size } } + + p.Metadata = entry + p.Locations.Add(*location) } diff --git a/syft/pkg/cataloger/portage/cataloger_test.go b/syft/pkg/cataloger/portage/cataloger_test.go index c0e6f71bb..6623f27e7 100644 --- a/syft/pkg/cataloger/portage/cataloger_test.go +++ b/syft/pkg/cataloger/portage/cataloger_test.go @@ -3,62 +3,58 @@ package portage import ( "testing" - "github.com/go-test/deep" - - "github.com/anchore/stereoscope/pkg/imagetest" + "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" "github.com/anchore/syft/syft/source" ) func TestPortageCataloger(t *testing.T) { - tests := []struct { - name string - expected []pkg.Package - }{ + + expectedPkgs := []pkg.Package{ { - name: "go-case", - expected: []pkg.Package{ - { - Name: "app-containers/skopeo", - Version: "1.5.1", - FoundBy: "portage-cataloger", - Licenses: []string{"Apache-2.0", "BSD", "BSD-2", "CC-BY-SA-4.0", "ISC", "MIT"}, - Type: pkg.PortagePkg, - MetadataType: pkg.PortageMetadataType, - Metadata: pkg.PortageMetadata{ - Package: "app-containers/skopeo", - Version: "1.5.1", - InstalledSize: 27937835, - Files: []pkg.PortageFileRecord{ - { - Path: "/usr/bin/skopeo", - Digest: &file.Digest{ - Algorithm: "md5", - Value: "376c02bd3b22804df8fdfdc895e7dbfb", - }, - }, - { - Path: "/etc/containers/policy.json", - Digest: &file.Digest{ - Algorithm: "md5", - Value: "c01eb6950f03419e09d4fc88cb42ff6f", - }, - }, - { - Path: "/etc/containers/registries.d/default.yaml", - Digest: &file.Digest{ - Algorithm: "md5", - Value: "e6e66cd3c24623e0667f26542e0e08f6", - }, - }, - { - Path: "/var/lib/atomic/sigstore/.keep_app-containers_skopeo-0", - Digest: &file.Digest{ - Algorithm: "md5", - Value: "d41d8cd98f00b204e9800998ecf8427e", - }, - }, + Name: "app-containers/skopeo", + Version: "1.5.1", + FoundBy: "portage-cataloger", + PURL: "pkg:ebuild/app-containers/skopeo@1.5.1", + Locations: source.NewLocationSet( + source.NewLocation("var/db/pkg/app-containers/skopeo-1.5.1/CONTENTS"), + source.NewLocation("var/db/pkg/app-containers/skopeo-1.5.1/LICENSE"), + source.NewLocation("var/db/pkg/app-containers/skopeo-1.5.1/SIZE"), + ), + Licenses: []string{"Apache-2.0", "BSD", "BSD-2", "CC-BY-SA-4.0", "ISC", "MIT"}, + Type: pkg.PortagePkg, + MetadataType: pkg.PortageMetadataType, + Metadata: pkg.PortageMetadata{ + InstalledSize: 27937835, + Files: []pkg.PortageFileRecord{ + { + Path: "/usr/bin/skopeo", + Digest: &file.Digest{ + Algorithm: "md5", + Value: "376c02bd3b22804df8fdfdc895e7dbfb", + }, + }, + { + Path: "/etc/containers/policy.json", + Digest: &file.Digest{ + Algorithm: "md5", + Value: "c01eb6950f03419e09d4fc88cb42ff6f", + }, + }, + { + Path: "/etc/containers/registries.d/default.yaml", + Digest: &file.Digest{ + Algorithm: "md5", + Value: "e6e66cd3c24623e0667f26542e0e08f6", + }, + }, + { + Path: "/var/lib/atomic/sigstore/.keep_app-containers_skopeo-0", + Digest: &file.Digest{ + Algorithm: "md5", + Value: "d41d8cd98f00b204e9800998ecf8427e", }, }, }, @@ -66,41 +62,12 @@ func TestPortageCataloger(t *testing.T) { }, } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { + // TODO: relationships are not under test yet + var expectedRelationships []artifact.Relationship - img := imagetest.GetFixtureImage(t, "docker-archive", "image-portage") - - s, err := source.NewFromImage(img, "") - if err != nil { - t.Fatal(err) - } - - c := NewPortageCataloger() - - resolver, err := s.FileResolver(source.SquashedScope) - if err != nil { - t.Errorf("could not get resolver error: %+v", err) - } - - actual, _, err := c.Catalog(resolver) - if err != nil { - t.Fatalf("failed to catalog: %+v", err) - } - - if len(actual) != len(test.expected) { - for _, a := range actual { - t.Logf(" %+v", a) - } - t.Fatalf("unexpected package count: %d!=%d", len(actual), len(test.expected)) - } - - // test remaining fields... - for _, d := range deep.Equal(actual, test.expected) { - t.Errorf("diff: %+v", d) - } - - }) - } + pkgtest.NewCatalogTester(). + FromDirectory(t, "test-fixtures/image-portage"). + Expects(expectedPkgs, expectedRelationships). + TestCataloger(t, NewPortageCataloger()) } diff --git a/syft/pkg/cataloger/portage/purl.go b/syft/pkg/cataloger/portage/purl.go new file mode 100644 index 000000000..9ff016b92 --- /dev/null +++ b/syft/pkg/cataloger/portage/purl.go @@ -0,0 +1,18 @@ +package portage + +import ( + "github.com/anchore/packageurl-go" +) + +func packageURL(name, version string) string { + var qualifiers packageurl.Qualifiers + + return packageurl.NewPackageURL( + "ebuild", // currently this is the proposed type for portage packages at https://github.com/package-url/purl-spec/blob/master/PURL-TYPES.rst + "", + name, + version, + qualifiers, + "", + ).ToString() +} diff --git a/syft/pkg/cataloger/portage/purl_test.go b/syft/pkg/cataloger/portage/purl_test.go new file mode 100644 index 000000000..80541c26c --- /dev/null +++ b/syft/pkg/cataloger/portage/purl_test.go @@ -0,0 +1,28 @@ +package portage + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_packageURL(t *testing.T) { + + tests := []struct { + name string + version string + want string + }{ + { + "app-admin/eselect", + "1.4.15", + "pkg:ebuild/app-admin/eselect@1.4.15", + }, + } + for _, tt := range tests { + t.Run(fmt.Sprintf("%s@%s", tt.name, tt.version), func(t *testing.T) { + assert.Equal(t, tt.want, packageURL(tt.name, tt.version)) + }) + } +} diff --git a/syft/pkg/cataloger/portage/test-fixtures/image-portage/Dockerfile b/syft/pkg/cataloger/portage/test-fixtures/image-portage/Dockerfile deleted file mode 100644 index 770e60b56..000000000 --- a/syft/pkg/cataloger/portage/test-fixtures/image-portage/Dockerfile +++ /dev/null @@ -1,2 +0,0 @@ -FROM scratch -COPY . . \ No newline at end of file diff --git a/syft/pkg/portage_metadata.go b/syft/pkg/portage_metadata.go index 97ae36772..e3db119e5 100644 --- a/syft/pkg/portage_metadata.go +++ b/syft/pkg/portage_metadata.go @@ -4,12 +4,8 @@ import ( "github.com/anchore/syft/syft/file" ) -const PortageDBGlob = "**/var/db/pkg/*/*/CONTENTS" - // PortageMetadata represents all captured data for a Package package DB entry. type PortageMetadata struct { - Package string `mapstructure:"Package" json:"package"` - Version string `mapstructure:"Version" json:"version"` InstalledSize int `mapstructure:"InstalledSize" json:"installedSize" cyclonedx:"installedSize"` Files []PortageFileRecord `json:"files"` }