mirror of https://github.com/wolfSSL/wolfTPM.git
176 lines
7.2 KiB
YAML
176 lines
7.2 KiB
YAML
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: wolfTPM SBOM generation (linux)
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 20
|
|
steps:
|
|
- name: Checkout wolftpm
|
|
uses: actions/checkout@v4
|
|
with:
|
|
path: wolftpm
|
|
|
|
# wolfTPM links wolfSSL/wolfCrypt (RNG, parameter encryption, PK
|
|
# callbacks), so its SBOM records wolfSSL as a dependency. wolfSSL is
|
|
# built + installed here so wolfTPM has a library to link, and the same
|
|
# source tree (scripts/gen-sbom + wolfssl/version.h) is passed to
|
|
# `make sbom` via WOLFSSL_DIR -- so the recorded wolfSSL dependency
|
|
# version matches the linked one. 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 build tooling and SBOM validator (pyspdxtools)
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y build-essential autoconf automake libtool \
|
|
pkg-config
|
|
python3 -m pip install --user 'spdx-tools==0.8.*'
|
|
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
|
|
|
|
- name: Build and install wolfssl
|
|
working-directory: wolfssl
|
|
run: |
|
|
autoreconf -ivf
|
|
./configure --enable-wolftpm --enable-pkcallbacks \
|
|
--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 and on
|
|
# --dep-wolfssl so this workflow is safe against a gen-sbom that predates
|
|
# 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 wolftpm
|
|
if: steps.gate.outputs.have == 'yes'
|
|
working-directory: wolftpm
|
|
run: |
|
|
autoreconf -ivf
|
|
./configure --enable-swtpm --disable-fwtpm --disable-examples \
|
|
--with-wolfcrypt="$GITHUB_WORKSPACE/wolfssl-install"
|
|
make -j"$(nproc)"
|
|
|
|
- name: Generate SBOM
|
|
if: steps.gate.outputs.have == 'yes'
|
|
working-directory: wolftpm
|
|
run: make sbom WOLFSSL_DIR="$GITHUB_WORKSPACE/wolfssl"
|
|
|
|
- name: Outputs exist and SPDX validates
|
|
if: steps.gate.outputs.have == 'yes'
|
|
working-directory: wolftpm
|
|
run: |
|
|
ls wolftpm-*.cdx.json wolftpm-*.spdx.json wolftpm-*.spdx
|
|
pyspdxtools --infile wolftpm-*.spdx.json
|
|
|
|
- name: CycloneDX identity, licence, and captured options
|
|
if: steps.gate.outputs.have == 'yes'
|
|
working-directory: wolftpm
|
|
run: |
|
|
python3 - <<'PY'
|
|
import glob, json
|
|
cdx = json.load(open(glob.glob('wolftpm-*.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'] == 'wolftpm', m['name']
|
|
assert m['purl'].startswith('pkg:github/wolfSSL/wolftpm@'), 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
|
|
# Identity is the hashed library artifact.
|
|
assert {h['alg'] for h in m.get('hashes', [])}, 'no component hash'
|
|
# SBOM_OPTIONS_H must have been parsed: wolfTPM records its feature
|
|
# macros in wolftpm/options.h, so the SBOM's build properties must be
|
|
# populated.
|
|
props = [p for p in m.get('properties', [])
|
|
if p.get('name', '').startswith('wolfssl:build:')]
|
|
assert props, 'no wolfssl:build:* properties captured from options.h'
|
|
print('CDX ok:', m['name'], m['purl'], ids, f'{len(props)} build props')
|
|
PY
|
|
|
|
- name: Reproducible across two runs (SOURCE_DATE_EPOCH)
|
|
if: steps.gate.outputs.have == 'yes'
|
|
working-directory: wolftpm
|
|
run: |
|
|
rm -f wolftpm-*.cdx.json wolftpm-*.spdx.json wolftpm-*.spdx
|
|
SOURCE_DATE_EPOCH=1700000000 make sbom \
|
|
WOLFSSL_DIR="$GITHUB_WORKSPACE/wolfssl"
|
|
sha256sum wolftpm-*.cdx.json wolftpm-*.spdx.json > /tmp/a.sums
|
|
rm -f wolftpm-*.cdx.json wolftpm-*.spdx.json wolftpm-*.spdx
|
|
SOURCE_DATE_EPOCH=1700000000 make sbom \
|
|
WOLFSSL_DIR="$GITHUB_WORKSPACE/wolfssl"
|
|
sha256sum wolftpm-*.cdx.json wolftpm-*.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: wolftpm
|
|
run: |
|
|
python3 - <<'PY'
|
|
import glob, json
|
|
d = json.load(open(glob.glob('wolftpm-*.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-wolftpm', '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: wolftpm-sbom-${{ github.sha }}
|
|
path: |
|
|
wolftpm/wolftpm-*.cdx.json
|
|
wolftpm/wolftpm-*.spdx.json
|
|
wolftpm/wolftpm-*.spdx
|
|
if-no-files-found: warn
|
|
retention-days: 90
|