Skip to content

feat: add make sbom target - #1050

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

feat: add make sbom target#1050
MarkAtwood wants to merge 15 commits into
wolfSSL:masterfrom
MarkAtwood:feat/add-sbom-make-target

Conversation

@MarkAtwood

Copy link
Copy Markdown
Contributor

Summary

Adds make sbom, make install-sbom, and make uninstall-sbom targets to the autotools build.

  • Stages a temporary install to extract the built .so for hashing
  • Dumps preprocessor defines via cc -dM -E for the options snapshot
  • Calls gen-sbom (from wolfssl scripts/gen-sbom) to produce CycloneDX and SPDX outputs
  • Validates SPDX with pyspdxtools

Usage

```sh
./configure --with-wolfssl=/path/to/wolfssl/install
make
make sbom WOLFSSL_DIR=/path/to/wolfssl/source
```

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

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

  • ./configure --with-wolfssl=... && make && make sbom WOLFSSL_DIR=...
  • Verify three output files are produced
  • pyspdxtools --infile wolfssh-*.spdx.json passes validation

Adds sbom, install-sbom, and uninstall-sbom targets.
Runs gen-sbom to produce CDX and SPDX outputs.
Requires WOLFSSL_DIR pointing to a wolfssl tree with
the feat/sbom-embedded branch (includes gen-sbom).

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

Adds SBOM generation and install/uninstall targets to the autotools build, intended to produce CycloneDX + SPDX outputs and validate SPDX on the build host.

Changes:

  • Add make sbom, make install-sbom, and make uninstall-sbom targets plus related variables/cleanup in Makefile.am.
  • Extend configure.ac to discover python3 / pyspdxtools and to break out libtool version-info components into separate substituted variables.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
Makefile.am Adds SBOM generation/validation targets and output staging/cleanup rules.
configure.ac Adds tool discovery for SBOM prerequisites and refactors libtool version-info variables.

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

Comment thread Makefile.am Outdated
Comment thread Makefile.am Outdated
Comment thread Makefile.am Outdated
Comment thread configure.ac Outdated
- Include AM_CPPFLAGS and config.h in the options snapshot so the
  configured feature set (WOLFSSH_SCP/SFTP/NO_WOLFSSH_SERVER/...)
  appears in the SBOM instead of being empty.
- Discover static (.a), Mach-O (.dylib) and Windows (.dll/.lib)
  artifacts, not just a versioned .so.
- Set SOURCE_DATE_EPOCH from the last git commit for reproducible output.
- Clean staging tree and temp defines file via trap even on failure.
- Record wolfssl as an SBOM dependency (--dep-wolfssl), version
  auto-detected from WOLFSSL_DIR/wolfssl/version.h.
- configure.ac: detect git; drop unused version-info split + comment.

Signed-off-by: Sameeh Jubran <sameeh@wolfssl.com>

@ejohnstown ejohnstown 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.

Tested make sbom end-to-end on macOS (--enable-all, gen-sbom from wolfssl feat/sbom-embedded, spdx-tools + pcpp installed): exits 0, all three outputs produced, the Mach-O .dylib discovery branch fires, the trap cleans up the staging tree and temp defines file, the configured feature macros (WOLFSSH_SFTP/SCP/CERTS/AGENT/SSHD, NO_INLINE, NDEBUG) now appear in the SBOM, and pyspdxtools re-validation passes clean. Two follow-ups below, none blocking.

Comment thread Makefile.am Outdated
Comment thread Makefile.am Outdated
sameehj added 2 commits July 8, 2026 15:07
Default SBOM_LICENSE_OVERRIDE to GPL-3.0-only so the SBOM records the
correct license instead of NOASSERTION, and point the --dep-wolfssl
note at wolfSSL/wolfssl#10343.

Signed-off-by: Sameeh Jubran <sameeh@wolfssl.com>
Move the make sbom / install-sbom / uninstall-sbom recipe out of
Makefile.am into a reusable scripts/sbom.am fragment; Makefile.am now
just declares the product facts (name, LICENSING, wolfssl dependency,
GPL-3.0-only default) and includes it.

Add a GitHub Actions workflow covering both SBOM paths:
- autotools: build wolfSSL + wolfSSH, run make sbom, assert SPDX
  validity, CycloneDX identity, reproducibility, and the wolfssl
  dependency edge.
