Skip to content

feat: add sbom Makefile target#414

Open
MarkAtwood wants to merge 5 commits into
wolfSSL:mainfrom
MarkAtwood:feat/add-sbom-make-target
Open

feat: add sbom Makefile target#414
MarkAtwood wants to merge 5 commits into
wolfSSL:mainfrom
MarkAtwood:feat/add-sbom-make-target

Conversation

@MarkAtwood

Copy link
Copy Markdown

Summary

Adds a make sbom target that produces CycloneDX and SPDX SBOM files for wolfHSM.

  • Parses version from ChangeLog.md (e.g., # wolfHSM Release v1.4.0)
  • Enumerates sources from src/*.c (sorted)
  • Hashes each source file (SHA-256 combined fingerprint)
  • Calls gen-sbom to produce CDX + SPDX outputs
  • No build system changes — pure Makefile target

Usage

```sh
make sbom WOLFSSL_DIR=/path/to/wolfssl/source
```

Outputs: wolfhsm-<version>.cdx.json, wolfhsm-<version>.spdx.json

Requirements

  • WOLFSSL_DIR must point to a wolfssl source tree containing scripts/gen-sbom (available on the feat/sbom-embedded branch of wolfssl)
  • python3 and pip install spdx-tools on the build host

Test plan

  • make sbom WOLFSSL_DIR=...
  • Verify CDX and SPDX output files are produced with correct version and source count

Adds sbom target that calls gen-sbom to produce
CycloneDX and SPDX output files.
Parses version from ChangeLog.md.
Sources enumerated from src/*.c.
Requires WOLFSSL_DIR pointing to wolfssl tree with
the feat/sbom-embedded branch (includes gen-sbom).
@MarkAtwood MarkAtwood requested review from Copilot and sameehj June 23, 2026 01:39

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot couldn't run its full agentic review because no GitHub Actions runner was available. Make sure your repository has a runner available to run Copilot's review, or add a copilot-setup-steps.yml file specifying one with the runs-on attribute. See the docs for more details.

Adds a make sbom target to generate CycloneDX and SPDX SBOM artifacts for wolfHSM using wolfSSL’s scripts/gen-sbom.

Changes:

  • Introduces an sbom Makefile target with checks for version parsing and WOLFSSL_DIR.
  • Gathers sources from src/*.c and passes compiler defines + metadata into gen-sbom.
  • Emits versioned wolfhsm-<version>.cdx.json and wolfhsm-<version>.spdx.json outputs.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread Makefile Outdated
# ---- SBOM generation ----
CC ?= cc
WOLFSSL_DIR ?= ../../wolfssl
VERSION := $(shell sed -n 's/^# wolfHSM Release v\([0-9][0-9.]*\).*/\1/p' ChangeLog.md | head -1)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — changed VERSION := to a recursive VERSION = (and SBOM_CDX/SBOM_SPDX likewise) so the sed only runs when the sbom target is invoked. Confirmed make -n clean no longer shells out.

Comment thread Makefile Outdated
CC ?= cc
WOLFSSL_DIR ?= ../../wolfssl
VERSION := $(shell sed -n 's/^# wolfHSM Release v\([0-9][0-9.]*\).*/\1/p' ChangeLog.md | head -1)
SRCS := $(wildcard src/*.c)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — SRCS := $(sort $(wildcard src/*.c)) for reproducible ordering. Left the empty-list guard out: src/ always has .c files here and gen-sbom already errors on an empty --srcs.

Comment thread Makefile
--supplier "wolfSSL Inc." \
--license-file LICENSING \
--options-h "$$_defines" \
--srcs $(SRCS) \

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — SRCS := $(sort $(wildcard src/*.c)) for reproducible ordering.

Comment thread Makefile Outdated
Comment on lines +92 to +94
_py=$$(command -v python3 2>/dev/null || command -v python 2>/dev/null) && \
[ -n "$$_py" ] || { echo "ERROR: python3 not found." >&2; exit 1; } && \
"$$_py" $(WOLFSSL_DIR)/scripts/gen-sbom \

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — requires python3 explicitly now and errors if it's missing; dropped the python fallback.

Comment thread Makefile Outdated
Comment on lines +87 to +88
@_defines=$$(mktemp /tmp/wolfhsm-defines.XXXXXX) && \
trap 'rm -f "$$_defines"' EXIT && \

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — switched to trap '...' 0, the portable form.

@bigbrett bigbrett left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Running make sbom WOLFSSL_DIR=../wolfssl against the latest wolfSSL master I get the following error:

ERROR: ../wolfssl/scripts/gen-sbom not found.                                                                                                                                                 
       Use a wolfSSL tree that includes SBOM support.                                                                                                                                         
make: *** [Makefile:73: sbom] Error 1                       

Did this not merge into wolfSSL master yet? If so it shouldn't merge into wolfHSM.

Comment thread README.md Outdated
Comment on lines +34 to +50
wolfHSM uses a custom build system; invoke `gen-sbom` from the wolfssl source
tree directly:

```sh
python3 $WOLFSSL_DIR/scripts/gen-sbom \
--name wolfhsm \
--version $(head -1 $WOLFHSM_DIR/ChangeLog.md | grep -oE '[0-9]+\.[0-9]+\.[0-9]+') \
--supplier "wolfSSL Inc." \
--options-h $WOLFSSL_DIR/include/wolfssl/options.h \
--srcs $WOLFHSM_DIR/src/*.c
```

`WOLFSSL_DIR` must point to a wolfssl source tree containing `scripts/gen-sbom`
(branch `feat/sbom-embedded`, or `master` once wolfSSL/wolfssl#10343 merges).
`WOLFHSM_DIR` is the root of the wolfHSM source tree.

Requires `python3` and `pyspdxtools` (`pip install spdx-tools`).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this doc snippet looks outdated based on the changes in this PR, shouldn't we just recommend the end user run the new makefile target?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — the section now leads with make sbom WOLFSSL_DIR=../wolfssl and keeps the raw gen-sbom command as a documented fallback, with flags matching the target.

@bigbrett bigbrett assigned MarkAtwood and unassigned wolfSSL-Bot Jul 7, 2026
@jackctj117 jackctj117 self-requested a review July 10, 2026 16:42

@dgarske dgarske left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jackctj117 jackctj117 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skoll Multi-Scan Review

Modes: review + review-securityOverall recommendation: COMMENT
Findings: 5 total — 5 posted, 0 skipped
5 finding(s) posted as inline comments (see file-level comments below)

Posted findings

  • [Medium] [review] Default WOLFSSL_DIR is off by one level for the top-level MakefileMakefile:62
  • [Medium] [review+review-security] gen-sbom --options-h receives only compiler builtin macros, not wolfHSM/wolfSSL configMakefile:87-99
  • [Low] [review+review-security] VERSION shell-out runs on every make invocation, not just sbomMakefile:63
  • [Low] [review+review-security] Temp file uses hardcoded /tmp prefix, ignoring $TMPDIRMakefile:87
  • [Low] [review+review-security] README manual invocation diverges from the Makefile sbom targetREADME.md:37-44

Review generated by Skoll

Comment thread Makefile Outdated

# ---- SBOM generation ----
CC ?= cc
WOLFSSL_DIR ?= ../../wolfssl

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [Medium] Default WOLFSSL_DIR is off by one level for the top-level Makefile · convention

The new default WOLFSSL_DIR ?= ../../wolfssl is copied from the sub-Makefiles in test/, benchmark/, etc., which live one directory below the repo root — for them, ../../wolfssl correctly resolves to a sibling checkout. But this assignment is in the top-level Makefile (repo root), so ../../wolfssl resolves to the grandparent directory, i.e. \<grandparent>/wolfssl, not the documented sibling \<parent>/wolfssl. On a standard sibling checkout, make sbom with no override will fail the [ ! -d "$(WOLFSSL_DIR)" ] guard. The correct default here is ../wolfssl. This won't cause silent misbehavior (the directory-exists guard produces a clear error and the PR docs tell users to pass WOLFSSL_DIR= explicitly), but the default is misleading and inconsistent with the repo's documented sibling layout (see CLAUDE.md 'Dependency layout').

Fix: Change the top-level default to ../wolfssl so a standard sibling checkout works out-of-the-box, or drop the default entirely and rely solely on the guard message since the docs already require an explicit WOLFSSL_DIR=.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — the root Makefile's sibling checkout is ../wolfssl, so the default is now ../wolfssl.

Comment thread Makefile Outdated
fi
@echo "wolfHSM version: $(VERSION)"
@echo "Sources: $(words $(SRCS)) .c files in src/"
@_defines=$$(mktemp /tmp/wolfhsm-defines.XXXXXX) && \

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [Medium] gen-sbom --options-h receives only compiler builtin macros, not wolfHSM/wolfSSL config · Logic

The new sbom target generates the defines file passed to --options-h by preprocessing an EMPTY translation unit: $(CC) -dM -E -I. -I$(WOLFSSL_DIR) -x c /dev/null. Because /dev/null includes no headers, -dM -E emits only the compiler's predefined macros (e.g. __GNUC__, __STDC__), NOT any WOLFHSM_CFG_* or wolfSSL HAVE_*/NO_* configuration macros. The -I. / -I$(WOLFSSL_DIR) include paths have no effect since nothing is #included. As a result the SBOM's build-configuration record is effectively empty, which undermines the stated CRA-compliance purpose of accurately capturing the build configuration, and diverges from the README, which instead points --options-h at a real options.h. Severity views differ across scans: the review mode rated this Medium (Low confidence, treating capture of compiler builtins as possibly intentional), while the review-security mode rated it Low (High confidence); the stricter Medium is retained.

Fix: Confirm what gen-sbom expects in --options-h. If it should reflect wolfHSM/wolfSSL configured macros, preprocess a stub that includes the relevant config header (e.g. wolfhsm/wh_settings.h) with the appropriate user_settings/config defines, or point --options-h at a concrete options header as the README shows; if only compiler builtins are needed, drop the no-op -I./-I$(WOLFSSL_DIR) flags to avoid implying otherwise.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — confirmed. Preprocessing /dev/null with no -include emits only compiler builtins, which gen-sbom's noise filter then drops, so the SBOM's config section is effectively empty (the generated CDX had zero config macros). Deciding the right fix — point --options-h at a real options header, add -include <config header>, or use the --user-settings path — is a small design question I'd rather settle deliberately than paper over. Tracking it as a follow-up; not folding it into this PR unless you want it here.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update: implemented in 4cef60f rather than deferred. The recipe now preprocesses wolfhsm/wh_settings.h with the same defines/include path as the test build (-DWOLFHSM_CFG -DWOLFSSL_USER_SETTINGS, WOLFHSM_CFG_DIR on the path, default test/config), so the SBOM records the effective config — 31 WOLFHSM_CFG_* plus the wolfSSL options from user_settings.h, verified in the generated CDX. Integrators point WOLFHSM_CFG_DIR at their port's config dir. Known trade-off, documented inline on the target: wh_settings.h's system includes drag ~330 libc constants into the dump, and they are deliberately not filtered here because a prefix allowlist would drop real unprefixed options (GCM_TABLE_4BIT, FP_MAX_BITS, ...). The durable fix is a provenance-aware noise filter in gen-sbom itself (wolfSSL#10343); the Makefile comment spells out two implementation options and this recipe needs no change once that lands.

Comment thread Makefile Outdated
# ---- SBOM generation ----
CC ?= cc
WOLFSSL_DIR ?= ../../wolfssl
VERSION := $(shell sed -n 's/^# wolfHSM Release v\([0-9][0-9.]*\).*/\1/p' ChangeLog.md | head -1)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 [Low] VERSION shell-out runs on every make invocation, not just sbom · Logic

VERSION := $(shell sed -n ... ChangeLog.md | head -1) uses immediate (:=) expansion at the top level of the root Makefile, so the sed | head subshell runs on every invocation (make all, make test, make clean, etc.), even though VERSION is only used by the sbom target. This is harmless but wasteful, and if ChangeLog.md were ever absent the shell would still run (silently producing an empty value) for unrelated targets. Note the SBOM_CDX/SBOM_SPDX names reference VERSION, so a lazy = assignment keeps them working.

Fix: Use a recursive = assignment (evaluated only when referenced) or a target-specific variable so the shell only runs when the sbom target is invoked.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — recursive VERSION = now; the sed only runs when the sbom target is invoked.

Comment thread Makefile Outdated
fi
@echo "wolfHSM version: $(VERSION)"
@echo "Sources: $(words $(SRCS)) .c files in src/"
@_defines=$$(mktemp /tmp/wolfhsm-defines.XXXXXX) && \

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 [Low] Temp file uses hardcoded /tmp prefix, ignoring $TMPDIR · Resource Leak

The defines file is created with mktemp /tmp/wolfhsm-defines.XXXXXX, hardcoding /tmp. mktemp is used safely here (atomic creation with a random XXXXXX suffix and mode 0600, plus a trap ... EXIT cleanup scoped to the single recipe shell), so there is no symlink/predictability/TOCTOU vulnerability. However, hardcoding /tmp ignores $TMPDIR and can fail on environments where /tmp is unavailable or non-writable (some CI sandboxes, restricted containers, macOS).

Fix: Use mktemp without a hardcoded directory (respects $TMPDIR), e.g. mktemp "$${TMPDIR:-/tmp}/wolfhsm-defines.XXXXXX" or plain mktemp, for better portability across macOS/CI sandboxes.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — mktemp "$${TMPDIR:-/tmp}/wolfhsm-defines.XXXXXX" so it respects $TMPDIR.

Comment thread README.md
wolfHSM uses a custom build system; invoke `gen-sbom` from the wolfssl source
tree directly:

```sh

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 [Low] README manual invocation diverges from the Makefile sbom target · Logic

The README documents a manual gen-sbom invocation that differs from what make sbom actually runs: the README passes --options-h $WOLFSSL_DIR/include/wolfssl/options.h (a checked-in wolfSSL header) whereas the Makefile generates a defines file via $(CC) -dM -E; the README omits --license-file LICENSING, --cdx-out, and --spdx-out that the Makefile passes; and the README derives the version with head -1 ... | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' while the Makefile uses a sed pattern. Two documented-but-divergent invocation paths risk drifting out of sync and produce SBOMs with different content depending on which route a user follows.

Fix: Lead the README section with make sbom WOLFSSL_DIR=... (matching the new target, including --license-file) and keep the raw gen-sbom command as an explicit 'manual invocation' fallback, so both routes produce equivalent SBOMs and the documented flags stay consistent with the Makefile.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — README manual invocation now matches the Makefile target (same flags and version derivation), with the raw command kept as a documented fallback.

Comment thread Makefile Outdated
# ---- SBOM generation ----
CC ?= cc
WOLFSSL_DIR ?= ../../wolfssl
VERSION := $(shell sed -n 's/^# wolfHSM Release v\([0-9][0-9.]*\).*/\1/p' ChangeLog.md | head -1)

@jackctj117 jackctj117 Jul 10, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocker — Makefile fails to parse on GNU Make 3.81 (macOS default make)
Line 63's VERSION := $(shell sed ... '([0-9][0-9.]*)' ...) trips a known make-3.81 function-scanner bug on the (...) parens. Because it's a file-scope :=, the error aborts every target, not just sbom:
$ make -n clean
Makefile:63: *** unterminated call to function shell': missing )'. Stop.
Repro: make 3.81 fails; gmake 4.4.1 is fine. The base Makefile parses under 3.81, so this is a regression. Fix by dropping the capture-group parens (e.g. grep -oE 'v[0-9.]+' + strip) or computing VERSION inside the sbom recipe (which also resolves Copilot's every-invocation note).

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 4c0af52. The actual trigger is the # inside the file-scope $(shell): make <= 4.2 strips everything after it as a comment at read time, which is what leaves the unterminated function call — that's also why it aborted every target. The version extraction no longer uses # or escaped parens (sed -n 's/^. wolfHSM Release v//p' | cut -d' ' -f1), so nothing make-version-sensitive is left at file scope. Verified parse (make -n clean) and a full make sbom end-to-end on GNU Make 4.3; I don't have a 3.81 here, so a macOS confirm would be welcome.

@MarkAtwood

Copy link
Copy Markdown
Author

@bigbrett Correct — gen-sbom hasn't merged to wolfssl master. It's in wolfssl PR wolfSSL/wolfssl#10343, which is still open, so no wolfssl release or master checkout has scripts/gen-sbom yet. The target can't create that file; what it can do is fail clearly. I updated the error to name the dependency and point you at setting WOLFSSL_DIR to a tree that has the script, and the README says the same. So the wolfHSM side is ready to use the moment #10343 lands; it just no-ops with a clear message until then. I did not vendor a copy of gen-sbom into wolfHSM. If you'd rather hold this PR until #10343 merges, flag it and I'll mark it blocked.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants