From 52f9a7342e694e5f9e59a3e814a80d30fa8bb255 Mon Sep 17 00:00:00 2001 From: Mark Atwood Date: Tue, 23 Jun 2026 15:25:27 -0700 Subject: [PATCH 1/7] feat: add make sbom / install-sbom / uninstall-sbom targets Adds CycloneDX + SPDX SBOM generation via wolfssl's gen-sbom script. Usage: make sbom WOLFSSL_DIR=/path/to/wolfssl wolfCLU is a binary (not .so); artifact hash uses --srcs from wolfssl_SOURCES. Version from CLUWOLFSSL_VERSION_STRING in version.h. --- Makefile.am | 42 ++++++++++++++++++++++++++++++++++++++++++ configure.ac | 4 ++++ 2 files changed, 46 insertions(+) diff --git a/Makefile.am b/Makefile.am index 6ef17b86..c9a56c3a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -163,6 +163,48 @@ endif test: check #DISTCLEANFILES+= wolfssl-config +# SBOM generation. Requires WOLFSSL_DIR pointing to a wolfssl source tree +# containing scripts/gen-sbom (feat/sbom-embedded branch, or master once +# wolfSSL/wolfssl#10343 merges). wolfCLU ships a binary, not a .so, so the +# artifact hash is computed from the compiled sources via --srcs. +WOLFSSL_DIR ?= +PRODUCT = wolfclu +VERSION = $(shell grep CLUWOLFSSL_VERSION_STRING $(srcdir)/wolfclu/version.h 2>/dev/null | sed 's/.*"\(.*\)".*/\1/') +GEN_SBOM = $(WOLFSSL_DIR)/scripts/gen-sbom +WOLFSSL_INCLUDEDIR ?= $(WOLFSSL_DIR)/include +SBOM_OPTS = --name $(PRODUCT) \ + --version $(VERSION) \ + --supplier "wolfSSL Inc." \ + --options-h $(WOLFSSL_INCLUDEDIR)/wolfssl/options.h \ + --srcs $(addprefix $(srcdir)/,$(wolfssl_SOURCES)) + +SBOM_OUT_DIR = $(builddir) +SBOM_CDX = $(SBOM_OUT_DIR)/$(PRODUCT)-$(VERSION).cdx.json +SBOM_SPDX_J = $(SBOM_OUT_DIR)/$(PRODUCT)-$(VERSION).spdx.json +SBOM_SPDX_TV = $(SBOM_OUT_DIR)/$(PRODUCT)-$(VERSION).spdx + +.PHONY: sbom install-sbom uninstall-sbom + +sbom: all + @if test -z "$(WOLFSSL_DIR)"; then \ + echo "ERROR: WOLFSSL_DIR not set. Usage: make sbom WOLFSSL_DIR=/path/to/wolfssl"; \ + exit 1; \ + fi + @if test -z "$(PYTHON3)"; then echo "ERROR: python3 not found in PATH."; exit 1; fi + $(PYTHON3) $(GEN_SBOM) $(SBOM_OPTS) + +install-sbom: sbom + $(MKDIR_P) $(DESTDIR)$(datadir)/doc/$(PRODUCT) + $(INSTALL_DATA) $(SBOM_CDX) $(SBOM_SPDX_J) $(SBOM_SPDX_TV) \ + $(DESTDIR)$(datadir)/doc/$(PRODUCT)/ + +uninstall-sbom: + -rm -f $(DESTDIR)$(datadir)/doc/$(PRODUCT)/$(PRODUCT)-*.cdx.json + -rm -f $(DESTDIR)$(datadir)/doc/$(PRODUCT)/$(PRODUCT)-*.spdx.json + -rm -f $(DESTDIR)$(datadir)/doc/$(PRODUCT)/$(PRODUCT)-*.spdx + +uninstall-hook: uninstall-sbom + maintainer-clean-local: -rm Makefile.in diff --git a/configure.ac b/configure.ac index b125a366..e9166595 100644 --- a/configure.ac +++ b/configure.ac @@ -59,6 +59,10 @@ AM_PATH_PYTHON([3.6],, [:]) AC_SUBST([PYTHON]) AM_CONDITIONAL([HAVE_PYTHON], [test "$PYTHON" != ":"]) +# SBOM generation prerequisites +AC_CHECK_PROG([PYTHON3], [python3], [python3]) +AC_CHECK_PROG([PYSPDXTOOLS], [pyspdxtools], [pyspdxtools]) + # Checks for headers/libraries AC_CHECK_HEADERS([sys/time.h string.h termios.h unistd.h]) AC_CHECK_SIZEOF(long long, 8) From 7a246073fb836958ee8abb1ec1eba2ecb1f3863b Mon Sep 17 00:00:00 2001 From: Mark Atwood Date: Tue, 23 Jun 2026 17:40:07 -0700 Subject: [PATCH 2/7] docs: add SBOM/EU CRA Compliance section to README --- README.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/README.md b/README.md index 270f3e54..17dc9a8e 100644 --- a/README.md +++ b/README.md @@ -334,6 +334,34 @@ wolfssl ocsp \ The `-index` file uses OpenSSL's CA index format. +## SBOM / EU CRA Compliance + +wolfCLU generates a Software Bill of Materials (SBOM) in CycloneDX 1.6 and +SPDX 2.3 formats to support compliance with the EU Cyber Resilience Act (CRA). + +```sh +make sbom WOLFSSL_DIR=/path/to/wolfssl +``` + +Requires `python3` and `pyspdxtools` (`pip install spdx-tools`). `WOLFSSL_DIR` +must point to a wolfssl source tree containing `scripts/gen-sbom` (branch +`feat/sbom-embedded`, or `master` once wolfSSL/wolfssl#10343 merges). + +Output files in the build directory: + +| File | Format | +|------|--------| +| `wolfclu-0.2.0.cdx.json` | CycloneDX 1.6 | +| `wolfclu-0.2.0.spdx.json` | SPDX 2.3 JSON | +| `wolfclu-0.2.0.spdx` | SPDX 2.3 tag-value | + +```sh +make install-sbom # installs to $(datadir)/doc/wolfclu/ +make uninstall-sbom +``` + +For further CRA guidance see [wolfssl/doc/CRA.md](https://github.com/wolfSSL/wolfssl/blob/master/doc/CRA.md). + ## Contacts Please contact support@wolfssl.com with any questions or comments. From ae1817611b414c079bb940b30093789d5f29e989 Mon Sep 17 00:00:00 2001 From: Mark Atwood Date: Mon, 6 Jul 2026 15:39:36 -0700 Subject: [PATCH 3/7] fix: single unconditional .PHONY for sbom targets automake runs with -Werror; a second .PHONY (unconditional) overlapping the existing conditional .PHONY: manpages-gz triggered 'already defined' -> automake failed -> all CI configs red at autoreconf. Consolidate to one unconditional .PHONY covering manpages-gz + the sbom targets. --- Makefile.am | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Makefile.am b/Makefile.am index c9a56c3a..e5190d16 100644 --- a/Makefile.am +++ b/Makefile.am @@ -150,7 +150,6 @@ if ENABLE_MANPAGES # -n keeps the output byte-reproducible (no embedded filename/mtime). MAN_GZ = $(man_MANS:.1=.1.gz) -.PHONY: manpages-gz manpages-gz: $(MAN_GZ) manpages/%.1.gz: $(srcdir)/manpages/%.1 @@ -183,7 +182,7 @@ SBOM_CDX = $(SBOM_OUT_DIR)/$(PRODUCT)-$(VERSION).cdx.json SBOM_SPDX_J = $(SBOM_OUT_DIR)/$(PRODUCT)-$(VERSION).spdx.json SBOM_SPDX_TV = $(SBOM_OUT_DIR)/$(PRODUCT)-$(VERSION).spdx -.PHONY: sbom install-sbom uninstall-sbom +.PHONY: manpages-gz sbom install-sbom uninstall-sbom sbom: all @if test -z "$(WOLFSSL_DIR)"; then \ From 6fd9382d4421ea55df5587d4e11c2003ae4ca7aa Mon Sep 17 00:00:00 2001 From: Sameeh Jubran Date: Thu, 9 Jul 2026 09:53:59 +0300 Subject: [PATCH 4/7] sbom: adopt shared scripts/sbom.am fragment Signed-off-by: Sameeh Jubran --- Makefile.am | 62 ++++++--------- README.md | 29 +++++-- configure.ac | 9 ++- scripts/sbom.am | 196 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 246 insertions(+), 50 deletions(-) create mode 100644 scripts/sbom.am diff --git a/Makefile.am b/Makefile.am index e5190d16..67e95741 100644 --- a/Makefile.am +++ b/Makefile.am @@ -162,47 +162,27 @@ endif test: check #DISTCLEANFILES+= wolfssl-config -# SBOM generation. Requires WOLFSSL_DIR pointing to a wolfssl source tree -# containing scripts/gen-sbom (feat/sbom-embedded branch, or master once -# wolfSSL/wolfssl#10343 merges). wolfCLU ships a binary, not a .so, so the -# artifact hash is computed from the compiled sources via --srcs. -WOLFSSL_DIR ?= -PRODUCT = wolfclu -VERSION = $(shell grep CLUWOLFSSL_VERSION_STRING $(srcdir)/wolfclu/version.h 2>/dev/null | sed 's/.*"\(.*\)".*/\1/') -GEN_SBOM = $(WOLFSSL_DIR)/scripts/gen-sbom -WOLFSSL_INCLUDEDIR ?= $(WOLFSSL_DIR)/include -SBOM_OPTS = --name $(PRODUCT) \ - --version $(VERSION) \ - --supplier "wolfSSL Inc." \ - --options-h $(WOLFSSL_INCLUDEDIR)/wolfssl/options.h \ - --srcs $(addprefix $(srcdir)/,$(wolfssl_SOURCES)) - -SBOM_OUT_DIR = $(builddir) -SBOM_CDX = $(SBOM_OUT_DIR)/$(PRODUCT)-$(VERSION).cdx.json -SBOM_SPDX_J = $(SBOM_OUT_DIR)/$(PRODUCT)-$(VERSION).spdx.json -SBOM_SPDX_TV = $(SBOM_OUT_DIR)/$(PRODUCT)-$(VERSION).spdx - -.PHONY: manpages-gz sbom install-sbom uninstall-sbom - -sbom: all - @if test -z "$(WOLFSSL_DIR)"; then \ - echo "ERROR: WOLFSSL_DIR not set. Usage: make sbom WOLFSSL_DIR=/path/to/wolfssl"; \ - exit 1; \ - fi - @if test -z "$(PYTHON3)"; then echo "ERROR: python3 not found in PATH."; exit 1; fi - $(PYTHON3) $(GEN_SBOM) $(SBOM_OPTS) - -install-sbom: sbom - $(MKDIR_P) $(DESTDIR)$(datadir)/doc/$(PRODUCT) - $(INSTALL_DATA) $(SBOM_CDX) $(SBOM_SPDX_J) $(SBOM_SPDX_TV) \ - $(DESTDIR)$(datadir)/doc/$(PRODUCT)/ - -uninstall-sbom: - -rm -f $(DESTDIR)$(datadir)/doc/$(PRODUCT)/$(PRODUCT)-*.cdx.json - -rm -f $(DESTDIR)$(datadir)/doc/$(PRODUCT)/$(PRODUCT)-*.spdx.json - -rm -f $(DESTDIR)$(datadir)/doc/$(PRODUCT)/$(PRODUCT)-*.spdx - -uninstall-hook: uninstall-sbom +# manpages-gz is only a target under ENABLE_MANPAGES, but declare it .PHONY +# unconditionally so it merges with the unconditional .PHONY in scripts/sbom.am +# (a conditional + unconditional .PHONY pair trips automake -Werror). +.PHONY: manpages-gz + +# SBOM generation (CRA compliance). The recipe is shared across the wolfSSL +# stack's autotools products in scripts/sbom.am; wolfCLU just declares what it +# is (a CLI program that links wolfSSL) and includes it. WOLFSSL_DIR must point +# to a wolfssl source tree containing scripts/gen-sbom. +SBOM_PKGNAME = wolfclu +SBOM_LICENSE_FILE = $(srcdir)/COPYING +SBOM_ARTIFACT = bin +SBOM_BIN_NAME = wolfssl +SBOM_DEP_WOLFSSL = yes + +# wolfCLU is GPLv3-or-later (see source headers) or commercial. Pin the SPDX id +# so the SBOM is correct regardless of the gen-sbom version's licence detection; +# commercial licensees can override (e.g. LicenseRef-wolfSSL-Commercial). +SBOM_LICENSE_OVERRIDE ?= GPL-3.0-or-later + +include scripts/sbom.am maintainer-clean-local: diff --git a/README.md b/README.md index 17dc9a8e..e7010dbd 100644 --- a/README.md +++ b/README.md @@ -338,6 +338,12 @@ The `-index` file uses OpenSSL's CA index format. wolfCLU generates a Software Bill of Materials (SBOM) in CycloneDX 1.6 and SPDX 2.3 formats to support compliance with the EU Cyber Resilience Act (CRA). +The SBOM records the configured build options, hashes the built `wolfssl` +command-line binary (ELF, Mach-O, or PE), and (with a sufficiently new +`gen-sbom`) lists wolfSSL as a dependency so vulnerability scanners can +associate wolfSSL advisories with a wolfCLU deployment. Output is reproducible: +set `SOURCE_DATE_EPOCH` (or build from a git checkout, which uses the last +commit time) and repeated runs are byte-identical. ```sh make sbom WOLFSSL_DIR=/path/to/wolfssl @@ -347,19 +353,30 @@ Requires `python3` and `pyspdxtools` (`pip install spdx-tools`). `WOLFSSL_DIR` must point to a wolfssl source tree containing `scripts/gen-sbom` (branch `feat/sbom-embedded`, or `master` once wolfSSL/wolfssl#10343 merges). -Output files in the build directory: +Output: `wolfclu-.cdx.json`, `wolfclu-.spdx.json`, `wolfclu-.spdx` -| File | Format | -|------|--------| -| `wolfclu-0.2.0.cdx.json` | CycloneDX 1.6 | -| `wolfclu-0.2.0.spdx.json` | SPDX 2.3 JSON | -| `wolfclu-0.2.0.spdx` | SPDX 2.3 tag-value | +Optional overrides: + +- `SBOM_LICENSE_OVERRIDE` - SPDX expression to use instead of the licence + parsed from `COPYING` (defaults to `GPL-3.0-or-later`; e.g. + `LicenseRef-wolfSSL-Commercial` for commercial licensees). +- `SBOM_LICENSE_TEXT` - path to the licence text for any `LicenseRef-*` used in + `SBOM_LICENSE_OVERRIDE` (required by SPDX 2.3). +- `SBOM_WOLFSSL_VERSION` - version recorded for the wolfSSL dependency; + auto-detected from `WOLFSSL_DIR/wolfssl/version.h` when unset. ```sh make install-sbom # installs to $(datadir)/doc/wolfclu/ make uninstall-sbom ``` +The SBOM recipe is the shared `scripts/sbom.am` fragment used across the +wolfSSL stack's autotools products; wolfCLU only declares that it is a CLI +program linking wolfSSL. Recording wolfSSL as a dependency and emitting +wolfCLU-specific project URLs require the `gen-sbom` from wolfSSL/wolfssl#10343; +against an older `gen-sbom`, `make sbom` still succeeds and produces a valid +SBOM, but omits the wolfSSL dependency entry and inherits wolfSSL's project URLs. + For further CRA guidance see [wolfssl/doc/CRA.md](https://github.com/wolfSSL/wolfssl/blob/master/doc/CRA.md). ## Contacts diff --git a/configure.ac b/configure.ac index e9166595..8943af19 100644 --- a/configure.ac +++ b/configure.ac @@ -59,9 +59,12 @@ AM_PATH_PYTHON([3.6],, [:]) AC_SUBST([PYTHON]) AM_CONDITIONAL([HAVE_PYTHON], [test "$PYTHON" != ":"]) -# SBOM generation prerequisites -AC_CHECK_PROG([PYTHON3], [python3], [python3]) -AC_CHECK_PROG([PYSPDXTOOLS], [pyspdxtools], [pyspdxtools]) +# Tools used by the SBOM targets (see scripts/sbom.am `make sbom`). GIT is used +# only to derive SOURCE_DATE_EPOCH for reproducible SBOM output; all three are +# optional and the target reports a clear error when a required one is missing. +AC_PATH_PROG([PYTHON3], [python3]) +AC_PATH_PROG([PYSPDXTOOLS], [pyspdxtools]) +AC_PATH_PROG([GIT], [git]) # Checks for headers/libraries AC_CHECK_HEADERS([sys/time.h string.h termios.h unistd.h]) diff --git a/scripts/sbom.am b/scripts/sbom.am new file mode 100644 index 00000000..c3844fd2 --- /dev/null +++ b/scripts/sbom.am @@ -0,0 +1,196 @@ +# scripts/sbom.am - shared Automake recipe for CRA-compliant SBOM generation. +# +# One generator (gen-sbom) does the work; each product just describes itself and +# includes this fragment. It is deliberately product-agnostic: a Makefile.am +# sets a few variables (below) and does `include scripts/sbom.am` to get the +# `sbom`, `install-sbom` and `uninstall-sbom` targets. +# +# The canonical copy lives in the wolfSSL repository (scripts/sbom.am); keep +# product copies in sync. gen-sbom is taken from a vendored scripts/gen-sbom +# if a product ships one (used automatically), otherwise from a wolfSSL source +# tree via WOLFSSL_DIR. wolfSSH uses the WOLFSSL_DIR route; vendoring gen-sbom +# for fully offline tarball builds can be added later with no change here. +# +# --------------------------------------------------------------------------- +# The including Makefile.am MUST set, before `include scripts/sbom.am`: +# SBOM_PKGNAME Product name recorded in the SBOM (e.g. wolfssh). Drives +# the output filenames and gen-sbom --name. +# SBOM_LICENSE_FILE Path to the product's LICENSING file +# (e.g. $(srcdir)/LICENSING). +# +# Optional (defaults shown): +# SBOM_ARTIFACT lib | bin - which build output to hash. Default: lib. +# SBOM_LIB_STEM Library basename w/o extension. Default: lib$(SBOM_PKGNAME). +# SBOM_BIN_NAME Program name when SBOM_ARTIFACT = bin. Default: $(SBOM_PKGNAME). +# SBOM_DEP_WOLFSSL yes | no - record wolfSSL as a dependency. Default: no. +# SBOM_DEP_OPENSSL yes | no - record OpenSSL as a dependency (wolfProvider / +# wolfEngine). Default: no. +# SBOM_LICENSE_OVERRIDE SPDX expression to record instead of the licence +# detected from SBOM_LICENSE_FILE. +# SBOM_LICENSE_TEXT Path to licence text for any LicenseRef-* used in +# SBOM_LICENSE_OVERRIDE (required by SPDX 2.3). +# SBOM_WOLFSSL_VERSION Version recorded for the wolfSSL dependency; +# auto-detected from WOLFSSL_DIR/wolfssl/version.h when unset. +# SBOM_OPENSSL_VERSION Version recorded for the OpenSSL dependency; +# gen-sbom resolves it via pkg-config when unset. +# +# The wolfSSL/OpenSSL dependency flags are feature-detected against gen-sbom +# --help, so a product wired for them still produces a valid SBOM (with a NOTE) +# against a gen-sbom that predates the flag. +# +# gen-sbom is located at $(srcdir)/scripts/gen-sbom if vendored, else at +# $(WOLFSSL_DIR)/scripts/gen-sbom. python3, pyspdxtools and git come from +# configure (AC_PATH_PROG); git is used only to derive SOURCE_DATE_EPOCH. +# --------------------------------------------------------------------------- + +SBOM_ARTIFACT ?= lib +SBOM_LIB_STEM ?= lib$(SBOM_PKGNAME) +SBOM_BIN_NAME ?= $(SBOM_PKGNAME) +SBOM_DEP_WOLFSSL ?= no +SBOM_DEP_OPENSSL ?= no + +SBOM_CDX = $(SBOM_PKGNAME)-$(PACKAGE_VERSION).cdx.json +SBOM_SPDX = $(SBOM_PKGNAME)-$(PACKAGE_VERSION).spdx.json +SBOM_SPDX_TV = $(SBOM_PKGNAME)-$(PACKAGE_VERSION).spdx +sbomdir = $(datadir)/doc/$(PACKAGE) + +# Prefer a vendored gen-sbom; fall back to an external wolfSSL source tree. +# The fallback is $(wildcard)-guarded and only consulted when WOLFSSL_DIR is +# set, so an unset WOLFSSL_DIR leaves SBOM_GEN empty (and the sbom recipe's +# `test -f` prints the "set WOLFSSL_DIR" error) rather than resolving to an +# absolute /scripts/gen-sbom that could run an unrelated host script. +SBOM_GEN = $(firstword $(wildcard $(srcdir)/scripts/gen-sbom) \ + $(if $(WOLFSSL_DIR),$(wildcard $(WOLFSSL_DIR)/scripts/gen-sbom))) + +# Library artifact search order (versioned first) covering ELF, Mach-O and PE. +# Windows import libs (.lib) come with and without the "lib" prefix. +SBOM_LIB_GLOBS = \ + $(SBOM_LIB_STEM).so.[0-9]* \ + $(SBOM_LIB_STEM).so \ + $(SBOM_LIB_STEM).[0-9]*.dylib \ + $(SBOM_LIB_STEM).dylib \ + $(SBOM_LIB_STEM).dll \ + $(SBOM_LIB_STEM).dll.a \ + $(SBOM_LIB_STEM).lib \ + $(SBOM_PKGNAME).lib \ + $(SBOM_LIB_STEM).a + +# Automake requires CLEANFILES to be initialised with `=` before `+=`; the +# including Makefile.am must declare `CLEANFILES =` (typically in its primaries +# init block) before `include scripts/sbom.am`. +CLEANFILES += $(SBOM_CDX) $(SBOM_SPDX) $(SBOM_SPDX_TV) + +.PHONY: sbom install-sbom uninstall-sbom + +# Stage a `make install` into a private tree, discover the installed artifact +# (shared/static library or program; ELF/Mach-O/PE), hash it, capture the +# configured build macros (AM_CPPFLAGS + config.h), generate SPDX+CDX, validate +# the SPDX, then convert to tag-value. The staging tree and temp defines file +# are removed unconditionally via `trap`, even on failure. SOURCE_DATE_EPOCH is +# honoured for reproducible output (defaults to the last git commit time). +sbom: + @test -n "$(PYTHON3)" || { \ + echo "ERROR: 'python3' not found in PATH. Cannot generate SBOM."; \ + exit 1; } + @test -n "$(PYSPDXTOOLS)" || { \ + echo "ERROR: 'pyspdxtools' not found (pip install spdx-tools)."; \ + exit 1; } + @test -f "$(SBOM_GEN)" || { \ + echo "ERROR: gen-sbom not found. Vendor scripts/gen-sbom, or re-run:"; \ + echo " make sbom WOLFSSL_DIR=/path/to/wolfssl"; \ + exit 1; } + @rm -rf $(abs_builddir)/_sbom_staging + @set -e; \ + _defines=`mktemp $(abs_builddir)/_sbom_defines.XXXXXX`; \ + trap 'rm -rf $(abs_builddir)/_sbom_staging "$$_defines"' EXIT INT TERM HUP; \ + $(MAKE) install DESTDIR=$(abs_builddir)/_sbom_staging; \ + sbom_art=""; \ + if test "$(SBOM_ARTIFACT)" = bin; then \ + for art in \ + "$(abs_builddir)/_sbom_staging$(bindir)/$(SBOM_BIN_NAME)" \ + "$(abs_builddir)/_sbom_staging$(bindir)/$(SBOM_BIN_NAME)".exe; do \ + if test -f "$$art"; then sbom_art="$$art"; break; fi; \ + done; \ + else \ + for art in \ + $(addprefix "$(abs_builddir)/_sbom_staging$(libdir)"/,$(SBOM_LIB_GLOBS)) \ + $(addprefix "$(abs_builddir)/_sbom_staging$(bindir)"/,$(SBOM_LIB_STEM).dll $(SBOM_PKGNAME).dll); do \ + if test -f "$$art"; then sbom_art="$$art"; break; fi; \ + done; \ + fi; \ + if test -z "$$sbom_art"; then \ + echo ""; \ + echo "ERROR: no installed $(SBOM_PKGNAME) artifact found for SBOM."; \ + echo " (configure with --enable-shared or --enable-static)"; \ + echo ""; \ + exit 1; \ + fi; \ + echo "SBOM: hashing $$sbom_art"; \ + $(CC) -dM -E $(DEFAULT_INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(if $(wildcard $(abs_builddir)/config.h),-include $(abs_builddir)/config.h) \ + -x c /dev/null > "$$_defines"; \ + if test -z "$${SOURCE_DATE_EPOCH:-}" && test -n "$(GIT)" && \ + $(GIT) -C "$(srcdir)" rev-parse --git-dir >/dev/null 2>&1; then \ + sde=`$(GIT) -C "$(srcdir)" log -1 --format=%ct 2>/dev/null`; \ + if test -n "$$sde"; then SOURCE_DATE_EPOCH="$$sde"; export SOURCE_DATE_EPOCH; fi; \ + fi; \ + dep_args=""; \ + if test "$(SBOM_DEP_WOLFSSL)" = yes; then \ + if $(PYTHON3) "$(SBOM_GEN)" --help 2>/dev/null \ + | $(GREP) -q -- '--dep-wolfssl'; then \ + dep_args="$$dep_args --dep-wolfssl yes"; \ + wv="$(SBOM_WOLFSSL_VERSION)"; \ + if test -z "$$wv" && test -f "$(WOLFSSL_DIR)/wolfssl/version.h"; then \ + wv=`sed -n 's/.*LIBWOLFSSL_VERSION_STRING[ \t]*"\([^"]*\)".*/\1/p' \ + "$(WOLFSSL_DIR)/wolfssl/version.h"`; \ + fi; \ + if test -n "$$wv"; then \ + dep_args="$$dep_args --dep-version wolfssl=$$wv"; \ + fi; \ + else \ + echo "NOTE: this gen-sbom has no --dep-wolfssl support, so the SBOM"; \ + echo " will not list wolfssl as a dependency component. That"; \ + echo " support is added by wolfSSL/wolfssl#10343; until it merges"; \ + echo " to wolfssl master, point WOLFSSL_DIR at that PR's branch"; \ + echo " to enable it. The generated SBOM is valid either way."; \ + fi; \ + fi; \ + if test "$(SBOM_DEP_OPENSSL)" = yes; then \ + if $(PYTHON3) "$(SBOM_GEN)" --help 2>/dev/null \ + | $(GREP) -q -- '--dep-openssl'; then \ + dep_args="$$dep_args --dep-openssl yes"; \ + if test -n "$(SBOM_OPENSSL_VERSION)"; then \ + dep_args="$$dep_args --dep-version openssl=$(SBOM_OPENSSL_VERSION)"; \ + fi; \ + else \ + echo "NOTE: this gen-sbom has no --dep-openssl support; openssl will"; \ + echo " not be listed as a dependency component."; \ + fi; \ + fi; \ + $(PYTHON3) "$(SBOM_GEN)" \ + --name $(SBOM_PKGNAME) \ + --version $(PACKAGE_VERSION) \ + --supplier "wolfSSL Inc." \ + --license-file $(SBOM_LICENSE_FILE) \ + --options-h "$$_defines" \ + --lib "$$sbom_art" \ + $$dep_args \ + $(if $(SBOM_LICENSE_OVERRIDE),--license-override '$(SBOM_LICENSE_OVERRIDE)') \ + $(if $(SBOM_LICENSE_TEXT),--license-text '$(SBOM_LICENSE_TEXT)') \ + --cdx-out $(abs_builddir)/$(SBOM_CDX) \ + --spdx-out $(abs_builddir)/$(SBOM_SPDX); \ + $(PYSPDXTOOLS) --infile $(abs_builddir)/$(SBOM_SPDX) \ + --outfile $(abs_builddir)/$(SBOM_SPDX_TV) + +install-sbom: sbom + $(MKDIR_P) $(DESTDIR)$(sbomdir) + $(INSTALL_DATA) $(SBOM_CDX) $(DESTDIR)$(sbomdir)/ + $(INSTALL_DATA) $(SBOM_SPDX) $(DESTDIR)$(sbomdir)/ + $(INSTALL_DATA) $(SBOM_SPDX_TV) $(DESTDIR)$(sbomdir)/ + +uninstall-sbom: + -rm -f $(DESTDIR)$(sbomdir)/$(SBOM_CDX) + -rm -f $(DESTDIR)$(sbomdir)/$(SBOM_SPDX) + -rm -f $(DESTDIR)$(sbomdir)/$(SBOM_SPDX_TV) + +uninstall-hook: uninstall-sbom From cdac833670926c96ed02d6b4e7d3115a328bb1ca Mon Sep 17 00:00:00 2001 From: Sameeh Jubran Date: Thu, 9 Jul 2026 10:35:23 +0300 Subject: [PATCH 5/7] sbom: add SBOM generation CI workflow Signed-off-by: Sameeh Jubran --- .github/workflows/sbom.yml | 161 +++++++++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 .github/workflows/sbom.yml diff --git a/.github/workflows/sbom.yml b/.github/workflows/sbom.yml new file mode 100644 index 00000000..97e8f4c7 --- /dev/null +++ b/.github/workflows/sbom.yml @@ -0,0 +1,161 @@ +name: SBOM Test + +on: + push: + branches: [ 'master', 'main', 'release/**' ] + pull_request: + branches: [ '*' ] + workflow_dispatch: + inputs: + wolfssl_ref: + description: 'wolfssl git ref that provides scripts/gen-sbom' + # TODO: switch back to 'master' once wolfSSL/wolfssl#10343 merges. + default: 'refs/pull/10343/head' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +# This workflow only reads the repo and uploads artefacts; no API writes. +permissions: + contents: read + +jobs: + sbom: + name: wolfCLU SBOM generation (linux) + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - name: Checkout wolfclu + uses: actions/checkout@v4 + with: + path: wolfclu + + # wolfssl is built + installed so wolfCLU has a library to link, and its + # source tree (scripts/gen-sbom + wolfssl/version.h) is passed to + # `make sbom` via WOLFSSL_DIR. gen-sbom is not yet on wolfssl master, so + # default to the open PR head that carries it (wolfSSL/wolfssl#10343) so CI + # actually exercises `make sbom` instead of silently skipping. TODO: + # switch the fallback back to 'master' once #10343 merges. + - name: Checkout wolfssl (gen-sbom + library source) + uses: actions/checkout@v4 + with: + repository: wolfSSL/wolfssl + ref: ${{ github.event.inputs.wolfssl_ref || 'refs/pull/10343/head' }} + path: wolfssl + + - name: Install SBOM validator (pyspdxtools) + run: | + python3 -m pip install --user 'spdx-tools==0.8.*' + echo "$HOME/.local/bin" >> "$GITHUB_PATH" + + # --enable-all gives wolfCLU the OpenSSL-compat / cert-req APIs it links. + - name: Build and install wolfssl + working-directory: wolfssl + run: | + autoreconf -ivf + ./configure --enable-all --prefix="$GITHUB_WORKSPACE/wolfssl-install" + make -j"$(nproc)" + make install + + # gen-sbom lives in wolfssl and may not be on the checked-out ref yet (the + # wolfSSL SBOM change can land separately). Gate on its presence so this + # workflow is safe even if the ref is changed to one without it. + - name: Detect gen-sbom availability and capabilities + id: gate + run: | + GS="$GITHUB_WORKSPACE/wolfssl/scripts/gen-sbom" + if [ ! -f "$GS" ]; then + echo "have=no" >> "$GITHUB_OUTPUT" + echo "::notice::wolfssl scripts/gen-sbom not present on this ref; skipping SBOM generation." + exit 0 + fi + echo "have=yes" >> "$GITHUB_OUTPUT" + if python3 "$GS" --help 2>/dev/null | grep -q -- '--dep-wolfssl'; then + echo "dep_wolfssl=yes" >> "$GITHUB_OUTPUT" + else + echo "dep_wolfssl=no" >> "$GITHUB_OUTPUT" + echo "::notice::gen-sbom on this ref has no --dep-wolfssl; the wolfssl dependency + name-derived identity assertions will be skipped." + fi + + - name: Configure and build wolfclu + if: steps.gate.outputs.have == 'yes' + working-directory: wolfclu + run: | + autoreconf -ivf + ./configure --with-wolfssl="$GITHUB_WORKSPACE/wolfssl-install" + make -j"$(nproc)" + + - name: Generate SBOM + if: steps.gate.outputs.have == 'yes' + working-directory: wolfclu + run: make sbom WOLFSSL_DIR="$GITHUB_WORKSPACE/wolfssl" + + - name: Outputs exist and SPDX validates + if: steps.gate.outputs.have == 'yes' + working-directory: wolfclu + run: | + ls wolfclu-*.cdx.json wolfclu-*.spdx.json wolfclu-*.spdx + pyspdxtools --infile wolfclu-*.spdx.json + + - name: CycloneDX identity and licence + if: steps.gate.outputs.have == 'yes' + working-directory: wolfclu + run: | + python3 - <<'PY' + import glob, json + cdx = json.load(open(glob.glob('wolfclu-*.cdx.json')[0])) + assert cdx['bomFormat'] == 'CycloneDX', cdx.get('bomFormat') + assert cdx['specVersion'] == '1.6', cdx.get('specVersion') + m = cdx['metadata']['component'] + assert m['name'] == 'wolfclu', m['name'] + assert m['purl'].startswith('pkg:github/wolfSSL/wolfclu@'), m['purl'] + # Default override must land as GPL-3.0-or-later (matches source headers). + ids = [l.get('license', {}).get('id') for l in m.get('licenses', [])] + assert 'GPL-3.0-or-later' in ids, ids + # Embedded identity is the hashed binary artifact. + assert {h['alg'] for h in m.get('hashes', [])}, 'no component hash' + print('CDX ok:', m['name'], m['purl'], ids) + PY + + - name: Reproducible across two runs (SOURCE_DATE_EPOCH) + if: steps.gate.outputs.have == 'yes' + working-directory: wolfclu + run: | + rm -f wolfclu-*.cdx.json wolfclu-*.spdx.json wolfclu-*.spdx + SOURCE_DATE_EPOCH=1700000000 make sbom \ + WOLFSSL_DIR="$GITHUB_WORKSPACE/wolfssl" + sha256sum wolfclu-*.cdx.json wolfclu-*.spdx.json > /tmp/a.sums + rm -f wolfclu-*.cdx.json wolfclu-*.spdx.json wolfclu-*.spdx + SOURCE_DATE_EPOCH=1700000000 make sbom \ + WOLFSSL_DIR="$GITHUB_WORKSPACE/wolfssl" + sha256sum wolfclu-*.cdx.json wolfclu-*.spdx.json > /tmp/b.sums + diff /tmp/a.sums /tmp/b.sums + + - name: wolfssl recorded as a dependency + if: steps.gate.outputs.have == 'yes' && steps.gate.outputs.dep_wolfssl == 'yes' + working-directory: wolfclu + run: | + python3 - <<'PY' + import glob, json + d = json.load(open(glob.glob('wolfclu-*.spdx.json')[0])) + assert 'wolfssl' in {p['name'] for p in d['packages']}, \ + [p['name'] for p in d['packages']] + rels = [(r['spdxElementId'], r['relationshipType'], + r['relatedSpdxElement']) for r in d['relationships']] + assert ('SPDXRef-Package-wolfclu', 'DEPENDS_ON', + 'SPDXRef-Package-wolfssl') in rels, rels + print('wolfssl dependency ok') + PY + + - name: Upload SBOM artefacts + if: always() && steps.gate.outputs.have == 'yes' + uses: actions/upload-artifact@v4 + with: + name: wolfclu-sbom-${{ github.sha }} + path: | + wolfclu/wolfclu-*.cdx.json + wolfclu/wolfclu-*.spdx.json + wolfclu/wolfclu-*.spdx + if-no-files-found: warn + retention-days: 90 From 5589d6ae33f9fd0f82a94750de2228b6f6d7998d Mon Sep 17 00:00:00 2001 From: Sameeh Jubran Date: Tue, 14 Jul 2026 13:26:48 +0300 Subject: [PATCH 6/7] sbom: capture AM_CFLAGS feature macros and sync shared fragment Sync scripts/sbom.am with the canonical wolfSSL copy (capture AM_CFLAGS/CFLAGS, overridable SBOM_CONFIG_H, $(docdir) sbomdir, [[:space:]] version parse, GNU-make/uninstall docs), set SBOM_CONFIG_H=src/config.h and ship the fragment via EXTRA_DIST so the wolfCLU feature macros land in the SBOM, fall back to $PYTHON when python3 is absent, include the .spdx tag-value in the reproducibility check, and widen the workflow pull_request filter to '**'. Signed-off-by: Sameeh Jubran --- .github/workflows/sbom.yml | 6 +++--- Makefile.am | 7 +++++++ configure.ac | 4 +++- scripts/sbom.am | 40 +++++++++++++++++++++++++++++--------- 4 files changed, 44 insertions(+), 13 deletions(-) diff --git a/.github/workflows/sbom.yml b/.github/workflows/sbom.yml index 97e8f4c7..77f44889 100644 --- a/.github/workflows/sbom.yml +++ b/.github/workflows/sbom.yml @@ -4,7 +4,7 @@ on: push: branches: [ 'master', 'main', 'release/**' ] pull_request: - branches: [ '*' ] + branches: [ '**' ] workflow_dispatch: inputs: wolfssl_ref: @@ -125,11 +125,11 @@ jobs: rm -f wolfclu-*.cdx.json wolfclu-*.spdx.json wolfclu-*.spdx SOURCE_DATE_EPOCH=1700000000 make sbom \ WOLFSSL_DIR="$GITHUB_WORKSPACE/wolfssl" - sha256sum wolfclu-*.cdx.json wolfclu-*.spdx.json > /tmp/a.sums + sha256sum wolfclu-*.cdx.json wolfclu-*.spdx.json wolfclu-*.spdx > /tmp/a.sums rm -f wolfclu-*.cdx.json wolfclu-*.spdx.json wolfclu-*.spdx SOURCE_DATE_EPOCH=1700000000 make sbom \ WOLFSSL_DIR="$GITHUB_WORKSPACE/wolfssl" - sha256sum wolfclu-*.cdx.json wolfclu-*.spdx.json > /tmp/b.sums + sha256sum wolfclu-*.cdx.json wolfclu-*.spdx.json wolfclu-*.spdx > /tmp/b.sums diff /tmp/a.sums /tmp/b.sums - name: wolfssl recorded as a dependency diff --git a/Makefile.am b/Makefile.am index 67e95741..37f014d4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -182,6 +182,13 @@ SBOM_DEP_WOLFSSL = yes # commercial licensees can override (e.g. LicenseRef-wolfSSL-Commercial). SBOM_LICENSE_OVERRIDE ?= GPL-3.0-or-later +# wolfCLU declares AC_CONFIG_HEADERS([src/config.h]) and carries its feature +# macros (-DHAVE_WOLFSSL_OPTIONS, -DWOLFCLU_NO_FILESYSTEM, ...) in AM_CFLAGS, +# so point the SBOM build-options capture at the real config header. +SBOM_CONFIG_H = $(abs_builddir)/src/config.h + +EXTRA_DIST += scripts/sbom.am + include scripts/sbom.am diff --git a/configure.ac b/configure.ac index 8943af19..729ef4f5 100644 --- a/configure.ac +++ b/configure.ac @@ -62,7 +62,9 @@ AM_CONDITIONAL([HAVE_PYTHON], [test "$PYTHON" != ":"]) # Tools used by the SBOM targets (see scripts/sbom.am `make sbom`). GIT is used # only to derive SOURCE_DATE_EPOCH for reproducible SBOM output; all three are # optional and the target reports a clear error when a required one is missing. -AC_PATH_PROG([PYTHON3], [python3]) +# Fall back to the AM_PATH_PYTHON-discovered interpreter ($PYTHON) when no +# executable literally named `python3` is on PATH (e.g. python3.11 only). +AC_PATH_PROG([PYTHON3], [python3], [$PYTHON]) AC_PATH_PROG([PYSPDXTOOLS], [pyspdxtools]) AC_PATH_PROG([GIT], [git]) diff --git a/scripts/sbom.am b/scripts/sbom.am index c3844fd2..606e1fd5 100644 --- a/scripts/sbom.am +++ b/scripts/sbom.am @@ -5,11 +5,12 @@ # sets a few variables (below) and does `include scripts/sbom.am` to get the # `sbom`, `install-sbom` and `uninstall-sbom` targets. # -# The canonical copy lives in the wolfSSL repository (scripts/sbom.am); keep -# product copies in sync. gen-sbom is taken from a vendored scripts/gen-sbom -# if a product ships one (used automatically), otherwise from a wolfSSL source -# tree via WOLFSSL_DIR. wolfSSH uses the WOLFSSL_DIR route; vendoring gen-sbom -# for fully offline tarball builds can be added later with no change here. +# This is the canonical copy (wolfSSL repository, scripts/sbom.am); product +# repositories vendor a copy of it and must be kept in sync with this file. +# gen-sbom is taken from a vendored scripts/gen-sbom if a product ships one +# (used automatically), otherwise from a wolfSSL source tree via WOLFSSL_DIR. +# Products such as wolfSSH use the WOLFSSL_DIR route; vendoring gen-sbom for +# fully offline tarball builds can be added later with no change here. # # --------------------------------------------------------------------------- # The including Makefile.am MUST set, before `include scripts/sbom.am`: @@ -33,6 +34,13 @@ # auto-detected from WOLFSSL_DIR/wolfssl/version.h when unset. # SBOM_OPENSSL_VERSION Version recorded for the OpenSSL dependency; # gen-sbom resolves it via pkg-config when unset. +# SBOM_CONFIG_H Path to the configure-generated config header to +# force-include when capturing the configured build +# macros. Products whose AC_CONFIG_HEADERS lives in a +# subdirectory MUST override this so config.h defines are +# captured (e.g. wolfEngine: $(abs_builddir)/include/config.h; +# wolfCLU: $(abs_builddir)/src/config.h). +# Default: $(abs_builddir)/config.h. # # The wolfSSL/OpenSSL dependency flags are feature-detected against gen-sbom # --help, so a product wired for them still produces a valid SBOM (with a NOTE) @@ -41,6 +49,10 @@ # gen-sbom is located at $(srcdir)/scripts/gen-sbom if vendored, else at # $(WOLFSSL_DIR)/scripts/gen-sbom. python3, pyspdxtools and git come from # configure (AC_PATH_PROG); git is used only to derive SOURCE_DATE_EPOCH. +# +# NOTE: this fragment requires GNU make. It uses GNU conditional assignment +# (?=) and the GNU make functions $(wildcard), $(if), $(firstword) and +# $(addprefix); under a non-GNU make the SBOM targets will not work. # --------------------------------------------------------------------------- SBOM_ARTIFACT ?= lib @@ -48,11 +60,14 @@ SBOM_LIB_STEM ?= lib$(SBOM_PKGNAME) SBOM_BIN_NAME ?= $(SBOM_PKGNAME) SBOM_DEP_WOLFSSL ?= no SBOM_DEP_OPENSSL ?= no +SBOM_CONFIG_H ?= $(abs_builddir)/config.h SBOM_CDX = $(SBOM_PKGNAME)-$(PACKAGE_VERSION).cdx.json SBOM_SPDX = $(SBOM_PKGNAME)-$(PACKAGE_VERSION).spdx.json SBOM_SPDX_TV = $(SBOM_PKGNAME)-$(PACKAGE_VERSION).spdx -sbomdir = $(datadir)/doc/$(PACKAGE) +# Use Automake's $(docdir) so a user's --docdir override is honoured (this +# equals $(datadir)/doc/$(PACKAGE) by default). +sbomdir = $(docdir) # Prefer a vendored gen-sbom; fall back to an external wolfSSL source tree. # The fallback is $(wildcard)-guarded and only consulted when WOLFSSL_DIR is @@ -84,7 +99,9 @@ CLEANFILES += $(SBOM_CDX) $(SBOM_SPDX) $(SBOM_SPDX_TV) # Stage a `make install` into a private tree, discover the installed artifact # (shared/static library or program; ELF/Mach-O/PE), hash it, capture the -# configured build macros (AM_CPPFLAGS + config.h), generate SPDX+CDX, validate +# configured build macros (AM_CPPFLAGS/AM_CFLAGS/CFLAGS + config.h; some +# products carry their feature -D flags in AM_CFLAGS rather than AM_CPPFLAGS), +# generate SPDX+CDX, validate # the SPDX, then convert to tag-value. The staging tree and temp defines file # are removed unconditionally via `trap`, even on failure. SOURCE_DATE_EPOCH is # honoured for reproducible output (defaults to the last git commit time). @@ -127,7 +144,8 @@ sbom: fi; \ echo "SBOM: hashing $$sbom_art"; \ $(CC) -dM -E $(DEFAULT_INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ - $(if $(wildcard $(abs_builddir)/config.h),-include $(abs_builddir)/config.h) \ + $(AM_CFLAGS) $(CFLAGS) \ + $(if $(wildcard $(SBOM_CONFIG_H)),-include $(SBOM_CONFIG_H)) \ -x c /dev/null > "$$_defines"; \ if test -z "$${SOURCE_DATE_EPOCH:-}" && test -n "$(GIT)" && \ $(GIT) -C "$(srcdir)" rev-parse --git-dir >/dev/null 2>&1; then \ @@ -141,7 +159,7 @@ sbom: dep_args="$$dep_args --dep-wolfssl yes"; \ wv="$(SBOM_WOLFSSL_VERSION)"; \ if test -z "$$wv" && test -f "$(WOLFSSL_DIR)/wolfssl/version.h"; then \ - wv=`sed -n 's/.*LIBWOLFSSL_VERSION_STRING[ \t]*"\([^"]*\)".*/\1/p' \ + wv=`sed -n 's/.*LIBWOLFSSL_VERSION_STRING[[:space:]]*"\([^"]*\)".*/\1/p' \ "$(WOLFSSL_DIR)/wolfssl/version.h"`; \ fi; \ if test -n "$$wv"; then \ @@ -193,4 +211,8 @@ uninstall-sbom: -rm -f $(DESTDIR)$(sbomdir)/$(SBOM_SPDX) -rm -f $(DESTDIR)$(sbomdir)/$(SBOM_SPDX_TV) +# SBOM install is intentionally opt-in (`make install-sbom`), so `make install` +# does NOT place SBOM files. uninstall-sbom is still chained into the standard +# `make uninstall` via uninstall-hook so a prior `make install-sbom` is cleaned +# up; it uses `rm -f`, so it is a harmless no-op when no SBOM was installed. uninstall-hook: uninstall-sbom From 330dfe794d5d01a1bceaee2fd6c9a6320b261494 Mon Sep 17 00:00:00 2001 From: Sameeh Jubran Date: Thu, 16 Jul 2026 17:01:20 +0300 Subject: [PATCH 7/7] sbom: re-vendor unified canonical scripts/sbom.am Sync scripts/sbom.am with the unified canonical copy in wolfSSL (scripts/sbom.am), which adds the SBOM_OPTIONS_H override unifying options.h- and config.h/AM_CFLAGS-based macro capture into one fragment. wolfCLU keeps its subdir config.h override (SBOM_CONFIG_H = $(abs_builddir)/src/config.h) and does not set SBOM_OPTIONS_H, so the compiler + config.h path is unchanged; no behavior change. Keeps the vendored fragment byte-identical to the canonical. Signed-off-by: Sameeh Jubran --- scripts/sbom.am | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/scripts/sbom.am b/scripts/sbom.am index 606e1fd5..50973598 100644 --- a/scripts/sbom.am +++ b/scripts/sbom.am @@ -20,6 +20,12 @@ # (e.g. $(srcdir)/LICENSING). # # Optional (defaults shown): +# SBOM_OPTIONS_H Path to a product-generated options header (e.g. +# wolfMQTT's $(builddir)/wolfmqtt/options.h) that records +# the enabled build macros. Set this for products whose +# feature flags are NOT in config.h (no AC_DEFINE); when +# unset the recipe derives the macros from the compiler + +# config.h. Default: unset. # SBOM_ARTIFACT lib | bin - which build output to hash. Default: lib. # SBOM_LIB_STEM Library basename w/o extension. Default: lib$(SBOM_PKGNAME). # SBOM_BIN_NAME Program name when SBOM_ARTIFACT = bin. Default: $(SBOM_PKGNAME). @@ -99,8 +105,9 @@ CLEANFILES += $(SBOM_CDX) $(SBOM_SPDX) $(SBOM_SPDX_TV) # Stage a `make install` into a private tree, discover the installed artifact # (shared/static library or program; ELF/Mach-O/PE), hash it, capture the -# configured build macros (AM_CPPFLAGS/AM_CFLAGS/CFLAGS + config.h; some -# products carry their feature -D flags in AM_CFLAGS rather than AM_CPPFLAGS), +# configured build macros (from SBOM_OPTIONS_H if set, else AM_CPPFLAGS/ +# AM_CFLAGS/CFLAGS + config.h; some products carry their feature -D flags in +# AM_CFLAGS rather than AM_CPPFLAGS, and some outside config.h entirely), # generate SPDX+CDX, validate # the SPDX, then convert to tag-value. The staging tree and temp defines file # are removed unconditionally via `trap`, even on failure. SOURCE_DATE_EPOCH is @@ -143,10 +150,14 @@ sbom: exit 1; \ fi; \ echo "SBOM: hashing $$sbom_art"; \ - $(CC) -dM -E $(DEFAULT_INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ - $(AM_CFLAGS) $(CFLAGS) \ - $(if $(wildcard $(SBOM_CONFIG_H)),-include $(SBOM_CONFIG_H)) \ - -x c /dev/null > "$$_defines"; \ + opts_h="$(SBOM_OPTIONS_H)"; \ + if test -z "$$opts_h"; then \ + opts_h="$$_defines"; \ + $(CC) -dM -E $(DEFAULT_INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) \ + $(if $(wildcard $(SBOM_CONFIG_H)),-include $(SBOM_CONFIG_H)) \ + -x c /dev/null > "$$_defines"; \ + fi; \ if test -z "$${SOURCE_DATE_EPOCH:-}" && test -n "$(GIT)" && \ $(GIT) -C "$(srcdir)" rev-parse --git-dir >/dev/null 2>&1; then \ sde=`$(GIT) -C "$(srcdir)" log -1 --format=%ct 2>/dev/null`; \ @@ -190,7 +201,7 @@ sbom: --version $(PACKAGE_VERSION) \ --supplier "wolfSSL Inc." \ --license-file $(SBOM_LICENSE_FILE) \ - --options-h "$$_defines" \ + --options-h "$$opts_h" \ --lib "$$sbom_art" \ $$dep_args \ $(if $(SBOM_LICENSE_OVERRIDE),--license-override '$(SBOM_LICENSE_OVERRIDE)') \