- embedded: run gen-sbom against user_settings.h (via pcpp) + the
  wolfSSH source set, asserting the same identity/validity/reproducibility
  plus that user_settings.h options and the wolfssl dependency are
  captured.
Both gracefully skip on a gen-sbom that predates the wolfSSL SBOM
change.

Signed-off-by: Sameeh Jubran <sameeh@wolfssl.com>

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

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Comment thread scripts/sbom.am Outdated
Comment thread README.md Outdated
@aidangarske aidangarske assigned MarkAtwood and unassigned wolfSSL-Bot Jul 9, 2026
Guard the SBOM_GEN wolfSSL fallback with $(if $(WOLFSSL_DIR),...) so an
unset WOLFSSL_DIR leaves it empty (yielding the intended "set WOLFSSL_DIR"
error) instead of resolving to an absolute /scripts/gen-sbom that could run
an unrelated host script. Reword the fragment header to describe the actual
gen-sbom resolution (vendored copy if present, else WOLFSSL_DIR) rather than
claiming offline tarball builds that aren't wired up yet. Condition the
README's wolfSSL-dependency statement on a sufficiently new gen-sbom to
match the existing degradation note.

Signed-off-by: Sameeh Jubran <sameeh@wolfssl.com>

@aidangarske aidangarske 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.

🐺 Skoll Code Review

Overall recommendation: REQUEST_CHANGES
Findings: 2 total — 2 posted, 0 skipped

Posted findings

  • [High] Default SBOM license contradicts source license headersMakefile.am:98
  • [Medium] SBOM workflow can pass without exercising the new target.github/workflows/sbom.yml:64-79

Review generated by Skoll.

Comment thread Makefile.am Outdated
Signed-off-by: Sameeh Jubran <sameeh@wolfssl.com>

@jackctj117 jackctj117 left a comment

Copy link
Copy Markdown

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: reviewOverall recommendation: COMMENT
Findings: 5 total — 5 posted, 0 skipped
4 finding(s) posted as inline comments (see file-level comments below)
1 finding(s) not tied to a diff line (full detail below)

One or more scans did not complete — the results below are partial.

Posted findings

  • [Medium] [review] CI workflow pins wolfSSL to an unmerged PR ref for all events, including every pull_request.github/workflows/sbom.yml:3-13, 41-46
  • [Low] [review] wolfssl version.h parse uses [ \t] which does not match a literal tabscripts/sbom.am:144-145
  • [Low] [review] sbomdir hardcodes $(datadir)/doc/$(PACKAGE) instead of using $(docdir)scripts/sbom.am:55
  • [Low] [review] uninstall-hook removes SBOM files that plain make install never installsscripts/sbom.am:185-196

Findings not tied to a diff line

SBOM targets silently require GNU make; ?= and GNU-only make functions are undocumented

File: scripts/sbom.am:46-63, Makefile.am:98
Function: sbom
Severity: Medium
Category: question

The fragment relies on GNU make extensions: conditional assignment ?= (SBOM_ARTIFACT ?= lib, SBOM_LICENSE_OVERRIDE ?= GPL-3.0-or-later) and the GNU-only functions $(wildcard ...), $(if ...), $(firstword ...), and $(addprefix ...). ?= is not one of Automake's documented assignment operators (=, +=, :=); confirm autoreconf accepts it without error. Note CI only runs autoreconf/configure on the wolfSSH tree when gen-sbom is detected (the steps.gate.outputs.have == 'yes' gate), so a hard Automake failure would only surface when CI checks out a wolfssl ref carrying the script. Under a non-GNU make (e.g. BSD make), SBOM_GEN/SBOM_LIB_GLOBS evaluate to empty and make sbom fails with a misleading 'gen-sbom not found' / 'no artifact found' error rather than a clear 'GNU make required' message.

Recommendation: Confirm autoreconf accepts ?= on the toolchain versions you support, and document in the README/comment that the SBOM targets require GNU make. Optionally guard with a clearer error when GNU-make features are unavailable.

Referenced code: scripts/sbom.am:46-63, Makefile.am:98-100 (3 lines)


Review generated by Skoll

Comment thread .github/workflows/sbom.yml
Comment thread scripts/sbom.am Outdated
Comment thread scripts/sbom.am Outdated
Comment thread scripts/sbom.am
sameehj added 2 commits July 13, 2026 18:17
Sync the vendored fragment with the canonical wolfSSL copy: add
$(AM_CFLAGS) $(CFLAGS) to the -dM -E build-options capture and make the
config header path overridable via SBOM_CONFIG_H (default config.h) so
the SBOM records the configured feature macros.

