From ada8f009d25b1e542a757b97e6f8e8604cc82174 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Tue, 7 May 2024 13:29:46 -0400 Subject: [PATCH] Add relationships for ALPM packages (arch linux) (#2851) * add alpm relationships Signed-off-by: Alex Goodman * tweak reader linter rule to check for reader impl Signed-off-by: Alex Goodman * update JSON schema with alpm dependency information Signed-off-by: Alex Goodman --------- Signed-off-by: Alex Goodman --- internal/constants.go | 2 +- schema/json/schema-16.0.8.json | 2378 +++++++++++++++++ schema/json/schema-latest.json | 14 +- syft/pkg/alpm.go | 2 + syft/pkg/cataloger/arch/cataloger.go | 88 +- syft/pkg/cataloger/arch/cataloger_test.go | 436 +-- syft/pkg/cataloger/arch/package.go | 7 +- syft/pkg/cataloger/arch/parse_alpm_db.go | 135 +- syft/pkg/cataloger/arch/parse_alpm_db_test.go | 177 +- .../var/lib/pacman/local/emacs-29.3-3/desc | 38 + .../var/lib/pacman/local/fuzzy-1.2-3/desc | 8 + .../var/lib/pacman/local/fuzzy-1.2-3/files | 6 + .../var/lib/pacman/local/gmp-6.2.1-2/desc | 2 +- .../var/lib/pacman/local/gmp-6.2.1-2/files | 0 .../var/lib/pacman/local/gmp-6.2.1-2/mtree | Bin .../var/lib/pacman/local/madeup-20.30-4/desc | 8 + .../pacman/local/tree-sitter-0.22.6-1/desc | 41 + test/rules/rules.go | 2 +- 18 files changed, 3078 insertions(+), 266 deletions(-) create mode 100644 schema/json/schema-16.0.8.json create mode 100644 syft/pkg/cataloger/arch/test-fixtures/installed/var/lib/pacman/local/emacs-29.3-3/desc create mode 100644 syft/pkg/cataloger/arch/test-fixtures/installed/var/lib/pacman/local/fuzzy-1.2-3/desc create mode 100644 syft/pkg/cataloger/arch/test-fixtures/installed/var/lib/pacman/local/fuzzy-1.2-3/files rename syft/pkg/cataloger/arch/test-fixtures/{gmp-fixture => installed}/var/lib/pacman/local/gmp-6.2.1-2/desc (93%) rename syft/pkg/cataloger/arch/test-fixtures/{gmp-fixture => installed}/var/lib/pacman/local/gmp-6.2.1-2/files (100%) rename syft/pkg/cataloger/arch/test-fixtures/{gmp-fixture => installed}/var/lib/pacman/local/gmp-6.2.1-2/mtree (100%) create mode 100644 syft/pkg/cataloger/arch/test-fixtures/installed/var/lib/pacman/local/madeup-20.30-4/desc create mode 100644 syft/pkg/cataloger/arch/test-fixtures/installed/var/lib/pacman/local/tree-sitter-0.22.6-1/desc diff --git a/internal/constants.go b/internal/constants.go index dce60e57d..9be975780 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.7" + JSONSchemaVersion = "16.0.8" ) diff --git a/schema/json/schema-16.0.8.json b/schema/json/schema-16.0.8.json new file mode 100644 index 000000000..030c7a46c --- /dev/null +++ b/schema/json/schema-16.0.8.json @@ -0,0 +1,2378 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "anchore.io/schema/syft/json/16.0.8/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" + ] + }, + "CConanFileEntry": { + "properties": { + "ref": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "CConanInfoEntry": { + "properties": { + "ref": { + "type": "string" + }, + "package_id": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "CConanLockEntry": { + "properties": { + "ref": { + "type": "string" + }, + "package_id": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "build_requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "py_requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "options": { + "$ref": "#/$defs/KeyValues" + }, + "path": { + "type": "string" + }, + "context": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "CConanLockV2Entry": { + "properties": { + "ref": { + "type": "string" + }, + "packageID": { + "type": "string" + }, + "username": { + "type": "string" + }, + "channel": { + "type": "string" + }, + "recipeRevision": { + "type": "string" + }, + "packageRevision": { + "type": "string" + }, + "timestamp": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "CPE": { + "properties": { + "cpe": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "type": "object", + "required": [ + "cpe" + ] + }, + "ClassifierMatch": { + "properties": { + "classifier": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Location" + } + }, + "type": "object", + "required": [ + "classifier", + "location" + ] + }, + "CocoaPodfileLockEntry": { + "properties": { + "checksum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "checksum" + ] + }, + "Coordinates": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "DartPubspecLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "hosted_url": { + "type": "string" + }, + "vcs_url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Descriptor": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "configuration": true + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Digest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algorithm", + "value" + ] + }, + "Document": { + "properties": { + "artifacts": { + "items": { + "$ref": "#/$defs/Package" + }, + "type": "array" + }, + "artifactRelationships": { + "items": { + "$ref": "#/$defs/Relationship" + }, + "type": "array" + }, + "files": { + "items": { + "$ref": "#/$defs/File" + }, + "type": "array" + }, + "source": { + "$ref": "#/$defs/Source" + }, + "distro": { + "$ref": "#/$defs/LinuxRelease" + }, + "descriptor": { + "$ref": "#/$defs/Descriptor" + }, + "schema": { + "$ref": "#/$defs/Schema" + } + }, + "type": "object", + "required": [ + "artifacts", + "artifactRelationships", + "source", + "distro", + "descriptor", + "schema" + ] + }, + "DotnetDepsEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "path": { + "type": "string" + }, + "sha512": { + "type": "string" + }, + "hashPath": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "path", + "sha512", + "hashPath" + ] + }, + "DotnetPortableExecutableEntry": { + "properties": { + "assemblyVersion": { + "type": "string" + }, + "legalCopyright": { + "type": "string" + }, + "comments": { + "type": "string" + }, + "internalName": { + "type": "string" + }, + "companyName": { + "type": "string" + }, + "productName": { + "type": "string" + }, + "productVersion": { + "type": "string" + } + }, + "type": "object", + "required": [ + "assemblyVersion", + "legalCopyright", + "companyName", + "productName", + "productVersion" + ] + }, + "DpkgDbEntry": { + "properties": { + "package": { + "type": "string" + }, + "source": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "installedSize": { + "type": "integer" + }, + "provides": { + "items": { + "type": "string" + }, + "type": "array" + }, + "depends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "preDepends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "files": { + "items": { + "$ref": "#/$defs/DpkgFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "source", + "version", + "sourceVersion", + "architecture", + "maintainer", + "installedSize", + "files" + ] + }, + "DpkgFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "isConfigFile": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "path", + "isConfigFile" + ] + }, + "ELFSecurityFeatures": { + "properties": { + "symbolTableStripped": { + "type": "boolean" + }, + "stackCanary": { + "type": "boolean" + }, + "nx": { + "type": "boolean" + }, + "relRO": { + "type": "string" + }, + "pie": { + "type": "boolean" + }, + "dso": { + "type": "boolean" + }, + "safeStack": { + "type": "boolean" + }, + "cfi": { + "type": "boolean" + }, + "fortify": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "symbolTableStripped", + "nx", + "relRO", + "pie", + "dso" + ] + }, + "ElfBinaryPackageNoteJsonPayload": { + "properties": { + "type": { + "type": "string" + }, + "vendor": { + "type": "string" + }, + "system": { + "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" + } + }, + "type": "object", + "required": [ + "id", + "location" + ] + }, + "FileLicense": { + "properties": { + "value": { + "type": "string" + }, + "spdxExpression": { + "type": "string" + }, + "type": { + "type": "string" + }, + "evidence": { + "$ref": "#/$defs/FileLicenseEvidence" + } + }, + "type": "object", + "required": [ + "value", + "spdxExpression", + "type" + ] + }, + "FileLicenseEvidence": { + "properties": { + "confidence": { + "type": "integer" + }, + "offset": { + "type": "integer" + }, + "extent": { + "type": "integer" + } + }, + "type": "object", + "required": [ + "confidence", + "offset", + "extent" + ] + }, + "FileMetadataEntry": { + "properties": { + "mode": { + "type": "integer" + }, + "type": { + "type": "string" + }, + "linkDestination": { + "type": "string" + }, + "userID": { + "type": "integer" + }, + "groupID": { + "type": "integer" + }, + "mimeType": { + "type": "string" + }, + "size": { + "type": "integer" + } + }, + "type": "object", + "required": [ + "mode", + "type", + "userID", + "groupID", + "mimeType", + "size" + ] + }, + "GoModuleBuildinfoEntry": { + "properties": { + "goBuildSettings": { + "$ref": "#/$defs/KeyValues" + }, + "goCompiledVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "h1Digest": { + "type": "string" + }, + "mainModule": { + "type": "string" + }, + "goCryptoSettings": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "goCompiledVersion", + "architecture" + ] + }, + "GoModuleEntry": { + "properties": { + "h1Digest": { + "type": "string" + } + }, + "type": "object" + }, + "HaskellHackageStackEntry": { + "properties": { + "pkgHash": { + "type": "string" + } + }, + "type": "object" + }, + "HaskellHackageStackLockEntry": { + "properties": { + "pkgHash": { + "type": "string" + }, + "snapshotURL": { + "type": "string" + } + }, + "type": "object" + }, + "IDLikes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "JavaArchive": { + "properties": { + "virtualPath": { + "type": "string" + }, + "manifest": { + "$ref": "#/$defs/JavaManifest" + }, + "pomProperties": { + "$ref": "#/$defs/JavaPomProperties" + }, + "pomProject": { + "$ref": "#/$defs/JavaPomProject" + }, + "digest": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "virtualPath" + ] + }, + "JavaManifest": { + "properties": { + "main": { + "$ref": "#/$defs/KeyValues" + }, + "sections": { + "items": { + "$ref": "#/$defs/KeyValues" + }, + "type": "array" + } + }, + "type": "object" + }, + "JavaPomParent": { + "properties": { + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "type": "object", + "required": [ + "groupId", + "artifactId", + "version" + ] + }, + "JavaPomProject": { + "properties": { + "path": { + "type": "string" + }, + "parent": { + "$ref": "#/$defs/JavaPomParent" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "groupId", + "artifactId", + "version", + "name" + ] + }, + "JavaPomProperties": { + "properties": { + "path": { + "type": "string" + }, + "name": { + "type": "string" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "scope": { + "type": "string" + }, + "extraFields": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path", + "name", + "groupId", + "artifactId", + "version" + ] + }, + "JavascriptNpmPackage": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "author": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + }, + "private": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "homepage", + "description", + "url", + "private" + ] + }, + "JavascriptNpmPackageLockEntry": { + "properties": { + "resolved": { + "type": "string" + }, + "integrity": { + "type": "string" + } + }, + "type": "object", + "required": [ + "resolved", + "integrity" + ] + }, + "JavascriptYarnLockEntry": { + "properties": { + "resolved": { + "type": "string" + }, + "integrity": { + "type": "string" + } + }, + "type": "object", + "required": [ + "resolved", + "integrity" + ] + }, + "KeyValue": { + "properties": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "key", + "value" + ] + }, + "KeyValues": { + "items": { + "$ref": "#/$defs/KeyValue" + }, + "type": "array" + }, + "License": { + "properties": { + "value": { + "type": "string" + }, + "spdxExpression": { + "type": "string" + }, + "type": { + "type": "string" + }, + "urls": { + "items": { + "type": "string" + }, + "type": "array" + }, + "locations": { + "items": { + "$ref": "#/$defs/Location" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "value", + "spdxExpression", + "type", + "urls", + "locations" + ] + }, + "LinuxKernelArchive": { + "properties": { + "name": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "version": { + "type": "string" + }, + "extendedVersion": { + "type": "string" + }, + "buildTime": { + "type": "string" + }, + "author": { + "type": "string" + }, + "format": { + "type": "string" + }, + "rwRootFS": { + "type": "boolean" + }, + "swapDevice": { + "type": "integer" + }, + "rootDevice": { + "type": "integer" + }, + "videoMode": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "architecture", + "version" + ] + }, + "LinuxKernelModule": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "path": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "license": { + "type": "string" + }, + "kernelVersion": { + "type": "string" + }, + "versionMagic": { + "type": "string" + }, + "parameters": { + "patternProperties": { + ".*": { + "$ref": "#/$defs/LinuxKernelModuleParameter" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "LinuxKernelModuleParameter": { + "properties": { + "type": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "type": "object" + }, + "LinuxRelease": { + "properties": { + "prettyName": { + "type": "string" + }, + "name": { + "type": "string" + }, + "id": { + "type": "string" + }, + "idLike": { + "$ref": "#/$defs/IDLikes" + }, + "version": { + "type": "string" + }, + "versionID": { + "type": "string" + }, + "versionCodename": { + "type": "string" + }, + "buildID": { + "type": "string" + }, + "imageID": { + "type": "string" + }, + "imageVersion": { + "type": "string" + }, + "variant": { + "type": "string" + }, + "variantID": { + "type": "string" + }, + "homeURL": { + "type": "string" + }, + "supportURL": { + "type": "string" + }, + "bugReportURL": { + "type": "string" + }, + "privacyPolicyURL": { + "type": "string" + }, + "cpeName": { + "type": "string" + }, + "supportEnd": { + "type": "string" + } + }, + "type": "object" + }, + "Location": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + }, + "accessPath": { + "type": "string" + }, + "annotations": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path", + "accessPath" + ] + }, + "MicrosoftKbPatch": { + "properties": { + "product_id": { + "type": "string" + }, + "kb": { + "type": "string" + } + }, + "type": "object", + "required": [ + "product_id", + "kb" + ] + }, + "NixStoreEntry": { + "properties": { + "outputHash": { + "type": "string" + }, + "output": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "outputHash", + "files" + ] + }, + "Package": { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "type": { + "type": "string" + }, + "foundBy": { + "type": "string" + }, + "locations": { + "items": { + "$ref": "#/$defs/Location" + }, + "type": "array" + }, + "licenses": { + "$ref": "#/$defs/licenses" + }, + "language": { + "type": "string" + }, + "cpes": { + "$ref": "#/$defs/cpes" + }, + "purl": { + "type": "string" + }, + "metadataType": { + "type": "string" + }, + "metadata": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "#/$defs/AlpmDbEntry" + }, + { + "$ref": "#/$defs/ApkDbEntry" + }, + { + "$ref": "#/$defs/BinarySignature" + }, + { + "$ref": "#/$defs/CConanFileEntry" + }, + { + "$ref": "#/$defs/CConanInfoEntry" + }, + { + "$ref": "#/$defs/CConanLockEntry" + }, + { + "$ref": "#/$defs/CConanLockV2Entry" + }, + { + "$ref": "#/$defs/CocoaPodfileLockEntry" + }, + { + "$ref": "#/$defs/DartPubspecLockEntry" + }, + { + "$ref": "#/$defs/DotnetDepsEntry" + }, + { + "$ref": "#/$defs/DotnetPortableExecutableEntry" + }, + { + "$ref": "#/$defs/DpkgDbEntry" + }, + { + "$ref": "#/$defs/ElfBinaryPackageNoteJsonPayload" + }, + { + "$ref": "#/$defs/ElixirMixLockEntry" + }, + { + "$ref": "#/$defs/ErlangRebarLockEntry" + }, + { + "$ref": "#/$defs/GoModuleBuildinfoEntry" + }, + { + "$ref": "#/$defs/GoModuleEntry" + }, + { + "$ref": "#/$defs/HaskellHackageStackEntry" + }, + { + "$ref": "#/$defs/HaskellHackageStackLockEntry" + }, + { + "$ref": "#/$defs/JavaArchive" + }, + { + "$ref": "#/$defs/JavascriptNpmPackage" + }, + { + "$ref": "#/$defs/JavascriptNpmPackageLockEntry" + }, + { + "$ref": "#/$defs/JavascriptYarnLockEntry" + }, + { + "$ref": "#/$defs/LinuxKernelArchive" + }, + { + "$ref": "#/$defs/LinuxKernelModule" + }, + { + "$ref": "#/$defs/MicrosoftKbPatch" + }, + { + "$ref": "#/$defs/NixStoreEntry" + }, + { + "$ref": "#/$defs/PhpComposerInstalledEntry" + }, + { + "$ref": "#/$defs/PhpComposerLockEntry" + }, + { + "$ref": "#/$defs/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/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" + ] + }, + "PhpPeclEntry": { + "properties": { + "name": { + "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" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "authorEmail", + "platform", + "sitePackagesRootPath" + ] + }, + "PythonPipRequirementsEntry": { + "properties": { + "name": { + "type": "string" + }, + "extras": { + "items": { + "type": "string" + }, + "type": "array" + }, + "versionConstraint": { + "type": "string" + }, + "url": { + "type": "string" + }, + "markers": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "versionConstraint" + ] + }, + "PythonPipfileLockEntry": { + "properties": { + "hashes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "index": { + "type": "string" + } + }, + "type": "object", + "required": [ + "hashes", + "index" + ] + }, + "PythonPoetryLockEntry": { + "properties": { + "index": { + "type": "string" + } + }, + "type": "object", + "required": [ + "index" + ] + }, + "RDescription": { + "properties": { + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "url": { + "items": { + "type": "string" + }, + "type": "array" + }, + "repository": { + "type": "string" + }, + "built": { + "type": "string" + }, + "needsCompilation": { + "type": "boolean" + }, + "imports": { + "items": { + "type": "string" + }, + "type": "array" + }, + "depends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "suggests": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "Relationship": { + "properties": { + "parent": { + "type": "string" + }, + "child": { + "type": "string" + }, + "type": { + "type": "string" + }, + "metadata": true + }, + "type": "object", + "required": [ + "parent", + "child", + "type" + ] + }, + "RpmArchive": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "epoch": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "architecture": { + "type": "string" + }, + "release": { + "type": "string" + }, + "sourceRpm": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "vendor": { + "type": "string" + }, + "modularityLabel": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/RpmFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "epoch", + "architecture", + "release", + "sourceRpm", + "size", + "vendor", + "files" + ] + }, + "RpmDbEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "epoch": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "architecture": { + "type": "string" + }, + "release": { + "type": "string" + }, + "sourceRpm": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "vendor": { + "type": "string" + }, + "modularityLabel": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/RpmFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "epoch", + "architecture", + "release", + "sourceRpm", + "size", + "vendor", + "files" + ] + }, + "RpmFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "mode": { + "type": "integer" + }, + "size": { + "type": "integer" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "userName": { + "type": "string" + }, + "groupName": { + "type": "string" + }, + "flags": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "mode", + "size", + "digest", + "userName", + "groupName", + "flags" + ] + }, + "RubyGemspec": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "type": "string" + }, + "type": "array" + }, + "homepage": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "RustCargoAuditEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source" + ] + }, + "RustCargoLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "type": "string" + }, + "checksum": { + "type": "string" + }, + "dependencies": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "checksum", + "dependencies" + ] + }, + "Schema": { + "properties": { + "version": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "version", + "url" + ] + }, + "Source": { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "type": { + "type": "string" + }, + "metadata": true + }, + "type": "object", + "required": [ + "id", + "name", + "version", + "type", + "metadata" + ] + }, + "SwiftPackageManagerLockEntry": { + "properties": { + "revision": { + "type": "string" + } + }, + "type": "object", + "required": [ + "revision" + ] + }, + "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 349553661..030c7a46c 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.7/document", + "$id": "anchore.io/schema/syft/json/16.0.8/document", "$ref": "#/$defs/Document", "$defs": { "AlpmDbEntry": { @@ -46,6 +46,18 @@ "$ref": "#/$defs/AlpmFileRecord" }, "type": "array" + }, + "provides": { + "items": { + "type": "string" + }, + "type": "array" + }, + "depends": { + "items": { + "type": "string" + }, + "type": "array" } }, "type": "object", diff --git a/syft/pkg/alpm.go b/syft/pkg/alpm.go index 1f9873f30..3a8cf8b16 100644 --- a/syft/pkg/alpm.go +++ b/syft/pkg/alpm.go @@ -27,6 +27,8 @@ type AlpmDBEntry struct { Reason int `mapstructure:"reason" json:"reason"` Files []AlpmFileRecord `mapstructure:"files" json:"files"` Backup []AlpmFileRecord `mapstructure:"backup" json:"backup"` + Provides []string `mapstructure:"provides" json:"provides,omitempty"` + Depends []string `mapstructure:"depends" json:"depends,omitempty"` } type AlpmFileRecord struct { diff --git a/syft/pkg/cataloger/arch/cataloger.go b/syft/pkg/cataloger/arch/cataloger.go index 70e8f7033..920dda60a 100644 --- a/syft/pkg/cataloger/arch/cataloger.go +++ b/syft/pkg/cataloger/arch/cataloger.go @@ -4,12 +4,96 @@ Package arch provides a concrete Cataloger implementations for packages relating package arch import ( + "context" + "strings" + + "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" ) +type cataloger struct { + *generic.Cataloger +} + // NewDBCataloger returns a new cataloger object initialized for arch linux pacman database flat-file stores. func NewDBCataloger() pkg.Cataloger { - return generic.NewCataloger("alpm-db-cataloger"). - WithParserByGlobs(parseAlpmDB, pkg.AlpmDBGlob) + return cataloger{ + Cataloger: generic.NewCataloger("alpm-db-cataloger"). + WithParserByGlobs(parseAlpmDB, pkg.AlpmDBGlob), + } +} + +func (c cataloger) Catalog(ctx context.Context, resolver file.Resolver) ([]pkg.Package, []artifact.Relationship, error) { + pkgs, rels, err := c.Cataloger.Catalog(ctx, resolver) + if err != nil { + return nil, nil, err + } + + rels = append(rels, associateRelationships(pkgs)...) + + return pkgs, rels, nil +} + +// associateRelationships will create relationships between packages based on the "Depends" and "Provides" +// fields for installed packages. If there is an installed package that has a dependency that is (somehow) not installed, +// then that relationship (between the installed and uninstalled package) will NOT be created. +func associateRelationships(pkgs []pkg.Package) (relationships []artifact.Relationship) { + // map["provides" + "package"] -> packages that provide that package + lookup := make(map[string][]pkg.Package) + + // read providers and add lookup keys as needed + for _, p := range pkgs { + meta, ok := p.Metadata.(pkg.AlpmDBEntry) + if !ok { + log.Warnf("cataloger failed to extract alpm 'provides' metadata for package %+v", p.Name) + continue + } + // allow for lookup by package name + lookup[p.Name] = append(lookup[p.Name], p) + + for _, provides := range meta.Provides { + // allow for lookup by exact specification + lookup[provides] = append(lookup[provides], p) + + // allow for lookup by library name only + k := stripVersionSpecifier(provides) + lookup[k] = append(lookup[k], p) + } + } + + // read "Depends" and match with provider keys + for _, p := range pkgs { + meta, ok := p.Metadata.(pkg.AlpmDBEntry) + if !ok { + log.Warnf("cataloger failed to extract alpm 'dependency' metadata for package %+v", p.Name) + continue + } + + for _, dep := range meta.Depends { + for _, depPkg := range lookup[dep] { + relationships = append(relationships, artifact.Relationship{ + From: depPkg, + To: p, + Type: artifact.DependencyOfRelationship, + }) + } + } + } + return relationships +} + +func stripVersionSpecifier(s string) string { + // examples: + // gcc-libs --> gcc-libs + // libtree-sitter.so=0-64 --> libtree-sitter.so + + items := strings.Split(s, "=") + if len(items) == 0 { + return s + } + + return strings.TrimSpace(items[0]) } diff --git a/syft/pkg/cataloger/arch/cataloger_test.go b/syft/pkg/cataloger/arch/cataloger_test.go index 91a735aeb..0badcb828 100644 --- a/syft/pkg/cataloger/arch/cataloger_test.go +++ b/syft/pkg/cataloger/arch/cataloger_test.go @@ -12,169 +12,289 @@ import ( ) func TestAlpmCataloger(t *testing.T) { - dbLocation := file.NewLocation("var/lib/pacman/local/gmp-6.2.1-2/desc") - expectedPkgs := []pkg.Package{ - { - Name: "gmp", - Version: "6.2.1-2", - Type: pkg.AlpmPkg, - FoundBy: "alpm-db-cataloger", - Licenses: pkg.NewLicenseSet( - pkg.NewLicenseFromLocations("LGPL3", dbLocation), - pkg.NewLicenseFromLocations("GPL", dbLocation), - ), - Locations: file.NewLocationSet(dbLocation), - CPEs: nil, - PURL: "", - Metadata: pkg.AlpmDBEntry{ - BasePackage: "gmp", - Package: "gmp", - Version: "6.2.1-2", - Description: "A free library for arbitrary precision arithmetic", - Architecture: "x86_64", - Size: 1044438, - Packager: "Antonio Rojas ", - URL: "https://gmplib.org/", - Validation: "pgp", - Reason: 1, - Files: []pkg.AlpmFileRecord{ - { - Path: "/usr", - Type: "dir", - Digests: []file.Digest{}, - }, - { - Path: "/usr/include", - Type: "dir", - Digests: []file.Digest{}, - }, - { - Path: "/usr/include/gmp.h", - Size: "84140", - Digests: []file.Digest{ - {Algorithm: "md5", Value: "76595f70565c72550eb520809bf86856"}, - {Algorithm: "sha256", Value: "91a614b9202453153fe3b7512d15e89659108b93ce8841c8e13789eb85da9e3a"}, - }, - }, - { - Path: "/usr/include/gmpxx.h", - Size: "129113", - Digests: []file.Digest{ - {Algorithm: "md5", Value: "ea3d21de4bcf7c696799c5c55dd3655b"}, - {Algorithm: "sha256", Value: "0011ae411a0bc1030e07d968b32fdc1343f5ac2a17b7d28f493e7976dde2ac82"}, - }, - }, - { - Path: "/usr/lib", - Type: "dir", - Digests: []file.Digest{}, - }, - { - Path: "/usr/lib/libgmp.so", - Type: "link", - Link: "libgmp.so.10.4.1", - Digests: []file.Digest{}, - }, - { - Path: "/usr/lib/libgmp.so.10", - Type: "link", - Link: "libgmp.so.10.4.1", - Digests: []file.Digest{}, - }, - { - Path: "/usr/lib/libgmp.so.10.4.1", - Size: "663224", - Digests: []file.Digest{ - {Algorithm: "md5", Value: "d6d03eadacdd9048d5b2adf577e9d722"}, - {Algorithm: "sha256", Value: "39898bd3d8d6785222432fa8b8aef7ce3b7e5bbfc66a52b7c0da09bed4adbe6a"}, - }, - }, - { - Path: "/usr/lib/libgmpxx.so", - Type: "link", - Link: "libgmpxx.so.4.6.1", - Digests: []file.Digest{}, - }, - { - Path: "/usr/lib/libgmpxx.so.4", - Type: "link", - Link: "libgmpxx.so.4.6.1", - Digests: []file.Digest{}, - }, - { - Path: "/usr/lib/libgmpxx.so.4.6.1", - Size: "30680", - Digests: []file.Digest{ - {Algorithm: "md5", Value: "dd5f0c4d635fa599fa7f4339c0e8814d"}, - {Algorithm: "sha256", Value: "0ef67cbde4841f58d2e4b41f59425eb87c9eeaf4e649c060b326342c53bedbec"}, - }, - }, - { - Path: "/usr/lib/pkgconfig", - Type: "dir", - Digests: []file.Digest{}, - }, - { - Path: "/usr/lib/pkgconfig/gmp.pc", - Size: "245", - Digests: []file.Digest{ - {Algorithm: "md5", Value: "a91a9f1b66218cb77b9cd2cdf341756d"}, - {Algorithm: "sha256", Value: "4e9de547a48c4e443781e9fa702a1ec5a23ee28b4bc520306cff2541a855be37"}, - }, - }, - { - Path: "/usr/lib/pkgconfig/gmpxx.pc", - Size: "280", - Digests: []file.Digest{ - {Algorithm: "md5", Value: "8c0f54e987934352177a6a30a811b001"}, - {Algorithm: "sha256", Value: "fc5dbfbe75977057ba50953d94b9daecf696c9fdfe5b94692b832b44ecca871b"}, - }, - }, - { - Path: "/usr/share", - Type: "dir", - Digests: []file.Digest{}, - }, - { - Path: "/usr/share/info", - Type: "dir", - Digests: []file.Digest{}, - }, - { - Path: "/usr/share/info/gmp.info-1.gz", - Size: "85892", - Digests: []file.Digest{ - {Algorithm: "md5", Value: "63304d4d2f0247fb8a999fae66a81c19"}, - {Algorithm: "sha256", Value: "86288c1531a2789db5da8b9838b5cde4db07bda230ae11eba23a1f33698bd14e"}, - }, - }, - { - Path: "/usr/share/info/gmp.info-2.gz", - Size: "48484", - Digests: []file.Digest{ - {Algorithm: "md5", Value: "4bb0dadec416d305232cac6eae712ff7"}, - {Algorithm: "sha256", Value: "b7443c1b529588d98a074266087f79b595657ac7274191c34b10a9ceedfa950e"}, - }, - }, - { - Path: "/usr/share/info/gmp.info.gz", - Size: "2380", - Digests: []file.Digest{ - {Algorithm: "md5", Value: "cf6880fb0d862ee1da0d13c3831b5720"}, - {Algorithm: "sha256", Value: "a13c8eecda3f3e5ad1e09773e47a9686f07d9d494eaddf326f3696bbef1548fd"}, - }, - }, - }, - Backup: []pkg.AlpmFileRecord{}, - }, + gmpDbLocation := file.NewLocation("var/lib/pacman/local/gmp-6.2.1-2/desc") + treeSitterDbLocation := file.NewLocation("var/lib/pacman/local/tree-sitter-0.22.6-1/desc") + emacsDbLocation := file.NewLocation("var/lib/pacman/local/emacs-29.3-3/desc") + fuzzyDbLocation := file.NewLocation("var/lib/pacman/local/fuzzy-1.2-3/desc") + madeupDbLocation := file.NewLocation("var/lib/pacman/local/madeup-20.30-4/desc") + + treeSitterPkg := pkg.Package{ + Name: "tree-sitter", + Version: "0.22.6-1", + Type: pkg.AlpmPkg, + FoundBy: "alpm-db-cataloger", + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocations("MIT", treeSitterDbLocation), + ), + Locations: file.NewLocationSet(treeSitterDbLocation), + Metadata: pkg.AlpmDBEntry{ + BasePackage: "tree-sitter", + Package: "tree-sitter", + Version: "0.22.6-1", + Description: "Incremental parsing library", + Architecture: "x86_64", + Size: 223539, + Packager: "Daniel M. Capella ", + URL: "https://github.com/tree-sitter/tree-sitter", + Validation: "pgp", + Reason: 1, + Files: []pkg.AlpmFileRecord{}, + Backup: []pkg.AlpmFileRecord{}, + Provides: []string{"libtree-sitter.so=0-64"}, }, } - // TODO: relationships are not under test yet - var expectedRelationships []artifact.Relationship + emacsPkg := pkg.Package{ + Name: "emacs", + Version: "29.3-3", + Type: pkg.AlpmPkg, + FoundBy: "alpm-db-cataloger", + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocations("GPL3", emacsDbLocation), + ), + Locations: file.NewLocationSet(emacsDbLocation), + Metadata: pkg.AlpmDBEntry{ + BasePackage: "emacs", + Package: "emacs", + Version: "29.3-3", + Description: "The extensible, customizable, self-documenting real-time display editor", + Architecture: "x86_64", + Size: 126427862, + Packager: "Frederik Schwan ", + URL: "https://www.gnu.org/software/emacs/emacs.html", + Validation: "pgp", + Files: []pkg.AlpmFileRecord{}, + Backup: []pkg.AlpmFileRecord{}, + Depends: []string{"libtree-sitter.so=0-64"}, + }, + } + + fuzzyPkg := pkg.Package{ + Name: "fuzzy", + Version: "1.2-3", + Type: pkg.AlpmPkg, + FoundBy: "alpm-db-cataloger", + Locations: file.NewLocationSet( + fuzzyDbLocation, + file.NewLocation("var/lib/pacman/local/fuzzy-1.2-3/files"), + ), + Metadata: pkg.AlpmDBEntry{ + Package: "fuzzy", + Version: "1.2-3", + Files: []pkg.AlpmFileRecord{}, + Backup: []pkg.AlpmFileRecord{ + { + Path: "/etc/fuzzy.conf", + Digests: []file.Digest{ + {Algorithm: "md5", Value: "79fce043df7dfc676ae5ecb903762d8b"}, + }, + }, + }, + Depends: []string{"tree-sitter"}, + }, + } + + madeupPkg := pkg.Package{ + Name: "madeup", + Version: "20.30-4", + Type: pkg.AlpmPkg, + FoundBy: "alpm-db-cataloger", + Locations: file.NewLocationSet(madeupDbLocation), + Metadata: pkg.AlpmDBEntry{ + Package: "madeup", + Version: "20.30-4", + Files: []pkg.AlpmFileRecord{}, + Backup: []pkg.AlpmFileRecord{}, + Depends: []string{"libtree-sitter.so"}, + }, + } + + gmpPkg := pkg.Package{ + Name: "gmp", + Version: "6.2.1-2", + Type: pkg.AlpmPkg, + FoundBy: "alpm-db-cataloger", + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocations("LGPL3", gmpDbLocation), + pkg.NewLicenseFromLocations("GPL", gmpDbLocation), + ), + Locations: file.NewLocationSet( + gmpDbLocation, + file.NewLocation("var/lib/pacman/local/gmp-6.2.1-2/files"), + file.NewLocation("var/lib/pacman/local/gmp-6.2.1-2/mtree"), + ), + Metadata: pkg.AlpmDBEntry{ + BasePackage: "gmp", + Package: "gmp", + Version: "6.2.1-2", + Description: "A free library for arbitrary precision arithmetic", + Architecture: "x86_64", + Size: 1044438, + Packager: "Antonio Rojas ", + URL: "https://gmplib.org/", + Validation: "pgp", + Reason: 1, + Depends: []string{"gcc-libs", "sh", "libtree-sitter.so=1-64"}, + Files: []pkg.AlpmFileRecord{ + { + Path: "/usr", + Type: "dir", + Digests: []file.Digest{}, + }, + { + Path: "/usr/include", + Type: "dir", + Digests: []file.Digest{}, + }, + { + Path: "/usr/include/gmp.h", + Size: "84140", + Digests: []file.Digest{ + {Algorithm: "md5", Value: "76595f70565c72550eb520809bf86856"}, + {Algorithm: "sha256", Value: "91a614b9202453153fe3b7512d15e89659108b93ce8841c8e13789eb85da9e3a"}, + }, + }, + { + Path: "/usr/include/gmpxx.h", + Size: "129113", + Digests: []file.Digest{ + {Algorithm: "md5", Value: "ea3d21de4bcf7c696799c5c55dd3655b"}, + {Algorithm: "sha256", Value: "0011ae411a0bc1030e07d968b32fdc1343f5ac2a17b7d28f493e7976dde2ac82"}, + }, + }, + { + Path: "/usr/lib", + Type: "dir", + Digests: []file.Digest{}, + }, + { + Path: "/usr/lib/libgmp.so", + Type: "link", + Link: "libgmp.so.10.4.1", + Digests: []file.Digest{}, + }, + { + Path: "/usr/lib/libgmp.so.10", + Type: "link", + Link: "libgmp.so.10.4.1", + Digests: []file.Digest{}, + }, + { + Path: "/usr/lib/libgmp.so.10.4.1", + Size: "663224", + Digests: []file.Digest{ + {Algorithm: "md5", Value: "d6d03eadacdd9048d5b2adf577e9d722"}, + {Algorithm: "sha256", Value: "39898bd3d8d6785222432fa8b8aef7ce3b7e5bbfc66a52b7c0da09bed4adbe6a"}, + }, + }, + { + Path: "/usr/lib/libgmpxx.so", + Type: "link", + Link: "libgmpxx.so.4.6.1", + Digests: []file.Digest{}, + }, + { + Path: "/usr/lib/libgmpxx.so.4", + Type: "link", + Link: "libgmpxx.so.4.6.1", + Digests: []file.Digest{}, + }, + { + Path: "/usr/lib/libgmpxx.so.4.6.1", + Size: "30680", + Digests: []file.Digest{ + {Algorithm: "md5", Value: "dd5f0c4d635fa599fa7f4339c0e8814d"}, + {Algorithm: "sha256", Value: "0ef67cbde4841f58d2e4b41f59425eb87c9eeaf4e649c060b326342c53bedbec"}, + }, + }, + { + Path: "/usr/lib/pkgconfig", + Type: "dir", + Digests: []file.Digest{}, + }, + { + Path: "/usr/lib/pkgconfig/gmp.pc", + Size: "245", + Digests: []file.Digest{ + {Algorithm: "md5", Value: "a91a9f1b66218cb77b9cd2cdf341756d"}, + {Algorithm: "sha256", Value: "4e9de547a48c4e443781e9fa702a1ec5a23ee28b4bc520306cff2541a855be37"}, + }, + }, + { + Path: "/usr/lib/pkgconfig/gmpxx.pc", + Size: "280", + Digests: []file.Digest{ + {Algorithm: "md5", Value: "8c0f54e987934352177a6a30a811b001"}, + {Algorithm: "sha256", Value: "fc5dbfbe75977057ba50953d94b9daecf696c9fdfe5b94692b832b44ecca871b"}, + }, + }, + { + Path: "/usr/share", + Type: "dir", + Digests: []file.Digest{}, + }, + { + Path: "/usr/share/info", + Type: "dir", + Digests: []file.Digest{}, + }, + { + Path: "/usr/share/info/gmp.info-1.gz", + Size: "85892", + Digests: []file.Digest{ + {Algorithm: "md5", Value: "63304d4d2f0247fb8a999fae66a81c19"}, + {Algorithm: "sha256", Value: "86288c1531a2789db5da8b9838b5cde4db07bda230ae11eba23a1f33698bd14e"}, + }, + }, + { + Path: "/usr/share/info/gmp.info-2.gz", + Size: "48484", + Digests: []file.Digest{ + {Algorithm: "md5", Value: "4bb0dadec416d305232cac6eae712ff7"}, + {Algorithm: "sha256", Value: "b7443c1b529588d98a074266087f79b595657ac7274191c34b10a9ceedfa950e"}, + }, + }, + { + Path: "/usr/share/info/gmp.info.gz", + Size: "2380", + Digests: []file.Digest{ + {Algorithm: "md5", Value: "cf6880fb0d862ee1da0d13c3831b5720"}, + {Algorithm: "sha256", Value: "a13c8eecda3f3e5ad1e09773e47a9686f07d9d494eaddf326f3696bbef1548fd"}, + }, + }, + }, + Backup: []pkg.AlpmFileRecord{}, + }, + } + + expectedPkgs := []pkg.Package{ + treeSitterPkg, + emacsPkg, + fuzzyPkg, + madeupPkg, + gmpPkg, + } + + expectedRelationships := []artifact.Relationship{ + { // exact spec lookup + From: treeSitterPkg, + To: emacsPkg, + Type: artifact.DependencyOfRelationship, + }, + { // package name lookup + From: treeSitterPkg, + To: fuzzyPkg, + Type: artifact.DependencyOfRelationship, + }, + { // library name lookup + From: treeSitterPkg, + To: madeupPkg, + Type: artifact.DependencyOfRelationship, + }, + } pkgtest.NewCatalogTester(). - FromDirectory(t, "test-fixtures/gmp-fixture"). + FromDirectory(t, "test-fixtures/installed"). WithCompareOptions(cmpopts.IgnoreFields(pkg.AlpmFileRecord{}, "Time")). Expects(expectedPkgs, expectedRelationships). TestCataloger(t, NewDBCataloger()) diff --git a/syft/pkg/cataloger/arch/package.go b/syft/pkg/cataloger/arch/package.go index f20a192f2..88b557c76 100644 --- a/syft/pkg/cataloger/arch/package.go +++ b/syft/pkg/cataloger/arch/package.go @@ -9,13 +9,16 @@ import ( "github.com/anchore/syft/syft/pkg" ) -func newPackage(m *parsedData, release *linux.Release, dbLocation file.Location) pkg.Package { +func newPackage(m *parsedData, release *linux.Release, dbLocation file.Location, otherLocations ...file.Location) pkg.Package { licenseCandidates := strings.Split(m.Licenses, "\n") + locs := file.NewLocationSet(dbLocation) + locs.Add(otherLocations...) + p := pkg.Package{ Name: m.Package, Version: m.Version, - Locations: file.NewLocationSet(dbLocation), + Locations: locs, Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(dbLocation.WithoutAnnotations(), licenseCandidates...)...), Type: pkg.AlpmPkg, PURL: packageURL(m, release), diff --git a/syft/pkg/cataloger/arch/parse_alpm_db.go b/syft/pkg/cataloger/arch/parse_alpm_db.go index dc97cbf7c..cb068df81 100644 --- a/syft/pkg/cataloger/arch/parse_alpm_db.go +++ b/syft/pkg/cataloger/arch/parse_alpm_db.go @@ -6,6 +6,7 @@ import ( "context" "fmt" "io" + "path" "path/filepath" "strconv" "strings" @@ -15,6 +16,7 @@ import ( "github.com/vbatts/go-mtree" "github.com/anchore/syft/internal" + "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/pkg" @@ -44,33 +46,26 @@ func parseAlpmDB(_ context.Context, resolver file.Resolver, env *generic.Environ return nil, nil, err } - base := filepath.Dir(reader.RealPath) - r, err := getFileReader(filepath.Join(base, "mtree"), resolver) - if err != nil { - return nil, nil, err + if data == nil { + return nil, nil, nil } - pkgFiles, err := parseMtree(r) - if err != nil { - return nil, nil, err - } + base := path.Dir(reader.RealPath) // replace the files found the pacman database with the files from the mtree These contain more metadata and // thus more useful. - // TODO: probably want to use MTREE and PKGINFO here - data.Files = pkgFiles + files, fileLoc := fetchPkgFiles(base, resolver) + backups, backupLoc := fetchBackupFiles(base, resolver) - // We only really do this to get any backup database entries from the files database - files := filepath.Join(base, "files") - _, err = getFileReader(files, resolver) - if err != nil { - return nil, nil, err + var locs []file.Location + if fileLoc != nil { + locs = append(locs, fileLoc.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.SupportingEvidenceAnnotation)) + data.Files = files } - filesMetadata, err := parseAlpmDBEntry(reader) - if err != nil { - return nil, nil, err - } else if filesMetadata != nil { - data.Backup = filesMetadata.Backup + + if backupLoc != nil { + locs = append(locs, backupLoc.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.SupportingEvidenceAnnotation)) + data.Backup = backups } if data.Package == "" { @@ -82,10 +77,67 @@ func parseAlpmDB(_ context.Context, resolver file.Resolver, env *generic.Environ data, env.LinuxRelease, reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), + locs..., ), }, nil, nil } +func fetchPkgFiles(base string, resolver file.Resolver) ([]pkg.AlpmFileRecord, *file.Location) { + // TODO: probably want to use MTREE and PKGINFO here + target := path.Join(base, "mtree") + + loc, err := getLocation(target, resolver) + if err != nil { + log.WithFields("error", err, "path", target).Trace("failed to find mtree file") + return []pkg.AlpmFileRecord{}, nil + } + if loc == nil { + return []pkg.AlpmFileRecord{}, nil + } + + reader, err := resolver.FileContentsByLocation(*loc) + if err != nil { + return []pkg.AlpmFileRecord{}, nil + } + defer internal.CloseAndLogError(reader, loc.RealPath) + + pkgFiles, err := parseMtree(reader) + if err != nil { + log.WithFields("error", err, "path", target).Trace("failed to parse mtree file") + return []pkg.AlpmFileRecord{}, nil + } + return pkgFiles, loc +} + +func fetchBackupFiles(base string, resolver file.Resolver) ([]pkg.AlpmFileRecord, *file.Location) { + // We only really do this to get any backup database entries from the files database + target := filepath.Join(base, "files") + + loc, err := getLocation(target, resolver) + if err != nil { + log.WithFields("error", err, "path", target).Trace("failed to find alpm files") + return []pkg.AlpmFileRecord{}, nil + } + if loc == nil { + return []pkg.AlpmFileRecord{}, nil + } + + reader, err := resolver.FileContentsByLocation(*loc) + if err != nil { + return []pkg.AlpmFileRecord{}, nil + } + defer internal.CloseAndLogError(reader, loc.RealPath) + + filesMetadata, err := parseAlpmDBEntry(reader) + if err != nil { + return []pkg.AlpmFileRecord{}, nil + } + if filesMetadata != nil { + return filesMetadata.Backup, loc + } + return []pkg.AlpmFileRecord{}, loc +} + func parseAlpmDBEntry(reader io.Reader) (*parsedData, error) { scanner := newScanner(reader) metadata, err := parseDatabase(scanner) @@ -119,7 +171,7 @@ func newScanner(reader io.Reader) *bufio.Scanner { return scanner } -func getFileReader(path string, resolver file.Resolver) (io.Reader, error) { +func getLocation(path string, resolver file.Resolver) (*file.Location, error) { locs, err := resolver.FilesByPath(path) if err != nil { return nil, err @@ -128,13 +180,11 @@ func getFileReader(path string, resolver file.Resolver) (io.Reader, error) { if len(locs) == 0 { return nil, fmt.Errorf("could not find file: %s", path) } - // TODO: Should we maybe check if we found the file - dbContentReader, err := resolver.FileContentsByLocation(locs[0]) - if err != nil { - return nil, err + + if len(locs) > 1 { + log.WithFields("path", path).Trace("multiple files found for path, using first path") } - defer internal.CloseAndLogError(dbContentReader, locs[0].RealPath) - return dbContentReader, nil + return &locs[0], nil } func parseDatabase(b *bufio.Scanner) (*parsedData, error) { @@ -157,9 +207,9 @@ func parseDatabase(b *bufio.Scanner) (*parsedData, error) { case "files": var files []map[string]string for _, f := range strings.Split(value, "\n") { - path := fmt.Sprintf("/%s", f) - if ok := ignoredFiles[path]; !ok { - files = append(files, map[string]string{"path": path}) + p := fmt.Sprintf("/%s", f) + if ok := ignoredFiles[p]; !ok { + files = append(files, map[string]string{"path": p}) } } pkgFields[key] = files @@ -167,10 +217,10 @@ func parseDatabase(b *bufio.Scanner) (*parsedData, error) { var backup []map[string]interface{} for _, f := range strings.Split(value, "\n") { fields := strings.SplitN(f, "\t", 2) - path := fmt.Sprintf("/%s", fields[0]) - if ok := ignoredFiles[path]; !ok { + p := fmt.Sprintf("/%s", fields[0]) + if ok := ignoredFiles[p]; !ok { backup = append(backup, map[string]interface{}{ - "path": path, + "path": p, "digests": []file.Digest{{ Algorithm: "md5", Value: fields[1], @@ -178,6 +228,8 @@ func parseDatabase(b *bufio.Scanner) (*parsedData, error) { } } pkgFields[key] = backup + case "depends", "provides": + pkgFields[key] = processLibrarySpecs(value) case "reason": fallthrough case "size": @@ -193,6 +245,19 @@ func parseDatabase(b *bufio.Scanner) (*parsedData, error) { return parsePkgFiles(pkgFields) } +func processLibrarySpecs(value string) []string { + lines := strings.Split(value, "\n") + librarySpecs := make([]string, 0) + for _, line := range lines { + line = strings.TrimSpace(line) + if line == "" { + continue + } + librarySpecs = append(librarySpecs, line) + } + return librarySpecs +} + func parsePkgFiles(pkgFields map[string]interface{}) (*parsedData, error) { var entry parsedData if err := mapstructure.Decode(pkgFields, &entry); err != nil { @@ -203,6 +268,10 @@ func parsePkgFiles(pkgFields map[string]interface{}) (*parsedData, error) { entry.Backup = make([]pkg.AlpmFileRecord, 0) } + if entry.Files == nil { + entry.Files = make([]pkg.AlpmFileRecord, 0) + } + if entry.Package == "" && len(entry.Files) == 0 && len(entry.Backup) == 0 { return nil, nil } diff --git a/syft/pkg/cataloger/arch/parse_alpm_db_test.go b/syft/pkg/cataloger/arch/parse_alpm_db_test.go index 213ca4cce..10b367a35 100644 --- a/syft/pkg/cataloger/arch/parse_alpm_db_test.go +++ b/syft/pkg/cataloger/arch/parse_alpm_db_test.go @@ -17,74 +17,120 @@ func TestDatabaseParser(t *testing.T) { tests := []struct { name string fixture string - expected pkg.AlpmDBEntry + expected *parsedData }{ { - name: "test alpm database parsing", + name: "simple desc parsing", fixture: "test-fixtures/files", - expected: pkg.AlpmDBEntry{ - Backup: []pkg.AlpmFileRecord{ - { - Path: "/etc/pacman.conf", - Digests: []file.Digest{{ - Algorithm: "md5", - Value: "de541390e52468165b96511c4665bff4", - }}, + expected: &parsedData{ + AlpmDBEntry: pkg.AlpmDBEntry{ + Backup: []pkg.AlpmFileRecord{ + { + Path: "/etc/pacman.conf", + Digests: []file.Digest{{ + Algorithm: "md5", + Value: "de541390e52468165b96511c4665bff4", + }}, + }, + { + Path: "/etc/makepkg.conf", + Digests: []file.Digest{{ + Algorithm: "md5", + Value: "79fce043df7dfc676ae5ecb903762d8b", + }}, + }, }, - { - Path: "/etc/makepkg.conf", - Digests: []file.Digest{{ - Algorithm: "md5", - Value: "79fce043df7dfc676ae5ecb903762d8b", - }}, + Files: []pkg.AlpmFileRecord{ + { + Path: "/etc/", + }, + { + Path: "/etc/makepkg.conf", + }, + { + Path: "/etc/pacman.conf", + }, + { + Path: "/usr/", + }, + { + Path: "/usr/bin/", + }, + { + Path: "/usr/bin/makepkg", + }, + { + Path: "/usr/bin/makepkg-template", + }, + { + Path: "/usr/bin/pacman", + }, + { + Path: "/usr/bin/pacman-conf", + }, + { + Path: "/var/", + }, + { + Path: "/var/cache/", + }, + { + Path: "/var/cache/pacman/", + }, + { + Path: "/var/cache/pacman/pkg/", + }, + { + Path: "/var/lib/", + }, + { + Path: "/var/lib/pacman/", + }, }, }, - Files: []pkg.AlpmFileRecord{ - { - Path: "/etc/", - }, - { - Path: "/etc/makepkg.conf", - }, - { - Path: "/etc/pacman.conf", - }, - { - Path: "/usr/", - }, - { - Path: "/usr/bin/", - }, - { - Path: "/usr/bin/makepkg", - }, - { - Path: "/usr/bin/makepkg-template", - }, - { - Path: "/usr/bin/pacman", - }, - { - Path: "/usr/bin/pacman-conf", - }, - { - Path: "/var/", - }, - { - Path: "/var/cache/", - }, - { - Path: "/var/cache/pacman/", - }, - { - Path: "/var/cache/pacman/pkg/", - }, - { - Path: "/var/lib/", - }, - { - Path: "/var/lib/pacman/", - }, + }, + }, + { + name: "with dependencies", + fixture: "test-fixtures/installed/var/lib/pacman/local/gmp-6.2.1-2/desc", + expected: &parsedData{ + Licenses: "LGPL3\nGPL", + AlpmDBEntry: pkg.AlpmDBEntry{ + BasePackage: "gmp", + Package: "gmp", + Version: "6.2.1-2", + Description: "A free library for arbitrary precision arithmetic", + Architecture: "x86_64", + Size: 1044438, + Packager: "Antonio Rojas ", + URL: "https://gmplib.org/", + Validation: "pgp", + Reason: 1, + Files: []pkg.AlpmFileRecord{}, + Backup: []pkg.AlpmFileRecord{}, + Depends: []string{"gcc-libs", "sh", "libtree-sitter.so=1-64"}, + }, + }, + }, + { + name: "with provides", + fixture: "test-fixtures/installed/var/lib/pacman/local/tree-sitter-0.22.6-1/desc", + expected: &parsedData{ + Licenses: "MIT", + AlpmDBEntry: pkg.AlpmDBEntry{ + BasePackage: "tree-sitter", + Package: "tree-sitter", + Version: "0.22.6-1", + Description: "Incremental parsing library", + Architecture: "x86_64", + Size: 223539, + Packager: "Daniel M. Capella ", + URL: "https://github.com/tree-sitter/tree-sitter", + Validation: "pgp", + Reason: 1, + Files: []pkg.AlpmFileRecord{}, + Backup: []pkg.AlpmFileRecord{}, + Provides: []string{"libtree-sitter.so=0-64"}, }, }, }, @@ -101,13 +147,10 @@ func TestDatabaseParser(t *testing.T) { entry, err := parseAlpmDBEntry(reader) require.NoError(t, err) - if diff := cmp.Diff(entry.Files, test.expected.Files); diff != "" { - t.Errorf("Files mismatch (-want +got):\n%s", diff) + if diff := cmp.Diff(test.expected, entry); diff != "" { + t.Errorf("parsed data mismatch (-want +got):\n%s", diff) } - if diff := cmp.Diff(entry.Backup, test.expected.Backup); diff != "" { - t.Errorf("Backup mismatch (-want +got):\n%s", diff) - } }) } } diff --git a/syft/pkg/cataloger/arch/test-fixtures/installed/var/lib/pacman/local/emacs-29.3-3/desc b/syft/pkg/cataloger/arch/test-fixtures/installed/var/lib/pacman/local/emacs-29.3-3/desc new file mode 100644 index 000000000..fef256ef5 --- /dev/null +++ b/syft/pkg/cataloger/arch/test-fixtures/installed/var/lib/pacman/local/emacs-29.3-3/desc @@ -0,0 +1,38 @@ +%NAME% +emacs + +%VERSION% +29.3-3 + +%BASE% +emacs + +%DESC% +The extensible, customizable, self-documenting real-time display editor + +%URL% +https://www.gnu.org/software/emacs/emacs.html + +%ARCH% +x86_64 + +%BUILDDATE% +1714249917 + +%INSTALLDATE% +1715026363 + +%PACKAGER% +Frederik Schwan + +%SIZE% +126427862 + +%LICENSE% +GPL3 + +%VALIDATION% +pgp + +%DEPENDS% +libtree-sitter.so=0-64 diff --git a/syft/pkg/cataloger/arch/test-fixtures/installed/var/lib/pacman/local/fuzzy-1.2-3/desc b/syft/pkg/cataloger/arch/test-fixtures/installed/var/lib/pacman/local/fuzzy-1.2-3/desc new file mode 100644 index 000000000..843dcbf89 --- /dev/null +++ b/syft/pkg/cataloger/arch/test-fixtures/installed/var/lib/pacman/local/fuzzy-1.2-3/desc @@ -0,0 +1,8 @@ +%NAME% +fuzzy + +%VERSION% +1.2-3 + +%DEPENDS% +tree-sitter diff --git a/syft/pkg/cataloger/arch/test-fixtures/installed/var/lib/pacman/local/fuzzy-1.2-3/files b/syft/pkg/cataloger/arch/test-fixtures/installed/var/lib/pacman/local/fuzzy-1.2-3/files new file mode 100644 index 000000000..638a80c37 --- /dev/null +++ b/syft/pkg/cataloger/arch/test-fixtures/installed/var/lib/pacman/local/fuzzy-1.2-3/files @@ -0,0 +1,6 @@ +%FILES% +etc/ +etc/fuzzy.conf + +%BACKUP% +etc/fuzzy.conf 79fce043df7dfc676ae5ecb903762d8b diff --git a/syft/pkg/cataloger/arch/test-fixtures/gmp-fixture/var/lib/pacman/local/gmp-6.2.1-2/desc b/syft/pkg/cataloger/arch/test-fixtures/installed/var/lib/pacman/local/gmp-6.2.1-2/desc similarity index 93% rename from syft/pkg/cataloger/arch/test-fixtures/gmp-fixture/var/lib/pacman/local/gmp-6.2.1-2/desc rename to syft/pkg/cataloger/arch/test-fixtures/installed/var/lib/pacman/local/gmp-6.2.1-2/desc index 39ff43a9e..c16a97bcf 100644 --- a/syft/pkg/cataloger/arch/test-fixtures/gmp-fixture/var/lib/pacman/local/gmp-6.2.1-2/desc +++ b/syft/pkg/cataloger/arch/test-fixtures/installed/var/lib/pacman/local/gmp-6.2.1-2/desc @@ -41,4 +41,4 @@ pgp %DEPENDS% gcc-libs sh - +libtree-sitter.so=1-64 diff --git a/syft/pkg/cataloger/arch/test-fixtures/gmp-fixture/var/lib/pacman/local/gmp-6.2.1-2/files b/syft/pkg/cataloger/arch/test-fixtures/installed/var/lib/pacman/local/gmp-6.2.1-2/files similarity index 100% rename from syft/pkg/cataloger/arch/test-fixtures/gmp-fixture/var/lib/pacman/local/gmp-6.2.1-2/files rename to syft/pkg/cataloger/arch/test-fixtures/installed/var/lib/pacman/local/gmp-6.2.1-2/files diff --git a/syft/pkg/cataloger/arch/test-fixtures/gmp-fixture/var/lib/pacman/local/gmp-6.2.1-2/mtree b/syft/pkg/cataloger/arch/test-fixtures/installed/var/lib/pacman/local/gmp-6.2.1-2/mtree similarity index 100% rename from syft/pkg/cataloger/arch/test-fixtures/gmp-fixture/var/lib/pacman/local/gmp-6.2.1-2/mtree rename to syft/pkg/cataloger/arch/test-fixtures/installed/var/lib/pacman/local/gmp-6.2.1-2/mtree diff --git a/syft/pkg/cataloger/arch/test-fixtures/installed/var/lib/pacman/local/madeup-20.30-4/desc b/syft/pkg/cataloger/arch/test-fixtures/installed/var/lib/pacman/local/madeup-20.30-4/desc new file mode 100644 index 000000000..d97efe060 --- /dev/null +++ b/syft/pkg/cataloger/arch/test-fixtures/installed/var/lib/pacman/local/madeup-20.30-4/desc @@ -0,0 +1,8 @@ +%NAME% +madeup + +%VERSION% +20.30-4 + +%DEPENDS% +libtree-sitter.so diff --git a/syft/pkg/cataloger/arch/test-fixtures/installed/var/lib/pacman/local/tree-sitter-0.22.6-1/desc b/syft/pkg/cataloger/arch/test-fixtures/installed/var/lib/pacman/local/tree-sitter-0.22.6-1/desc new file mode 100644 index 000000000..e60f00d1a --- /dev/null +++ b/syft/pkg/cataloger/arch/test-fixtures/installed/var/lib/pacman/local/tree-sitter-0.22.6-1/desc @@ -0,0 +1,41 @@ +%NAME% +tree-sitter + +%VERSION% +0.22.6-1 + +%BASE% +tree-sitter + +%DESC% +Incremental parsing library + +%URL% +https://github.com/tree-sitter/tree-sitter + +%ARCH% +x86_64 + +%BUILDDATE% +1714945746 + +%INSTALLDATE% +1715026360 + +%PACKAGER% +Daniel M. Capella + +%SIZE% +223539 + +%REASON% +1 + +%LICENSE% +MIT + +%VALIDATION% +pgp + +%PROVIDES% +libtree-sitter.so=0-64 diff --git a/test/rules/rules.go b/test/rules/rules.go index 300af5eed..24e98ae20 100644 --- a/test/rules/rules.go +++ b/test/rules/rules.go @@ -8,8 +8,8 @@ import "github.com/quasilyte/go-ruleguard/dsl" func resourceCleanup(m dsl.Matcher) { m.Match(`$res, $err := $resolver.FileContentsByLocation($loc); if $*_ { $*_ }; $next`). Where(m["res"].Type.Implements(`io.Closer`) && + m["res"].Type.Implements(`io.Reader`) && m["err"].Type.Implements(`error`) && - m["res"].Type.Implements(`io.Closer`) && !m["next"].Text.Matches(`defer internal.CloseAndLogError`)). Report(`please call "defer internal.CloseAndLogError($res, $loc.RealPath)" right after checking the error returned from $resolver.FileContentsByLocation.`) }