Skip to content

Commit 45e2586

Browse files
committed
refactor(sbom): DRY library glob, tighten regression tests
Hoist the shared dynamic-library basenames into a Make variable used by both `sbom:` and `bomsh:`; add a sha256_file negative test and freshness checks for the build_timestamp fallback. Signed-off-by: Sameeh Jubran <sameeh@wolfssl.com>
1 parent 271d883 commit 45e2586

2 files changed

Lines changed: 47 additions & 10 deletions

File tree

Makefile.am

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,18 @@ SBOM_SPDX = wolfssl-$(PACKAGE_VERSION).spdx.json
357357
SBOM_SPDX_TV = wolfssl-$(PACKAGE_VERSION).spdx
358358
sbomdir = $(datadir)/doc/$(PACKAGE)
359359

360+
# Shared-library / Mach-O basenames in priority order (versioned first).
361+
# Both `sbom:` and `bomsh:` glob for these under their own search prefixes;
362+
# adding a new platform-specific dynamic-library extension here updates
363+
# both targets at once. Static (.a) and Windows (.dll/.lib) variants are
364+
# listed inline at each call-site because their ordering and prefixes
365+
# differ between the install tree and the build tree.
366+
WOLFSSL_LIB_DSO_BASENAMES = \
367+
libwolfssl.so.[0-9]* \
368+
libwolfssl.so \
369+
libwolfssl.[0-9]*.dylib \
370+
libwolfssl.dylib
371+
360372
.PHONY: sbom install-sbom uninstall-sbom
361373

362374
# Stage a `make install` into a private tree, discover the installed library
@@ -395,10 +407,7 @@ sbom:
395407
$(MAKE) install DESTDIR=$(abs_builddir)/_sbom_staging; \
396408
sbom_lib=""; \
397409
for lib in \
398-
"$(abs_builddir)/_sbom_staging$(libdir)"/libwolfssl.so.[0-9]* \
399-
"$(abs_builddir)/_sbom_staging$(libdir)"/libwolfssl.so \
400-
"$(abs_builddir)/_sbom_staging$(libdir)"/libwolfssl.[0-9]*.dylib \
401-
"$(abs_builddir)/_sbom_staging$(libdir)"/libwolfssl.dylib \
410+
$(addprefix "$(abs_builddir)/_sbom_staging$(libdir)"/,$(WOLFSSL_LIB_DSO_BASENAMES)) \
402411
"$(abs_builddir)/_sbom_staging$(libdir)"/libwolfssl.dll \
403412
"$(abs_builddir)/_sbom_staging$(libdir)"/libwolfssl.dll.a \
404413
"$(abs_builddir)/_sbom_staging$(libdir)"/libwolfssl.lib \
@@ -495,10 +504,7 @@ bomsh:
495504
fi; \
496505
bomsh_artifact=""; \
497506
for lib in \
498-
$(abs_builddir)/src/.libs/libwolfssl.so.[0-9]* \
499-
$(abs_builddir)/src/.libs/libwolfssl.so \
500-
$(abs_builddir)/src/.libs/libwolfssl.[0-9]*.dylib \
501-
$(abs_builddir)/src/.libs/libwolfssl.dylib \
507+
$(addprefix $(abs_builddir)/src/.libs/,$(WOLFSSL_LIB_DSO_BASENAMES)) \
502508
$(abs_builddir)/src/.libs/libwolfssl.a \
503509
$(abs_builddir)/src/libwolfssl.a; do \
504510
if test -f "$$lib"; then bomsh_artifact="$$lib"; break; fi; \

scripts/test_gen_sbom.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import tempfile
1919
import unittest
2020
import uuid
21+
from datetime import datetime, timedelta, timezone
2122
from importlib.machinery import SourceFileLoader
2223

2324

@@ -226,15 +227,24 @@ def test_two_calls_with_same_sde_match(self):
226227
def test_invalid_sde_falls_back_to_now(self):
227228
os.environ['SOURCE_DATE_EPOCH'] = 'not-a-number'
228229
dt, ts = gs.build_timestamp()
229-
# Should still produce a UTC ISO-Z timestamp; we only check shape.
230+
# Shape check.
230231
self.assertRegex(
231232
ts, r'\A\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z\Z')
233+
# Freshness check: regression guard against a future change that
234+
# accidentally hard-codes the fallback (e.g. epoch zero). Five
235+
# seconds is generous for a unit test on slow runners.
236+
self.assertLess(
237+
abs(dt - datetime.now(tz=timezone.utc)),
238+
timedelta(seconds=5))
232239

233240
def test_no_sde_is_current_utc(self):
234241
os.environ.pop('SOURCE_DATE_EPOCH', None)
235-
_, ts = gs.build_timestamp()
242+
dt, ts = gs.build_timestamp()
236243
self.assertRegex(
237244
ts, r'\A\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z\Z')
245+
self.assertLess(
246+
abs(dt - datetime.now(tz=timezone.utc)),
247+
timedelta(seconds=5))
238248

239249

240250
class TestLoadLicenseText(unittest.TestCase):
@@ -257,6 +267,27 @@ def test_missing_file_exits(self):
257267
gs.load_license_text('/no/such/path/please.txt')
258268

259269

270+
class TestSha256File(unittest.TestCase):
271+
def test_real_file_hashes_to_known_value(self):
272+
# Empty file's SHA-256 is well-known; sanity-checks the chunked
273+
# read path produces the same digest as a one-shot hash.
274+
with tempfile.NamedTemporaryFile('wb', delete=False) as f:
275+
path = f.name
276+
try:
277+
empty_sha256 = ('e3b0c44298fc1c149afbf4c8996fb924'
278+
'27ae41e4649b934ca495991b7852b855')
279+
self.assertEqual(gs.sha256_file(path), empty_sha256)
280+
finally:
281+
os.unlink(path)
282+
283+
def test_missing_file_exits_cleanly(self):
284+
# Regression guard: gen-sbom must surface a missing --lib path as
285+
# a clean non-zero exit, not an unhandled OSError, so `make sbom`
286+
# fails fast with a useful message instead of a Python traceback.
287+
with self.assertRaises(SystemExit):
288+
gs.sha256_file('/no/such/library/please.so')
289+
290+
260291
class TestParseOptionsH(unittest.TestCase):
261292
def _parse(self, body):
262293
with tempfile.NamedTemporaryFile('w', suffix='.h',

0 commit comments

Comments
 (0)