Skip to content

Commit 5b94506

Browse files
committed
Use benchmark.compare_purls to assert the SBOM PURLs #1726
Signed-off-by: tdruez <[email protected]>
1 parent 56b12bb commit 5b94506

File tree

2 files changed

+233
-3
lines changed

2 files changed

+233
-3
lines changed

scanpipe/pipes/benchmark.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,18 @@ def get_expected_purls(project):
4949
return sorted(set(expected_purls))
5050

5151

52+
def get_unique_project_purls(project):
53+
"""
54+
Return the sorted list of unique Package URLs (PURLs) discovered in the project.
55+
56+
Extracts the ``purl`` field from all discovered packages, removes duplicates,
57+
and sorts the result to provide a deterministic list of project PURLs.
58+
"""
59+
project_packages = project.discoveredpackages.only_package_url_fields()
60+
sorted_unique_purls = sorted({package.purl for package in project_packages})
61+
return sorted_unique_purls
62+
63+
5264
def compare_purls(project, expected_purls):
5365
"""
5466
Compare discovered project PURLs against the expected PURLs.
@@ -57,10 +69,10 @@ def compare_purls(project, expected_purls):
5769
- Lines starting with '-' are missing from the project.
5870
- Lines starting with '+' are unexpected in the project.
5971
"""
60-
project_packages = project.discoveredpackages.only_package_url_fields()
61-
sorted_unique_purls = sorted({package.purl for package in project_packages})
72+
sorted_project_purls = get_unique_project_purls(project)
73+
print(sorted_project_purls)
6274

63-
diff_result = difflib.ndiff(sorted_unique_purls, expected_purls)
75+
diff_result = difflib.ndiff(sorted_project_purls, expected_purls)
6476

6577
# Keep only lines that are diffs (- or +)
6678
filtered_diff = [line for line in diff_result if line.startswith(("-", "+"))]

scanpipe/tests/test_sca_integrations.py

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@
4545
"packages": 16,
4646
"packages_vulnerable": 7,
4747
"dependencies": 25,
48+
"purls": [
49+
"pkg:unknown/[email protected]",
50+
],
4851
},
4952
5053
Tip: run the test once without expected values, check the counts from the
@@ -62,6 +65,7 @@
6265

6366
from django.test import TestCase
6467

68+
from scanpipe.pipes import benchmark
6569
from scanpipe.tests import make_project
6670

6771
# Mapping of SBOM filenames to expected counts.
@@ -71,6 +75,7 @@
7175
# - ``packages``: DiscoveredPackages
7276
# - ``packages_vulnerable``: Vulnerable DiscoveredPackages
7377
# - ``dependencies``: DiscoveredDependencies
78+
# - ``purls``: The list of PURLs present in the SBOM
7479
SCA_INTEGRATIONS_TEST_DATA = {
7580
### Anchore Grype
7681
# $ grype -v -o cyclonedx-json \
@@ -80,6 +85,102 @@
8085
"packages": 94,
8186
"packages_vulnerable": 7,
8287
"dependencies": 20,
88+
"purls": [
89+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=alpine-3.17.0&upstream=alpine-baselayout",
90+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=alpine-3.17.0",
91+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=alpine-3.17.0",
92+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=alpine-3.17.0",
93+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=alpine-3.17.0&upstream=busybox",
94+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=alpine-3.17.0",
95+
"pkg:apk/alpine/ca-certificates-bundle@20220614-r2?arch=x86_64&distro=alpine-3.17.0&upstream=ca-certificates",
96+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=alpine-3.17.0&upstream=libc-dev",
97+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=alpine-3.17.0&upstream=openssl",
98+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=alpine-3.17.0&upstream=openssl",
99+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=alpine-3.17.0&upstream=musl",
100+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=alpine-3.17.0",
101+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=alpine-3.17.0&upstream=pax-utils",
102+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=alpine-3.17.0&upstream=busybox",
103+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=alpine-3.17.0",
104+
"pkg:unknown/[email protected]",
105+
"pkg:unknown/bin/busybox",
106+
"pkg:unknown/etc/apk/keys/alpine-devel%40lists.alpinelinux.org-4a6a0840.rsa.pub",
107+
"pkg:unknown/etc/apk/keys/alpine-devel%40lists.alpinelinux.org-5243ef4b.rsa.pub",
108+
"pkg:unknown/etc/apk/keys/alpine-devel%40lists.alpinelinux.org-5261cecb.rsa.pub",
109+
"pkg:unknown/etc/apk/keys/alpine-devel%40lists.alpinelinux.org-6165ee59.rsa.pub",
110+
"pkg:unknown/etc/apk/keys/alpine-devel%40lists.alpinelinux.org-61666e3f.rsa.pub",
111+
"pkg:unknown/etc/crontabs/root",
112+
"pkg:unknown/etc/fstab",
113+
"pkg:unknown/etc/group",
114+
"pkg:unknown/etc/hostname",
115+
"pkg:unknown/etc/hosts",
116+
"pkg:unknown/etc/inittab",
117+
"pkg:unknown/etc/logrotate.d/acpid",
118+
"pkg:unknown/etc/modprobe.d/aliases.conf",
119+
"pkg:unknown/etc/modprobe.d/blacklist.conf",
120+
"pkg:unknown/etc/modprobe.d/i386.conf",
121+
"pkg:unknown/etc/modprobe.d/kms.conf",
122+
"pkg:unknown/etc/modules",
123+
"pkg:unknown/etc/motd",
124+
"pkg:unknown/etc/network/if-up.d/dad",
125+
"pkg:unknown/etc/nsswitch.conf",
126+
"pkg:unknown/etc/passwd",
127+
"pkg:unknown/etc/profile",
128+
"pkg:unknown/etc/profile.d/README",
129+
"pkg:unknown/etc/profile.d/color_prompt.sh.disabled",
130+
"pkg:unknown/etc/profile.d/locale.sh",
131+
"pkg:unknown/etc/protocols",
132+
"pkg:unknown/etc/securetty",
133+
"pkg:unknown/etc/services",
134+
"pkg:unknown/etc/shadow",
135+
"pkg:unknown/etc/shells",
136+
"pkg:unknown/etc/ssl/certs/ca-certificates.crt",
137+
"pkg:unknown/etc/ssl/ct_log_list.cnf",
138+
"pkg:unknown/etc/ssl/ct_log_list.cnf.dist",
139+
"pkg:unknown/etc/ssl/misc/CA.pl",
140+
"pkg:unknown/etc/ssl/misc/tsget.pl",
141+
"pkg:unknown/etc/ssl/openssl.cnf",
142+
"pkg:unknown/etc/ssl/openssl.cnf.dist",
143+
"pkg:unknown/etc/sysctl.conf",
144+
"pkg:unknown/etc/udhcpd.conf",
145+
"pkg:unknown/lib/apk/db/installed",
146+
"pkg:unknown/lib/ld-musl-x86_64.so.1",
147+
"pkg:unknown/lib/libapk.so.3.12.0",
148+
"pkg:unknown/lib/libcrypto.so.3",
149+
"pkg:unknown/lib/libssl.so.3",
150+
"pkg:unknown/lib/libz.so.1.2.13",
151+
"pkg:unknown/lib/sysctl.d/00-alpine.conf",
152+
"pkg:unknown/sbin/apk",
153+
"pkg:unknown/sbin/ldconfig",
154+
"pkg:unknown/usr/bin/getconf",
155+
"pkg:unknown/usr/bin/getent",
156+
"pkg:unknown/usr/bin/iconv",
157+
"pkg:unknown/usr/bin/ldd",
158+
"pkg:unknown/usr/bin/scanelf",
159+
"pkg:unknown/usr/bin/ssl_client",
160+
"pkg:unknown/usr/lib/engines-3/afalg.so",
161+
"pkg:unknown/usr/lib/engines-3/capi.so",
162+
"pkg:unknown/usr/lib/engines-3/loader_attic.so",
163+
"pkg:unknown/usr/lib/engines-3/padlock.so",
164+
"pkg:unknown/usr/lib/ossl-modules/legacy.so",
165+
"pkg:unknown/usr/share/apk/keys/alpine-devel%40lists.alpinelinux.org-4a6a0840.rsa.pub",
166+
"pkg:unknown/usr/share/apk/keys/alpine-devel%40lists.alpinelinux.org-5243ef4b.rsa.pub",
167+
"pkg:unknown/usr/share/apk/keys/alpine-devel%40lists.alpinelinux.org-524d27bb.rsa.pub",
168+
"pkg:unknown/usr/share/apk/keys/alpine-devel%40lists.alpinelinux.org-5261cecb.rsa.pub",
169+
"pkg:unknown/usr/share/apk/keys/alpine-devel%40lists.alpinelinux.org-58199dcc.rsa.pub",
170+
"pkg:unknown/usr/share/apk/keys/alpine-devel%40lists.alpinelinux.org-58cbb476.rsa.pub",
171+
"pkg:unknown/usr/share/apk/keys/alpine-devel%40lists.alpinelinux.org-58e4f17d.rsa.pub",
172+
"pkg:unknown/usr/share/apk/keys/alpine-devel%40lists.alpinelinux.org-5e69ca50.rsa.pub",
173+
"pkg:unknown/usr/share/apk/keys/alpine-devel%40lists.alpinelinux.org-60ac2099.rsa.pub",
174+
"pkg:unknown/usr/share/apk/keys/alpine-devel%40lists.alpinelinux.org-6165ee59.rsa.pub",
175+
"pkg:unknown/usr/share/apk/keys/alpine-devel%40lists.alpinelinux.org-61666e3f.rsa.pub",
176+
"pkg:unknown/usr/share/apk/keys/alpine-devel%40lists.alpinelinux.org-616a9724.rsa.pub",
177+
"pkg:unknown/usr/share/apk/keys/alpine-devel%40lists.alpinelinux.org-616abc23.rsa.pub",
178+
"pkg:unknown/usr/share/apk/keys/alpine-devel%40lists.alpinelinux.org-616ac3bc.rsa.pub",
179+
"pkg:unknown/usr/share/apk/keys/alpine-devel%40lists.alpinelinux.org-616adfeb.rsa.pub",
180+
"pkg:unknown/usr/share/apk/keys/alpine-devel%40lists.alpinelinux.org-616ae350.rsa.pub",
181+
"pkg:unknown/usr/share/apk/keys/alpine-devel%40lists.alpinelinux.org-616db30d.rsa.pub",
182+
"pkg:unknown/usr/share/udhcpc/default.script",
183+
],
83184
},
84185
### CycloneDX cdxgen
85186
# $ cdxgen alpine:3.17.0 --type docker --spec-version 1.6 --json-pretty \
@@ -89,6 +190,22 @@
89190
"packages": 14,
90191
"packages_vulnerable": 0,
91192
"dependencies": 0,
193+
"purls": [
194+
"pkg:generic/apk",
195+
"pkg:generic/busybox",
196+
"pkg:generic/getconf",
197+
"pkg:generic/getent",
198+
"pkg:generic/iconv",
199+
"pkg:generic/ld-musl-x86_64.so.1",
200+
"pkg:generic/ldconfig",
201+
"pkg:generic/ldd",
202+
"pkg:generic/libapk.so.3.12.0",
203+
"pkg:generic/libcrypto.so.3",
204+
"pkg:generic/libssl.so.3",
205+
"pkg:generic/libz.so.1.2.13",
206+
"pkg:generic/scanelf",
207+
"pkg:generic/ssl_client",
208+
],
92209
},
93210
### OWASP dep-scan
94211
# $ depscan --src alpine:3.17.0 --type docker
@@ -97,6 +214,41 @@
97214
"packages": 33,
98215
"packages_vulnerable": 3,
99216
"dependencies": 20,
217+
"purls": [
218+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=alpine-3.17.0&distro_name=alpine-3.17",
219+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=alpine-3.17.0&distro_name=alpine-3.17",
220+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=alpine-3.17.0&distro_name=alpine-3.17",
221+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=alpine-3.17.0&distro_name=alpine-3.17",
222+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=alpine-3.17.0&distro_name=alpine-3.17",
223+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=alpine-3.17.0&distro_name=alpine-3.17",
224+
"pkg:apk/alpine/ca-certificates-bundle@20220614-r2?arch=x86_64&distro=alpine-3.17.0&distro_name=alpine-3.17",
225+
"pkg:apk/alpine/ca-certificates@20220614-r2?arch=x86_64&distro=alpine-3.17.0&distro_name=alpine-3.17",
226+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=alpine-3.17.0&distro_name=alpine-3.17",
227+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=alpine-3.17.0&distro_name=alpine-3.17",
228+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=alpine-3.17.0&distro_name=alpine-3.17",
229+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=alpine-3.17.0&distro_name=alpine-3.17",
230+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=alpine-3.17.0&distro_name=alpine-3.17",
231+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=alpine-3.17.0&distro_name=alpine-3.17",
232+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=alpine-3.17.0&distro_name=alpine-3.17",
233+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=alpine-3.17.0&distro_name=alpine-3.17",
234+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=alpine-3.17.0&distro_name=alpine-3.17",
235+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=alpine-3.17.0&distro_name=alpine-3.17",
236+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=alpine-3.17.0&distro_name=alpine-3.17",
237+
"pkg:generic/apk",
238+
"pkg:generic/busybox",
239+
"pkg:generic/getconf",
240+
"pkg:generic/getent",
241+
"pkg:generic/iconv",
242+
"pkg:generic/ld-musl-x86_64.so.1",
243+
"pkg:generic/ldconfig",
244+
"pkg:generic/ldd",
245+
"pkg:generic/libapk.so.3.12.0",
246+
"pkg:generic/libcrypto.so.3",
247+
"pkg:generic/libssl.so.3",
248+
"pkg:generic/libz.so.1.2.13",
249+
"pkg:generic/scanelf",
250+
"pkg:generic/ssl_client",
251+
],
100252
},
101253
### OSV-Scanner
102254
# $ osv-scanner scan image alpine:3.17.0 \
@@ -108,6 +260,24 @@
108260
"packages": 16,
109261
"packages_vulnerable": 0,
110262
"dependencies": 15,
263+
"purls": [
264+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=3.17.0&origin=alpine-baselayout",
265+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=3.17.0&origin=alpine-baselayout",
266+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=3.17.0&origin=alpine-keys",
267+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=3.17.0&origin=apk-tools",
268+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=3.17.0&origin=busybox",
269+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=3.17.0&origin=busybox",
270+
"pkg:apk/alpine/ca-certificates-bundle@20220614-r2?arch=x86_64&distro=3.17.0&origin=ca-certificates",
271+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=3.17.0&origin=libc-dev",
272+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=3.17.0&origin=openssl",
273+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=3.17.0&origin=openssl",
274+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=3.17.0&origin=musl",
275+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=3.17.0&origin=musl",
276+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=3.17.0&origin=pax-utils",
277+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=3.17.0&origin=busybox",
278+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=3.17.0&origin=zlib",
279+
"pkg:unknown/main@0",
280+
],
111281
},
112282
# Example file from osv-scanner documentation:
113283
# https://google.github.io/osv-scanner/output/#cyclonedx
@@ -116,6 +286,11 @@
116286
"packages": 3,
117287
"packages_vulnerable": 1,
118288
"dependencies": 0,
289+
"purls": [
290+
"pkg:composer/league/[email protected]",
291+
"pkg:npm/[email protected]",
292+
"pkg:npm/[email protected]",
293+
],
119294
},
120295
### SBOM Tool
121296
# $ sbom-tool generate -di alpine:3.17.0 \
@@ -125,6 +300,24 @@
125300
"packages": 16,
126301
"packages_vulnerable": 0,
127302
"dependencies": 15,
303+
"purls": [
304+
"pkg:swid/Company/sbom.company.com/[email protected]?tag_id=60e3f440-f9a8-449e-b516-da3049700fff",
305+
"pkg:unknown/[email protected]",
306+
"pkg:unknown/[email protected]",
307+
"pkg:unknown/[email protected]",
308+
"pkg:unknown/[email protected]",
309+
"pkg:unknown/[email protected]",
310+
"pkg:unknown/[email protected]",
311+
"pkg:unknown/ca-certificates-bundle@20220614-r2",
312+
"pkg:unknown/[email protected]",
313+
"pkg:unknown/[email protected]",
314+
"pkg:unknown/[email protected]",
315+
"pkg:unknown/[email protected]",
316+
"pkg:unknown/[email protected]",
317+
"pkg:unknown/[email protected]",
318+
"pkg:unknown/[email protected]",
319+
"pkg:unknown/[email protected]",
320+
],
128321
},
129322
### Trivy
130323
# $ trivy image --scanners vuln,license --format cyclonedx \
@@ -134,6 +327,24 @@
134327
"packages": 16,
135328
"packages_vulnerable": 7,
136329
"dependencies": 25,
330+
"purls": [
331+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=3.17.0",
332+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=3.17.0",
333+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=3.17.0",
334+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=3.17.0",
335+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=3.17.0",
336+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=3.17.0",
337+
"pkg:apk/alpine/ca-certificates-bundle@20220614-r2?arch=x86_64&distro=3.17.0",
338+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=3.17.0",
339+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=3.17.0",
340+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=3.17.0",
341+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=3.17.0",
342+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=3.17.0",
343+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=3.17.0",
344+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=3.17.0",
345+
"pkg:apk/alpine/[email protected]?arch=x86_64&distro=3.17.0",
346+
"pkg:unknown/[email protected]",
347+
],
137348
},
138349
}
139350

@@ -192,3 +403,10 @@ def _test_scanpipe_sca_integrations_tool(self, sbom_filename, expected_results):
192403
project.discovereddependencies.count(),
193404
msg=sbom_filename,
194405
)
406+
407+
# Verify that all the expected PURLs are present in the project
408+
expected_purls = expected_results.get("purls", [])
409+
if expected_purls:
410+
purls_diff = benchmark.compare_purls(project, expected_purls)
411+
formatted_diff = "\n".join(purls_diff)
412+
self.assertFalse(purls_diff, msg=f"\n{sbom_filename}\n{formatted_diff}")

0 commit comments

Comments
 (0)