@@ -28,7 +28,9 @@ from datetime import datetime, timezone
2828# 1.8 CPE and PURL identifiers drop `+` build metadata (OpenSSL
2929# BUILD_METADATA, PEP 440 local versions). A raw `+` is not legal
3030# in CPE 2.3 and does not match an upstream git tag. version /
31- # versionInfo still records the full string.
31+ # versionInfo still records the full string. Constructors (cpe23_uri,
32+ # wolfssl_project_purl, DEP_META lambdas) strip; this is not a
33+ # full CPE sanitizer.
3234# 1.7 wolfBoot CPE is registered in the NVD Official CPE Dictionary
3335# (published 2026-08-10). PRODUCT_CPE status flips to registered so
3436# the main package emits `cpe` and the pending properties stop.
@@ -140,12 +142,21 @@ def identifier_version(version):
140142 A raw `+` is not a legal CPE 2.3 version character, and the suffix
141143 is not an upstream git tag, so NVD / OSV cannot match it. Keep the
142144 full string in version / versionInfo; use this for identifiers.
145+
146+ This is not a full CPE 2.3 sanitizer: other illegal characters
147+ (`:`, `*`, whitespace) are not rewritten.
143148 """
144149 if not version :
145150 return version
146151 return version .split ('+' , 1 )[0 ]
147152
148153
154+ def cpe23_uri (vendor , product , version ):
155+ """CPE 2.3 application URI. Version uses identifier_version()."""
156+ ident = identifier_version (version )
157+ return f'cpe:2.3:a:{ vendor } :{ product } :{ ident } :*:*:*:*:*:*:*'
158+
159+
149160def wolfssl_project_purl (name , version ):
150161 """Canonical pkg:github PURL for a wolfSSL-stack project."""
151162 return github_purl (
@@ -183,10 +194,7 @@ PRODUCT_CPE = {
183194
184195
185196def _product_cpe_string (meta , version ):
186- ident = identifier_version (version )
187- return (
188- f"cpe:2.3:a:{ meta ['vendor' ]} :{ meta ['product' ]} :{ ident } :*:*:*:*:*:*:*"
189- )
197+ return cpe23_uri (meta ['vendor' ], meta ['product' ], version )
190198
191199
192200def product_cpe (name , version ):
@@ -313,7 +321,7 @@ DEP_META = {
313321 'pkgconfig' : 'wolfssl' ,
314322 'purl' : lambda v : wolfssl_project_purl ('wolfssl' , v ),
315323 # The CPE NVD registers for the wolfSSL library.
316- 'cpe' : lambda v : f'cpe:2.3:a: wolfssl: wolfssl: { v } :*:*:*:*:*:*:*' ,
324+ 'cpe' : lambda v : cpe23_uri ( ' wolfssl' , ' wolfssl' , v ) ,
317325 },
318326 # wolfCrypt is a separate NVD product (cpe:2.3:a:wolfssl:wolfcrypt).
319327 # Embedders such as wolfBoot compile wolfcrypt sources into the image;
@@ -334,7 +342,7 @@ DEP_META = {
334342 # does not collide with the wolfssl component's own PURL. NVD
335343 # matching keys on the wolfcrypt CPE below.
336344 'purl' : lambda v : wolfssl_project_purl ('wolfssl' , v ) + '#wolfcrypt' ,
337- 'cpe' : lambda v : f'cpe:2.3:a: wolfssl: wolfcrypt: { v } :*:*:*:*:*:*:*' ,
345+ 'cpe' : lambda v : cpe23_uri ( ' wolfssl' , ' wolfcrypt' , v ) ,
338346 },
339347 'libz' : {
340348 'name' : 'zlib' ,
@@ -345,8 +353,9 @@ DEP_META = {
345353 # pkg:github resolves in OSV / GHSA / Snyk / Trivy without the
346354 # vendor:product mapping a pkg:generic PURL would force. zlib tags
347355 # its releases `vX.Y.Z`, so the bare pkg-config version needs the `v`.
348- 'purl' : lambda v : github_purl ('madler' , 'zlib' , f'v{ v } ' ),
349- 'cpe' : lambda v : f'cpe:2.3:a:zlib:zlib:{ v } :*:*:*:*:*:*:*' ,
356+ 'purl' : lambda v : github_purl (
357+ 'madler' , 'zlib' , f'v{ identifier_version (v )} ' ),
358+ 'cpe' : lambda v : cpe23_uri ('zlib' , 'zlib' , v ),
350359 },
351360 # openssl, declared as a dependency by the OpenSSL-compat products
352361 # (wolfProvider, wolfEngine) that link libcrypto/libssl alongside wolfSSL.
@@ -361,8 +370,9 @@ DEP_META = {
361370 'license' : 'Apache-2.0' ,
362371 'download' : 'https://github.com/openssl/openssl' ,
363372 'pkgconfig' : 'openssl' ,
364- 'purl' : lambda v : github_purl ('openssl' , 'openssl' , f'openssl-{ v } ' ),
365- 'cpe' : lambda v : f'cpe:2.3:a:openssl:openssl:{ v } :*:*:*:*:*:*:*' ,
373+ 'purl' : lambda v : github_purl (
374+ 'openssl' , 'openssl' , f'openssl-{ identifier_version (v )} ' ),
375+ 'cpe' : lambda v : cpe23_uri ('openssl' , 'openssl' , v ),
366376 },
367377}
368378
@@ -1007,13 +1017,14 @@ def cdx_dep_component(name, pkg_version, key, dep_version_overrides=None):
10071017 'externalReferences' : [{'type' : 'vcs' , 'url' : meta ['download' ]}],
10081018 }
10091019 if version :
1010- ident = identifier_version (version )
1020+ # Pass the full version: constructors apply identifier_version().
1021+ # version / versionInfo keep the local string (BUILD_METADATA).
10111022 comp ['version' ] = version
1012- comp ['purl' ] = meta ['purl' ](ident )
1023+ comp ['purl' ] = meta ['purl' ](version )
10131024 # Both identifiers are version-bearing, so neither can be emitted
10141025 # without a resolved version: a CPE with an empty version field
10151026 # matches every release of the dependency in an NVD scan.
1016- comp ['cpe' ] = meta ['cpe' ](ident )
1027+ comp ['cpe' ] = meta ['cpe' ](version )
10171028 else :
10181029 print (f"WARNING: version unknown for { meta ['name' ]} ; "
10191030 "omitting version, purl and cpe" , file = sys .stderr )
@@ -1037,17 +1048,16 @@ def spdx_dep_package(key, dep_version_overrides=None):
10371048 'copyrightText' : 'NOASSERTION' ,
10381049 }
10391050 if version :
1040- ident = identifier_version (version )
10411051 pkg ['externalRefs' ] = [
10421052 {
10431053 'referenceCategory' : 'SECURITY' ,
10441054 'referenceType' : 'cpe23Type' ,
1045- 'referenceLocator' : meta ['cpe' ](ident ),
1055+ 'referenceLocator' : meta ['cpe' ](version ),
10461056 },
10471057 {
10481058 'referenceCategory' : 'PACKAGE-MANAGER' ,
10491059 'referenceType' : 'purl' ,
1050- 'referenceLocator' : meta ['purl' ](ident ),
1060+ 'referenceLocator' : meta ['purl' ](version ),
10511061 },
10521062 ]
10531063 return spdx_id , pkg
0 commit comments