Signed-off-by: Sameeh Jubran <sameeh@wolfssl.com>
Re-sync scripts/sbom.am with the canonical wolfSSL copy ($(docdir)
sbomdir, [[:space:]] version parse, GNU-make and uninstall-sbom docs)
and widen the SBOM workflow pull_request filter to '**'.

Signed-off-by: Sameeh Jubran <sameeh@wolfssl.com>
@sameehj
sameehj requested a review from aidangarske July 15, 2026 12:45

@ejohnstown ejohnstown 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.

I guess my only issue is this PR depends on wolfSSL's PR 10343 to be merged first. The GitHub Action works without the PR merged, so that's OK. But someone may see the note about the make sbom and it not work if wolfSSH is released with this without the wolfSSL merge.

@aidangarske
aidangarske removed their request for review July 15, 2026 21:13
@ejohnstown
ejohnstown self-requested a review July 15, 2026 21:53
Sync scripts/sbom.am with the unified canonical copy in wolfSSL
(scripts/sbom.am), which adds the SBOM_OPTIONS_H override so one
fragment supports both options.h- and config.h/AM_CFLAGS-based macro
capture. wolfssh uses the default config.h path and does not set
SBOM_OPTIONS_H, so behavior is unchanged. Keeps the vendored fragment
byte-identical to the canonical.

Signed-off-by: Sameeh Jubran <sameeh@wolfssl.com>
@MarkAtwood

Copy link
Copy Markdown
Contributor Author

Note on the review threads above: the recipe moved out of Makefile.am into scripts/sbom.am, so the line numbers in the older comments now point at the wrong file. Makefile.am keeps five declaration variables and an include scripts/sbom.am.

wolfSSL/wolfssl#10343 merged on 2026-07-27, but the SBOM workflow still
checked out wolfssl at refs/pull/10343/head. GitHub keeps PR refs alive
after merge, so CI stayed green while validating a pre-merge snapshot
rather than master. Point wolfssl_ref and its fallback at master.

Distinguish the two gen-sbom lookup failures. A set-but-wrong
WOLFSSL_DIR previously printed the "re-run with WOLFSSL_DIR=..." advice
to someone who had already set it; it now names the path that was
probed and states that WOLFSSL_DIR is a source tree, not an install
prefix.

Refresh the README and in-recipe notes that still told users to track
the #10343 branch until it merged.
The gate skipped SBOM generation and exited 0 whenever wolfssl's
scripts/gen-sbom was absent, so the job could report success having
never exercised `make sbom`. That was deliberate while the script was
unmerged, but it landed on master in wolfSSL/wolfssl#10343.

On master, a missing gen-sbom or one too old for --dep-wolfssl is a
regression, so fail instead of skipping: a green run now always means
the target was exercised and the wolfssl-dependency assertions ran.
Only a deliberately pinned older wolfssl_ref still degrades to a skip.

The ref is passed through env rather than interpolated into the shell
script, to keep the workflow expression out of the command line.
@MarkAtwood
MarkAtwood dismissed aidangarske’s stale review August 26, 2026 01:37

Both Skoll findings are addressed. [High] default SBOM license: fixed in ccbaecc, Makefile.am:98 is now SBOM_LICENSE_OVERRIDE ?= GPL-3.0-or-later. [Medium] workflow can pass without exercising the new target: fixed in feaf692, the gate now fails on master when gen-sbom is missing or lacks --dep-wolfssl, so a green run means make sbom actually ran. Dismissing this automated review; human review still pending.

@ejohnstown ejohnstown 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.

Re-reviewed at 7e22d45a (+1 commit since my last pass: "fix: unpin SBOM CI from merged wolfssl PR ref").

Built and ran everything on macOS/arm64 (gcc-15, GNU make 3.81): --enable-all and plain ./configure against a wolfSSL 5.9.2 install, WOLFSSL_DIR pointing at a wolfssl checkout at origin/master (b83f5d60f), --prefix=/opt/wolfssh-sbom. The prefix is root-owned here, so the install was exercised through DESTDIR staging of the identical recipe.

Fixed: CI/docs unpinning

Verified master everywhere -- workflow input default, checkout ref, and both skip-notice strings -- plus the README and the sbom.am NOTE. wolfSSL master's gen-sbom is what I tested against throughout, exit 0.

