diff --git a/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden b/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden index 3b23a84e5..355967410 100644 --- a/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden +++ b/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden @@ -1,10 +1,10 @@ { "bomFormat": "CycloneDX", "specVersion": "1.4", - "serialNumber": "urn:uuid:3ea3363f-3945-4859-9ba1-9a395983d248", + "serialNumber": "urn:uuid:f426926b-4867-4b52-9142-23997f685f2c", "version": 1, "metadata": { - "timestamp": "2022-05-23T12:05:00-07:00", + "timestamp": "2022-10-24T09:54:37-04:00", "tools": [ { "vendor": "anchore", @@ -20,7 +20,7 @@ }, "components": [ { - "bom-ref": "b85dbb4e6ece5082", + "bom-ref": "e624319940d8d36a", "type": "library", "name": "package-1", "version": "1.0.1", @@ -57,7 +57,7 @@ ] }, { - "bom-ref": "pkg:deb/debian/package-2@2.0.1?package-id=ceda99598967ae8d", + "bom-ref": "pkg:deb/debian/package-2@2.0.1?package-id=b8645f4ac2a0891e", "type": "library", "name": "package-2", "version": "2.0.1", diff --git a/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden b/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden index 6dac17e18..984423df0 100644 --- a/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden +++ b/syft/formats/cyclonedxjson/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden @@ -1,10 +1,10 @@ { "bomFormat": "CycloneDX", "specVersion": "1.4", - "serialNumber": "urn:uuid:c825402b-bbfa-4ad5-81b1-6a8332a6a8b6", + "serialNumber": "urn:uuid:41bbbcc7-694d-4b07-a678-0afb67dabdf9", "version": 1, "metadata": { - "timestamp": "2022-05-23T12:05:01-07:00", + "timestamp": "2022-10-24T09:54:37-04:00", "tools": [ { "vendor": "anchore", @@ -13,7 +13,7 @@ } ], "component": { - "bom-ref": "e779c1ed804ba529", + "bom-ref": "522dc6b135a55bb4", "type": "container", "name": "user-image-input", "version": "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368" @@ -21,7 +21,7 @@ }, "components": [ { - "bom-ref": "2a46171f91c8d4bc", + "bom-ref": "5ffee24fb164cffc", "type": "library", "name": "package-1", "version": "1.0.1", @@ -53,7 +53,7 @@ }, { "name": "syft:location:0:layerID", - "value": "sha256:cd8f3884f1211d65c19ce5bbc5174bcd2ce8ba96b63e5b3693969a53279c4405" + "value": "sha256:fb6beecb75b39f4bb813dbf177e501edd5ddb3e69bb45cedeb78c676ee1b7a59" }, { "name": "syft:location:0:path", @@ -62,7 +62,7 @@ ] }, { - "bom-ref": "pkg:deb/debian/package-2@2.0.1?package-id=ae77680e9b1d087e", + "bom-ref": "pkg:deb/debian/package-2@2.0.1?package-id=8b16570b2b4155c3", "type": "library", "name": "package-2", "version": "2.0.1", @@ -83,7 +83,7 @@ }, { "name": "syft:location:0:layerID", - "value": "sha256:42d2ea51c688e6dc7be81a305acbe006d27a6ef0c26ae3888fd0d4ce44f69265" + "value": "sha256:319b588ce64253a87b533c8ed01cf0025e0eac98e7b516e12532957e1244fdec" }, { "name": "syft:location:0:path", diff --git a/syft/formats/cyclonedxjson/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden b/syft/formats/cyclonedxjson/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden index 3d93b6d3a..11a1958c8 100644 Binary files a/syft/formats/cyclonedxjson/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden and b/syft/formats/cyclonedxjson/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden differ diff --git a/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden b/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden index 7505cd83b..a399d21c5 100644 --- a/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden +++ b/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden @@ -1,7 +1,7 @@ - + - 2022-05-23T12:02:41-07:00 + 2022-10-24T09:54:54-04:00 anchore @@ -14,7 +14,7 @@ - + package-1 1.0.1 @@ -32,7 +32,7 @@ /some/path/pkg1 - + package-2 2.0.1 cpe:2.3:*:some:package:2:*:*:*:*:*:*:* diff --git a/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden b/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden index 6ef8367e6..1783aba0d 100644 --- a/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden +++ b/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden @@ -1,7 +1,7 @@ - + - 2022-05-23T12:02:42-07:00 + 2022-10-24T09:54:54-04:00 anchore @@ -9,13 +9,13 @@ v0.42.0-bogus - + user-image-input sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368 - + package-1 1.0.1 @@ -30,11 +30,11 @@ python PythonPackageMetadata python - sha256:cd8f3884f1211d65c19ce5bbc5174bcd2ce8ba96b63e5b3693969a53279c4405 + sha256:fb6beecb75b39f4bb813dbf177e501edd5ddb3e69bb45cedeb78c676ee1b7a59 /somefile-1.txt - + package-2 2.0.1 cpe:2.3:*:some:package:2:*:*:*:*:*:*:* @@ -43,7 +43,7 @@ the-cataloger-2 DpkgMetadata deb - sha256:42d2ea51c688e6dc7be81a305acbe006d27a6ef0c26ae3888fd0d4ce44f69265 + sha256:319b588ce64253a87b533c8ed01cf0025e0eac98e7b516e12532957e1244fdec /somefile-2.txt 0 diff --git a/syft/formats/cyclonedxxml/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden b/syft/formats/cyclonedxxml/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden index 3d93b6d3a..11a1958c8 100644 Binary files a/syft/formats/cyclonedxxml/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden and b/syft/formats/cyclonedxxml/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden differ diff --git a/syft/formats/spdx22json/test-fixtures/snapshot/TestSPDXJSONDirectoryEncoder.golden b/syft/formats/spdx22json/test-fixtures/snapshot/TestSPDXJSONDirectoryEncoder.golden index f237501b5..35b8c11b0 100644 --- a/syft/formats/spdx22json/test-fixtures/snapshot/TestSPDXJSONDirectoryEncoder.golden +++ b/syft/formats/spdx22json/test-fixtures/snapshot/TestSPDXJSONDirectoryEncoder.golden @@ -3,18 +3,18 @@ "name": "/some/path", "spdxVersion": "SPDX-2.2", "creationInfo": { - "created": "2022-05-23T19:10:22.25645Z", + "created": "2022-10-24T13:54:19.225779Z", "creators": [ "Organization: Anchore, Inc", "Tool: syft-v0.42.0-bogus" ], - "licenseListVersion": "3.17" + "licenseListVersion": "3.18" }, "dataLicense": "CC0-1.0", - "documentNamespace": "https://anchore.com/syft/dir/some/path-81dbcbfa-251d-4ad5-9b01-be91afb16469", + "documentNamespace": "https://anchore.com/syft/dir/some/path-cd89c782-240b-461e-81a1-63863e02642f", "packages": [ { - "SPDXID": "SPDXRef-b85dbb4e6ece5082", + "SPDXID": "SPDXRef-e624319940d8d36a", "name": "package-1", "licenseConcluded": "MIT", "downloadLocation": "NOASSERTION", @@ -36,7 +36,7 @@ "versionInfo": "1.0.1" }, { - "SPDXID": "SPDXRef-ceda99598967ae8d", + "SPDXID": "SPDXRef-b8645f4ac2a0891e", "name": "package-2", "licenseConcluded": "NONE", "downloadLocation": "NOASSERTION", diff --git a/syft/formats/spdx22json/test-fixtures/snapshot/TestSPDXJSONImageEncoder.golden b/syft/formats/spdx22json/test-fixtures/snapshot/TestSPDXJSONImageEncoder.golden index f1891ad57..6b857338f 100644 --- a/syft/formats/spdx22json/test-fixtures/snapshot/TestSPDXJSONImageEncoder.golden +++ b/syft/formats/spdx22json/test-fixtures/snapshot/TestSPDXJSONImageEncoder.golden @@ -3,18 +3,18 @@ "name": "user-image-input", "spdxVersion": "SPDX-2.2", "creationInfo": { - "created": "2022-05-23T19:10:22.412847Z", + "created": "2022-10-24T13:54:19.477217Z", "creators": [ "Organization: Anchore, Inc", "Tool: syft-v0.42.0-bogus" ], - "licenseListVersion": "3.17" + "licenseListVersion": "3.18" }, "dataLicense": "CC0-1.0", - "documentNamespace": "https://anchore.com/syft/image/user-image-input-c9945597-78ce-4e9b-89d2-68b8e4e4ccb9", + "documentNamespace": "https://anchore.com/syft/image/user-image-input-0b40ce75-7e54-4760-bd9d-4fa833b352dd", "packages": [ { - "SPDXID": "SPDXRef-2a46171f91c8d4bc", + "SPDXID": "SPDXRef-5ffee24fb164cffc", "name": "package-1", "licenseConcluded": "MIT", "downloadLocation": "NOASSERTION", @@ -36,7 +36,7 @@ "versionInfo": "1.0.1" }, { - "SPDXID": "SPDXRef-ae77680e9b1d087e", + "SPDXID": "SPDXRef-8b16570b2b4155c3", "name": "package-2", "licenseConcluded": "NONE", "downloadLocation": "NOASSERTION", diff --git a/syft/formats/spdx22json/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden b/syft/formats/spdx22json/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden index 445c69d82..da5ece14c 100644 --- a/syft/formats/spdx22json/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden +++ b/syft/formats/spdx22json/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden @@ -3,7 +3,7 @@ "name": "user-image-input", "spdxVersion": "SPDX-2.2", "creationInfo": { - "created": "2022-09-19T18:39:05.841331Z", + "created": "2022-10-24T13:54:19.48428Z", "creators": [ "Organization: Anchore, Inc", "Tool: syft-v0.42.0-bogus" @@ -11,10 +11,10 @@ "licenseListVersion": "3.18" }, "dataLicense": "CC0-1.0", - "documentNamespace": "https://anchore.com/syft/image/user-image-input-6cf0595e-7d69-4990-aef5-8183b52023b9", + "documentNamespace": "https://anchore.com/syft/image/user-image-input-1a4dc179-1222-463c-b4e9-619131af7e97", "packages": [ { - "SPDXID": "SPDXRef-2a46171f91c8d4bc", + "SPDXID": "SPDXRef-5ffee24fb164cffc", "name": "package-1", "licenseConcluded": "MIT", "downloadLocation": "NOASSERTION", @@ -44,7 +44,7 @@ "versionInfo": "1.0.1" }, { - "SPDXID": "SPDXRef-ae77680e9b1d087e", + "SPDXID": "SPDXRef-8b16570b2b4155c3", "name": "package-2", "licenseConcluded": "NONE", "downloadLocation": "NOASSERTION", @@ -118,32 +118,32 @@ ], "relationships": [ { - "spdxElementId": "SPDXRef-2a46171f91c8d4bc", + "spdxElementId": "SPDXRef-5ffee24fb164cffc", "relationshipType": "CONTAINS", "relatedSpdxElement": "SPDXRef-5265a4dde3edbf7c" }, { - "spdxElementId": "SPDXRef-2a46171f91c8d4bc", + "spdxElementId": "SPDXRef-5ffee24fb164cffc", "relationshipType": "CONTAINS", "relatedSpdxElement": "SPDXRef-839d99ee67d9d174" }, { - "spdxElementId": "SPDXRef-2a46171f91c8d4bc", + "spdxElementId": "SPDXRef-5ffee24fb164cffc", "relationshipType": "CONTAINS", "relatedSpdxElement": "SPDXRef-9c2f7510199b17f6" }, { - "spdxElementId": "SPDXRef-2a46171f91c8d4bc", + "spdxElementId": "SPDXRef-5ffee24fb164cffc", "relationshipType": "CONTAINS", "relatedSpdxElement": "SPDXRef-c641caa71518099f" }, { - "spdxElementId": "SPDXRef-2a46171f91c8d4bc", + "spdxElementId": "SPDXRef-5ffee24fb164cffc", "relationshipType": "CONTAINS", "relatedSpdxElement": "SPDXRef-c6f5b29dca12661f" }, { - "spdxElementId": "SPDXRef-2a46171f91c8d4bc", + "spdxElementId": "SPDXRef-5ffee24fb164cffc", "relationshipType": "CONTAINS", "relatedSpdxElement": "SPDXRef-f9e49132a4b96ccd" } diff --git a/syft/formats/spdx22json/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden b/syft/formats/spdx22json/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden index 3d93b6d3a..11a1958c8 100644 Binary files a/syft/formats/spdx22json/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden and b/syft/formats/spdx22json/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden differ diff --git a/syft/formats/spdx22tagvalue/test-fixtures/snapshot/TestSPDXJSONSPDXIDs.golden b/syft/formats/spdx22tagvalue/test-fixtures/snapshot/TestSPDXJSONSPDXIDs.golden index e9e540f62..e8a34bd73 100644 --- a/syft/formats/spdx22tagvalue/test-fixtures/snapshot/TestSPDXJSONSPDXIDs.golden +++ b/syft/formats/spdx22tagvalue/test-fixtures/snapshot/TestSPDXJSONSPDXIDs.golden @@ -2,16 +2,16 @@ SPDXVersion: SPDX-2.2 DataLicense: CC0-1.0 SPDXID: SPDXRef-DOCUMENT DocumentName: . -DocumentNamespace: https://anchore.com/syft/dir/bdb67358-651c-4dd8-b5ee-5318936eb16a -LicenseListVersion: 3.17 +DocumentNamespace: https://anchore.com/syft/dir/4593d944-756e-49aa-af4e-b1a5acf09b97 +LicenseListVersion: 3.18 Creator: Organization: Anchore, Inc Creator: Tool: syft-v0.42.0-bogus -Created: 2022-06-07T19:33:39Z +Created: 2022-10-24T13:53:53Z ##### Package: @at-sign PackageName: @at-sign -SPDXID: SPDXRef-Package---at-sign-739e4f0d93fb8298 +SPDXID: SPDXRef-Package---at-sign-fe69bc18c2698fc4 PackageDownloadLocation: NOASSERTION FilesAnalyzed: false PackageLicenseConcluded: NONE @@ -21,7 +21,7 @@ PackageCopyrightText: NOASSERTION ##### Package: some/slashes PackageName: some/slashes -SPDXID: SPDXRef-Package--some-slashes-26db06648b24bff9 +SPDXID: SPDXRef-Package--some-slashes-57ed206c09e6e5f4 PackageDownloadLocation: NOASSERTION FilesAnalyzed: false PackageLicenseConcluded: NONE @@ -31,7 +31,7 @@ PackageCopyrightText: NOASSERTION ##### Package: under_scores PackageName: under_scores -SPDXID: SPDXRef-Package--under-scores-250cbfefcdea318b +SPDXID: SPDXRef-Package--under-scores-8b7505907fdaf19d PackageDownloadLocation: NOASSERTION FilesAnalyzed: false PackageLicenseConcluded: NONE diff --git a/syft/formats/spdx22tagvalue/test-fixtures/snapshot/TestSPDXTagValueDirectoryEncoder.golden b/syft/formats/spdx22tagvalue/test-fixtures/snapshot/TestSPDXTagValueDirectoryEncoder.golden index 83e333e4b..13eca0f9e 100644 --- a/syft/formats/spdx22tagvalue/test-fixtures/snapshot/TestSPDXTagValueDirectoryEncoder.golden +++ b/syft/formats/spdx22tagvalue/test-fixtures/snapshot/TestSPDXTagValueDirectoryEncoder.golden @@ -2,16 +2,16 @@ SPDXVersion: SPDX-2.2 DataLicense: CC0-1.0 SPDXID: SPDXRef-DOCUMENT DocumentName: /some/path -DocumentNamespace: https://anchore.com/syft/dir/some/path-c6b20d03-1478-4513-9feb-1ec427d4b547 -LicenseListVersion: 3.17 +DocumentNamespace: https://anchore.com/syft/dir/some/path-a4e58523-00d0-4135-9d21-cf586fbd340c +LicenseListVersion: 3.18 Creator: Organization: Anchore, Inc Creator: Tool: syft-v0.42.0-bogus -Created: 2022-05-24T22:51:02Z +Created: 2022-10-24T13:53:52Z ##### Package: package-2 PackageName: package-2 -SPDXID: SPDXRef-Package-deb-package-2-ceda99598967ae8d +SPDXID: SPDXRef-Package-deb-package-2-b8645f4ac2a0891e PackageVersion: 2.0.1 PackageDownloadLocation: NOASSERTION FilesAnalyzed: false @@ -24,7 +24,7 @@ ExternalRef: PACKAGE_MANAGER purl pkg:deb/debian/package-2@2.0.1 ##### Package: package-1 PackageName: package-1 -SPDXID: SPDXRef-Package-python-package-1-b85dbb4e6ece5082 +SPDXID: SPDXRef-Package-python-package-1-e624319940d8d36a PackageVersion: 1.0.1 PackageDownloadLocation: NOASSERTION FilesAnalyzed: false diff --git a/syft/formats/spdx22tagvalue/test-fixtures/snapshot/TestSPDXTagValueImageEncoder.golden b/syft/formats/spdx22tagvalue/test-fixtures/snapshot/TestSPDXTagValueImageEncoder.golden index aae5ebf53..e9d613185 100644 --- a/syft/formats/spdx22tagvalue/test-fixtures/snapshot/TestSPDXTagValueImageEncoder.golden +++ b/syft/formats/spdx22tagvalue/test-fixtures/snapshot/TestSPDXTagValueImageEncoder.golden @@ -2,16 +2,16 @@ SPDXVersion: SPDX-2.2 DataLicense: CC0-1.0 SPDXID: SPDXRef-DOCUMENT DocumentName: user-image-input -DocumentNamespace: https://anchore.com/syft/image/user-image-input-12a877bc-fe9b-40ef-aa9c-4d34f108d0d6 -LicenseListVersion: 3.17 +DocumentNamespace: https://anchore.com/syft/image/user-image-input-639f628a-5f8b-4050-a69e-90c85f0d7837 +LicenseListVersion: 3.18 Creator: Organization: Anchore, Inc Creator: Tool: syft-v0.42.0-bogus -Created: 2022-05-24T22:51:02Z +Created: 2022-10-24T13:53:53Z ##### Package: package-2 PackageName: package-2 -SPDXID: SPDXRef-Package-deb-package-2-ae77680e9b1d087e +SPDXID: SPDXRef-Package-deb-package-2-8b16570b2b4155c3 PackageVersion: 2.0.1 PackageDownloadLocation: NOASSERTION FilesAnalyzed: false @@ -24,7 +24,7 @@ ExternalRef: PACKAGE_MANAGER purl pkg:deb/debian/package-2@2.0.1 ##### Package: package-1 PackageName: package-1 -SPDXID: SPDXRef-Package-python-package-1-2a46171f91c8d4bc +SPDXID: SPDXRef-Package-python-package-1-5ffee24fb164cffc PackageVersion: 1.0.1 PackageDownloadLocation: NOASSERTION FilesAnalyzed: false diff --git a/syft/formats/spdx22tagvalue/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden b/syft/formats/spdx22tagvalue/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden index 3d93b6d3a..11a1958c8 100644 Binary files a/syft/formats/spdx22tagvalue/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden and b/syft/formats/spdx22tagvalue/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden differ diff --git a/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden b/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden index 0f965356e..235e3dfde 100644 --- a/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden +++ b/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden @@ -1,7 +1,7 @@ { "artifacts": [ { - "id": "b85dbb4e6ece5082", + "id": "e624319940d8d36a", "name": "package-1", "version": "1.0.1", "type": "python", @@ -36,7 +36,7 @@ } }, { - "id": "ceda99598967ae8d", + "id": "b8645f4ac2a0891e", "name": "package-2", "version": "2.0.1", "type": "deb", diff --git a/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden b/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden index bd4d508c1..cb8c86461 100644 --- a/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden +++ b/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden @@ -1,7 +1,7 @@ { "artifacts": [ { - "id": "b3fa3ee64756b0c6", + "id": "8373dcf05581b932", "name": "package-1", "version": "1.0.1", "type": "python", @@ -31,7 +31,7 @@ } }, { - "id": "b324f4d9ee5413fe", + "id": "c3d4da40f387eec7", "name": "package-2", "version": "2.0.1", "type": "deb", diff --git a/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden b/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden index d54336304..1cfd1cc2e 100644 --- a/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden +++ b/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden @@ -1,7 +1,7 @@ { "artifacts": [ { - "id": "2a46171f91c8d4bc", + "id": "5ffee24fb164cffc", "name": "package-1", "version": "1.0.1", "type": "python", @@ -9,7 +9,7 @@ "locations": [ { "path": "/somefile-1.txt", - "layerID": "sha256:4965affaf42a7174561882c5fd87e2db6f0b07df532459ba86f98a8bd2af11de" + "layerID": "sha256:fb6beecb75b39f4bb813dbf177e501edd5ddb3e69bb45cedeb78c676ee1b7a59" } ], "licenses": [ @@ -32,7 +32,7 @@ } }, { - "id": "ae77680e9b1d087e", + "id": "8b16570b2b4155c3", "name": "package-2", "version": "2.0.1", "type": "deb", @@ -40,7 +40,7 @@ "locations": [ { "path": "/somefile-2.txt", - "layerID": "sha256:460c3e27be163efe75df048c4d4cf3a22e7e363f02521fa2e82a3bd257a682d4" + "layerID": "sha256:319b588ce64253a87b533c8ed01cf0025e0eac98e7b516e12532957e1244fdec" } ], "licenses": [], @@ -64,11 +64,11 @@ ], "artifactRelationships": [], "source": { - "id": "00afea0209d754683fdfcdd47cfea94ec9f2e81286be444e297a8c776c4accbf", + "id": "1a678f111c8ddc66fd82687bb024e0dd6af61314404937a80e810c0cf317b796", "type": "image", "target": { "userInput": "user-image-input", - "imageID": "sha256:6b1b476e6dc187bb688566606cf7a59d7804d81169967d8c6bb121627b0a387f", + "imageID": "sha256:3c51b06feb0cda8ee62d0e3755ef2a8496a6b71f8a55b245f07f31c4bb813d31", "manifestDigest": "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368", "mediaType": "application/vnd.docker.distribution.manifest.v2+json", "tags": [ @@ -78,17 +78,17 @@ "layers": [ { "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", - "digest": "sha256:4965affaf42a7174561882c5fd87e2db6f0b07df532459ba86f98a8bd2af11de", + "digest": "sha256:fb6beecb75b39f4bb813dbf177e501edd5ddb3e69bb45cedeb78c676ee1b7a59", "size": 22 }, { "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", - "digest": "sha256:460c3e27be163efe75df048c4d4cf3a22e7e363f02521fa2e82a3bd257a682d4", + "digest": "sha256:319b588ce64253a87b533c8ed01cf0025e0eac98e7b516e12532957e1244fdec", "size": 16 } ], - "manifest": "eyJzY2hlbWFWZXJzaW9uIjoyLCJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmRpc3RyaWJ1dGlvbi5tYW5pZmVzdC52Mitqc29uIiwiY29uZmlnIjp7Im1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuY29udGFpbmVyLmltYWdlLnYxK2pzb24iLCJzaXplIjo2NzMsImRpZ2VzdCI6InNoYTI1Njo2YjFiNDc2ZTZkYzE4N2JiNjg4NTY2NjA2Y2Y3YTU5ZDc4MDRkODExNjk5NjdkOGM2YmIxMjE2MjdiMGEzODdmIn0sImxheWVycyI6W3sibWVkaWFUeXBlIjoiYXBwbGljYXRpb24vdm5kLmRvY2tlci5pbWFnZS5yb290ZnMuZGlmZi50YXIuZ3ppcCIsInNpemUiOjIwNDgsImRpZ2VzdCI6InNoYTI1Njo0OTY1YWZmYWY0MmE3MTc0NTYxODgyYzVmZDg3ZTJkYjZmMGIwN2RmNTMyNDU5YmE4NmY5OGE4YmQyYWYxMWRlIn0seyJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmltYWdlLnJvb3Rmcy5kaWZmLnRhci5nemlwIiwic2l6ZSI6MjA0OCwiZGlnZXN0Ijoic2hhMjU2OjQ2MGMzZTI3YmUxNjNlZmU3NWRmMDQ4YzRkNGNmM2EyMmU3ZTM2M2YwMjUyMWZhMmU4MmEzYmQyNTdhNjgyZDQifV19", - "config": "eyJhcmNoaXRlY3R1cmUiOiJhbWQ2NCIsImNvbmZpZyI6eyJFbnYiOlsiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iXSwiV29ya2luZ0RpciI6Ii8iLCJPbkJ1aWxkIjpudWxsfSwiY3JlYXRlZCI6IjIwMjItMTAtMDVUMTQ6MjQ6NTguNzc0NTY2MjM2WiIsImhpc3RvcnkiOlt7ImNyZWF0ZWQiOiIyMDIyLTEwLTA1VDE0OjI0OjU4Ljc0NDY3NTEyOVoiLCJjcmVhdGVkX2J5IjoiQUREIGZpbGUtMS50eHQgL3NvbWVmaWxlLTEudHh0ICMgYnVpbGRraXQiLCJjb21tZW50IjoiYnVpbGRraXQuZG9ja2VyZmlsZS52MCJ9LHsiY3JlYXRlZCI6IjIwMjItMTAtMDVUMTQ6MjQ6NTguNzc0NTY2MjM2WiIsImNyZWF0ZWRfYnkiOiJBREQgZmlsZS0yLnR4dCAvc29tZWZpbGUtMi50eHQgIyBidWlsZGtpdCIsImNvbW1lbnQiOiJidWlsZGtpdC5kb2NrZXJmaWxlLnYwIn1dLCJvcyI6ImxpbnV4Iiwicm9vdGZzIjp7InR5cGUiOiJsYXllcnMiLCJkaWZmX2lkcyI6WyJzaGEyNTY6NDk2NWFmZmFmNDJhNzE3NDU2MTg4MmM1ZmQ4N2UyZGI2ZjBiMDdkZjUzMjQ1OWJhODZmOThhOGJkMmFmMTFkZSIsInNoYTI1Njo0NjBjM2UyN2JlMTYzZWZlNzVkZjA0OGM0ZDRjZjNhMjJlN2UzNjNmMDI1MjFmYTJlODJhM2JkMjU3YTY4MmQ0Il19fQ==", + "manifest": "eyJzY2hlbWFWZXJzaW9uIjoyLCJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmRpc3RyaWJ1dGlvbi5tYW5pZmVzdC52Mitqc29uIiwiY29uZmlnIjp7Im1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuY29udGFpbmVyLmltYWdlLnYxK2pzb24iLCJzaXplIjo2NzMsImRpZ2VzdCI6InNoYTI1NjozYzUxYjA2ZmViMGNkYThlZTYyZDBlMzc1NWVmMmE4NDk2YTZiNzFmOGE1NWIyNDVmMDdmMzFjNGJiODEzZDMxIn0sImxheWVycyI6W3sibWVkaWFUeXBlIjoiYXBwbGljYXRpb24vdm5kLmRvY2tlci5pbWFnZS5yb290ZnMuZGlmZi50YXIuZ3ppcCIsInNpemUiOjIwNDgsImRpZ2VzdCI6InNoYTI1NjpmYjZiZWVjYjc1YjM5ZjRiYjgxM2RiZjE3N2U1MDFlZGQ1ZGRiM2U2OWJiNDVjZWRlYjc4YzY3NmVlMWI3YTU5In0seyJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmltYWdlLnJvb3Rmcy5kaWZmLnRhci5nemlwIiwic2l6ZSI6MjA0OCwiZGlnZXN0Ijoic2hhMjU2OjMxOWI1ODhjZTY0MjUzYTg3YjUzM2M4ZWQwMWNmMDAyNWUwZWFjOThlN2I1MTZlMTI1MzI5NTdlMTI0NGZkZWMifV19", + "config": "eyJhcmNoaXRlY3R1cmUiOiJhbWQ2NCIsImNvbmZpZyI6eyJFbnYiOlsiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iXSwiV29ya2luZ0RpciI6Ii8iLCJPbkJ1aWxkIjpudWxsfSwiY3JlYXRlZCI6IjIwMjItMDgtMDFUMjA6MDk6MjIuNTA5NDIxNzEyWiIsImhpc3RvcnkiOlt7ImNyZWF0ZWQiOiIyMDIyLTA4LTAxVDIwOjA5OjIyLjQ4Nzg5NTUxOVoiLCJjcmVhdGVkX2J5IjoiQUREIGZpbGUtMS50eHQgL3NvbWVmaWxlLTEudHh0ICMgYnVpbGRraXQiLCJjb21tZW50IjoiYnVpbGRraXQuZG9ja2VyZmlsZS52MCJ9LHsiY3JlYXRlZCI6IjIwMjItMDgtMDFUMjA6MDk6MjIuNTA5NDIxNzEyWiIsImNyZWF0ZWRfYnkiOiJBREQgZmlsZS0yLnR4dCAvc29tZWZpbGUtMi50eHQgIyBidWlsZGtpdCIsImNvbW1lbnQiOiJidWlsZGtpdC5kb2NrZXJmaWxlLnYwIn1dLCJvcyI6ImxpbnV4Iiwicm9vdGZzIjp7InR5cGUiOiJsYXllcnMiLCJkaWZmX2lkcyI6WyJzaGEyNTY6ZmI2YmVlY2I3NWIzOWY0YmI4MTNkYmYxNzdlNTAxZWRkNWRkYjNlNjliYjQ1Y2VkZWI3OGM2NzZlZTFiN2E1OSIsInNoYTI1NjozMTliNTg4Y2U2NDI1M2E4N2I1MzNjOGVkMDFjZjAwMjVlMGVhYzk4ZTdiNTE2ZTEyNTMyOTU3ZTEyNDRmZGVjIl19fQ==", "repoDigests": [], "architecture": "", "os": "" diff --git a/syft/formats/syftjson/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden b/syft/formats/syftjson/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden index 538b5e585..11a1958c8 100644 Binary files a/syft/formats/syftjson/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden and b/syft/formats/syftjson/test-fixtures/snapshot/stereoscope-fixture-image-simple.golden differ diff --git a/syft/linux/identify_release.go b/syft/linux/identify_release.go index 8b24cae8d..7678dfbcd 100644 --- a/syft/linux/identify_release.go +++ b/syft/linux/identify_release.go @@ -55,30 +55,32 @@ var identityFiles = []parseEntry{ // IdentifyRelease parses distro-specific files to discover and raise linux distribution release details. func IdentifyRelease(resolver source.FileResolver) *Release { + logger := log.Nested("operation", "identify-release") for _, entry := range identityFiles { locations, err := resolver.FilesByPath(entry.path) if err != nil { - log.Warnf("unable to get path locations from %s: %+v", entry.path, err) + logger.WithFields("error", err, "path", entry.path).Trace("unable to get path") continue } for _, location := range locations { contentReader, err := resolver.FileContentsByLocation(location) if err != nil { - log.Debugf("unable to get contents from %s: %s", entry.path, err) + logger.WithFields("error", err, "path", location.RealPath).Trace("unable to get contents") continue } content, err := io.ReadAll(contentReader) internal.CloseAndLogError(contentReader, location.VirtualPath) if err != nil { - log.Warnf("unable to read %q: %+v", location.RealPath, err) - break + logger.WithFields("error", err, "path", location.RealPath).Trace("unable to read contents") + continue } release, err := entry.fn(string(content)) if err != nil { - log.Warnf("unable to parse %q", location.RealPath) + logger.WithFields("error", err, "path", location.RealPath).Trace("unable to parse contents") + continue } if release != nil { diff --git a/syft/pkg/alpm_metadata.go b/syft/pkg/alpm_metadata.go index 567b9fd81..3e807be9b 100644 --- a/syft/pkg/alpm_metadata.go +++ b/syft/pkg/alpm_metadata.go @@ -6,11 +6,11 @@ import ( "github.com/scylladb/go-set/strset" - "github.com/anchore/packageurl-go" "github.com/anchore/syft/syft/file" - "github.com/anchore/syft/syft/linux" ) +var _ FileOwner = (*AlpmMetadata)(nil) + const AlpmDBGlob = "**/var/lib/pacman/local/**/desc" type AlpmMetadata struct { @@ -40,34 +40,6 @@ type AlpmFileRecord struct { Digests []file.Digest `mapstructure:"digests" json:"digest,omitempty"` } -// PackageURL returns the PURL for the specific Arch Linux package (see https://github.com/package-url/purl-spec) -func (m AlpmMetadata) PackageURL(distro *linux.Release) string { - qualifiers := map[string]string{ - PURLQualifierArch: m.Architecture, - } - - if m.BasePackage != "" { - qualifiers[PURLQualifierUpstream] = m.BasePackage - } - - distroID := "" - if distro != nil { - distroID = distro.ID - } - - return packageurl.NewPackageURL( - "alpm", - distroID, - m.Package, - m.Version, - purlQualifiers( - qualifiers, - distro, - ), - "", - ).ToString() -} - func (m AlpmMetadata) OwnedFiles() (result []string) { s := strset.New() for _, f := range m.Files { diff --git a/syft/pkg/apk_metadata.go b/syft/pkg/apk_metadata.go index ca8881872..0c8c9ac5f 100644 --- a/syft/pkg/apk_metadata.go +++ b/syft/pkg/apk_metadata.go @@ -65,7 +65,7 @@ func (m ApkMetadata) PackageURL(distro *linux.Release) string { "", m.Package, m.Version, - purlQualifiers( + PURLQualifiers( qualifiers, distro, ), diff --git a/syft/pkg/cataloger/alpm/cataloger.go b/syft/pkg/cataloger/alpm/cataloger.go index 87a7b285e..39bc7d816 100644 --- a/syft/pkg/cataloger/alpm/cataloger.go +++ b/syft/pkg/cataloger/alpm/cataloger.go @@ -1,48 +1,13 @@ package alpm import ( - "fmt" - - "github.com/anchore/syft/internal" - "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/pkg" - "github.com/anchore/syft/syft/source" + "github.com/anchore/syft/syft/pkg/cataloger/generic" ) const catalogerName = "alpmdb-cataloger" -type Cataloger struct{} - -// NewAlpmdbCataloger returns a new ALPM DB cataloger object. -func NewAlpmdbCataloger() *Cataloger { - return &Cataloger{} -} - -// Name returns a string that uniquely describes a cataloger -func (c *Cataloger) Name() string { - return catalogerName -} - -// Catalog is given an object to resolve file references and content, this function returns any discovered Packages after analyzing rpm db installation. -func (c *Cataloger) Catalog(resolver source.FileResolver) ([]pkg.Package, []artifact.Relationship, error) { - fileMatches, err := resolver.FilesByGlob(pkg.AlpmDBGlob) - if err != nil { - return nil, nil, fmt.Errorf("failed to find rpmdb's by glob: %w", err) - } - - var pkgs []pkg.Package - for _, location := range fileMatches { - dbContentReader, err := resolver.FileContentsByLocation(location) - if err != nil { - return nil, nil, err - } - - discoveredPkgs, err := parseAlpmDB(resolver, location.RealPath, dbContentReader) - internal.CloseAndLogError(dbContentReader, location.VirtualPath) - if err != nil { - return nil, nil, fmt.Errorf("unable to catalog package=%+v: %w", location.RealPath, err) - } - pkgs = append(pkgs, discoveredPkgs...) - } - return pkgs, nil, nil +func NewAlpmdbCataloger() *generic.Cataloger { + return generic.NewCataloger(catalogerName). + WithParserByGlobs(parseAlpmDB, pkg.AlpmDBGlob) } diff --git a/syft/pkg/cataloger/alpm/package.go b/syft/pkg/cataloger/alpm/package.go new file mode 100644 index 000000000..ac9ac1b65 --- /dev/null +++ b/syft/pkg/cataloger/alpm/package.go @@ -0,0 +1,52 @@ +package alpm + +import ( + "strings" + + "github.com/anchore/packageurl-go" + "github.com/anchore/syft/syft/linux" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/source" +) + +func newPackage(m pkg.AlpmMetadata, release *linux.Release, locations ...source.Location) pkg.Package { + p := pkg.Package{ + Name: m.Package, + Version: m.Version, + Locations: source.NewLocationSet(locations...), + Type: pkg.AlpmPkg, + Licenses: strings.Split(m.License, " "), + PURL: packageURL(m, release), + MetadataType: pkg.AlpmMetadataType, + Metadata: m, + } + p.SetID() + return p +} + +func packageURL(m pkg.AlpmMetadata, distro *linux.Release) string { + if distro == nil || distro.ID != "arch" { + // note: there is no namespace variation (like with debian ID_LIKE for ubuntu ID, for example) + return "" + } + + qualifiers := map[string]string{ + pkg.PURLQualifierArch: m.Architecture, + } + + if m.BasePackage != "" { + qualifiers[pkg.PURLQualifierUpstream] = m.BasePackage + } + + return packageurl.NewPackageURL( + "alpm", // `alpm` for Arch Linux and other users of the libalpm/pacman package manager. (see https://github.com/package-url/purl-spec/pull/164) + distro.ID, + m.Package, + m.Version, + pkg.PURLQualifiers( + qualifiers, + distro, + ), + "", + ).ToString() +} diff --git a/syft/pkg/alpm_metadata_test.go b/syft/pkg/cataloger/alpm/package_test.go similarity index 80% rename from syft/pkg/alpm_metadata_test.go rename to syft/pkg/cataloger/alpm/package_test.go index e31ad02a1..bb88a749c 100644 --- a/syft/pkg/alpm_metadata_test.go +++ b/syft/pkg/cataloger/alpm/package_test.go @@ -1,4 +1,4 @@ -package pkg +package alpm import ( "testing" @@ -7,18 +7,32 @@ import ( "github.com/anchore/packageurl-go" "github.com/anchore/syft/syft/linux" + "github.com/anchore/syft/syft/pkg" ) -func TestAlpmMetadata_pURL(t *testing.T) { +func Test_PackageURL(t *testing.T) { tests := []struct { name string - metadata AlpmMetadata + metadata pkg.AlpmMetadata distro linux.Release expected string }{ + { + name: "bad distro id", + metadata: pkg.AlpmMetadata{ + Package: "p", + Version: "v", + Architecture: "a", + }, + distro: linux.Release{ + ID: "something-else", + BuildID: "rolling", + }, + expected: "", + }, { name: "gocase", - metadata: AlpmMetadata{ + metadata: pkg.AlpmMetadata{ Package: "p", Version: "v", Architecture: "a", @@ -31,7 +45,7 @@ func TestAlpmMetadata_pURL(t *testing.T) { }, { name: "missing architecture", - metadata: AlpmMetadata{ + metadata: pkg.AlpmMetadata{ Package: "p", Version: "v", }, @@ -41,7 +55,7 @@ func TestAlpmMetadata_pURL(t *testing.T) { expected: "pkg:alpm/arch/p@v?distro=arch", }, { - metadata: AlpmMetadata{ + metadata: pkg.AlpmMetadata{ Package: "python", Version: "3.10.0", Architecture: "any", @@ -53,7 +67,7 @@ func TestAlpmMetadata_pURL(t *testing.T) { expected: "pkg:alpm/arch/python@3.10.0?arch=any&distro=arch-rolling", }, { - metadata: AlpmMetadata{ + metadata: pkg.AlpmMetadata{ Package: "g plus plus", Version: "v84", Architecture: "x86_64", @@ -66,7 +80,7 @@ func TestAlpmMetadata_pURL(t *testing.T) { }, { name: "add source information as qualifier", - metadata: AlpmMetadata{ + metadata: pkg.AlpmMetadata{ Package: "p", Version: "v", Architecture: "a", @@ -82,12 +96,17 @@ func TestAlpmMetadata_pURL(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - actual := test.metadata.PackageURL(&test.distro) + actual := packageURL(test.metadata, &test.distro) if actual != test.expected { dmp := diffmatchpatch.New() diffs := dmp.DiffMain(test.expected, actual, true) t.Errorf("diff: %s", dmp.DiffPrettyText(diffs)) } + + if test.expected == "" { + return + } + // verify packageurl can parse purl, err := packageurl.FromString(actual) if err != nil { diff --git a/syft/pkg/cataloger/alpm/parse_alpm_db.go b/syft/pkg/cataloger/alpm/parse_alpm_db.go index bf40fb3a3..1f376b766 100644 --- a/syft/pkg/cataloger/alpm/parse_alpm_db.go +++ b/syft/pkg/cataloger/alpm/parse_alpm_db.go @@ -13,11 +13,15 @@ import ( "github.com/mitchellh/mapstructure" "github.com/vbatts/go-mtree" + "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/pkg/cataloger/generic" "github.com/anchore/syft/syft/source" ) +var _ generic.Parser = parseAlpmDB + var ( ignoredFiles = map[string]bool{ "/set": true, @@ -27,16 +31,50 @@ var ( } ) -func newAlpmDBPackage(d *pkg.AlpmMetadata) *pkg.Package { - return &pkg.Package{ - Name: d.Package, - Version: d.Version, - FoundBy: catalogerName, - Type: "alpm", - Licenses: strings.Split(d.License, " "), - MetadataType: pkg.AlpmMetadataType, - Metadata: *d, +func parseAlpmDB(resolver source.FileResolver, env *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { + metadata, err := parseAlpmDBEntry(reader) + if err != nil { + return nil, nil, err } + + base := filepath.Dir(reader.RealPath) + r, err := getFileReader(filepath.Join(base, "mtree"), resolver) + if err != nil { + return nil, nil, err + } + pkgFiles, err := parseMtree(r) + if err != nil { + return nil, nil, err + } + // The replace the files found the the pacman database with the files from the mtree These contain more metadata and + // thus more useful. + metadata.Files = pkgFiles + + // 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 + } + filesMetadata, err := parseAlpmDBEntry(reader) + if err != nil { + return nil, nil, err + } else if filesMetadata != nil { + metadata.Backup = filesMetadata.Backup + } + + return []pkg.Package{ + newPackage(*metadata, env.LinuxRelease, reader.Location), + }, nil, nil +} + +func parseAlpmDBEntry(reader io.Reader) (*pkg.AlpmMetadata, error) { + scanner := newScanner(reader) + metadata, err := parseDatabase(scanner) + if err != nil { + return nil, err + } + return metadata, nil } func newScanner(reader io.Reader) *bufio.Scanner { @@ -194,53 +232,3 @@ func parseMtree(r io.Reader) ([]pkg.AlpmFileRecord, error) { } return entries, nil } - -func parseAlpmDBEntry(reader io.Reader) (*pkg.AlpmMetadata, error) { - scanner := newScanner(reader) - metadata, err := parseDatabase(scanner) - if err != nil { - return nil, err - } - if metadata == nil { - return nil, nil - } - return metadata, nil -} - -func parseAlpmDB(resolver source.FileResolver, desc string, reader io.Reader) ([]pkg.Package, error) { - metadata, err := parseAlpmDBEntry(reader) - if err != nil { - return nil, err - } - - base := filepath.Dir(desc) - mtree := filepath.Join(base, "mtree") - r, err := getFileReader(mtree, resolver) - if err != nil { - return nil, err - } - pkgFiles, err := parseMtree(r) - if err != nil { - return nil, err - } - // The replace the files found the the pacman database with the files from the mtree These contain more metadata and - // thus more useful. - metadata.Files = pkgFiles - - // 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, err - } - filesMetadata, err := parseAlpmDBEntry(reader) - if err != nil { - return nil, err - } else if filesMetadata != nil { - metadata.Backup = filesMetadata.Backup - } - - p := *newAlpmDBPackage(metadata) - p.SetID() - return []pkg.Package{p}, nil -} diff --git a/syft/pkg/cataloger/generic/cataloger.go b/syft/pkg/cataloger/generic/cataloger.go new file mode 100644 index 000000000..c843c680d --- /dev/null +++ b/syft/pkg/cataloger/generic/cataloger.go @@ -0,0 +1,151 @@ +package generic + +import ( + "github.com/anchore/syft/internal" + "github.com/anchore/syft/internal/log" + "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/linux" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/source" +) + +type processor func(resolver source.FileResolver, env Environment) []request + +type request struct { + source.Location + Parser +} + +// Cataloger implements the Catalog interface and is responsible for dispatching the proper parser function for +// a given path or glob pattern. This is intended to be reusable across many package cataloger types. +type Cataloger struct { + processor []processor + upstreamCataloger string +} + +func (c *Cataloger) WithParserByGlobs(parser Parser, globs ...string) *Cataloger { + c.processor = append(c.processor, + func(resolver source.FileResolver, env Environment) []request { + var requests []request + for _, g := range globs { + // TODO: add more trace logging here + matches, err := resolver.FilesByGlob(g) + if err != nil { + log.Warnf("unable to process glob=%q: %+v", g, err) + continue + } + requests = append(requests, makeRequests(parser, matches)...) + } + return requests + }, + ) + return c +} + +func (c *Cataloger) WithParserByMimeTypes(parser Parser, types ...string) *Cataloger { + c.processor = append(c.processor, + func(resolver source.FileResolver, env Environment) []request { + var requests []request + for _, t := range types { + // TODO: add more trace logging here + matches, err := resolver.FilesByMIMEType(t) + if err != nil { + log.Warnf("unable to process mimetype=%q: %+v", t, err) + continue + } + requests = append(requests, makeRequests(parser, matches)...) + } + return requests + }, + ) + return c +} + +func (c *Cataloger) WithParserByPath(parser Parser, paths ...string) *Cataloger { + c.processor = append(c.processor, + func(resolver source.FileResolver, env Environment) []request { + var requests []request + for _, g := range paths { + // TODO: add more trace logging here + matches, err := resolver.FilesByPath(g) + if err != nil { + log.Warnf("unable to process path=%q: %+v", g, err) + continue + } + requests = append(requests, makeRequests(parser, matches)...) + } + return requests + }, + ) + return c +} + +func makeRequests(parser Parser, locations []source.Location) []request { + var requests []request + for _, l := range locations { + requests = append(requests, request{ + Location: l, + Parser: parser, + }) + } + return requests +} + +// NewCataloger if provided path-to-parser-function and glob-to-parser-function lookups creates a Cataloger +func NewCataloger(upstreamCataloger string) *Cataloger { + return &Cataloger{ + upstreamCataloger: upstreamCataloger, + } +} + +// Name returns a string that uniquely describes the upstream cataloger that this Generic Cataloger represents. +func (c *Cataloger) Name() string { + return c.upstreamCataloger +} + +// Catalog is given an object to resolve file references and content, this function returns any discovered Packages after analyzing the catalog source. +func (c *Cataloger) Catalog(resolver source.FileResolver) ([]pkg.Package, []artifact.Relationship, error) { + var packages []pkg.Package + var relationships []artifact.Relationship + + logger := log.Nested("cataloger", c.upstreamCataloger) + + env := Environment{ + // TODO: consider passing into the cataloger, this would affect the cataloger interface (and all implementations). This can be deferred until later. + LinuxRelease: linux.IdentifyRelease(resolver), + } + + for _, req := range c.selectFiles(resolver) { + location, parser := req.Location, req.Parser + + contentReader, err := resolver.FileContentsByLocation(location) + if err != nil { + logger.WithFields("location", location.RealPath, "error", err).Warn("unable to fetch contents") + continue + } + + discoveredPackages, discoveredRelationships, err := parser(resolver, &env, source.NewLocationReadCloser(location, contentReader)) + internal.CloseAndLogError(contentReader, location.VirtualPath) + if err != nil { + logger.WithFields("location", location.RealPath, "error", err).Warnf("cataloger failed") + continue + } + + for _, p := range discoveredPackages { + p.FoundBy = c.upstreamCataloger + packages = append(packages, p) + } + + relationships = append(relationships, discoveredRelationships...) + } + return packages, relationships, nil +} + +// selectFiles takes a set of file trees and resolves and file references of interest for future cataloging +func (c *Cataloger) selectFiles(resolver source.FileResolver) []request { + var requests []request + for _, proc := range c.processor { + requests = append(requests, proc(resolver, Environment{})...) + } + return requests +} diff --git a/syft/pkg/cataloger/generic/cataloger_test.go b/syft/pkg/cataloger/generic/cataloger_test.go new file mode 100644 index 000000000..fd864787a --- /dev/null +++ b/syft/pkg/cataloger/generic/cataloger_test.go @@ -0,0 +1,87 @@ +package generic + +import ( + "fmt" + "io/ioutil" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/source" +) + +func Test_Cataloger(t *testing.T) { + allParsedPaths := make(map[string]bool) + parser := func(resolver source.FileResolver, env *Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { + allParsedPaths[reader.AccessPath()] = true + contents, err := ioutil.ReadAll(reader) + require.NoError(t, err) + + if len(contents) == 0 { + return nil, nil, nil + } + + p := pkg.Package{ + Name: string(contents), + Locations: source.NewLocationSet(reader.Location), + } + r := artifact.Relationship{ + From: p, + To: p, + Type: artifact.ContainsRelationship, + } + + return []pkg.Package{p}, []artifact.Relationship{r}, nil + } + + upstream := "some-other-cataloger" + + expectedSelection := []string{"test-fixtures/last/path.txt", "test-fixtures/another-path.txt", "test-fixtures/a-path.txt", "test-fixtures/empty.txt"} + resolver := source.NewMockResolverForPaths(expectedSelection...) + cataloger := NewCataloger(upstream). + WithParserByPath(parser, "test-fixtures/another-path.txt", "test-fixtures/last/path.txt"). + WithParserByGlobs(parser, "**/a-path.txt", "**/empty.txt") + + actualPkgs, relationships, err := cataloger.Catalog(resolver) + assert.NoError(t, err) + + expectedPkgs := make(map[string]pkg.Package) + for _, path := range expectedSelection { + require.True(t, allParsedPaths[path]) + if path == "test-fixtures/empty.txt" { + continue // note: empty.txt won't become a package + } + expectedPkgs[path] = pkg.Package{ + FoundBy: upstream, + Name: fmt.Sprintf("%s file contents!", path), + } + } + + assert.Len(t, allParsedPaths, len(expectedSelection)) + assert.Len(t, actualPkgs, len(expectedPkgs)) + assert.Len(t, relationships, len(actualPkgs)) + + for _, p := range actualPkgs { + ls := p.Locations.ToSlice() + require.NotEmpty(t, ls) + ref := ls[0] + exP, ok := expectedPkgs[ref.RealPath] + if !ok { + t.Errorf("missing expected pkg: ref=%+v", ref) + continue + } + + // assigned by the generic cataloger + if p.FoundBy != exP.FoundBy { + t.Errorf("bad upstream: %s", p.FoundBy) + } + + // assigned by the parser + if exP.Name != p.Name { + t.Errorf("bad contents mapping: %+v", p.Locations) + } + } +} diff --git a/syft/pkg/cataloger/generic/parser.go b/syft/pkg/cataloger/generic/parser.go new file mode 100644 index 000000000..32b62f579 --- /dev/null +++ b/syft/pkg/cataloger/generic/parser.go @@ -0,0 +1,14 @@ +package generic + +import ( + "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/linux" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/source" +) + +type Environment struct { + LinuxRelease *linux.Release +} + +type Parser func(source.FileResolver, *Environment, source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) diff --git a/syft/pkg/cataloger/generic/test-fixtures/a-path.txt b/syft/pkg/cataloger/generic/test-fixtures/a-path.txt new file mode 100644 index 000000000..67e954034 --- /dev/null +++ b/syft/pkg/cataloger/generic/test-fixtures/a-path.txt @@ -0,0 +1 @@ +test-fixtures/a-path.txt file contents! \ No newline at end of file diff --git a/syft/pkg/cataloger/generic/test-fixtures/another-path.txt b/syft/pkg/cataloger/generic/test-fixtures/another-path.txt new file mode 100644 index 000000000..0d654f8fe --- /dev/null +++ b/syft/pkg/cataloger/generic/test-fixtures/another-path.txt @@ -0,0 +1 @@ +test-fixtures/another-path.txt file contents! \ No newline at end of file diff --git a/syft/pkg/cataloger/generic/test-fixtures/empty.txt b/syft/pkg/cataloger/generic/test-fixtures/empty.txt new file mode 100644 index 000000000..e69de29bb diff --git a/syft/pkg/cataloger/generic/test-fixtures/last/path.txt b/syft/pkg/cataloger/generic/test-fixtures/last/path.txt new file mode 100644 index 000000000..3d4a165ab --- /dev/null +++ b/syft/pkg/cataloger/generic/test-fixtures/last/path.txt @@ -0,0 +1 @@ +test-fixtures/last/path.txt file contents! \ No newline at end of file diff --git a/syft/pkg/dpkg_metadata.go b/syft/pkg/dpkg_metadata.go index fb405a85b..cf8e6cf05 100644 --- a/syft/pkg/dpkg_metadata.go +++ b/syft/pkg/dpkg_metadata.go @@ -65,7 +65,7 @@ func (m DpkgMetadata) PackageURL(distro *linux.Release) string { namespace, m.Package, m.Version, - purlQualifiers( + PURLQualifiers( qualifiers, distro, ), diff --git a/syft/pkg/package.go b/syft/pkg/package.go index ccfbbb01f..2f0981646 100644 --- a/syft/pkg/package.go +++ b/syft/pkg/package.go @@ -17,7 +17,7 @@ type Package struct { id artifact.ID `hash:"ignore"` Name string // the package name Version string // the version of the package - FoundBy string `cyclonedx:"foundBy"` // the specific cataloger that discovered this package + FoundBy string `hash:"ignore" cyclonedx:"foundBy"` // the specific cataloger that discovered this package Locations source.LocationSet // the locations that lead to the discovery of this package (note: this is not necessarily the locations that make up this package) Licenses []string // licenses discovered with the package metadata Language Language `cyclonedx:"language"` // the language ecosystem this package belongs to (e.g. JavaScript, Python, etc) diff --git a/syft/pkg/package_test.go b/syft/pkg/package_test.go index 7c3246a08..732f053ad 100644 --- a/syft/pkg/package_test.go +++ b/syft/pkg/package_test.go @@ -190,14 +190,6 @@ func TestIDUniqueness(t *testing.T) { }, expectedIDComparison: assert.NotEqual, }, - { - name: "foundBy is reflected", - transform: func(pkg Package) Package { - pkg.FoundBy = "new!" - return pkg - }, - expectedIDComparison: assert.NotEqual, - }, { name: "metadata mutation is reflected", transform: func(pkg Package) Package { diff --git a/syft/pkg/rpm_metadata.go b/syft/pkg/rpm_metadata.go index f2d8880d6..5e5ed6294 100644 --- a/syft/pkg/rpm_metadata.go +++ b/syft/pkg/rpm_metadata.go @@ -80,7 +80,7 @@ func (m RpmMetadata) PackageURL(distro *linux.Release) string { // for purl the epoch is a qualifier, not part of the version // see https://github.com/package-url/purl-spec/blob/master/PURL-TYPES.rst under the RPM section fmt.Sprintf("%s-%s", m.Version, m.Release), - purlQualifiers( + PURLQualifiers( qualifiers, distro, ), diff --git a/syft/pkg/url.go b/syft/pkg/url.go index 35b8f9c06..da4b62c30 100644 --- a/syft/pkg/url.go +++ b/syft/pkg/url.go @@ -67,7 +67,7 @@ func URL(p Package, release *linux.Release) string { ).ToString() } -func purlQualifiers(vars map[string]string, release *linux.Release) (q packageurl.Qualifiers) { +func PURLQualifiers(vars map[string]string, release *linux.Release) (q packageurl.Qualifiers) { keys := make([]string, 0, len(vars)) for k := range vars { keys = append(keys, k) diff --git a/syft/pkg/url_test.go b/syft/pkg/url_test.go index e2a01af91..64da300b1 100644 --- a/syft/pkg/url_test.go +++ b/syft/pkg/url_test.go @@ -200,24 +200,6 @@ func TestPackageURL(t *testing.T) { expected: "pkg:maven/g.id/a@v", }, - { - name: "alpm", - distro: &linux.Release{ - ID: "arch", - BuildID: "rolling", - }, - pkg: Package{ - Name: "linux", - Version: "5.10.0", - Type: AlpmPkg, - Metadata: AlpmMetadata{ - Package: "linux", - Version: "5.10.0", - }, - }, - - expected: "pkg:alpm/arch/linux@5.10.0?distro=arch-rolling", - }, { name: "cocoapods", pkg: Package{ @@ -288,6 +270,7 @@ func TestPackageURL(t *testing.T) { // testing microsoft packages is not valid for purl at this time expectedTypes.Remove(string(KbPkg)) expectedTypes.Remove(string(PortagePkg)) + expectedTypes.Remove(string(AlpmPkg)) for _, test := range tests { t.Run(test.name, func(t *testing.T) { diff --git a/syft/source/location.go b/syft/source/location.go index 04acee9f5..f131057d1 100644 --- a/syft/source/location.go +++ b/syft/source/location.go @@ -92,6 +92,13 @@ func NewVirtualLocationFromDirectory(responsePath, virtualResponsePath string, r } } +func (l Location) AccessPath() string { + if l.VirtualPath != "" { + return l.VirtualPath + } + return l.RealPath +} + func (l Location) String() string { str := "" if l.ref.ID() != 0 { diff --git a/syft/source/location_read_closer.go b/syft/source/location_read_closer.go new file mode 100644 index 000000000..b5aa2b6ef --- /dev/null +++ b/syft/source/location_read_closer.go @@ -0,0 +1,15 @@ +package source + +import "io" + +type LocationReadCloser struct { + Location + io.ReadCloser +} + +func NewLocationReadCloser(location Location, reader io.ReadCloser) LocationReadCloser { + return LocationReadCloser{ + Location: location, + ReadCloser: reader, + } +}