diff --git a/internal/constants.go b/internal/constants.go index 71a134a72..a11eb7e46 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.0.0" + JSONSchemaVersion = "4.1.0" ) diff --git a/schema/json/schema-4.1.0.json b/schema/json/schema-4.1.0.json new file mode 100644 index 000000000..bdd5489ee --- /dev/null +++ b/schema/json/schema-4.1.0.json @@ -0,0 +1,1578 @@ +{ + "$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", + "metadata" + ], + "properties": { + "class": { + "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": [ + "package", + "version", + "installedSize", + "files" + ], + "properties": { + "package": { + "type": "string" + }, + "version": { + "type": "string" + }, + "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/encoder_test.go b/syft/formats/syftjson/encoder_test.go index 22d66e1f2..e20fa33d2 100644 --- a/syft/formats/syftjson/encoder_test.go +++ b/syft/formats/syftjson/encoder_test.go @@ -156,6 +156,7 @@ func TestEncodeFullJSONDocument(t *testing.T) { }, }, Source: source.Metadata{ + ID: "c2b46b4eb06296933b7cf0722683964e9ecbd93265b9ef6ae9642e3952afbba0", Scheme: source.ImageScheme, ImageMetadata: source.ImageMetadata{ UserInput: "user-image-input", diff --git a/syft/formats/syftjson/model/source.go b/syft/formats/syftjson/model/source.go index 169405220..d546cf11c 100644 --- a/syft/formats/syftjson/model/source.go +++ b/syft/formats/syftjson/model/source.go @@ -10,12 +10,14 @@ import ( // Source object represents the thing that was cataloged type Source struct { + ID string `json:"id"` Type string `json:"type"` Target interface{} `json:"target"` } // sourceUnpacker is used to unmarshal Source objects type sourceUnpacker struct { + ID string `json:"id,omitempty"` Type string `json:"type"` Target json.RawMessage `json:"target"` } @@ -28,6 +30,7 @@ func (s *Source) UnmarshalJSON(b []byte) error { } s.Type = unpacker.Type + s.ID = unpacker.ID switch s.Type { case "directory", "file": diff --git a/syft/formats/syftjson/model/source_test.go b/syft/formats/syftjson/model/source_test.go index bcd4b9bd1..ee7969d74 100644 --- a/syft/formats/syftjson/model/source_test.go +++ b/syft/formats/syftjson/model/source_test.go @@ -20,10 +20,12 @@ func TestSource_UnmarshalJSON(t *testing.T) { { name: "directory", input: []byte(`{ + "id": "foobar", "type": "directory", "target":"/var/lib/foo" }`), expectedSource: &Source{ + ID: "foobar", Type: "directory", Target: "/var/lib/foo", }, @@ -32,6 +34,7 @@ func TestSource_UnmarshalJSON(t *testing.T) { { name: "image", input: []byte(`{ + "id": "foobar", "type": "image", "target": { "userInput": "alpine:3.10", @@ -55,6 +58,7 @@ func TestSource_UnmarshalJSON(t *testing.T) { } }`), expectedSource: &Source{ + ID: "foobar", Type: "image", Target: source.ImageMetadata{ UserInput: "alpine:3.10", @@ -98,10 +102,12 @@ func TestSource_UnmarshalJSON(t *testing.T) { { name: "file", input: []byte(`{ + "id": "foobar", "type": "file", "target":"/var/lib/foo/go.mod" }`), expectedSource: &Source{ + ID: "foobar", Type: "file", Target: "/var/lib/foo/go.mod", }, @@ -110,10 +116,12 @@ func TestSource_UnmarshalJSON(t *testing.T) { { name: "unknown source type", input: []byte(`{ + "id": "foobar", "type": "unknown-thing", "target":"/var/lib/foo" }`), expectedSource: &Source{ + ID: "foobar", Type: "unknown-thing", }, errAssertion: assert.Error, diff --git a/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden b/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden index f7118ea6d..0f965356e 100644 --- a/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden +++ b/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden @@ -67,6 +67,7 @@ ], "artifactRelationships": [], "source": { + "id": "eda6cf0b63f1a1d2eaf7792a2a98c832c21a18e6992bcebffe6381781cc85cbc", "type": "directory", "target": "/some/path" }, @@ -88,7 +89,7 @@ } }, "schema": { - "version": "4.0.0", - "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-4.0.0.json" + "version": "4.1.0", + "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-4.1.0.json" } } diff --git a/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden b/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden index 664b82f9c..bd4d508c1 100644 --- a/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden +++ b/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden @@ -139,6 +139,7 @@ } ], "source": { + "id": "c2b46b4eb06296933b7cf0722683964e9ecbd93265b9ef6ae9642e3952afbba0", "type": "image", "target": { "userInput": "user-image-input", @@ -184,7 +185,7 @@ } }, "schema": { - "version": "4.0.0", - "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-4.0.0.json" + "version": "4.1.0", + "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-4.1.0.json" } } diff --git a/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden b/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden index a7dda4af6..d54336304 100644 --- a/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden +++ b/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden @@ -9,7 +9,7 @@ "locations": [ { "path": "/somefile-1.txt", - "layerID": "sha256:7ef28e9c2d56471ee090b578a678bdf28c3b5a311ca7b2e28c2a4185e5bb34c0" + "layerID": "sha256:4965affaf42a7174561882c5fd87e2db6f0b07df532459ba86f98a8bd2af11de" } ], "licenses": [ @@ -40,7 +40,7 @@ "locations": [ { "path": "/somefile-2.txt", - "layerID": "sha256:86da8aee621161bea2efaf27a2709ddab5e7d44e30ecdfda728b02c03a28fd98" + "layerID": "sha256:460c3e27be163efe75df048c4d4cf3a22e7e363f02521fa2e82a3bd257a682d4" } ], "licenses": [], @@ -64,10 +64,11 @@ ], "artifactRelationships": [], "source": { + "id": "00afea0209d754683fdfcdd47cfea94ec9f2e81286be444e297a8c776c4accbf", "type": "image", "target": { "userInput": "user-image-input", - "imageID": "sha256:5dd5f5f4247e4e946f555f0de7681a631a5240b614e52717d0aed04808e8c65f", + "imageID": "sha256:6b1b476e6dc187bb688566606cf7a59d7804d81169967d8c6bb121627b0a387f", "manifestDigest": "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368", "mediaType": "application/vnd.docker.distribution.manifest.v2+json", "tags": [ @@ -77,17 +78,17 @@ "layers": [ { "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", - "digest": "sha256:7ef28e9c2d56471ee090b578a678bdf28c3b5a311ca7b2e28c2a4185e5bb34c0", + "digest": "sha256:4965affaf42a7174561882c5fd87e2db6f0b07df532459ba86f98a8bd2af11de", "size": 22 }, { "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", - "digest": "sha256:86da8aee621161bea2efaf27a2709ddab5e7d44e30ecdfda728b02c03a28fd98", + "digest": "sha256:460c3e27be163efe75df048c4d4cf3a22e7e363f02521fa2e82a3bd257a682d4", "size": 16 } ], - "manifest": "eyJzY2hlbWFWZXJzaW9uIjoyLCJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmRpc3RyaWJ1dGlvbi5tYW5pZmVzdC52Mitqc29uIiwiY29uZmlnIjp7Im1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuY29udGFpbmVyLmltYWdlLnYxK2pzb24iLCJzaXplIjo2NzMsImRpZ2VzdCI6InNoYTI1Njo1ZGQ1ZjVmNDI0N2U0ZTk0NmY1NTVmMGRlNzY4MWE2MzFhNTI0MGI2MTRlNTI3MTdkMGFlZDA0ODA4ZThjNjVmIn0sImxheWVycyI6W3sibWVkaWFUeXBlIjoiYXBwbGljYXRpb24vdm5kLmRvY2tlci5pbWFnZS5yb290ZnMuZGlmZi50YXIuZ3ppcCIsInNpemUiOjIwNDgsImRpZ2VzdCI6InNoYTI1Njo3ZWYyOGU5YzJkNTY0NzFlZTA5MGI1NzhhNjc4YmRmMjhjM2I1YTMxMWNhN2IyZTI4YzJhNDE4NWU1YmIzNGMwIn0seyJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmltYWdlLnJvb3Rmcy5kaWZmLnRhci5nemlwIiwic2l6ZSI6MjA0OCwiZGlnZXN0Ijoic2hhMjU2Ojg2ZGE4YWVlNjIxMTYxYmVhMmVmYWYyN2EyNzA5ZGRhYjVlN2Q0NGUzMGVjZGZkYTcyOGIwMmMwM2EyOGZkOTgifV19", - "config": "eyJhcmNoaXRlY3R1cmUiOiJhbWQ2NCIsImNvbmZpZyI6eyJFbnYiOlsiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iXSwiV29ya2luZ0RpciI6Ii8iLCJPbkJ1aWxkIjpudWxsfSwiY3JlYXRlZCI6IjIwMjItMDYtMDJUMTQ6MzQ6MzQuNzE5MTM1MTc0WiIsImhpc3RvcnkiOlt7ImNyZWF0ZWQiOiIyMDIyLTA2LTAyVDE0OjM0OjM0LjY4NjkzMzI2M1oiLCJjcmVhdGVkX2J5IjoiQUREIGZpbGUtMS50eHQgL3NvbWVmaWxlLTEudHh0ICMgYnVpbGRraXQiLCJjb21tZW50IjoiYnVpbGRraXQuZG9ja2VyZmlsZS52MCJ9LHsiY3JlYXRlZCI6IjIwMjItMDYtMDJUMTQ6MzQ6MzQuNzE5MTM1MTc0WiIsImNyZWF0ZWRfYnkiOiJBREQgZmlsZS0yLnR4dCAvc29tZWZpbGUtMi50eHQgIyBidWlsZGtpdCIsImNvbW1lbnQiOiJidWlsZGtpdC5kb2NrZXJmaWxlLnYwIn1dLCJvcyI6ImxpbnV4Iiwicm9vdGZzIjp7InR5cGUiOiJsYXllcnMiLCJkaWZmX2lkcyI6WyJzaGEyNTY6N2VmMjhlOWMyZDU2NDcxZWUwOTBiNTc4YTY3OGJkZjI4YzNiNWEzMTFjYTdiMmUyOGMyYTQxODVlNWJiMzRjMCIsInNoYTI1Njo4NmRhOGFlZTYyMTE2MWJlYTJlZmFmMjdhMjcwOWRkYWI1ZTdkNDRlMzBlY2RmZGE3MjhiMDJjMDNhMjhmZDk4Il19fQ==", + "manifest": "eyJzY2hlbWFWZXJzaW9uIjoyLCJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmRpc3RyaWJ1dGlvbi5tYW5pZmVzdC52Mitqc29uIiwiY29uZmlnIjp7Im1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuY29udGFpbmVyLmltYWdlLnYxK2pzb24iLCJzaXplIjo2NzMsImRpZ2VzdCI6InNoYTI1Njo2YjFiNDc2ZTZkYzE4N2JiNjg4NTY2NjA2Y2Y3YTU5ZDc4MDRkODExNjk5NjdkOGM2YmIxMjE2MjdiMGEzODdmIn0sImxheWVycyI6W3sibWVkaWFUeXBlIjoiYXBwbGljYXRpb24vdm5kLmRvY2tlci5pbWFnZS5yb290ZnMuZGlmZi50YXIuZ3ppcCIsInNpemUiOjIwNDgsImRpZ2VzdCI6InNoYTI1Njo0OTY1YWZmYWY0MmE3MTc0NTYxODgyYzVmZDg3ZTJkYjZmMGIwN2RmNTMyNDU5YmE4NmY5OGE4YmQyYWYxMWRlIn0seyJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmltYWdlLnJvb3Rmcy5kaWZmLnRhci5nemlwIiwic2l6ZSI6MjA0OCwiZGlnZXN0Ijoic2hhMjU2OjQ2MGMzZTI3YmUxNjNlZmU3NWRmMDQ4YzRkNGNmM2EyMmU3ZTM2M2YwMjUyMWZhMmU4MmEzYmQyNTdhNjgyZDQifV19", + "config": "eyJhcmNoaXRlY3R1cmUiOiJhbWQ2NCIsImNvbmZpZyI6eyJFbnYiOlsiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iXSwiV29ya2luZ0RpciI6Ii8iLCJPbkJ1aWxkIjpudWxsfSwiY3JlYXRlZCI6IjIwMjItMTAtMDVUMTQ6MjQ6NTguNzc0NTY2MjM2WiIsImhpc3RvcnkiOlt7ImNyZWF0ZWQiOiIyMDIyLTEwLTA1VDE0OjI0OjU4Ljc0NDY3NTEyOVoiLCJjcmVhdGVkX2J5IjoiQUREIGZpbGUtMS50eHQgL3NvbWVmaWxlLTEudHh0ICMgYnVpbGRraXQiLCJjb21tZW50IjoiYnVpbGRraXQuZG9ja2VyZmlsZS52MCJ9LHsiY3JlYXRlZCI6IjIwMjItMTAtMDVUMTQ6MjQ6NTguNzc0NTY2MjM2WiIsImNyZWF0ZWRfYnkiOiJBREQgZmlsZS0yLnR4dCAvc29tZWZpbGUtMi50eHQgIyBidWlsZGtpdCIsImNvbW1lbnQiOiJidWlsZGtpdC5kb2NrZXJmaWxlLnYwIn1dLCJvcyI6ImxpbnV4Iiwicm9vdGZzIjp7InR5cGUiOiJsYXllcnMiLCJkaWZmX2lkcyI6WyJzaGEyNTY6NDk2NWFmZmFmNDJhNzE3NDU2MTg4MmM1ZmQ4N2UyZGI2ZjBiMDdkZjUzMjQ1OWJhODZmOThhOGJkMmFmMTFkZSIsInNoYTI1Njo0NjBjM2UyN2JlMTYzZWZlNzVkZjA0OGM0ZDRjZjNhMjJlN2UzNjNmMDI1MjFmYTJlODJhM2JkMjU3YTY4MmQ0Il19fQ==", "repoDigests": [], "architecture": "", "os": "" @@ -111,7 +112,7 @@ } }, "schema": { - "version": "4.0.0", - "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-4.0.0.json" + "version": "4.1.0", + "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-4.1.0.json" } } diff --git a/syft/formats/syftjson/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden b/syft/formats/syftjson/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden index 3cd33e2b8..538b5e585 100644 Binary files a/syft/formats/syftjson/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden and b/syft/formats/syftjson/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden differ diff --git a/syft/formats/syftjson/to_format_model.go b/syft/formats/syftjson/to_format_model.go index 9d4f4bfd4..12638f78e 100644 --- a/syft/formats/syftjson/to_format_model.go +++ b/syft/formats/syftjson/to_format_model.go @@ -17,8 +17,6 @@ import ( ) // ToFormatModel transforms the sbom import a format-specific model. -// note: this is needed for anchore import functionality -// TODO: unexport this when/if anchore import functionality is removed func ToFormatModel(s sbom.SBOM) model.Document { src, err := toSourceModel(s.Source) if err != nil { @@ -236,16 +234,19 @@ func toSourceModel(src source.Metadata) (model.Source, error) { metadata.Tags = []string{} } return model.Source{ + ID: src.ID, Type: "image", Target: metadata, }, nil case source.DirectoryScheme: return model.Source{ + ID: src.ID, Type: "directory", Target: src.Path, }, nil case source.FileScheme: return model.Source{ + ID: src.ID, Type: "file", Target: src.Path, }, nil diff --git a/syft/formats/syftjson/to_format_model_test.go b/syft/formats/syftjson/to_format_model_test.go index b09c74ac1..500f9983c 100644 --- a/syft/formats/syftjson/to_format_model_test.go +++ b/syft/formats/syftjson/to_format_model_test.go @@ -26,10 +26,12 @@ func Test_toSourceModel(t *testing.T) { { name: "directory", src: source.Metadata{ + ID: "test-id", Scheme: source.DirectoryScheme, Path: "some/path", }, expected: model.Source{ + ID: "test-id", Type: "directory", Target: "some/path", }, @@ -37,10 +39,12 @@ func Test_toSourceModel(t *testing.T) { { name: "file", src: source.Metadata{ + ID: "test-id", Scheme: source.FileScheme, Path: "some/path", }, expected: model.Source{ + ID: "test-id", Type: "file", Target: "some/path", }, @@ -48,6 +52,7 @@ func Test_toSourceModel(t *testing.T) { { name: "image", src: source.Metadata{ + ID: "test-id", Scheme: source.ImageScheme, ImageMetadata: source.ImageMetadata{ UserInput: "user-input", @@ -57,6 +62,7 @@ func Test_toSourceModel(t *testing.T) { }, }, expected: model.Source{ + ID: "test-id", Type: "image", Target: source.ImageMetadata{ UserInput: "user-input", diff --git a/syft/formats/syftjson/to_syft_model.go b/syft/formats/syftjson/to_syft_model.go index 8a97ae68f..f4b99bd67 100644 --- a/syft/formats/syftjson/to_syft_model.go +++ b/syft/formats/syftjson/to_syft_model.go @@ -64,6 +64,9 @@ func toSyftRelationships(doc *model.Document, catalog *pkg.Catalog, relationship } } + // set source metadata in identifier map + idMap[doc.Source.ID] = toSyftSource(doc.Source) + for _, f := range doc.Files { idMap[f.ID] = f.Location } @@ -78,6 +81,14 @@ func toSyftRelationships(doc *model.Document, catalog *pkg.Catalog, relationship return out } +func toSyftSource(s model.Source) *source.Source { + newSrc := &source.Source{ + Metadata: *toSyftSourceData(s), + } + newSrc.SetID() + return newSrc +} + func toSyftRelationship(idMap map[string]interface{}, relationship model.Relationship, idAliases map[string]string) *artifact.Relationship { id := func(id string) string { aliased, ok := idAliases[id] @@ -86,16 +97,19 @@ func toSyftRelationship(idMap map[string]interface{}, relationship model.Relatio } return id } + from, ok := idMap[id(relationship.Parent)].(artifact.Identifiable) if !ok { log.Warnf("relationship mapping from key %s is not a valid artifact.Identifiable type: %+v", relationship.Parent, idMap[relationship.Parent]) return nil } + to, ok := idMap[id(relationship.Child)].(artifact.Identifiable) if !ok { log.Warnf("relationship mapping to key %s is not a valid artifact.Identifiable type: %+v", relationship.Child, idMap[relationship.Child]) return nil } + typ := artifact.RelationshipType(relationship.Type) switch typ { @@ -126,16 +140,19 @@ func toSyftSourceData(s model.Source) *source.Metadata { switch s.Type { case "directory": return &source.Metadata{ + ID: s.ID, Scheme: source.DirectoryScheme, Path: s.Target.(string), } case "file": return &source.Metadata{ + ID: s.ID, Scheme: source.FileScheme, Path: s.Target.(string), } case "image": return &source.Metadata{ + ID: s.ID, Scheme: source.ImageScheme, ImageMetadata: s.Target.(source.ImageMetadata), } diff --git a/syft/lib.go b/syft/lib.go index 12aa33960..474f3f57b 100644 --- a/syft/lib.go +++ b/syft/lib.go @@ -74,9 +74,24 @@ func CatalogPackages(src *source.Source, cfg cataloger.Config) (*pkg.Catalog, [] return nil, nil, nil, err } + relationships = append(relationships, newSourceRelationshipsFromCatalog(src, catalog)...) + return catalog, relationships, release, nil } +func newSourceRelationshipsFromCatalog(src *source.Source, c *pkg.Catalog) []artifact.Relationship { + relationships := make([]artifact.Relationship, 0) // Should we pre-allocate this by giving catalog a Len() method? + for p := range c.Enumerate() { + relationships = append(relationships, artifact.Relationship{ + From: src, + To: p, + Type: artifact.ContainsRelationship, + }) + } + + return relationships +} + // SetLogger sets the logger object used for all syft logging calls. func SetLogger(logger logger.Logger) { log.Log = logger diff --git a/syft/source/metadata.go b/syft/source/metadata.go index b9747362e..42981c221 100644 --- a/syft/source/metadata.go +++ b/syft/source/metadata.go @@ -2,6 +2,7 @@ package source // Metadata represents any static source data that helps describe "what" was cataloged. type Metadata struct { + ID string `hash:"ignore"` // the id generated from the parent source struct Scheme Scheme // the source data scheme type (directory or image) ImageMetadata ImageMetadata // all image info (image only) Path string // the root path to be cataloged (directory only) diff --git a/syft/source/source.go b/syft/source/source.go index cab3e6540..d4be9cc03 100644 --- a/syft/source/source.go +++ b/syft/source/source.go @@ -27,13 +27,13 @@ import ( // Source is an object that captures the data source to be cataloged, configuration, and a specific resolver used // in cataloging (based on the data source and configuration) type Source struct { - id artifact.ID - Image *image.Image // the image object to be cataloged (image only) + id artifact.ID `hash:"ignore"` + Image *image.Image `hash:"ignore"` // the image object to be cataloged (image only) Metadata Metadata - directoryResolver *directoryResolver + directoryResolver *directoryResolver `hash:"ignore"` path string mutex *sync.Mutex - Exclusions []string + Exclusions []string `hash:"ignore"` } // Input is an object that captures the detected user input regarding source location, scheme, and provider type. @@ -241,28 +241,33 @@ func generateFileSource(fs afero.Fs, location string) (*Source, func(), error) { // NewFromDirectory creates a new source object tailored to catalog a given filesystem directory recursively. func NewFromDirectory(path string) (Source, error) { - return Source{ + s := Source{ mutex: &sync.Mutex{}, Metadata: Metadata{ Scheme: DirectoryScheme, Path: path, }, path: path, - }, nil + } + s.SetID() + return s, nil } // NewFromFile creates a new source object tailored to catalog a file. func NewFromFile(path string) (Source, func()) { analysisPath, cleanupFn := fileAnalysisPath(path) - return Source{ + s := Source{ mutex: &sync.Mutex{}, Metadata: Metadata{ Scheme: FileScheme, Path: path, }, path: analysisPath, - }, cleanupFn + } + + s.SetID() + return s, cleanupFn } // fileAnalysisPath returns the path given, or in the case the path is an archive, the location where the archive @@ -298,16 +303,18 @@ func NewFromImage(img *image.Image, userImageStr string) (Source, error) { return Source{}, fmt.Errorf("no image given") } - return Source{ + s := Source{ Image: img, Metadata: Metadata{ Scheme: ImageScheme, ImageMetadata: NewImageMetadata(img, userImageStr), }, - }, nil + } + s.SetID() + return s, nil } -func (s Source) ID() artifact.ID { +func (s *Source) ID() artifact.ID { if s.id == "" { s.SetID() } @@ -333,7 +340,7 @@ func (s *Source) SetID() { } d = di.String() case ImageScheme: - manifestDigest := digest.FromBytes(s.Image.Metadata.RawManifest).String() + manifestDigest := digest.FromBytes(s.Metadata.ImageMetadata.RawManifest).String() if manifestDigest != "" { d = manifestDigest break @@ -341,7 +348,7 @@ func (s *Source) SetID() { // calcuate chain ID for image sources where manifestDigest is not available // https://github.com/opencontainers/image-spec/blob/main/config.md#layer-chainid - d = calculateChainID(s.Image) + d = calculateChainID(s.Metadata.ImageMetadata.Layers) if d == "" { // TODO what happens here if image has no layers? // Is this case possible @@ -353,27 +360,28 @@ func (s *Source) SetID() { } s.id = artifact.ID(strings.TrimPrefix(d, "sha256:")) + s.Metadata.ID = strings.TrimPrefix(d, "sha256:") } -func calculateChainID(img *image.Image) string { - if len(img.Layers) < 1 { +func calculateChainID(lm []LayerMetadata) string { + if len(lm) < 1 { return "" } // DiffID(L0) = digest of layer 0 // https://github.com/anchore/stereoscope/blob/1b1b744a919964f38d14e1416fb3f25221b761ce/pkg/image/layer_metadata.go#L19-L32 - chainID := img.Layers[0].Metadata.Digest - id := chain(chainID, img.Layers[1:]) + chainID := lm[0].Digest + id := chain(chainID, lm[1:]) return id } -func chain(chainID string, layers []*image.Layer) string { +func chain(chainID string, layers []LayerMetadata) string { if len(layers) < 1 { return chainID } - chainID = digest.FromString(layers[0].Metadata.Digest + " " + chainID).String() + chainID = digest.FromString(layers[0].Digest + " " + chainID).String() return chain(chainID, layers[1:]) } diff --git a/syft/source/source_test.go b/syft/source/source_test.go index 48c68fb84..ca9818774 100644 --- a/syft/source/source_test.go +++ b/syft/source/source_test.go @@ -120,7 +120,7 @@ func TestSetID(t *testing.T) { Path: "test-fixtures/image-simple", }, }, - expected: artifact.ID("febd2d6148dc327d"), + expected: artifact.ID("26e8f4daad203793"), }, }