The new "not an install prefix" error is a real improvement and fires correctly:

$ make sbom WOLFSSL_DIR=/opt/wolfssl
ERROR: no gen-sbom at /opt/wolfssl/scripts/gen-sbom
       WOLFSSL_DIR must name a wolfSSL *source tree*, not an
       install prefix such as /usr/local or /opt/wolfssl.
make: *** [sbom] Error 1

New: the vendored scripts/sbom.am has diverged from the canonical copy

That fix was made by editing scripts/sbom.am in this repo only. The file's own header says:

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.

sha256 of scripts/sbom.am:

wolfssl/scripts/sbom.am   282fd7ca...
wolfssh, previous head    282fd7ca...   (identical)
wolfssh, this head        5118dc2e...   (diverged)

Two hunks are wolfSSH-only now: the WOLFSSL_DIR-aware error block and the reworded --dep-wolfssl NOTE. A wolfssl checkout at origin/master does not carry them, and I found no open wolfSSL PR syncing them. Both changes are product-agnostic and belong upstream: land them in wolfSSL first, re-vendor here, and the file goes back to byte-identical. A CI check that diffs the vendored copy against the canonical one would stop this drifting again.

Still open

1. The recorded wolfSSL dependency version does not track the linked wolfSSL

This is the one I would hold on. SBOM_WOLFSSL_VERSION falls back to $(WOLFSSL_DIR)/wolfssl/version.h, but WOLFSSL_DIR is only there to locate gen-sbom and is a different input from --with-wolfssl. Built and linked against wolfSSL 5.9.2, with WOLFSSL_DIR pointing at a tree whose version.h says 9.9.9:

component: wolfssl 9.9.9 pkg:github/wolfSSL/wolfssl@v9.9.9

Worse, with gen-sbom present but no wolfssl/version.h under WOLFSSL_DIR, make sbom exits 0 and emits:

component: wolfssl None None

No version, no purl, no warning -- silently unscannable, which defeats the README's stated reason for the entry ("so vulnerability scanners can associate wolfSSL advisories with a wolfSSH deployment"). gen-sbom's pkg-config fallback does not rescue it, because a non-default prefix is not on the default PKG_CONFIG_PATH and the recipe does not set one.

The new error text sharpens the case: it tells users WOLFSSL_DIR is a source tree and explicitly not an install prefix -- i.e. not the thing you linked. Those are two independent inputs and only the wrong one feeds the dependency version.

Fixable entirely on the wolfSSH side through the documented SBOM_WOLFSSL_VERSION hook, so the canonical sbom.am does not need to change. I prototyped and verified this:

# configure.ac, after AC_CHECK_LIB([wolfssl],[wolfCrypt_Init],...)
# Record the version of the wolfSSL actually being linked, for the SBOM's
# dependency entry.  WOLFSSL_DIR (used by `make sbom` only to locate gen-sbom)
# may be an unrelated source tree, so it must not be the version source.
AC_MSG_CHECKING([wolfSSL version for SBOM])
WOLFSSL_VERSION=
if test -f "${wcpath}/include/wolfssl/version.h"; then
  WOLFSSL_VERSION=`sed -n ['s/.*LIBWOLFSSL_VERSION_STRING[ 	]*"\([^"]*\)".*/\1/p'] "${wcpath}/include/wolfssl/version.h"`
fi
AS_IF([test -n "$WOLFSSL_VERSION"],
      [AC_MSG_RESULT([$WOLFSSL_VERSION])],
      [AC_MSG_RESULT([unknown])])
AC_SUBST([WOLFSSL_VERSION])
# Makefile.am, after SBOM_DEP_WOLFSSL = yes
# Version of the linked wolfSSL, detected by configure from --with-wolfssl.
SBOM_WOLFSSL_VERSION = @WOLFSSL_VERSION@

With that applied it records 5.9.2 even when WOLFSSL_DIR claims 9.9.9, and an explicit make sbom SBOM_WOLFSSL_VERSION=... still overrides.

2. The CI regression guard does not guard

assert any(n.startswith('wolfssl:build:') for n in props), \
    'no wolfssl:build:* properties - options snapshot is empty'

The comment says this catches the options snapshot regressing to empty -- the bug fixed by "scripts/sbom.am: capture AM_CFLAGS/CFLAGS and configurable config.h". It does not. Reconfiguring plainly (exactly as CI does) and reintroducing that regression, i.e. dropping AM_CPPFLAGS from the -dM -E capture:

