diff --git a/README.md b/README.md index 789798045..43cba0a34 100644 --- a/README.md +++ b/README.md @@ -140,11 +140,11 @@ Note that flags using the @ can be used for earlier versions of each sp - Linux kernel archives (vmlinz) - Linux kernel modules (ko) - Nix (outputs in /nix/store) -- PHP (composer) +- PHP (composer, PECL, Pear) - Python (wheel, egg, poetry, requirements.txt) - Red Hat (rpm) - Ruby (gem) -- Rust (cargo.lock) +- Rust (cargo.lock, auditable binary) - Swift (cocoapods, swift-package-manager) - Wordpress plugins - Terraform providers (.terraform.lock.hcl) diff --git a/cmd/syft/internal/test/integration/catalog_packages_cases_test.go b/cmd/syft/internal/test/integration/catalog_packages_cases_test.go index d07785fd3..f7b6bbcb6 100644 --- a/cmd/syft/internal/test/integration/catalog_packages_cases_test.go +++ b/cmd/syft/internal/test/integration/catalog_packages_cases_test.go @@ -502,8 +502,8 @@ var commonTestCases = []testCase{ }, }, { - name: "find php pecl package", - pkgType: pkg.PhpPeclPkg, + name: "find php pear/pecl package", + pkgType: pkg.PhpPearPkg, pkgLanguage: pkg.PHP, pkgInfo: map[string]string{ "memcached": "3.2.0", diff --git a/cmd/syft/internal/test/integration/catalog_packages_test.go b/cmd/syft/internal/test/integration/catalog_packages_test.go index e1be4ee5a..ec7940e00 100644 --- a/cmd/syft/internal/test/integration/catalog_packages_test.go +++ b/cmd/syft/internal/test/integration/catalog_packages_test.go @@ -85,6 +85,7 @@ func TestPkgCoverageImage(t *testing.T) { definedPkgs.Remove(string(pkg.GithubActionPkg)) definedPkgs.Remove(string(pkg.GithubActionWorkflowPkg)) definedPkgs.Remove(string(pkg.TerraformPkg)) + definedPkgs.Remove(string(pkg.PhpPeclPkg)) // we have coverage for pear instead var cases []testCase cases = append(cases, commonTestCases...) @@ -227,6 +228,7 @@ func TestPkgCoverageDirectory(t *testing.T) { definedPkgs.Remove(string(pkg.LinuxKernelModulePkg)) definedPkgs.Remove(string(pkg.Rpkg)) definedPkgs.Remove(string(pkg.UnknownPkg)) + definedPkgs.Remove(string(pkg.PhpPeclPkg)) // this is covered as pear packages // for directory scans we should not expect to see any of the following package types definedPkgs.Remove(string(pkg.KbPkg)) diff --git a/cmd/syft/internal/test/integration/package_catalogers_represented_test.go b/cmd/syft/internal/test/integration/package_catalogers_represented_test.go index bca070fc8..21d81e3d2 100644 --- a/cmd/syft/internal/test/integration/package_catalogers_represented_test.go +++ b/cmd/syft/internal/test/integration/package_catalogers_represented_test.go @@ -56,6 +56,7 @@ func TestAllPackageCatalogersReachableInTasks(t *testing.T) { // not reachable since they are deprecated "dotnet-portable-executable-cataloger", "dotnet-deps-cataloger", + "php-pecl-serialized-cataloger", // not reachable by design "sbom-cataloger", ) diff --git a/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/php/.registry/.channel.pecl.php.net/memcached.reg b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/php/.registry/.channel.pecl.php.net/memcached.reg index fca1bd301..5592335eb 100644 --- a/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/php/.registry/.channel.pecl.php.net/memcached.reg +++ b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/php/.registry/.channel.pecl.php.net/memcached.reg @@ -1 +1 @@ -a:5:{s:4:"name";s:9:"memcached";s:4:"date";s:10:"2022-01-11";s:4:"time";s:8:"15:23:47";s:7:"version";a:2:{s:7:"release";s:5:"3.2.0";s:3:"api";s:5:"3.2.0";}s:7:"license";a:2:{s:7:"attribs";a:1:{s:3:"uri";s:26:"http://www.php.net/license";}s:8:"_content";s:11:"PHP License";}} \ No newline at end of file +a:6:{s:4:"name";s:9:"memcached";s:7:"channel";s:12:"pecl.php.net";s:4:"date";s:10:"2022-01-11";s:4:"time";s:8:"15:23:47";s:7:"version";a:2:{s:7:"release";s:5:"3.2.0";s:3:"api";s:5:"3.2.0";}s:7:"license";a:2:{s:7:"attribs";a:1:{s:3:"uri";s:26:"http://www.php.net/license";}s:8:"_content";s:11:"PHP License";}} \ No newline at end of file diff --git a/internal/constants.go b/internal/constants.go index 0068cdd0a..3e36abe69 100644 --- a/internal/constants.go +++ b/internal/constants.go @@ -3,5 +3,5 @@ package internal const ( // JSONSchemaVersion is the current schema version output by the JSON encoder // This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment. - JSONSchemaVersion = "16.0.25" + JSONSchemaVersion = "16.0.26" ) diff --git a/internal/task/package_tasks.go b/internal/task/package_tasks.go index ff138d48d..b4fa19a74 100644 --- a/internal/task/package_tasks.go +++ b/internal/task/package_tasks.go @@ -100,7 +100,7 @@ func DefaultPackageTaskFactories() Factories { pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, JavaScript, Node, NPM, ), newSimplePackageTaskFactory(php.NewComposerLockCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "php", "composer"), - newSimplePackageTaskFactory(php.NewPeclCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, pkgcataloging.ImageTag, "php", "pecl"), + newSimplePackageTaskFactory(php.NewPearCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, pkgcataloging.ImageTag, "php", "pear"), newPackageTaskFactory( func(cfg CatalogingFactoryConfig) pkg.Cataloger { return python.NewPackageCataloger(cfg.PackagesConfig.Python) @@ -166,5 +166,6 @@ func DefaultPackageTaskFactories() Factories { // these are catalogers that should not be selectable other than specific inclusion via name or "deprecated" tag (to remain backwards compatible) newSimplePackageTaskFactory(dotnet.NewDotnetDepsCataloger, pkgcataloging.DeprecatedTag), // TODO: remove in syft v2.0 newSimplePackageTaskFactory(dotnet.NewDotnetPortableExecutableCataloger, pkgcataloging.DeprecatedTag), // TODO: remove in syft v2.0 + newSimplePackageTaskFactory(php.NewPeclCataloger, pkgcataloging.DeprecatedTag), // TODO: remove in syft v2.0 } } diff --git a/schema/json/schema-16.0.26.json b/schema/json/schema-16.0.26.json new file mode 100644 index 000000000..c5dacf929 --- /dev/null +++ b/schema/json/schema-16.0.26.json @@ -0,0 +1,2944 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "anchore.io/schema/syft/json/16.0.26/document", + "$ref": "#/$defs/Document", + "$defs": { + "AlpmDbEntry": { + "properties": { + "basepackage": { + "type": "string" + }, + "package": { + "type": "string" + }, + "version": { + "type": "string" + }, + "description": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "packager": { + "type": "string" + }, + "url": { + "type": "string" + }, + "validation": { + "type": "string" + }, + "reason": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/AlpmFileRecord" + }, + "type": "array" + }, + "backup": { + "items": { + "$ref": "#/$defs/AlpmFileRecord" + }, + "type": "array" + }, + "provides": { + "items": { + "type": "string" + }, + "type": "array" + }, + "depends": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "basepackage", + "package", + "version", + "description", + "architecture", + "size", + "packager", + "url", + "validation", + "reason", + "files", + "backup" + ] + }, + "AlpmFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "type": { + "type": "string" + }, + "uid": { + "type": "string" + }, + "gid": { + "type": "string" + }, + "time": { + "type": "string", + "format": "date-time" + }, + "size": { + "type": "string" + }, + "link": { + "type": "string" + }, + "digest": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object" + }, + "ApkDbEntry": { + "properties": { + "package": { + "type": "string" + }, + "originPackage": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "version": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "url": { + "type": "string" + }, + "description": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "installedSize": { + "type": "integer" + }, + "pullDependencies": { + "items": { + "type": "string" + }, + "type": "array" + }, + "provides": { + "items": { + "type": "string" + }, + "type": "array" + }, + "pullChecksum": { + "type": "string" + }, + "gitCommitOfApkPort": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/ApkFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "originPackage", + "maintainer", + "version", + "architecture", + "url", + "description", + "size", + "installedSize", + "pullDependencies", + "provides", + "pullChecksum", + "gitCommitOfApkPort", + "files" + ] + }, + "ApkFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "ownerUid": { + "type": "string" + }, + "ownerGid": { + "type": "string" + }, + "permissions": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "BinarySignature": { + "properties": { + "matches": { + "items": { + "$ref": "#/$defs/ClassifierMatch" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "matches" + ] + }, + "BitnamiSbomEntry": { + "properties": { + "name": { + "type": "string" + }, + "arch": { + "type": "string" + }, + "distro": { + "type": "string" + }, + "revision": { + "type": "string" + }, + "version": { + "type": "string" + }, + "path": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "arch", + "distro", + "revision", + "version", + "path", + "files" + ] + }, + "CConanFileEntry": { + "properties": { + "ref": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "CConanInfoEntry": { + "properties": { + "ref": { + "type": "string" + }, + "package_id": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "CConanLockEntry": { + "properties": { + "ref": { + "type": "string" + }, + "package_id": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "build_requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "py_requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "options": { + "$ref": "#/$defs/KeyValues" + }, + "path": { + "type": "string" + }, + "context": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "CConanLockV2Entry": { + "properties": { + "ref": { + "type": "string" + }, + "packageID": { + "type": "string" + }, + "username": { + "type": "string" + }, + "channel": { + "type": "string" + }, + "recipeRevision": { + "type": "string" + }, + "packageRevision": { + "type": "string" + }, + "timestamp": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "CPE": { + "properties": { + "cpe": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "type": "object", + "required": [ + "cpe" + ] + }, + "ClassifierMatch": { + "properties": { + "classifier": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Location" + } + }, + "type": "object", + "required": [ + "classifier", + "location" + ] + }, + "CocoaPodfileLockEntry": { + "properties": { + "checksum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "checksum" + ] + }, + "Coordinates": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "DartPubspecLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "hosted_url": { + "type": "string" + }, + "vcs_url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Descriptor": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "configuration": true + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Digest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algorithm", + "value" + ] + }, + "Document": { + "properties": { + "artifacts": { + "items": { + "$ref": "#/$defs/Package" + }, + "type": "array" + }, + "artifactRelationships": { + "items": { + "$ref": "#/$defs/Relationship" + }, + "type": "array" + }, + "files": { + "items": { + "$ref": "#/$defs/File" + }, + "type": "array" + }, + "source": { + "$ref": "#/$defs/Source" + }, + "distro": { + "$ref": "#/$defs/LinuxRelease" + }, + "descriptor": { + "$ref": "#/$defs/Descriptor" + }, + "schema": { + "$ref": "#/$defs/Schema" + } + }, + "type": "object", + "required": [ + "artifacts", + "artifactRelationships", + "source", + "distro", + "descriptor", + "schema" + ] + }, + "DotnetDepsEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "path": { + "type": "string" + }, + "sha512": { + "type": "string" + }, + "hashPath": { + "type": "string" + }, + "executables": { + "patternProperties": { + ".*": { + "$ref": "#/$defs/DotnetPortableExecutableEntry" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "name", + "version", + "path", + "sha512", + "hashPath" + ] + }, + "DotnetPackagesLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "contentHash": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "contentHash", + "type" + ] + }, + "DotnetPortableExecutableEntry": { + "properties": { + "assemblyVersion": { + "type": "string" + }, + "legalCopyright": { + "type": "string" + }, + "comments": { + "type": "string" + }, + "internalName": { + "type": "string" + }, + "companyName": { + "type": "string" + }, + "productName": { + "type": "string" + }, + "productVersion": { + "type": "string" + } + }, + "type": "object", + "required": [ + "assemblyVersion", + "legalCopyright", + "companyName", + "productName", + "productVersion" + ] + }, + "DpkgArchiveEntry": { + "properties": { + "package": { + "type": "string" + }, + "source": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "installedSize": { + "type": "integer" + }, + "provides": { + "items": { + "type": "string" + }, + "type": "array" + }, + "depends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "preDepends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "files": { + "items": { + "$ref": "#/$defs/DpkgFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "source", + "version", + "sourceVersion", + "architecture", + "maintainer", + "installedSize", + "files" + ] + }, + "DpkgDbEntry": { + "properties": { + "package": { + "type": "string" + }, + "source": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "installedSize": { + "type": "integer" + }, + "provides": { + "items": { + "type": "string" + }, + "type": "array" + }, + "depends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "preDepends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "files": { + "items": { + "$ref": "#/$defs/DpkgFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "source", + "version", + "sourceVersion", + "architecture", + "maintainer", + "installedSize", + "files" + ] + }, + "DpkgFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "isConfigFile": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "path", + "isConfigFile" + ] + }, + "ELFSecurityFeatures": { + "properties": { + "symbolTableStripped": { + "type": "boolean" + }, + "stackCanary": { + "type": "boolean" + }, + "nx": { + "type": "boolean" + }, + "relRO": { + "type": "string" + }, + "pie": { + "type": "boolean" + }, + "dso": { + "type": "boolean" + }, + "safeStack": { + "type": "boolean" + }, + "cfi": { + "type": "boolean" + }, + "fortify": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "symbolTableStripped", + "nx", + "relRO", + "pie", + "dso" + ] + }, + "ElfBinaryPackageNoteJsonPayload": { + "properties": { + "type": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "osCPE": { + "type": "string" + }, + "os": { + "type": "string" + }, + "osVersion": { + "type": "string" + }, + "system": { + "type": "string" + }, + "vendor": { + "type": "string" + }, + "sourceRepo": { + "type": "string" + }, + "commit": { + "type": "string" + } + }, + "type": "object" + }, + "ElixirMixLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "pkgHashExt": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "pkgHash", + "pkgHashExt" + ] + }, + "ErlangRebarLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "pkgHashExt": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "pkgHash", + "pkgHashExt" + ] + }, + "Executable": { + "properties": { + "format": { + "type": "string" + }, + "hasExports": { + "type": "boolean" + }, + "hasEntrypoint": { + "type": "boolean" + }, + "importedLibraries": { + "items": { + "type": "string" + }, + "type": "array" + }, + "elfSecurityFeatures": { + "$ref": "#/$defs/ELFSecurityFeatures" + } + }, + "type": "object", + "required": [ + "format", + "hasExports", + "hasEntrypoint", + "importedLibraries" + ] + }, + "File": { + "properties": { + "id": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Coordinates" + }, + "metadata": { + "$ref": "#/$defs/FileMetadataEntry" + }, + "contents": { + "type": "string" + }, + "digests": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + }, + "licenses": { + "items": { + "$ref": "#/$defs/FileLicense" + }, + "type": "array" + }, + "executable": { + "$ref": "#/$defs/Executable" + }, + "unknowns": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "id", + "location" + ] + }, + "FileLicense": { + "properties": { + "value": { + "type": "string" + }, + "spdxExpression": { + "type": "string" + }, + "type": { + "type": "string" + }, + "evidence": { + "$ref": "#/$defs/FileLicenseEvidence" + } + }, + "type": "object", + "required": [ + "value", + "spdxExpression", + "type" + ] + }, + "FileLicenseEvidence": { + "properties": { + "confidence": { + "type": "integer" + }, + "offset": { + "type": "integer" + }, + "extent": { + "type": "integer" + } + }, + "type": "object", + "required": [ + "confidence", + "offset", + "extent" + ] + }, + "FileMetadataEntry": { + "properties": { + "mode": { + "type": "integer" + }, + "type": { + "type": "string" + }, + "linkDestination": { + "type": "string" + }, + "userID": { + "type": "integer" + }, + "groupID": { + "type": "integer" + }, + "mimeType": { + "type": "string" + }, + "size": { + "type": "integer" + } + }, + "type": "object", + "required": [ + "mode", + "type", + "userID", + "groupID", + "mimeType", + "size" + ] + }, + "GithubActionsUseStatement": { + "properties": { + "value": { + "type": "string" + }, + "comment": { + "type": "string" + } + }, + "type": "object", + "required": [ + "value" + ] + }, + "GoModuleBuildinfoEntry": { + "properties": { + "goBuildSettings": { + "$ref": "#/$defs/KeyValues" + }, + "goCompiledVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "h1Digest": { + "type": "string" + }, + "mainModule": { + "type": "string" + }, + "goCryptoSettings": { + "items": { + "type": "string" + }, + "type": "array" + }, + "goExperiments": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "goCompiledVersion", + "architecture" + ] + }, + "GoModuleEntry": { + "properties": { + "h1Digest": { + "type": "string" + } + }, + "type": "object" + }, + "HaskellHackageStackEntry": { + "properties": { + "pkgHash": { + "type": "string" + } + }, + "type": "object" + }, + "HaskellHackageStackLockEntry": { + "properties": { + "pkgHash": { + "type": "string" + }, + "snapshotURL": { + "type": "string" + } + }, + "type": "object" + }, + "IDLikes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "JavaArchive": { + "properties": { + "virtualPath": { + "type": "string" + }, + "manifest": { + "$ref": "#/$defs/JavaManifest" + }, + "pomProperties": { + "$ref": "#/$defs/JavaPomProperties" + }, + "pomProject": { + "$ref": "#/$defs/JavaPomProject" + }, + "digest": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "virtualPath" + ] + }, + "JavaJvmInstallation": { + "properties": { + "release": { + "$ref": "#/$defs/JavaVMRelease" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "release", + "files" + ] + }, + "JavaManifest": { + "properties": { + "main": { + "$ref": "#/$defs/KeyValues" + }, + "sections": { + "items": { + "$ref": "#/$defs/KeyValues" + }, + "type": "array" + } + }, + "type": "object" + }, + "JavaPomParent": { + "properties": { + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "type": "object", + "required": [ + "groupId", + "artifactId", + "version" + ] + }, + "JavaPomProject": { + "properties": { + "path": { + "type": "string" + }, + "parent": { + "$ref": "#/$defs/JavaPomParent" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "groupId", + "artifactId", + "version", + "name" + ] + }, + "JavaPomProperties": { + "properties": { + "path": { + "type": "string" + }, + "name": { + "type": "string" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "scope": { + "type": "string" + }, + "extraFields": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path", + "name", + "groupId", + "artifactId", + "version" + ] + }, + "JavaVMRelease": { + "properties": { + "implementor": { + "type": "string" + }, + "implementorVersion": { + "type": "string" + }, + "javaRuntimeVersion": { + "type": "string" + }, + "javaVersion": { + "type": "string" + }, + "javaVersionDate": { + "type": "string" + }, + "libc": { + "type": "string" + }, + "modules": { + "items": { + "type": "string" + }, + "type": "array" + }, + "osArch": { + "type": "string" + }, + "osName": { + "type": "string" + }, + "osVersion": { + "type": "string" + }, + "source": { + "type": "string" + }, + "buildSource": { + "type": "string" + }, + "buildSourceRepo": { + "type": "string" + }, + "sourceRepo": { + "type": "string" + }, + "fullVersion": { + "type": "string" + }, + "semanticVersion": { + "type": "string" + }, + "buildInfo": { + "type": "string" + }, + "jvmVariant": { + "type": "string" + }, + "jvmVersion": { + "type": "string" + }, + "imageType": { + "type": "string" + }, + "buildType": { + "type": "string" + } + }, + "type": "object" + }, + "JavascriptNpmPackage": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "author": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + }, + "private": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "homepage", + "description", + "url", + "private" + ] + }, + "JavascriptNpmPackageLockEntry": { + "properties": { + "resolved": { + "type": "string" + }, + "integrity": { + "type": "string" + } + }, + "type": "object", + "required": [ + "resolved", + "integrity" + ] + }, + "JavascriptYarnLockEntry": { + "properties": { + "resolved": { + "type": "string" + }, + "integrity": { + "type": "string" + } + }, + "type": "object", + "required": [ + "resolved", + "integrity" + ] + }, + "KeyValue": { + "properties": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "key", + "value" + ] + }, + "KeyValues": { + "items": { + "$ref": "#/$defs/KeyValue" + }, + "type": "array" + }, + "License": { + "properties": { + "value": { + "type": "string" + }, + "spdxExpression": { + "type": "string" + }, + "type": { + "type": "string" + }, + "urls": { + "items": { + "type": "string" + }, + "type": "array" + }, + "locations": { + "items": { + "$ref": "#/$defs/Location" + }, + "type": "array" + }, + "contents": { + "type": "string" + } + }, + "type": "object", + "required": [ + "value", + "spdxExpression", + "type", + "urls", + "locations" + ] + }, + "LinuxKernelArchive": { + "properties": { + "name": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "version": { + "type": "string" + }, + "extendedVersion": { + "type": "string" + }, + "buildTime": { + "type": "string" + }, + "author": { + "type": "string" + }, + "format": { + "type": "string" + }, + "rwRootFS": { + "type": "boolean" + }, + "swapDevice": { + "type": "integer" + }, + "rootDevice": { + "type": "integer" + }, + "videoMode": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "architecture", + "version" + ] + }, + "LinuxKernelModule": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "path": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "license": { + "type": "string" + }, + "kernelVersion": { + "type": "string" + }, + "versionMagic": { + "type": "string" + }, + "parameters": { + "patternProperties": { + ".*": { + "$ref": "#/$defs/LinuxKernelModuleParameter" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "LinuxKernelModuleParameter": { + "properties": { + "type": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "type": "object" + }, + "LinuxRelease": { + "properties": { + "prettyName": { + "type": "string" + }, + "name": { + "type": "string" + }, + "id": { + "type": "string" + }, + "idLike": { + "$ref": "#/$defs/IDLikes" + }, + "version": { + "type": "string" + }, + "versionID": { + "type": "string" + }, + "versionCodename": { + "type": "string" + }, + "buildID": { + "type": "string" + }, + "imageID": { + "type": "string" + }, + "imageVersion": { + "type": "string" + }, + "variant": { + "type": "string" + }, + "variantID": { + "type": "string" + }, + "homeURL": { + "type": "string" + }, + "supportURL": { + "type": "string" + }, + "bugReportURL": { + "type": "string" + }, + "privacyPolicyURL": { + "type": "string" + }, + "cpeName": { + "type": "string" + }, + "supportEnd": { + "type": "string" + } + }, + "type": "object" + }, + "Location": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + }, + "accessPath": { + "type": "string" + }, + "annotations": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path", + "accessPath" + ] + }, + "LuarocksPackage": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "license": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + }, + "dependencies": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "name", + "version", + "license", + "homepage", + "description", + "url", + "dependencies" + ] + }, + "MicrosoftKbPatch": { + "properties": { + "product_id": { + "type": "string" + }, + "kb": { + "type": "string" + } + }, + "type": "object", + "required": [ + "product_id", + "kb" + ] + }, + "NixStoreEntry": { + "properties": { + "outputHash": { + "type": "string" + }, + "output": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "outputHash", + "files" + ] + }, + "OpamPackage": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "licenses": { + "items": { + "type": "string" + }, + "type": "array" + }, + "url": { + "type": "string" + }, + "checksum": { + "items": { + "type": "string" + }, + "type": "array" + }, + "homepage": { + "type": "string" + }, + "dependencies": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "licenses", + "url", + "checksum", + "homepage", + "dependencies" + ] + }, + "Package": { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "type": { + "type": "string" + }, + "foundBy": { + "type": "string" + }, + "locations": { + "items": { + "$ref": "#/$defs/Location" + }, + "type": "array" + }, + "licenses": { + "$ref": "#/$defs/licenses" + }, + "language": { + "type": "string" + }, + "cpes": { + "$ref": "#/$defs/cpes" + }, + "purl": { + "type": "string" + }, + "metadataType": { + "type": "string" + }, + "metadata": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "#/$defs/AlpmDbEntry" + }, + { + "$ref": "#/$defs/ApkDbEntry" + }, + { + "$ref": "#/$defs/BinarySignature" + }, + { + "$ref": "#/$defs/BitnamiSbomEntry" + }, + { + "$ref": "#/$defs/CConanFileEntry" + }, + { + "$ref": "#/$defs/CConanInfoEntry" + }, + { + "$ref": "#/$defs/CConanLockEntry" + }, + { + "$ref": "#/$defs/CConanLockV2Entry" + }, + { + "$ref": "#/$defs/CocoaPodfileLockEntry" + }, + { + "$ref": "#/$defs/DartPubspecLockEntry" + }, + { + "$ref": "#/$defs/DotnetDepsEntry" + }, + { + "$ref": "#/$defs/DotnetPackagesLockEntry" + }, + { + "$ref": "#/$defs/DotnetPortableExecutableEntry" + }, + { + "$ref": "#/$defs/DpkgArchiveEntry" + }, + { + "$ref": "#/$defs/DpkgDbEntry" + }, + { + "$ref": "#/$defs/ElfBinaryPackageNoteJsonPayload" + }, + { + "$ref": "#/$defs/ElixirMixLockEntry" + }, + { + "$ref": "#/$defs/ErlangRebarLockEntry" + }, + { + "$ref": "#/$defs/GithubActionsUseStatement" + }, + { + "$ref": "#/$defs/GoModuleBuildinfoEntry" + }, + { + "$ref": "#/$defs/GoModuleEntry" + }, + { + "$ref": "#/$defs/HaskellHackageStackEntry" + }, + { + "$ref": "#/$defs/HaskellHackageStackLockEntry" + }, + { + "$ref": "#/$defs/JavaArchive" + }, + { + "$ref": "#/$defs/JavaJvmInstallation" + }, + { + "$ref": "#/$defs/JavascriptNpmPackage" + }, + { + "$ref": "#/$defs/JavascriptNpmPackageLockEntry" + }, + { + "$ref": "#/$defs/JavascriptYarnLockEntry" + }, + { + "$ref": "#/$defs/LinuxKernelArchive" + }, + { + "$ref": "#/$defs/LinuxKernelModule" + }, + { + "$ref": "#/$defs/LuarocksPackage" + }, + { + "$ref": "#/$defs/MicrosoftKbPatch" + }, + { + "$ref": "#/$defs/NixStoreEntry" + }, + { + "$ref": "#/$defs/OpamPackage" + }, + { + "$ref": "#/$defs/PhpComposerInstalledEntry" + }, + { + "$ref": "#/$defs/PhpComposerLockEntry" + }, + { + "$ref": "#/$defs/PhpPearEntry" + }, + { + "$ref": "#/$defs/PhpPeclEntry" + }, + { + "$ref": "#/$defs/PortageDbEntry" + }, + { + "$ref": "#/$defs/PythonPackage" + }, + { + "$ref": "#/$defs/PythonPipRequirementsEntry" + }, + { + "$ref": "#/$defs/PythonPipfileLockEntry" + }, + { + "$ref": "#/$defs/PythonPoetryLockEntry" + }, + { + "$ref": "#/$defs/RDescription" + }, + { + "$ref": "#/$defs/RpmArchive" + }, + { + "$ref": "#/$defs/RpmDbEntry" + }, + { + "$ref": "#/$defs/RubyGemspec" + }, + { + "$ref": "#/$defs/RustCargoAuditEntry" + }, + { + "$ref": "#/$defs/RustCargoLockEntry" + }, + { + "$ref": "#/$defs/SwiftPackageManagerLockEntry" + }, + { + "$ref": "#/$defs/SwiplpackPackage" + }, + { + "$ref": "#/$defs/TerraformLockProviderEntry" + }, + { + "$ref": "#/$defs/WordpressPluginEntry" + } + ] + } + }, + "type": "object", + "required": [ + "id", + "name", + "version", + "type", + "foundBy", + "locations", + "licenses", + "language", + "cpes", + "purl" + ] + }, + "PhpComposerAuthors": { + "properties": { + "name": { + "type": "string" + }, + "email": { + "type": "string" + }, + "homepage": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name" + ] + }, + "PhpComposerExternalReference": { + "properties": { + "type": { + "type": "string" + }, + "url": { + "type": "string" + }, + "reference": { + "type": "string" + }, + "shasum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "type", + "url", + "reference" + ] + }, + "PhpComposerInstalledEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "dist": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "require": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "provide": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "require-dev": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "suggest": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "license": { + "items": { + "type": "string" + }, + "type": "array" + }, + "type": { + "type": "string" + }, + "notification-url": { + "type": "string" + }, + "bin": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "$ref": "#/$defs/PhpComposerAuthors" + }, + "type": "array" + }, + "description": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "keywords": { + "items": { + "type": "string" + }, + "type": "array" + }, + "time": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "dist" + ] + }, + "PhpComposerLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "dist": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "require": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "provide": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "require-dev": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "suggest": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "license": { + "items": { + "type": "string" + }, + "type": "array" + }, + "type": { + "type": "string" + }, + "notification-url": { + "type": "string" + }, + "bin": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "$ref": "#/$defs/PhpComposerAuthors" + }, + "type": "array" + }, + "description": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "keywords": { + "items": { + "type": "string" + }, + "type": "array" + }, + "time": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "dist" + ] + }, + "PhpPearEntry": { + "properties": { + "name": { + "type": "string" + }, + "channel": { + "type": "string" + }, + "version": { + "type": "string" + }, + "license": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "PhpPeclEntry": { + "properties": { + "name": { + "type": "string" + }, + "channel": { + "type": "string" + }, + "version": { + "type": "string" + }, + "license": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "PortageDbEntry": { + "properties": { + "installedSize": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/PortageFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "installedSize", + "files" + ] + }, + "PortageFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "PythonDirectURLOriginInfo": { + "properties": { + "url": { + "type": "string" + }, + "commitId": { + "type": "string" + }, + "vcs": { + "type": "string" + } + }, + "type": "object", + "required": [ + "url" + ] + }, + "PythonFileDigest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algorithm", + "value" + ] + }, + "PythonFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/PythonFileDigest" + }, + "size": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "PythonPackage": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "author": { + "type": "string" + }, + "authorEmail": { + "type": "string" + }, + "platform": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/PythonFileRecord" + }, + "type": "array" + }, + "sitePackagesRootPath": { + "type": "string" + }, + "topLevelPackages": { + "items": { + "type": "string" + }, + "type": "array" + }, + "directUrlOrigin": { + "$ref": "#/$defs/PythonDirectURLOriginInfo" + }, + "requiresPython": { + "type": "string" + }, + "requiresDist": { + "items": { + "type": "string" + }, + "type": "array" + }, + "providesExtra": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "authorEmail", + "platform", + "sitePackagesRootPath" + ] + }, + "PythonPipRequirementsEntry": { + "properties": { + "name": { + "type": "string" + }, + "extras": { + "items": { + "type": "string" + }, + "type": "array" + }, + "versionConstraint": { + "type": "string" + }, + "url": { + "type": "string" + }, + "markers": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "versionConstraint" + ] + }, + "PythonPipfileLockEntry": { + "properties": { + "hashes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "index": { + "type": "string" + } + }, + "type": "object", + "required": [ + "hashes", + "index" + ] + }, + "PythonPoetryLockDependencyEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "optional": { + "type": "boolean" + }, + "markers": { + "type": "string" + }, + "extras": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "optional" + ] + }, + "PythonPoetryLockEntry": { + "properties": { + "index": { + "type": "string" + }, + "dependencies": { + "items": { + "$ref": "#/$defs/PythonPoetryLockDependencyEntry" + }, + "type": "array" + }, + "extras": { + "items": { + "$ref": "#/$defs/PythonPoetryLockExtraEntry" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "index", + "dependencies" + ] + }, + "PythonPoetryLockExtraEntry": { + "properties": { + "name": { + "type": "string" + }, + "dependencies": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "dependencies" + ] + }, + "RDescription": { + "properties": { + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "url": { + "items": { + "type": "string" + }, + "type": "array" + }, + "repository": { + "type": "string" + }, + "built": { + "type": "string" + }, + "needsCompilation": { + "type": "boolean" + }, + "imports": { + "items": { + "type": "string" + }, + "type": "array" + }, + "depends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "suggests": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "Relationship": { + "properties": { + "parent": { + "type": "string" + }, + "child": { + "type": "string" + }, + "type": { + "type": "string" + }, + "metadata": true + }, + "type": "object", + "required": [ + "parent", + "child", + "type" + ] + }, + "RpmArchive": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "epoch": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "architecture": { + "type": "string" + }, + "release": { + "type": "string" + }, + "sourceRpm": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "vendor": { + "type": "string" + }, + "modularityLabel": { + "type": "string" + }, + "provides": { + "items": { + "type": "string" + }, + "type": "array" + }, + "requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "files": { + "items": { + "$ref": "#/$defs/RpmFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "epoch", + "architecture", + "release", + "sourceRpm", + "size", + "vendor", + "files" + ] + }, + "RpmDbEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "epoch": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "architecture": { + "type": "string" + }, + "release": { + "type": "string" + }, + "sourceRpm": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "vendor": { + "type": "string" + }, + "modularityLabel": { + "type": "string" + }, + "provides": { + "items": { + "type": "string" + }, + "type": "array" + }, + "requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "files": { + "items": { + "$ref": "#/$defs/RpmFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "epoch", + "architecture", + "release", + "sourceRpm", + "size", + "vendor", + "files" + ] + }, + "RpmFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "mode": { + "type": "integer" + }, + "size": { + "type": "integer" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "userName": { + "type": "string" + }, + "groupName": { + "type": "string" + }, + "flags": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "mode", + "size", + "digest", + "userName", + "groupName", + "flags" + ] + }, + "RubyGemspec": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "type": "string" + }, + "type": "array" + }, + "homepage": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "RustCargoAuditEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source" + ] + }, + "RustCargoLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "type": "string" + }, + "checksum": { + "type": "string" + }, + "dependencies": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "checksum", + "dependencies" + ] + }, + "Schema": { + "properties": { + "version": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "version", + "url" + ] + }, + "Source": { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "type": { + "type": "string" + }, + "metadata": true + }, + "type": "object", + "required": [ + "id", + "name", + "version", + "type", + "metadata" + ] + }, + "SwiftPackageManagerLockEntry": { + "properties": { + "revision": { + "type": "string" + } + }, + "type": "object", + "required": [ + "revision" + ] + }, + "SwiplpackPackage": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "author": { + "type": "string" + }, + "authorEmail": { + "type": "string" + }, + "packager": { + "type": "string" + }, + "packagerEmail": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "dependencies": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "authorEmail", + "packager", + "packagerEmail", + "homepage", + "dependencies" + ] + }, + "TerraformLockProviderEntry": { + "properties": { + "url": { + "type": "string" + }, + "constraints": { + "type": "string" + }, + "version": { + "type": "string" + }, + "hashes": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "url", + "constraints", + "version", + "hashes" + ] + }, + "WordpressPluginEntry": { + "properties": { + "pluginInstallDirectory": { + "type": "string" + }, + "author": { + "type": "string" + }, + "authorUri": { + "type": "string" + } + }, + "type": "object", + "required": [ + "pluginInstallDirectory" + ] + }, + "cpes": { + "items": { + "$ref": "#/$defs/CPE" + }, + "type": "array" + }, + "licenses": { + "items": { + "$ref": "#/$defs/License" + }, + "type": "array" + } + } +} diff --git a/schema/json/schema-latest.json b/schema/json/schema-latest.json index f42cb5342..c5dacf929 100644 --- a/schema/json/schema-latest.json +++ b/schema/json/schema-latest.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "anchore.io/schema/syft/json/16.0.25/document", + "$id": "anchore.io/schema/syft/json/16.0.26/document", "$ref": "#/$defs/Document", "$defs": { "AlpmDbEntry": { @@ -1872,6 +1872,9 @@ { "$ref": "#/$defs/PhpComposerLockEntry" }, + { + "$ref": "#/$defs/PhpPearEntry" + }, { "$ref": "#/$defs/PhpPeclEntry" }, @@ -2164,11 +2167,38 @@ "dist" ] }, + "PhpPearEntry": { + "properties": { + "name": { + "type": "string" + }, + "channel": { + "type": "string" + }, + "version": { + "type": "string" + }, + "license": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, "PhpPeclEntry": { "properties": { "name": { "type": "string" }, + "channel": { + "type": "string" + }, "version": { "type": "string" }, diff --git a/syft/format/internal/spdxutil/helpers/originator_supplier_test.go b/syft/format/internal/spdxutil/helpers/originator_supplier_test.go index ee65023fb..d9b966833 100644 --- a/syft/format/internal/spdxutil/helpers/originator_supplier_test.go +++ b/syft/format/internal/spdxutil/helpers/originator_supplier_test.go @@ -34,6 +34,7 @@ func Test_OriginatorSupplier(t *testing.T) { pkg.NixStoreEntry{}, pkg.NpmPackageLockEntry{}, pkg.PhpComposerInstalledEntry{}, + pkg.PhpPearEntry{}, pkg.PhpPeclEntry{}, pkg.PortageEntry{}, pkg.PythonPipfileLockEntry{}, diff --git a/syft/format/internal/spdxutil/helpers/source_info.go b/syft/format/internal/spdxutil/helpers/source_info.go index f9aebad65..fbe82a1f5 100644 --- a/syft/format/internal/spdxutil/helpers/source_info.go +++ b/syft/format/internal/spdxutil/helpers/source_info.go @@ -40,6 +40,8 @@ func SourceInfo(p pkg.Package) string { answer = "acquired package info from rust cargo manifest" case pkg.PhpComposerPkg: answer = "acquired package info from PHP composer manifest" + case pkg.PhpPearPkg: + answer = "acquired package info from PHP Pear manifest" case pkg.PhpPeclPkg: answer = "acquired package info from PHP Pecl manifest" case pkg.CocoapodsPkg: diff --git a/syft/format/internal/spdxutil/helpers/source_info_test.go b/syft/format/internal/spdxutil/helpers/source_info_test.go index 02b074482..a2c6e7587 100644 --- a/syft/format/internal/spdxutil/helpers/source_info_test.go +++ b/syft/format/internal/spdxutil/helpers/source_info_test.go @@ -143,6 +143,14 @@ func Test_SourceInfo(t *testing.T) { "from PHP Pecl manifest", }, }, + { + input: pkg.Package{ + Type: pkg.PhpPearPkg, + }, + expected: []string{ + "from PHP Pear manifest", + }, + }, { input: pkg.Package{ Type: pkg.DartPubPkg, diff --git a/syft/internal/packagemetadata/generated.go b/syft/internal/packagemetadata/generated.go index a543afc99..b30bd4cf4 100644 --- a/syft/internal/packagemetadata/generated.go +++ b/syft/internal/packagemetadata/generated.go @@ -42,6 +42,7 @@ func AllTypes() []any { pkg.OpamPackage{}, pkg.PhpComposerInstalledEntry{}, pkg.PhpComposerLockEntry{}, + pkg.PhpPearEntry{}, pkg.PhpPeclEntry{}, pkg.PortageEntry{}, pkg.PythonPackage{}, diff --git a/syft/internal/packagemetadata/names.go b/syft/internal/packagemetadata/names.go index dd4fb2c0b..0cbee1bde 100644 --- a/syft/internal/packagemetadata/names.go +++ b/syft/internal/packagemetadata/names.go @@ -96,6 +96,7 @@ var jsonTypes = makeJSONTypes( jsonNames(pkg.PhpComposerLockEntry{}, "php-composer-lock-entry", "PhpComposerJsonMetadata"), jsonNamesWithoutLookup(pkg.PhpComposerInstalledEntry{}, "php-composer-installed-entry", "PhpComposerJsonMetadata"), // the legacy value is split into two types, where the other is preferred jsonNames(pkg.PhpPeclEntry{}, "php-pecl-entry", "PhpPeclMetadata"), + jsonNames(pkg.PhpPearEntry{}, "php-pear-entry"), jsonNames(pkg.PortageEntry{}, "portage-db-entry", "PortageMetadata"), jsonNames(pkg.PythonPackage{}, "python-package", "PythonPackageMetadata"), jsonNames(pkg.PythonPipfileLockEntry{}, "python-pipfile-lock-entry", "PythonPipfileLockMetadata"), diff --git a/syft/pkg/cataloger/php/cataloger.go b/syft/pkg/cataloger/php/cataloger.go index 91e2e89b6..b5ee52ae6 100644 --- a/syft/pkg/cataloger/php/cataloger.go +++ b/syft/pkg/cataloger/php/cataloger.go @@ -23,8 +23,16 @@ func NewComposerLockCataloger() pkg.Cataloger { WithParserByGlobs(parseComposerLock, "**/composer.lock") } -// NewPeclCataloger returns a new cataloger for PHP PECL metadata“. +// NewPearCataloger returns a new cataloger for PHP Pear metadata (including Pecl metadata). +func NewPearCataloger() pkg.Cataloger { + return generic.NewCataloger("php-pear-serialized-cataloger"). + WithParserByGlobs(parsePear, "**/php/.registry/**/*.reg") +} + +// NewPeclCataloger returns a new cataloger for PHP Pecl metadata. Note: this will also catalog Pear metadata so should +// not be used in conjunction with the Pear Cataloger. +// Deprecated: please use NewPearCataloger instead. func NewPeclCataloger() pkg.Cataloger { return generic.NewCataloger("php-pecl-serialized-cataloger"). - WithParserByGlobs(parsePeclSerialized, "**/php/.registry/.channel.*/*.reg") + WithParserByGlobs(parsePecl, "**/php/.registry/.channel.*/*.reg") } diff --git a/syft/pkg/cataloger/php/cataloger_test.go b/syft/pkg/cataloger/php/cataloger_test.go index bdfd99e3a..0d944aa08 100644 --- a/syft/pkg/cataloger/php/cataloger_test.go +++ b/syft/pkg/cataloger/php/cataloger_test.go @@ -56,6 +56,31 @@ func Test_ComposerLockCataloger_Globs(t *testing.T) { } } +func Test_PearCataloger_Globs(t *testing.T) { + tests := []struct { + name string + fixture string + expected []string + }{ + { + name: "obtain pear files", + fixture: "test-fixtures/glob-paths", + expected: []string{ + "php/.registry/.channel.pecl.php.net/memcached.reg", + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + pkgtest.NewCatalogTester(). + FromDirectory(t, test.fixture). + ExpectsResolverContentQueries(test.expected). + TestCataloger(t, NewPearCataloger()) + }) + } +} + func Test_PeclCataloger_Globs(t *testing.T) { tests := []struct { name string @@ -63,7 +88,7 @@ func Test_PeclCataloger_Globs(t *testing.T) { expected []string }{ { - name: "obtain pecl files", + name: "obtain pear files", fixture: "test-fixtures/glob-paths", expected: []string{ "php/.registry/.channel.pecl.php.net/memcached.reg", diff --git a/syft/pkg/cataloger/php/package.go b/syft/pkg/cataloger/php/package.go index f35960373..de22ab5f3 100644 --- a/syft/pkg/cataloger/php/package.go +++ b/syft/pkg/cataloger/php/package.go @@ -14,7 +14,7 @@ func newComposerLockPackage(pd parsedLockData, indexLocation file.Location) pkg. Version: pd.Version, Locations: file.NewLocationSet(indexLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)), Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(indexLocation, pd.License...)...), - PURL: packageURL(pd.Name, pd.Version), + PURL: packageURLFromComposer(pd.Name, pd.Version), Language: pkg.PHP, Type: pkg.PhpComposerPkg, Metadata: pd.PhpComposerLockEntry, @@ -30,7 +30,7 @@ func newComposerInstalledPackage(pd parsedInstalledData, indexLocation file.Loca Version: pd.Version, Locations: file.NewLocationSet(indexLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)), Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(indexLocation, pd.License...)...), - PURL: packageURL(pd.Name, pd.Version), + PURL: packageURLFromComposer(pd.Name, pd.Version), Language: pkg.PHP, Type: pkg.PhpComposerPkg, Metadata: pd.PhpComposerInstalledEntry, @@ -40,23 +40,39 @@ func newComposerInstalledPackage(pd parsedInstalledData, indexLocation file.Loca return p } -func newPeclPackage(pd pkg.PhpPeclEntry, indexLocation file.Location) pkg.Package { +func newPearPackage(pd peclPearData, indexLocation file.Location) pkg.Package { p := pkg.Package{ Name: pd.Name, Version: pd.Version, Locations: file.NewLocationSet(indexLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)), Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(indexLocation, pd.License...)...), - PURL: packageURLFromPecl(pd.Name, pd.Version), + PURL: packageURLFromPear(pd.Name, pd.Channel, pd.Version), Language: pkg.PHP, - Type: pkg.PhpPeclPkg, - Metadata: pd, + Type: pkg.PhpPearPkg, + Metadata: pd.ToPear(), } p.SetID() return p } -func packageURL(name, version string) string { +func newPeclPackage(pd peclPearData, indexLocation file.Location) pkg.Package { + p := pkg.Package{ + Name: pd.Name, + Version: pd.Version, + Locations: file.NewLocationSet(indexLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)), + Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(indexLocation, pd.License...)...), + PURL: packageURLFromPear(pd.Name, pd.Channel, pd.Version), + Language: pkg.PHP, + Type: pkg.PhpPeclPkg, + Metadata: pd.ToPecl(), + } + + p.SetID() + return p +} + +func packageURLFromComposer(name, version string) string { var pkgName, vendor string fields := strings.Split(name, "/") switch len(fields) { @@ -82,10 +98,15 @@ func packageURL(name, version string) string { return pURL.ToString() } -func packageURLFromPecl(pkgName, version string) string { +func packageURLFromPear(pkgName, channel, version string) string { + namespace := channel + if namespace == "" { + namespace = "pecl.php.net" + } + pURL := packageurl.NewPackageURL( - "pecl", - "", + "pear", + namespace, pkgName, version, nil, diff --git a/syft/pkg/cataloger/php/package_test.go b/syft/pkg/cataloger/php/package_test.go index 63f4a735b..b1a65a60f 100644 --- a/syft/pkg/cataloger/php/package_test.go +++ b/syft/pkg/cataloger/php/package_test.go @@ -35,7 +35,7 @@ func Test_packageURL(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - actual := packageURL(test.packageName, test.packageVersion) + actual := packageURLFromComposer(test.packageName, test.packageVersion) if actual != test.expected { dmp := diffmatchpatch.New() diffs := dmp.DiffMain(test.expected, actual, true) @@ -45,22 +45,30 @@ func Test_packageURL(t *testing.T) { } } -func Test_packageURLFromPecl(t *testing.T) { +func Test_packageURLFromPear(t *testing.T) { tests := []struct { name string + channel string version string expected string }{ { name: "memcached", + channel: "pear.php.net", version: "3.2.0", - expected: "pkg:pecl/memcached@3.2.0", + expected: "pkg:pear/pear.php.net/memcached@3.2.0", + }, + { + name: "memcached", + channel: "", // important! + version: "3.2.0", + expected: "pkg:pear/pecl.php.net/memcached@3.2.0", }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - actual := packageURLFromPecl(test.name, test.version) + actual := packageURLFromPear(test.name, test.channel, test.version) if actual != test.expected { dmp := diffmatchpatch.New() diffs := dmp.DiffMain(test.expected, actual, true) diff --git a/syft/pkg/cataloger/php/parse_pecl_pear.go b/syft/pkg/cataloger/php/parse_pecl_pear.go new file mode 100644 index 000000000..ac67a0865 --- /dev/null +++ b/syft/pkg/cataloger/php/parse_pecl_pear.go @@ -0,0 +1,112 @@ +package php + +import ( + "context" + "fmt" + "io" + + "github.com/elliotchance/phpserialize" + + "github.com/anchore/syft/internal/unknown" + "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" +) + +type peclPearData struct { + Name string + Channel string + Version string + License []string +} + +func (p *peclPearData) ToPear() pkg.PhpPearEntry { + return pkg.PhpPearEntry{ + Name: p.Name, + Channel: p.Channel, + Version: p.Version, + License: p.License, + } +} + +func (p *peclPearData) ToPecl() pkg.PhpPeclEntry { + return pkg.PhpPeclEntry(p.ToPear()) +} + +func parsePecl(_ context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { + m, err := parsePeclPearSerialized(reader) + if err != nil { + return nil, nil, err + } + if m == nil { + return nil, nil, unknown.New(reader.Location, fmt.Errorf("no pecl package found")) + } + return []pkg.Package{newPeclPackage(*m, reader.Location)}, nil, nil +} + +func parsePear(_ context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { + m, err := parsePeclPearSerialized(reader) + if err != nil { + return nil, nil, err + } + if m == nil { + return nil, nil, unknown.New(reader.Location, fmt.Errorf("no pear package found")) + } + return []pkg.Package{newPearPackage(*m, reader.Location)}, nil, nil +} + +// parsePeclPearSerialized is a parser function for Pear metadata contents, returning "Default" php packages discovered. +func parsePeclPearSerialized(reader file.LocationReadCloser) (*peclPearData, error) { + data, err := io.ReadAll(reader) + + if err != nil { + return nil, fmt.Errorf("failed to read file: %w", err) + } + + metadata, err := phpserialize.UnmarshalAssociativeArray( + data, + ) + + if err != nil { + return nil, fmt.Errorf("failed to parse pear metadata file: %w", err) + } + + name, ok := metadata["name"].(string) + if !ok { + return nil, fmt.Errorf("failed to parse pear package name: %w", err) + } + + channel, ok := metadata["channel"].(string) + if !ok { + // this could be the v5 format + channel = "" + } + + version := readStruct(metadata, "version", "release") + license := readStruct(metadata, "license", "_content") + + return &peclPearData{ + Name: name, + Channel: channel, + Version: version, + License: []string{ + license, + }, + }, nil +} + +func readStruct(metadata any, fields ...string) string { + if len(fields) > 0 { + value, ok := metadata.(map[any]any) + if !ok { + return "" + } + return readStruct(value[fields[0]], fields[1:]...) + } + value, ok := metadata.(string) + if !ok { + return "" + } + return value +} diff --git a/syft/pkg/cataloger/php/parse_pecl_pear_test.go b/syft/pkg/cataloger/php/parse_pecl_pear_test.go new file mode 100644 index 000000000..c2a439bae --- /dev/null +++ b/syft/pkg/cataloger/php/parse_pecl_pear_test.go @@ -0,0 +1,139 @@ +package php + +import ( + "testing" + + "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" +) + +func TestParsePear(t *testing.T) { + tests := []struct { + name string + fixture string + expectedPkgs []pkg.Package + expectedRelationships []artifact.Relationship + }{ + { + name: "v6 format", + fixture: "test-fixtures/memcached-v6-format.reg", + expectedPkgs: []pkg.Package{ + { + Name: "memcached", + Version: "3.2.0", + PURL: "pkg:pear/pecl.php.net/memcached@3.2.0", + Locations: file.NewLocationSet(file.NewLocation("test-fixtures/memcached-v6-format.reg")), + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocations("PHP License", file.NewLocation("test-fixtures/memcached-v6-format.reg")), + ), + Language: pkg.PHP, + Type: pkg.PhpPearPkg, + Metadata: pkg.PhpPearEntry{ + Name: "memcached", + Channel: "pecl.php.net", + Version: "3.2.0", + License: []string{"PHP License"}, + }, + }, + }, + }, + { + name: "v5 format", + fixture: "test-fixtures/memcached-v5-format.reg", + expectedPkgs: []pkg.Package{ + { + Name: "memcached", + Version: "3.2.0", + PURL: "pkg:pear/pecl.php.net/memcached@3.2.0", + Locations: file.NewLocationSet(file.NewLocation("test-fixtures/memcached-v5-format.reg")), + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocations("PHP License", file.NewLocation("test-fixtures/memcached-v5-format.reg")), + ), + Language: pkg.PHP, + Type: pkg.PhpPearPkg, + Metadata: pkg.PhpPearEntry{ // important: missing channel + Name: "memcached", + Version: "3.2.0", + License: []string{"PHP License"}, + }, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + pkgtest.TestFileParser(t, tt.fixture, parsePear, tt.expectedPkgs, tt.expectedRelationships) + }) + } +} + +func TestParsePecl(t *testing.T) { + tests := []struct { + name string + fixture string + expectedPkgs []pkg.Package + expectedRelationships []artifact.Relationship + }{ + { + name: "v6 format", + fixture: "test-fixtures/memcached-v6-format.reg", + expectedPkgs: []pkg.Package{ + { + Name: "memcached", + Version: "3.2.0", + PURL: "pkg:pear/pecl.php.net/memcached@3.2.0", + Locations: file.NewLocationSet(file.NewLocation("test-fixtures/memcached-v6-format.reg")), + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocations("PHP License", file.NewLocation("test-fixtures/memcached-v6-format.reg")), + ), + Language: pkg.PHP, + Type: pkg.PhpPeclPkg, // important! + Metadata: pkg.PhpPeclEntry{ // important! + Name: "memcached", + Channel: "pecl.php.net", + Version: "3.2.0", + License: []string{"PHP License"}, + }, + }, + }, + }, + { + name: "v5 format", + fixture: "test-fixtures/memcached-v5-format.reg", + expectedPkgs: []pkg.Package{ + { + Name: "memcached", + Version: "3.2.0", + PURL: "pkg:pear/pecl.php.net/memcached@3.2.0", + Locations: file.NewLocationSet(file.NewLocation("test-fixtures/memcached-v5-format.reg")), + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocations("PHP License", file.NewLocation("test-fixtures/memcached-v5-format.reg")), + ), + Language: pkg.PHP, + Type: pkg.PhpPeclPkg, // important! + Metadata: pkg.PhpPeclEntry{ // important! + Name: "memcached", + Version: "3.2.0", + License: []string{"PHP License"}, + }, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + pkgtest.TestFileParser(t, tt.fixture, parsePecl, tt.expectedPkgs, tt.expectedRelationships) + }) + } +} + +func Test_corruptPecl(t *testing.T) { + pkgtest.NewCatalogTester(). + FromFile(t, "test-fixtures/glob-paths/php/.registry/.channel.pecl.php.net/memcached.reg"). + WithError(). + TestParser(t, parseComposerLock) +} diff --git a/syft/pkg/cataloger/php/parse_pecl_serialized.go b/syft/pkg/cataloger/php/parse_pecl_serialized.go deleted file mode 100644 index 7f48f2096..000000000 --- a/syft/pkg/cataloger/php/parse_pecl_serialized.go +++ /dev/null @@ -1,73 +0,0 @@ -package php - -import ( - "context" - "fmt" - "io" - - "github.com/elliotchance/phpserialize" - - "github.com/anchore/syft/internal/log" - "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" -) - -// parsePeclSerialized is a parser function for PECL metadata contents, returning "Default" php packages discovered. -func parsePeclSerialized(_ context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { - var pkgs []pkg.Package - data, err := io.ReadAll(reader) - - if err != nil { - return nil, nil, fmt.Errorf("failed to read file: %w", err) - } - - metadata, err := phpserialize.UnmarshalAssociativeArray( - data, - ) - - if err != nil { - return nil, nil, fmt.Errorf("failed to parse pecl metadata file: %w", err) - } - - name, ok := metadata["name"].(string) - if !ok { - return nil, nil, fmt.Errorf("failed to parse pecl package name: %w", err) - } - - version := readStruct(metadata, "version", "release") - license := readStruct(metadata, "license", "_content") - - pkgs = append( - pkgs, - newPeclPackage( - pkg.PhpPeclEntry{ - Name: name, - Version: version, - License: []string{ - license, - }, - }, - reader.Location, - ), - ) - - return pkgs, nil, nil -} - -func readStruct(metadata any, fields ...string) string { - if len(fields) > 0 { - value, ok := metadata.(map[any]any) - if !ok { - log.Tracef("unable to read '%s' from: %v", fields[0], metadata) - return "" - } - return readStruct(value[fields[0]], fields[1:]...) - } - value, ok := metadata.(string) - if !ok { - log.Tracef("unable to read value from: %v", metadata) - } - return value -} diff --git a/syft/pkg/cataloger/php/parse_pecl_serialized_test.go b/syft/pkg/cataloger/php/parse_pecl_serialized_test.go deleted file mode 100644 index 84f84ea72..000000000 --- a/syft/pkg/cataloger/php/parse_pecl_serialized_test.go +++ /dev/null @@ -1,42 +0,0 @@ -package php - -import ( - "testing" - - "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" -) - -func TestParsePeclSerialized(t *testing.T) { - var expectedRelationships []artifact.Relationship - fixture := "test-fixtures/memcached.reg" - locations := file.NewLocationSet(file.NewLocation(fixture)) - expectedPkgs := []pkg.Package{ - { - Name: "memcached", - Version: "3.2.0", - PURL: "pkg:pecl/memcached@3.2.0", - Locations: locations, - Licenses: pkg.NewLicenseSet( - pkg.NewLicenseFromLocations("PHP License", file.NewLocation(fixture)), - ), - Language: pkg.PHP, - Type: pkg.PhpPeclPkg, - Metadata: pkg.PhpPeclEntry{ - Name: "memcached", - Version: "3.2.0", - License: []string{"PHP License"}, - }, - }, - } - pkgtest.TestFileParser(t, fixture, parsePeclSerialized, expectedPkgs, expectedRelationships) -} - -func Test_corruptPecl(t *testing.T) { - pkgtest.NewCatalogTester(). - FromFile(t, "test-fixtures/glob-paths/php/.registry/.channel.pecl.php.net/memcached.reg"). - WithError(). - TestParser(t, parseComposerLock) -} diff --git a/syft/pkg/cataloger/php/test-fixtures/memcached.reg b/syft/pkg/cataloger/php/test-fixtures/memcached-v5-format.reg similarity index 100% rename from syft/pkg/cataloger/php/test-fixtures/memcached.reg rename to syft/pkg/cataloger/php/test-fixtures/memcached-v5-format.reg diff --git a/syft/pkg/cataloger/php/test-fixtures/memcached-v6-format.reg b/syft/pkg/cataloger/php/test-fixtures/memcached-v6-format.reg new file mode 100644 index 000000000..5592335eb --- /dev/null +++ b/syft/pkg/cataloger/php/test-fixtures/memcached-v6-format.reg @@ -0,0 +1 @@ +a:6:{s:4:"name";s:9:"memcached";s:7:"channel";s:12:"pecl.php.net";s:4:"date";s:10:"2022-01-11";s:4:"time";s:8:"15:23:47";s:7:"version";a:2:{s:7:"release";s:5:"3.2.0";s:3:"api";s:5:"3.2.0";}s:7:"license";a:2:{s:7:"attribs";a:1:{s:3:"uri";s:26:"http://www.php.net/license";}s:8:"_content";s:11:"PHP License";}} \ No newline at end of file diff --git a/syft/pkg/php.go b/syft/pkg/php.go index 0180d745e..2759a229c 100644 --- a/syft/pkg/php.go +++ b/syft/pkg/php.go @@ -37,8 +37,14 @@ type PhpComposerAuthors struct { Homepage string `json:"homepage,omitempty"` } -type PhpPeclEntry struct { +// PhpPeclEntry represents a single package entry found within php pecl metadata files. +// Deprecated: please use PhpPearEntry instead with the pear cataloger. +type PhpPeclEntry PhpPearEntry + +// PhpPearEntry represents a single package entry found within php pear metadata files. +type PhpPearEntry struct { Name string `json:"name"` + Channel string `json:"channel,omitempty"` Version string `json:"version"` License []string `json:"license,omitempty"` } diff --git a/syft/pkg/type.go b/syft/pkg/type.go index 749623bdb..d9090ca24 100644 --- a/syft/pkg/type.go +++ b/syft/pkg/type.go @@ -36,7 +36,8 @@ const ( NpmPkg Type = "npm" OpamPkg Type = "opam" PhpComposerPkg Type = "php-composer" - PhpPeclPkg Type = "php-pecl" + PhpPeclPkg Type = "php-pecl" // Deprecated: will be removed in syft v2.0 + PhpPearPkg Type = "php-pear" PortagePkg Type = "portage" PythonPkg Type = "python" Rpkg Type = "R-package" @@ -78,6 +79,7 @@ var AllPkgs = []Type{ OpamPkg, PhpComposerPkg, PhpPeclPkg, + PhpPearPkg, PortagePkg, PythonPkg, Rpkg, @@ -132,8 +134,8 @@ func (t Type) PackageURLType() string { return packageurl.TypeGeneric case PhpComposerPkg: return packageurl.TypeComposer - case PhpPeclPkg: - return "pecl" + case PhpPearPkg, PhpPeclPkg: + return "pear" case PythonPkg: return packageurl.TypePyPi case PortagePkg: @@ -192,8 +194,8 @@ func TypeByName(name string) Type { return DebPkg case packageurl.TypeComposer: return PhpComposerPkg - case "pecl": - return PhpPeclPkg + case "pear", "pecl": + return PhpPearPkg case packageurl.TypeGolang: return GoModulePkg case packageurl.TypeGem: diff --git a/syft/pkg/type_test.go b/syft/pkg/type_test.go index 1f0534868..d5ccd9469 100644 --- a/syft/pkg/type_test.go +++ b/syft/pkg/type_test.go @@ -63,8 +63,16 @@ func TestTypeFromPURL(t *testing.T) { expected: PhpComposerPkg, }, { - purl: "pkg:pecl/memcached@3.2.0", - expected: PhpPeclPkg, + purl: "pkg:pear/pecl.php.net/memcached@3.2.0", // pecl namespace + expected: PhpPearPkg, + }, + { + purl: "pkg:pear/pear.php.net/memcached@3.2.0", // pear namespace + expected: PhpPearPkg, + }, + { + purl: "pkg:pecl/pecl.php.net/memcached@3.2.0", // note: this is an invalid purl, but we will handle it anyway in case folks created the type pre-emptively + expected: PhpPearPkg, // we should still consider this a pear package }, { purl: "pkg:maven/org.apache.xmlgraphics/batik-anim@1.9.1?type=zip&classifier=dist", @@ -124,7 +132,7 @@ func TestTypeFromPURL(t *testing.T) { }, } - var pkgTypes []string + var pkgTypes = strset.New() var expectedTypes = strset.New() for _, ty := range AllPkgs { expectedTypes.Add(string(ty)) @@ -141,19 +149,19 @@ func TestTypeFromPURL(t *testing.T) { expectedTypes.Remove(string(WordpressPluginPkg)) expectedTypes.Remove(string(TerraformPkg)) expectedTypes.Remove(string(GraalVMNativeImagePkg)) + expectedTypes.Remove(string(PhpPeclPkg)) // we should always consider this a pear package for _, test := range tests { t.Run(string(test.expected), func(t *testing.T) { actual := TypeFromPURL(test.purl) if actual != "" { - pkgTypes = append(pkgTypes, string(actual)) + pkgTypes.Add(string(actual)) } assert.Equal(t, test.expected, actual) }) } - assert.ElementsMatch(t, expectedTypes.List(), pkgTypes, "missing one or more package types to test against (maybe a package type was added?)") - + assert.ElementsMatch(t, expectedTypes.List(), pkgTypes.List(), "missing one or more package types to test against (maybe a package type was added?)") }