diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 716e5ab900..2a2fba5800 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -17,6 +17,9 @@ v35.4.0 (unreleased) - Display the optional steps in the Pipelines autodoc. https://github.com/aboutcode-org/scancode.io/issues/1822 +- Add new ``benchmark_purls`` pipeline. + https://github.com/aboutcode-org/scancode.io/issues/1804 + v35.3.0 (2025-08-20) -------------------- diff --git a/docs/built-in-pipelines.rst b/docs/built-in-pipelines.rst index 98b08be866..55baefa6df 100644 --- a/docs/built-in-pipelines.rst +++ b/docs/built-in-pipelines.rst @@ -46,6 +46,47 @@ Analyse Docker Windows Image :members: :member-order: bysource +.. _pipeline_benchmark_purls: + +Benchmark PURLs (addon) +----------------------- + +To check an **SBOM against a list of expected Package URLs (PURLs)**: + +1. **Create a new project** and provide two inputs: + + * The SBOM file you want to check. + * A list of expected PURLs in a ``*-purls.txt`` file with one PURL per line. + + .. tip:: You may also flag any filename using the ``purls`` input tag. + +2. **Run the pipelines**: + + * Select and run the ``load_sbom`` pipeline to load the SBOM. + * Run the ``benchmark_purls`` pipeline to validate against the expected PURLs. + +3. **Download the results** from the "output" section of the project. + +The output file contains only the differences between the discovered PURLs and +the expected PURLs: + +* Lines starting with ``-`` are missing from the project. +* Lines starting with ``+`` are unexpected in the project. + +.. note:: + The ``load_sbom`` pipeline is provided as an example to benchmark external + tools using SBOMs as inputs. You can also run ``benchmark_purls`` directly + after any ScanCode.io pipeline to validate the discovered PURLs. + +.. tip:: + You can provide multiple expected PURLs files. + + +.. autoclass:: scanpipe.pipelines.benchmark_purls.BenchmarkPurls() + :members: + :member-order: bysource + + .. _pipeline_collect_strings_gettext: Collect string with Xgettext (addon) diff --git a/docs/scanpipe-pipes.rst b/docs/scanpipe-pipes.rst index 3db7bc8a51..f7485ab01e 100644 --- a/docs/scanpipe-pipes.rst +++ b/docs/scanpipe-pipes.rst @@ -8,6 +8,11 @@ Generic .. automodule:: scanpipe.pipes :members: +Benchmark +--------- +.. automodule:: scanpipe.pipes.benchmark + :members: + ClamAV ------ .. automodule:: scanpipe.pipes.clamav diff --git a/pyproject.toml b/pyproject.toml index c3fa2f21a8..41fcc1d28c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -135,6 +135,7 @@ run = "scancodeio:combined_run" analyze_docker_image = "scanpipe.pipelines.analyze_docker:Docker" analyze_root_filesystem_or_vm_image = "scanpipe.pipelines.analyze_root_filesystem:RootFS" analyze_windows_docker_image = "scanpipe.pipelines.analyze_docker_windows:DockerWindows" +benchmark_purls = "scanpipe.pipelines.benchmark_purls:BenchmarkPurls" collect_strings_gettext = "scanpipe.pipelines.collect_strings_gettext:CollectStringsGettext" collect_symbols_ctags = "scanpipe.pipelines.collect_symbols_ctags:CollectSymbolsCtags" collect_symbols_pygments = "scanpipe.pipelines.collect_symbols_pygments:CollectSymbolsPygments" diff --git a/scanpipe/models.py b/scanpipe/models.py index c9aa0637ac..7d700b0fe5 100644 --- a/scanpipe/models.py +++ b/scanpipe/models.py @@ -1147,12 +1147,12 @@ def get_output_file_path(self, name, extension): filename = f"{name}-{filename_now()}.{extension}" return self.output_path / filename - def get_latest_output(self, filename): + def get_latest_output(self, filename, extension="json"): """ Return the latest output file with the "filename" prefix, for example "scancode-.json". """ - output_files = sorted(self.output_path.glob(f"*{filename}*.json")) + output_files = sorted(self.output_path.glob(f"*{filename}*.{extension}")) if output_files: return output_files[-1] diff --git a/scanpipe/pipelines/benchmark_purls.py b/scanpipe/pipelines/benchmark_purls.py new file mode 100644 index 0000000000..b92be7c1f6 --- /dev/null +++ b/scanpipe/pipelines/benchmark_purls.py @@ -0,0 +1,57 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# http://nexb.com and https://github.com/aboutcode-org/scancode.io +# The ScanCode.io software is licensed under the Apache License version 2.0. +# Data generated with ScanCode.io is provided as-is without warranties. +# ScanCode is a trademark of nexB Inc. +# +# You may not use this software except in compliance with the License. +# You may obtain a copy of the License at: http://apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software distributed +# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +# CONDITIONS OF ANY KIND, either express or implied. See the License for the +# specific language governing permissions and limitations under the License. +# +# Data Generated with ScanCode.io is provided on an "AS IS" BASIS, WITHOUT WARRANTIES +# OR CONDITIONS OF ANY KIND, either express or implied. No content created from +# ScanCode.io should be considered or used as legal advice. Consult an Attorney +# for any legal advice. +# +# ScanCode.io is a free software code scanning tool from nexB Inc. and others. +# Visit https://github.com/aboutcode-org/scancode.io for support and download. + +from scanpipe.pipelines import Pipeline +from scanpipe.pipes import benchmark + + +class BenchmarkPurls(Pipeline): + """ + Validate discovered project packages against a reference list of expected PURLs. + + The expected PURLs must be provided as a .txt file with one PURL per line. + Input files are recognized if: + + - They are tagged with "purls", or + - Their filename ends with "purls.txt" (e.g., "expected_purls.txt"). + + """ + + download_inputs = False + is_addon = True + + @classmethod + def steps(cls): + return ( + cls.get_expected_purls, + cls.compare_purls, + ) + + def get_expected_purls(self): + """Load the expected PURLs defined in the project inputs.""" + self.expected_purls = benchmark.get_expected_purls(self.project) + + def compare_purls(self): + """Run the PURLs diff and write the results to a project output file.""" + diff_results = benchmark.compare_purls(self.project, self.expected_purls) + output_file = self.project.get_output_file_path("benchmark_purls", "txt") + output_file.write_text("\n".join(diff_results)) diff --git a/scanpipe/pipes/benchmark.py b/scanpipe/pipes/benchmark.py new file mode 100644 index 0000000000..52a405cf8a --- /dev/null +++ b/scanpipe/pipes/benchmark.py @@ -0,0 +1,68 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# http://nexb.com and https://github.com/aboutcode-org/scancode.io +# The ScanCode.io software is licensed under the Apache License version 2.0. +# Data generated with ScanCode.io is provided as-is without warranties. +# ScanCode is a trademark of nexB Inc. +# +# You may not use this software except in compliance with the License. +# You may obtain a copy of the License at: http://apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software distributed +# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +# CONDITIONS OF ANY KIND, either express or implied. See the License for the +# specific language governing permissions and limitations under the License. +# +# Data Generated with ScanCode.io is provided on an "AS IS" BASIS, WITHOUT WARRANTIES +# OR CONDITIONS OF ANY KIND, either express or implied. No content created from +# ScanCode.io should be considered or used as legal advice. Consult an Attorney +# for any legal advice. +# +# ScanCode.io is a free software code scanning tool from nexB Inc. and others. +# Visit https://github.com/aboutcode-org/scancode.io for support and download. + +import difflib + + +def get_expected_purls(project): + """ + Load the expected Package URLs (PURLs) from the project's input files. + + A file is considered an expected PURLs source if: + - Its filename ends with ``*purls.txt``, or + - Its download URL includes the "#purls" tag. + + Each line in the file should contain one PURL. Returns a sorted, + deduplicated list of PURLs. Raises an exception if no input is found. + """ + purls_files = list(project.inputs("*purls.txt")) + purls_files.extend( + [input.path for input in project.inputsources.filter(tag="purls")] + ) + + expected_purls = [] + for file_path in purls_files: + expected_purls.extend(file_path.read_text().splitlines()) + + if not expected_purls: + raise Exception("Expected PURLs not provided.") + + return sorted(set(expected_purls)) + + +def compare_purls(project, expected_purls): + """ + Compare discovered project PURLs against the expected PURLs. + + Returns only the differences: + - Lines starting with '-' are missing from the project. + - Lines starting with '+' are unexpected in the project. + """ + project_packages = project.discoveredpackages.only_package_url_fields() + sorted_unique_purls = sorted({package.purl for package in project_packages}) + + diff_result = difflib.ndiff(sorted_unique_purls, expected_purls) + + # Keep only lines that are diffs (- or +) + filtered_diff = [line for line in diff_result if line.startswith(("-", "+"))] + + return filtered_diff diff --git a/scanpipe/tests/data/benchmark/alpine-3.22.1-expected-benchmark.txt b/scanpipe/tests/data/benchmark/alpine-3.22.1-expected-benchmark.txt new file mode 100644 index 0000000000..99941f8e1b --- /dev/null +++ b/scanpipe/tests/data/benchmark/alpine-3.22.1-expected-benchmark.txt @@ -0,0 +1,3 @@ +- pkg:alpine/alpine-keys@2.5-r0?arch=x86_64 ++ pkg:alpine/zlib@1.3.2-r2?arch=x86_64 ++ pkg:deb/debian/alpine-keys@2.5-r0?arch=x86_64 \ No newline at end of file diff --git a/scanpipe/tests/data/benchmark/alpine-3.22.1-expected-purls.txt b/scanpipe/tests/data/benchmark/alpine-3.22.1-expected-purls.txt new file mode 100644 index 0000000000..f9f66d0fcd --- /dev/null +++ b/scanpipe/tests/data/benchmark/alpine-3.22.1-expected-purls.txt @@ -0,0 +1,17 @@ +pkg:alpine/alpine-baselayout@3.7.0-r0?arch=x86_64 +pkg:alpine/alpine-baselayout-data@3.7.0-r0?arch=x86_64 +pkg:deb/debian/alpine-keys@2.5-r0?arch=x86_64 +pkg:alpine/alpine-release@3.22.1-r0?arch=x86_64 +pkg:alpine/apk-tools@2.14.9-r2?arch=x86_64 +pkg:alpine/busybox@1.37.0-r18?arch=x86_64 +pkg:alpine/busybox-binsh@1.37.0-r18?arch=x86_64 +pkg:alpine/ca-certificates-bundle@20250619-r0?arch=x86_64 +pkg:alpine/libapk2@2.14.9-r2?arch=x86_64 +pkg:alpine/libcrypto3@3.5.1-r0?arch=x86_64 +pkg:alpine/libssl3@3.5.1-r0?arch=x86_64 +pkg:alpine/musl@1.2.5-r10?arch=x86_64 +pkg:alpine/musl-utils@1.2.5-r10?arch=x86_64 +pkg:alpine/scanelf@1.3.8-r1?arch=x86_64 +pkg:alpine/ssl_client@1.37.0-r18?arch=x86_64 +pkg:alpine/zlib@1.3.1-r2?arch=x86_64 +pkg:alpine/zlib@1.3.2-r2?arch=x86_64 diff --git a/scanpipe/tests/data/benchmark/scancodeio_alpine_3.22.1.cdx.json b/scanpipe/tests/data/benchmark/scancodeio_alpine_3.22.1.cdx.json new file mode 100644 index 0000000000..44cbd02c36 --- /dev/null +++ b/scanpipe/tests/data/benchmark/scancodeio_alpine_3.22.1.cdx.json @@ -0,0 +1,580 @@ +{ + "$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.6", + "serialNumber": "urn:uuid:5a01ec9d-04e9-4a0e-b00e-acdd4ba89e5e", + "version": 1, + "metadata": { + "component": { + "bom-ref": "7bdbcef4-069b-48e8-98f0-40cfad8831b0", + "name": "alpine_3.22.1", + "type": "library" + }, + "properties": [ + { + "name": "notice", + "value": "Generated with ScanCode.io and provided on an \"AS IS\" BASIS, WITHOUT WARRANTIES\nOR CONDITIONS OF ANY KIND, either express or implied.\nNo content created from ScanCode.io should be considered or used as legal advice.\nConsult an Attorney for any legal advice.\nScanCode.io is a free software code scanning tool from nexB Inc. and others\nlicensed under the Apache License version 2.0.\nScanCode is a trademark of nexB Inc.\nVisit https://github.com/nexB/scancode.io for support and download.\n" + } + ], + "timestamp": "2025-09-02T10:15:59.743697+00:00", + "tools": [ + { + "name": "ScanCode.io", + "version": "35.3.0" + } + ] + }, + "components": [ + { + "bom-ref": "pkg:alpine/alpine-baselayout@3.7.0-r0?arch=x86_64&uuid=5e1c9abe-d389-4ec4-9743-16fd05589686", + "copyright": "", + "description": "Alpine base dir structure and init scripts", + "externalReferences": [ + { + "type": "vcs", + "url": "git+https://git.alpinelinux.org/aports/commit/?id=540d8477019887cc6534152e92c3b12676e35d7b" + } + ], + "licenses": [ + { + "expression": "GPL-2.0-only" + } + ], + "name": "alpine-baselayout", + "properties": [ + { + "name": "aboutcode:homepage_url", + "value": "https://git.alpinelinux.org/cgit/aports/tree/main/alpine-baselayout" + }, + { + "name": "aboutcode:package_uid", + "value": "pkg:alpine/alpine-baselayout@3.7.0-r0?arch=x86_64&uuid=5e1c9abe-d389-4ec4-9743-16fd05589686" + } + ], + "purl": "pkg:alpine/alpine-baselayout@3.7.0-r0?arch=x86_64", + "type": "library", + "version": "3.7.0-r0" + }, + { + "bom-ref": "pkg:alpine/alpine-baselayout-data@3.7.0-r0?arch=x86_64&uuid=79dba268-e693-45bf-871f-bd487cecf059", + "copyright": "", + "description": "Alpine base dir structure and init scripts", + "externalReferences": [ + { + "type": "vcs", + "url": "git+https://git.alpinelinux.org/aports/commit/?id=540d8477019887cc6534152e92c3b12676e35d7b" + } + ], + "licenses": [ + { + "expression": "GPL-2.0-only" + } + ], + "name": "alpine-baselayout-data", + "properties": [ + { + "name": "aboutcode:homepage_url", + "value": "https://git.alpinelinux.org/cgit/aports/tree/main/alpine-baselayout" + }, + { + "name": "aboutcode:package_uid", + "value": "pkg:alpine/alpine-baselayout-data@3.7.0-r0?arch=x86_64&uuid=79dba268-e693-45bf-871f-bd487cecf059" + } + ], + "purl": "pkg:alpine/alpine-baselayout-data@3.7.0-r0?arch=x86_64", + "type": "library", + "version": "3.7.0-r0" + }, + { + "bom-ref": "pkg:alpine/alpine-keys@2.5-r0?arch=x86_64&uuid=17722541-2c9c-4197-b608-000024809c10", + "copyright": "", + "description": "Public keys for Alpine Linux packages", + "externalReferences": [ + { + "type": "vcs", + "url": "git+https://git.alpinelinux.org/aports/commit/?id=6d473fb38effb2389f567b29fb7eb27039b3a279" + } + ], + "licenses": [ + { + "expression": "MIT" + } + ], + "name": "alpine-keys", + "properties": [ + { + "name": "aboutcode:homepage_url", + "value": "https://alpinelinux.org" + }, + { + "name": "aboutcode:package_uid", + "value": "pkg:alpine/alpine-keys@2.5-r0?arch=x86_64&uuid=17722541-2c9c-4197-b608-000024809c10" + } + ], + "purl": "pkg:alpine/alpine-keys@2.5-r0?arch=x86_64", + "type": "library", + "version": "2.5-r0" + }, + { + "bom-ref": "pkg:alpine/alpine-release@3.22.1-r0?arch=x86_64&uuid=01456176-0d31-4790-ae90-1302ac777f02", + "copyright": "", + "description": "Alpine release data", + "externalReferences": [ + { + "type": "vcs", + "url": "git+https://git.alpinelinux.org/aports/commit/?id=96586cb7ed12d722ab560ee2ec0fc5294a9dc84b" + } + ], + "licenses": [ + { + "expression": "MIT" + } + ], + "name": "alpine-release", + "properties": [ + { + "name": "aboutcode:homepage_url", + "value": "https://alpinelinux.org" + }, + { + "name": "aboutcode:package_uid", + "value": "pkg:alpine/alpine-release@3.22.1-r0?arch=x86_64&uuid=01456176-0d31-4790-ae90-1302ac777f02" + } + ], + "purl": "pkg:alpine/alpine-release@3.22.1-r0?arch=x86_64", + "type": "library", + "version": "3.22.1-r0" + }, + { + "bom-ref": "pkg:alpine/apk-tools@2.14.9-r2?arch=x86_64&uuid=952e40e5-9797-475c-bbac-3c21e41af9d4", + "copyright": "", + "description": "Alpine Package Keeper - package manager for alpine", + "externalReferences": [ + { + "type": "vcs", + "url": "git+https://git.alpinelinux.org/aports/commit/?id=a73d5616eaf4e16a219366d848d8e46b85d458ea" + } + ], + "licenses": [ + { + "expression": "GPL-2.0-only" + } + ], + "name": "apk-tools", + "properties": [ + { + "name": "aboutcode:homepage_url", + "value": "https://gitlab.alpinelinux.org/alpine/apk-tools" + }, + { + "name": "aboutcode:package_uid", + "value": "pkg:alpine/apk-tools@2.14.9-r2?arch=x86_64&uuid=952e40e5-9797-475c-bbac-3c21e41af9d4" + } + ], + "purl": "pkg:alpine/apk-tools@2.14.9-r2?arch=x86_64", + "type": "library", + "version": "2.14.9-r2" + }, + { + "bom-ref": "pkg:alpine/busybox@1.37.0-r18?arch=x86_64&uuid=16daa514-4087-427d-acf1-b024f5c3e64b", + "copyright": "", + "description": "Size optimized toolbox of many common UNIX utilities", + "externalReferences": [ + { + "type": "vcs", + "url": "git+https://git.alpinelinux.org/aports/commit/?id=28ab379633dca30ff5d3ebd36b274d15a2f15719" + } + ], + "licenses": [ + { + "expression": "GPL-2.0-only" + } + ], + "name": "busybox", + "properties": [ + { + "name": "aboutcode:homepage_url", + "value": "https://busybox.net/" + }, + { + "name": "aboutcode:package_uid", + "value": "pkg:alpine/busybox@1.37.0-r18?arch=x86_64&uuid=16daa514-4087-427d-acf1-b024f5c3e64b" + } + ], + "purl": "pkg:alpine/busybox@1.37.0-r18?arch=x86_64", + "type": "library", + "version": "1.37.0-r18" + }, + { + "bom-ref": "pkg:alpine/busybox-binsh@1.37.0-r18?arch=x86_64&uuid=1cd47628-7794-497d-b482-74917a45477a", + "copyright": "", + "description": "busybox ash /bin/sh", + "externalReferences": [ + { + "type": "vcs", + "url": "git+https://git.alpinelinux.org/aports/commit/?id=28ab379633dca30ff5d3ebd36b274d15a2f15719" + } + ], + "licenses": [ + { + "expression": "GPL-2.0-only" + } + ], + "name": "busybox-binsh", + "properties": [ + { + "name": "aboutcode:homepage_url", + "value": "https://busybox.net/" + }, + { + "name": "aboutcode:package_uid", + "value": "pkg:alpine/busybox-binsh@1.37.0-r18?arch=x86_64&uuid=1cd47628-7794-497d-b482-74917a45477a" + } + ], + "purl": "pkg:alpine/busybox-binsh@1.37.0-r18?arch=x86_64", + "type": "library", + "version": "1.37.0-r18" + }, + { + "bom-ref": "pkg:alpine/ca-certificates-bundle@20250619-r0?arch=x86_64&uuid=f80e5418-5b1f-4d52-806b-740ba17fe1b0", + "copyright": "", + "description": "Pre generated bundle of Mozilla certificates", + "externalReferences": [ + { + "type": "vcs", + "url": "git+https://git.alpinelinux.org/aports/commit/?id=78856465c851ab2ce0cb1e12fe9f57d04204919a" + } + ], + "licenses": [ + { + "expression": "MPL-2.0 AND MIT" + } + ], + "name": "ca-certificates-bundle", + "properties": [ + { + "name": "aboutcode:homepage_url", + "value": "https://www.mozilla.org/en-US/about/governance/policies/security-group/certs/" + }, + { + "name": "aboutcode:package_uid", + "value": "pkg:alpine/ca-certificates-bundle@20250619-r0?arch=x86_64&uuid=f80e5418-5b1f-4d52-806b-740ba17fe1b0" + } + ], + "purl": "pkg:alpine/ca-certificates-bundle@20250619-r0?arch=x86_64", + "type": "library", + "version": "20250619-r0" + }, + { + "bom-ref": "pkg:alpine/libapk2@2.14.9-r2?arch=x86_64&uuid=cf4b2dd9-82fa-45eb-a8c4-5af7428fa43a", + "copyright": "", + "description": "Alpine Package Keeper - package manager for alpine", + "externalReferences": [ + { + "type": "vcs", + "url": "git+https://git.alpinelinux.org/aports/commit/?id=a73d5616eaf4e16a219366d848d8e46b85d458ea" + } + ], + "licenses": [ + { + "expression": "GPL-2.0-only" + } + ], + "name": "libapk2", + "properties": [ + { + "name": "aboutcode:homepage_url", + "value": "https://gitlab.alpinelinux.org/alpine/apk-tools" + }, + { + "name": "aboutcode:package_uid", + "value": "pkg:alpine/libapk2@2.14.9-r2?arch=x86_64&uuid=cf4b2dd9-82fa-45eb-a8c4-5af7428fa43a" + } + ], + "purl": "pkg:alpine/libapk2@2.14.9-r2?arch=x86_64", + "type": "library", + "version": "2.14.9-r2" + }, + { + "bom-ref": "pkg:alpine/libcrypto3@3.5.1-r0?arch=x86_64&uuid=4643159d-61bc-43e1-a1cd-72e2f543da1b", + "copyright": "", + "description": "Crypto library from openssl", + "externalReferences": [ + { + "type": "vcs", + "url": "git+https://git.alpinelinux.org/aports/commit/?id=370a62f0ac139d30d09aba7ed93fcbf455a032ae" + } + ], + "licenses": [ + { + "expression": "Apache-2.0" + } + ], + "name": "libcrypto3", + "properties": [ + { + "name": "aboutcode:homepage_url", + "value": "https://www.openssl.org/" + }, + { + "name": "aboutcode:package_uid", + "value": "pkg:alpine/libcrypto3@3.5.1-r0?arch=x86_64&uuid=4643159d-61bc-43e1-a1cd-72e2f543da1b" + } + ], + "purl": "pkg:alpine/libcrypto3@3.5.1-r0?arch=x86_64", + "type": "library", + "version": "3.5.1-r0" + }, + { + "bom-ref": "pkg:alpine/libssl3@3.5.1-r0?arch=x86_64&uuid=ebcd8874-944c-47d3-af95-355b488adbcc", + "copyright": "", + "description": "SSL shared libraries", + "externalReferences": [ + { + "type": "vcs", + "url": "git+https://git.alpinelinux.org/aports/commit/?id=370a62f0ac139d30d09aba7ed93fcbf455a032ae" + } + ], + "licenses": [ + { + "expression": "Apache-2.0" + } + ], + "name": "libssl3", + "properties": [ + { + "name": "aboutcode:homepage_url", + "value": "https://www.openssl.org/" + }, + { + "name": "aboutcode:package_uid", + "value": "pkg:alpine/libssl3@3.5.1-r0?arch=x86_64&uuid=ebcd8874-944c-47d3-af95-355b488adbcc" + } + ], + "purl": "pkg:alpine/libssl3@3.5.1-r0?arch=x86_64", + "type": "library", + "version": "3.5.1-r0" + }, + { + "bom-ref": "pkg:alpine/musl@1.2.5-r10?arch=x86_64&uuid=b40261e8-1a2f-4d9d-8010-6e13db9b80d7", + "copyright": "", + "description": "the musl c library (libc) implementation", + "externalReferences": [ + { + "type": "vcs", + "url": "git+https://git.alpinelinux.org/aports/commit/?id=1a5f5e699b2eae883e73c93a789f87bd89a2a190" + } + ], + "licenses": [ + { + "expression": "MIT" + } + ], + "name": "musl", + "properties": [ + { + "name": "aboutcode:homepage_url", + "value": "https://musl.libc.org/" + }, + { + "name": "aboutcode:package_uid", + "value": "pkg:alpine/musl@1.2.5-r10?arch=x86_64&uuid=b40261e8-1a2f-4d9d-8010-6e13db9b80d7" + } + ], + "purl": "pkg:alpine/musl@1.2.5-r10?arch=x86_64", + "type": "library", + "version": "1.2.5-r10" + }, + { + "bom-ref": "pkg:alpine/musl-utils@1.2.5-r10?arch=x86_64&uuid=3942f96e-224f-4aaa-9be7-310d082de737", + "copyright": "", + "description": "the musl c library (libc) implementation", + "externalReferences": [ + { + "type": "vcs", + "url": "git+https://git.alpinelinux.org/aports/commit/?id=1a5f5e699b2eae883e73c93a789f87bd89a2a190" + } + ], + "licenses": [ + { + "expression": "MIT AND BSD-2-Clause AND GPL-2.0-or-later" + } + ], + "name": "musl-utils", + "properties": [ + { + "name": "aboutcode:homepage_url", + "value": "https://musl.libc.org/" + }, + { + "name": "aboutcode:package_uid", + "value": "pkg:alpine/musl-utils@1.2.5-r10?arch=x86_64&uuid=3942f96e-224f-4aaa-9be7-310d082de737" + } + ], + "purl": "pkg:alpine/musl-utils@1.2.5-r10?arch=x86_64", + "type": "library", + "version": "1.2.5-r10" + }, + { + "bom-ref": "pkg:alpine/scanelf@1.3.8-r1?arch=x86_64&uuid=48546d84-5a6c-427c-8b5b-b20e98b96f23", + "copyright": "", + "description": "Scan ELF binaries for stuff", + "externalReferences": [ + { + "type": "vcs", + "url": "git+https://git.alpinelinux.org/aports/commit/?id=9818c555f344e3085d0dd1f3d917d8e23e244cb1" + } + ], + "licenses": [ + { + "expression": "GPL-2.0-only" + } + ], + "name": "scanelf", + "properties": [ + { + "name": "aboutcode:homepage_url", + "value": "https://wiki.gentoo.org/wiki/Hardened/PaX_Utilities" + }, + { + "name": "aboutcode:package_uid", + "value": "pkg:alpine/scanelf@1.3.8-r1?arch=x86_64&uuid=48546d84-5a6c-427c-8b5b-b20e98b96f23" + } + ], + "purl": "pkg:alpine/scanelf@1.3.8-r1?arch=x86_64", + "type": "library", + "version": "1.3.8-r1" + }, + { + "bom-ref": "pkg:alpine/ssl_client@1.37.0-r18?arch=x86_64&uuid=c7ae18d1-5277-4487-85ce-1e1363bf5a52", + "copyright": "", + "description": "External ssl_client for busybox wget", + "externalReferences": [ + { + "type": "vcs", + "url": "git+https://git.alpinelinux.org/aports/commit/?id=28ab379633dca30ff5d3ebd36b274d15a2f15719" + } + ], + "licenses": [ + { + "expression": "GPL-2.0-only" + } + ], + "name": "ssl_client", + "properties": [ + { + "name": "aboutcode:homepage_url", + "value": "https://busybox.net/" + }, + { + "name": "aboutcode:package_uid", + "value": "pkg:alpine/ssl_client@1.37.0-r18?arch=x86_64&uuid=c7ae18d1-5277-4487-85ce-1e1363bf5a52" + } + ], + "purl": "pkg:alpine/ssl_client@1.37.0-r18?arch=x86_64", + "type": "library", + "version": "1.37.0-r18" + }, + { + "bom-ref": "pkg:alpine/zlib@1.3.1-r2?arch=x86_64&uuid=5d218285-63a3-4ce4-ac27-bc8cabfffdaf", + "copyright": "", + "description": "A compression/decompression Library", + "externalReferences": [ + { + "type": "vcs", + "url": "git+https://git.alpinelinux.org/aports/commit/?id=4c2722e7ecc1c5e3ba882429044a101497121223" + } + ], + "licenses": [ + { + "expression": "Zlib" + } + ], + "name": "zlib", + "properties": [ + { + "name": "aboutcode:homepage_url", + "value": "https://zlib.net/" + }, + { + "name": "aboutcode:package_uid", + "value": "pkg:alpine/zlib@1.3.1-r2?arch=x86_64&uuid=5d218285-63a3-4ce4-ac27-bc8cabfffdaf" + } + ], + "purl": "pkg:alpine/zlib@1.3.1-r2?arch=x86_64", + "type": "library", + "version": "1.3.1-r2" + } + ], + "dependencies": [ + { + "dependsOn": [ + "pkg:alpine/alpine-baselayout-data@3.7.0-r0?arch=x86_64&uuid=79dba268-e693-45bf-871f-bd487cecf059", + "pkg:alpine/alpine-baselayout@3.7.0-r0?arch=x86_64&uuid=5e1c9abe-d389-4ec4-9743-16fd05589686", + "pkg:alpine/alpine-keys@2.5-r0?arch=x86_64&uuid=17722541-2c9c-4197-b608-000024809c10", + "pkg:alpine/alpine-release@3.22.1-r0?arch=x86_64&uuid=01456176-0d31-4790-ae90-1302ac777f02", + "pkg:alpine/apk-tools@2.14.9-r2?arch=x86_64&uuid=952e40e5-9797-475c-bbac-3c21e41af9d4", + "pkg:alpine/busybox-binsh@1.37.0-r18?arch=x86_64&uuid=1cd47628-7794-497d-b482-74917a45477a", + "pkg:alpine/busybox@1.37.0-r18?arch=x86_64&uuid=16daa514-4087-427d-acf1-b024f5c3e64b", + "pkg:alpine/ca-certificates-bundle@20250619-r0?arch=x86_64&uuid=f80e5418-5b1f-4d52-806b-740ba17fe1b0", + "pkg:alpine/libapk2@2.14.9-r2?arch=x86_64&uuid=cf4b2dd9-82fa-45eb-a8c4-5af7428fa43a", + "pkg:alpine/libcrypto3@3.5.1-r0?arch=x86_64&uuid=4643159d-61bc-43e1-a1cd-72e2f543da1b", + "pkg:alpine/libssl3@3.5.1-r0?arch=x86_64&uuid=ebcd8874-944c-47d3-af95-355b488adbcc", + "pkg:alpine/musl-utils@1.2.5-r10?arch=x86_64&uuid=3942f96e-224f-4aaa-9be7-310d082de737", + "pkg:alpine/musl@1.2.5-r10?arch=x86_64&uuid=b40261e8-1a2f-4d9d-8010-6e13db9b80d7", + "pkg:alpine/scanelf@1.3.8-r1?arch=x86_64&uuid=48546d84-5a6c-427c-8b5b-b20e98b96f23", + "pkg:alpine/ssl_client@1.37.0-r18?arch=x86_64&uuid=c7ae18d1-5277-4487-85ce-1e1363bf5a52", + "pkg:alpine/zlib@1.3.1-r2?arch=x86_64&uuid=5d218285-63a3-4ce4-ac27-bc8cabfffdaf" + ], + "ref": "7bdbcef4-069b-48e8-98f0-40cfad8831b0" + }, + { + "ref": "pkg:alpine/alpine-baselayout-data@3.7.0-r0?arch=x86_64&uuid=79dba268-e693-45bf-871f-bd487cecf059" + }, + { + "ref": "pkg:alpine/alpine-baselayout@3.7.0-r0?arch=x86_64&uuid=5e1c9abe-d389-4ec4-9743-16fd05589686" + }, + { + "ref": "pkg:alpine/alpine-keys@2.5-r0?arch=x86_64&uuid=17722541-2c9c-4197-b608-000024809c10" + }, + { + "ref": "pkg:alpine/alpine-release@3.22.1-r0?arch=x86_64&uuid=01456176-0d31-4790-ae90-1302ac777f02" + }, + { + "ref": "pkg:alpine/apk-tools@2.14.9-r2?arch=x86_64&uuid=952e40e5-9797-475c-bbac-3c21e41af9d4" + }, + { + "ref": "pkg:alpine/busybox-binsh@1.37.0-r18?arch=x86_64&uuid=1cd47628-7794-497d-b482-74917a45477a" + }, + { + "ref": "pkg:alpine/busybox@1.37.0-r18?arch=x86_64&uuid=16daa514-4087-427d-acf1-b024f5c3e64b" + }, + { + "ref": "pkg:alpine/ca-certificates-bundle@20250619-r0?arch=x86_64&uuid=f80e5418-5b1f-4d52-806b-740ba17fe1b0" + }, + { + "ref": "pkg:alpine/libapk2@2.14.9-r2?arch=x86_64&uuid=cf4b2dd9-82fa-45eb-a8c4-5af7428fa43a" + }, + { + "ref": "pkg:alpine/libcrypto3@3.5.1-r0?arch=x86_64&uuid=4643159d-61bc-43e1-a1cd-72e2f543da1b" + }, + { + "ref": "pkg:alpine/libssl3@3.5.1-r0?arch=x86_64&uuid=ebcd8874-944c-47d3-af95-355b488adbcc" + }, + { + "ref": "pkg:alpine/musl-utils@1.2.5-r10?arch=x86_64&uuid=3942f96e-224f-4aaa-9be7-310d082de737" + }, + { + "ref": "pkg:alpine/musl@1.2.5-r10?arch=x86_64&uuid=b40261e8-1a2f-4d9d-8010-6e13db9b80d7" + }, + { + "ref": "pkg:alpine/scanelf@1.3.8-r1?arch=x86_64&uuid=48546d84-5a6c-427c-8b5b-b20e98b96f23" + }, + { + "ref": "pkg:alpine/ssl_client@1.37.0-r18?arch=x86_64&uuid=c7ae18d1-5277-4487-85ce-1e1363bf5a52" + }, + { + "ref": "pkg:alpine/zlib@1.3.1-r2?arch=x86_64&uuid=5d218285-63a3-4ce4-ac27-bc8cabfffdaf" + } + ] +} \ No newline at end of file diff --git a/scanpipe/tests/test_pipelines.py b/scanpipe/tests/test_pipelines.py index c003600a2d..d3cd60af45 100644 --- a/scanpipe/tests/test_pipelines.py +++ b/scanpipe/tests/test_pipelines.py @@ -2000,3 +2000,28 @@ def test_scanpipe_enrich_with_purldb_pipeline_integration( run.refresh_from_db() self.assertIn("pkg:npm/csvtojson@2.0.10 ['release_date'", run.log) self.assertIn("1 discovered package enriched with the PurlDB.", run.log) + + def test_scanpipe_benchmark_purls_pipeline_integration(self): + project1 = make_project(name="Analysis") + + file_location = self.data / "benchmark" / "scancodeio_alpine_3.22.1.cdx.json" + project1.copy_input_from(file_location) + file_location = self.data / "benchmark" / "alpine-3.22.1-expected-purls.txt" + project1.copy_input_from(file_location) + + run = project1.add_pipeline(pipeline_name="load_sbom") + pipeline = run.make_pipeline_instance() + pipeline.execute() + self.assertEqual(2, project1.codebaseresources.count()) + self.assertEqual(16, project1.discoveredpackages.count()) + + run = project1.add_pipeline(pipeline_name="benchmark_purls") + pipeline = run.make_pipeline_instance() + exitcode, out = pipeline.execute() + self.assertEqual(0, exitcode, msg=out) + + result_file = project1.get_latest_output( + filename="benchmark_purls", extension="txt" + ) + expected_file = self.data / "benchmark" / "alpine-3.22.1-expected-benchmark.txt" + self.assertEqual(expected_file.read_text(), result_file.read_text())