BASELINE  wolfssl:build:* count = 50   WOLFSSH_TERM present = True
REGRESSED wolfssl:build:* count = 48   WOLFSSH_TERM present = False
CI assertion any(startswith 'wolfssl:build:') -> True

config.h alone always supplies ~50 properties, so the assertion passes while the compiler-side capture is silently broken. Since CI configures plainly, WOLFSSH_TERM is the only marker that comes via AM_CPPFLAGS; asserting on wolfssl:build:WOLFSSH_TERM would make the guard real. (The embedded job's WOLFSSH_SFTP assertion is the strong one.)

3. Scope: the installed apps are not covered

An --enable-all install stages bin/wolfssh and bin/wolfsshd, but SBOM_ARTIFACT = lib means the only components are wolfssh (hash-source: lib) and wolfssl. sbom.am is lib-or-bin, so covering both needs a canonical change. Fine to defer, but given the CRA framing it is worth stating in the README rather than leaving implied.

4. Minor (both gen-sbom side, not this PR)

The main component is GPL-3.0-or-later while the wolfssl dependency component is hardcoded GPL-3.0-only, even though both projects' source headers carry the same "or (at your option) any later version" text. Also, properties on a wolfSSH SBOM are namespaced wolfssl:build:*.

Everything else verified green

  • Reproducible across two runs (byte-identical); CDX timestamp matches git log -1 --format=%ct
  • --enable-all captures all 12 WOLFSSH_* feature macros (63 properties vs 50 plain), and the library hash changes with configuration
  • CPPFLAGS=-DDEFAULT_WINDOW_SZ=16384 at configure time lands in the SBOM
  • VPATH build, and --disable-shared --enable-static correctly hashes libwolfssh.a
  • install-sbom installs only under share/doc/wolfssh; uninstall-hook chain removes them from a plain make uninstall (0 files left)
  • make clean removes the outputs (CLEANFILES correct)
  • autogen.sh with WARNINGS=all,error exits 0
  • make dist ships scripts/sbom.am; full tarball unpack -> configure -> make -> make sbom exits 0
  • pyspdxtools validates the SPDX JSON; tag-value output carries DESCRIBES and DEPENDS_ON
  • The GPL-3.0-or-later override is correct: src/ssh.c says "version 3 ... or (at your option) any later version", while LICENSING says only "GPLv3", which detect_license maps to GPL-3.0-only

Net: the mechanics hold up well outside the CI happy path. Findings 1 and 2 above, plus re-syncing scripts/sbom.am upstream, are what I would want before merge.

The wolfSSL dependency version came solely from
WOLFSSL_DIR/wolfssl/version.h. That header is generated by configure as
well as tracked, so `make distclean` in the wolfSSL tree removes it. With
it gone the --dep-version override was never passed and gen-sbom fell
back to `pkg-config --modversion wolfssl`, which reports the *installed*
wolfSSL. On a host whose installed build differs from WOLFSSL_DIR the
SBOM recorded that unrelated version (9.9.9 in the report) and still
exited 0, attesting a component wolfSSH was not built against.

Fall back to AC_INIT in WOLFSSL_DIR/configure.ac, which survives
distclean, and fail when neither source is readable rather than letting
an installed copy answer for the tree. SBOM_WOLFSSL_VERSION still
overrides both.
The dependency assertion checked only that wolfssl was present with a
DEPENDS_ON edge, never that the recorded version matched WOLFSSL_DIR, so
the SBOM could attest a version wolfSSH was not built against and still
pass. Compare versionInfo against the tree.

That assertion alone would not have caught it: the runner installs
wolfssl from the same tree it passes as WOLFSSL_DIR, so pkg-config and
wolfssl/version.h always agree and a wrong source is indistinguishable
from a right one. Add a step that forces them apart -- a stub pkg-config
reports a sentinel version, and a distclean'd copy of the tree has no
generated version.h, so the configure.ac fallback is the only route to a
correct answer. Reverting the recipe fix makes this step fail with the
sentinel, and the fallback path now has CI coverage it lacked.
The 2026-08-26 GitHub Actions major outage left this branch's checks in a
mixed state: several workflows recorded startup_failure with no jobs
created, and wolfSSHd Test could not be re-run because there was nothing
to retry. No code change; this empty commit exists only to trigger a
clean run of every workflow.
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