Skip to content

Commit 3ef8f32

Browse files
committed
fix(sbom): correctness fixes from code review
Hard-error on LicenseRef-* without --license-text, route NOASSERTION via license.name, strip C comments in options.h, NUL-join derived UUIDs, and detect git worktrees for SOURCE_DATE_EPOCH. Signed-off-by: Sameeh Jubran <sameeh@wolfssl.com>
1 parent 6ce2ec3 commit 3ef8f32

4 files changed

Lines changed: 38 additions & 18 deletions

File tree

Makefile.am

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,7 @@ sbom:
416416
fi; \
417417
echo "SBOM: hashing $$sbom_lib"; \
418418
if test -z "$${SOURCE_DATE_EPOCH:-}" && test -n "$(GIT)" && \
419-
test -d "$(srcdir)/.git"; then \
419+
$(GIT) -C "$(srcdir)" rev-parse --git-dir >/dev/null 2>&1; then \
420420
SOURCE_DATE_EPOCH=`$(GIT) -C "$(srcdir)" log -1 --format=%ct 2>/dev/null || echo`; \
421421
export SOURCE_DATE_EPOCH; \
422422
fi; \

doc/CRA.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,9 @@ the document; conformant validators (e.g. `pyspdxtools`, `ntia-conformance-check
147147
will reject the SBOM otherwise. The file should contain the plain-text
148148
licence agreement you received from wolfSSL.
149149

150-
If you omit `SBOM_LICENSE_TEXT` the generator emits a placeholder and prints
151-
a warning — useful for quick experiments, but the result is **not** valid for
152-
distribution to customers or regulators.
150+
If `SBOM_LICENSE_OVERRIDE` is set to a `LicenseRef-*` and `SBOM_LICENSE_TEXT`
151+
is missing, `make sbom` exits with an error rather than emit an invalid SBOM
152+
that might end up in front of a regulator.
153153

154154
For a stock SPDX-listed identifier (`Apache-2.0`, `MIT`, etc.) the
155155
`SBOM_LICENSE_TEXT` argument is unnecessary because validators already know

doc/SBOM.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,13 +98,12 @@ python3 scripts/gen-sbom \
9898
... other flags ...
9999
```
100100

101-
`--license-text` is required whenever `--license-override` is a custom
101+
`--license-text` is **required** whenever `--license-override` is a custom
102102
`LicenseRef-*`: SPDX 2.3 mandates that any LicenseRef in `licenseConcluded`
103103
or `licenseDeclared` be backed by a `hasExtractedLicensingInfos` entry that
104-
embeds the actual licence text. Without it, validators such as
105-
`pyspdxtools` and `ntia-conformance-checker` reject the document. The
106-
generator emits a placeholder and a warning in that case so the bug is
107-
visible, but the SBOM is *not* valid for downstream consumers.
104+
embeds the actual licence text. Running without it is a configuration
105+
error and the generator exits non-zero rather than emit a misleading SBOM
106+
that auditors might then circulate.
108107

109108
For an SPDX-listed override (`Apache-2.0`, `MIT`, etc.), `--license-text`
110109
is unnecessary because validators already know the canonical text.

scripts/gen-sbom

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,13 @@ SBOM_UUID_NAMESPACE = uuid.uuid5(uuid.NAMESPACE_URL, 'https://wolfssl.com/sbom/'
2020
def derived_uuid(*parts):
2121
"""Deterministic UUID from joined parts under the wolfSSL SBOM namespace.
2222
Re-runs of `make sbom` against the same source produce identical UUIDs,
23-
which is required for reproducible-build-style SBOM hashing."""
24-
return str(uuid.uuid5(SBOM_UUID_NAMESPACE, '/'.join(parts)))
23+
which is required for reproducible-build-style SBOM hashing.
24+
25+
Uses NUL as a separator so no aliasing is possible between e.g.
26+
derived_uuid('a/b', 'c') and derived_uuid('a', 'b/c'); NUL cannot
27+
appear in any of the call-site inputs (package name, version, role
28+
label, dep key)."""
29+
return str(uuid.uuid5(SBOM_UUID_NAMESPACE, '\x00'.join(parts)))
2530

2631

2732
def build_timestamp():
@@ -148,6 +153,11 @@ def cdx_license_block(license_expr, license_text):
148153
* `license.name` - a non-listed licence (e.g. a LicenseRef-*)
149154
* `expression` - a compound SPDX expression
150155
Picking the wrong shape causes downstream tooling to reject the SBOM."""
156+
# NOASSERTION is a reserved SPDX value, not a parseable SPDX expression;
157+
# emit it via license.name so CDX validators don't choke trying to parse
158+
# it as one.
159+
if license_expr == 'NOASSERTION':
160+
return [{'license': {'name': 'NOASSERTION'}}]
151161
if is_simple_spdx_id(license_expr):
152162
return [{'license': {'id': license_expr}}]
153163
refs = extract_license_refs(license_expr)
@@ -246,7 +256,11 @@ def dep_version(key):
246256

247257
def parse_options_h(path):
248258
"""Parse wolfssl/options.h and return sorted deduplicated list of
249-
(name, value) pairs for every #define found."""
259+
(name, value) pairs for every #define found.
260+
261+
Trailing C/C++ comments on a #define line (`#define HAVE_FOO 42 /* x */`
262+
or `// y`) are stripped; otherwise they would land verbatim in the
263+
SBOM build properties."""
250264
try:
251265
with open(path) as f:
252266
text = f.read()
@@ -255,8 +269,10 @@ def parse_options_h(path):
255269
return []
256270

257271
defines = {}
258-
for m in re.finditer(r'^#define[ \t]+(\w+)(?:[ \t]+(.+))?$', text, re.MULTILINE):
259-
defines[m.group(1)] = (m.group(2) or '').strip()
272+
for m in re.finditer(r'^#define[ \t]+(\w+)(?:[ \t]+(.*))?$', text, re.MULTILINE):
273+
raw = (m.group(2) or '')
274+
raw = re.split(r'/\*|//', raw, maxsplit=1)[0]
275+
defines[m.group(1)] = raw.strip()
260276
return sorted(defines.items())
261277

262278

@@ -515,10 +531,15 @@ def main():
515531

516532
license_text = load_license_text(args.license_text)
517533
if extract_license_refs(license_id) and license_text is None:
518-
print("WARNING: --license-override uses a LicenseRef-* but "
519-
"--license-text was not provided; the SBOM will embed a "
520-
"placeholder. Provide SBOM_LICENSE_TEXT=<path> for full "
521-
"SPDX compliance.", file=sys.stderr)
534+
sys.exit(
535+
"ERROR: --license-override contains a LicenseRef-* identifier "
536+
"but --license-text was not provided.\n"
537+
" SPDX 2.3 requires the licence text to be embedded in "
538+
"hasExtractedLicensingInfos for any LicenseRef-* used in "
539+
"licenseConcluded/licenseDeclared.\n"
540+
" Re-run with --license-text PATH (or "
541+
"`make sbom SBOM_LICENSE_TEXT=PATH`)."
542+
)
522543

523544
build_props = parse_options_h(args.options_h)
524545
lib_hash = sha256_file(args.lib)

0 commit comments

Comments
 (0)