mirror of https://github.com/wolfSSL/wolfBoot.git
sbom: re-vendor wolfGlass at b44ae8d
Picks up per-configuration serialNumber and documentNamespace, so two configurations of one wolfBoot version no longer share an identifier inside a scanner. Also brings CPE/PURL build-metadata stripping, --dep-* value validation, wolfcrypt version inheritance, and the sbom.am host-cc macro capture. The Makefile comment that recorded this as an open wolfGlass gap is no longer true, so it goes. Signed-off-by: Sameeh Jubran <sameeh@wolfssl.com>pull/824/head
parent
da1d2cfc27
commit
0b6bcd1221
8
Makefile
8
Makefile
|
|
@ -949,10 +949,10 @@ SBOM_COMPONENT_TYPE?=firmware
|
|||
SBOM_LICENSE_OVERRIDE?=GPL-3.0-or-later
|
||||
# One version of wolfBoot has about 100 configurations, and each one is a
|
||||
# different image with a different source set. Name the document after the
|
||||
# configuration so a second target does not overwrite the first. gen-sbom still
|
||||
# derives serialNumber and the SPDX documentNamespace from name and version
|
||||
# alone, which collides inside a scanner as well; that part is wolfGlass's to
|
||||
# fix, and this does not paper over it.
|
||||
# configuration so a second target does not overwrite the first. gen-sbom
|
||||
# folds the build configuration into the serialNumber and the SPDX
|
||||
# documentNamespace too, so the documents stay distinct inside a scanner and
|
||||
# not only on disk.
|
||||
SBOM_CONFIG_TAG:=$(TARGET)$(if $(SIGN),-$(SIGN))$(if $(HASH),-$(HASH))
|
||||
SBOM_CDX_OUT:=wolfboot-$(SBOM_CONFIG_TAG)-$(WOLFBOOT_VERSION).cdx.json
|
||||
SBOM_SPDX_OUT:=wolfboot-$(SBOM_CONFIG_TAG)-$(WOLFBOOT_VERSION).spdx.json
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
d34a906638444b6990218a49927bcebafc5a539b
|
||||
b44ae8d1d578ccf0b4269ffb395157cfb3fab0dc
|
||||
|
|
|
|||
|
|
@ -25,6 +25,12 @@ from datetime import datetime, timezone
|
|||
# shapes because dropping --dep-liboqs did not bump it, leaving vendored
|
||||
# copies indistinguishable by the only identifier the SBOM records.
|
||||
#
|
||||
# 1.8 CPE and PURL identifiers drop `+` build metadata (OpenSSL
|
||||
# BUILD_METADATA, PEP 440 local versions). A raw `+` is not legal
|
||||
# in CPE 2.3 and does not match an upstream git tag. version /
|
||||
# versionInfo still records the full string. Constructors (cpe23_uri,
|
||||
# wolfssl_project_purl, DEP_META lambdas) strip; this is not a
|
||||
# full CPE sanitizer.
|
||||
# 1.7 wolfBoot CPE is registered in the NVD Official CPE Dictionary
|
||||
# (published 2026-08-10). PRODUCT_CPE status flips to registered so
|
||||
# the main package emits `cpe` and the pending properties stop.
|
||||
|
|
@ -50,7 +56,7 @@ from datetime import datetime, timezone
|
|||
# -only/-or-later distinction cannot be inferred.
|
||||
# 1.2 Dropped --dep-liboqs (unversioned; see above).
|
||||
GEN_SBOM_TOOL_NAME = 'wolfssl-sbom-gen'
|
||||
GEN_SBOM_VERSION = '1.7'
|
||||
GEN_SBOM_VERSION = '1.8'
|
||||
|
||||
# Placeholder recorded in the component checksum fields when the operator
|
||||
# passes --no-artifact-hash: a build (ROM image, HSM firmware, binary-only
|
||||
|
|
@ -129,9 +135,32 @@ def github_purl(namespace, repo, tag):
|
|||
return f'pkg:github/{namespace.lower()}/{repo.lower()}@{tag}'
|
||||
|
||||
|
||||
def identifier_version(version):
|
||||
"""Return the version used in CPE 2.3 and PURL identifiers.
|
||||
|
||||
OpenSSL BUILD_METADATA and PEP 440 local versions append `+...`.
|
||||
A raw `+` is not a legal CPE 2.3 version character, and the suffix
|
||||
is not an upstream git tag, so NVD / OSV cannot match it. Keep the
|
||||
full string in version / versionInfo; use this for identifiers.
|
||||
|
||||
This is not a full CPE 2.3 sanitizer: other illegal characters
|
||||
(`:`, `*`, whitespace) are not rewritten.
|
||||
"""
|
||||
if not version:
|
||||
return version
|
||||
return version.split('+', 1)[0]
|
||||
|
||||
|
||||
def cpe23_uri(vendor, product, version):
|
||||
"""CPE 2.3 application URI. Version uses identifier_version()."""
|
||||
ident = identifier_version(version)
|
||||
return f'cpe:2.3:a:{vendor}:{product}:{ident}:*:*:*:*:*:*:*'
|
||||
|
||||
|
||||
def wolfssl_project_purl(name, version):
|
||||
"""Canonical pkg:github PURL for a wolfSSL-stack project."""
|
||||
return github_purl('wolfSSL', name, github_release_tag(name, version))
|
||||
return github_purl(
|
||||
'wolfSSL', name, github_release_tag(name, identifier_version(version)))
|
||||
|
||||
|
||||
# Official CPE 2.3 vendor:product pairs. Main-package CPEs are emitted only
|
||||
|
|
@ -165,9 +194,7 @@ PRODUCT_CPE = {
|
|||
|
||||
|
||||
def _product_cpe_string(meta, version):
|
||||
return (
|
||||
f"cpe:2.3:a:{meta['vendor']}:{meta['product']}:{version}:*:*:*:*:*:*:*"
|
||||
)
|
||||
return cpe23_uri(meta['vendor'], meta['product'], version)
|
||||
|
||||
|
||||
def product_cpe(name, version):
|
||||
|
|
@ -243,6 +270,59 @@ def derived_uuid(*parts):
|
|||
return str(uuid.uuid5(SBOM_UUID_NAMESPACE, '\x00'.join(parts)))
|
||||
|
||||
|
||||
def config_identity(lib_hash, license_id, build_props, enabled_deps,
|
||||
dep_versions, *, supplier=None, component_type=None,
|
||||
license_text=None, hash_kind=None, hash_source=None,
|
||||
file_names=None, wolfssl_subset=None, subset_basis=None):
|
||||
"""Stable digest of the build configuration behind one name+version.
|
||||
|
||||
Folded into the SBOM serialNumber / SPDX documentNamespace so two builds
|
||||
of one release with different configuration (FIPS vs non-FIPS, a different
|
||||
--srcs set, a different feature-flag set) get distinct identifiers, while
|
||||
an identical configuration still reproduces byte-for-byte. Fields are
|
||||
tagged and NUL-delimited so no two configurations serialize alike.
|
||||
|
||||
Invariant: every CLI input that can change the document body reaches this
|
||||
digest. One that does not is a collision -- two documents with differing
|
||||
content under one serialNumber. tests/test_sbom_identity.py classifies
|
||||
the whole option set and fails on an unclassified new option.
|
||||
|
||||
lib_hash is folded in deliberately, so on the --lib path the identity
|
||||
follows the built binary: the same sources under a different toolchain get
|
||||
a different serialNumber. An SBOM from --lib describes one artefact. The
|
||||
--srcs and --no-artifact-hash paths do not have this property."""
|
||||
h = hashlib.sha256()
|
||||
|
||||
def _field(tag, value):
|
||||
h.update(tag.encode())
|
||||
h.update(b'\0')
|
||||
h.update((value or '').encode())
|
||||
h.update(b'\0')
|
||||
|
||||
_field('lib_hash', lib_hash)
|
||||
_field('license', license_id)
|
||||
for k, v in sorted(build_props):
|
||||
_field('prop', f'{k}={v}')
|
||||
for key in sorted(enabled_deps):
|
||||
_field('dep', f'{key}={dep_versions.get(key) or ""}')
|
||||
_field('supplier', supplier)
|
||||
_field('component_type', component_type)
|
||||
# Digested, not embedded: the text is a document field of its own and can
|
||||
# differ while the SPDX identifier is unchanged.
|
||||
_field('license_text',
|
||||
hashlib.sha256(license_text.encode()).hexdigest()
|
||||
if license_text else '')
|
||||
_field('hash_kind', hash_kind)
|
||||
_field('hash_source', hash_source)
|
||||
# Filenames reach the document body without passing through lib_hash,
|
||||
# which covers bytes only: a rename changes the body but not the hash.
|
||||
for fname in sorted(file_names or []):
|
||||
_field('file', fname)
|
||||
_field('wolfssl_subset', wolfssl_subset)
|
||||
_field('subset_basis', subset_basis)
|
||||
return h.hexdigest()
|
||||
|
||||
|
||||
def build_timestamp():
|
||||
"""Return (datetime, ISO-8601-Z string) honoring SOURCE_DATE_EPOCH.
|
||||
Reproducible Builds convention: if the env var is set to a valid
|
||||
|
|
@ -294,7 +374,7 @@ DEP_META = {
|
|||
'pkgconfig': 'wolfssl',
|
||||
'purl': lambda v: wolfssl_project_purl('wolfssl', v),
|
||||
# The CPE NVD registers for the wolfSSL library.
|
||||
'cpe': lambda v: f'cpe:2.3:a:wolfssl:wolfssl:{v}:*:*:*:*:*:*:*',
|
||||
'cpe': lambda v: cpe23_uri('wolfssl', 'wolfssl', v),
|
||||
},
|
||||
# wolfCrypt is a separate NVD product (cpe:2.3:a:wolfssl:wolfcrypt).
|
||||
# Embedders such as wolfBoot compile wolfcrypt sources into the image;
|
||||
|
|
@ -315,7 +395,7 @@ DEP_META = {
|
|||
# does not collide with the wolfssl component's own PURL. NVD
|
||||
# matching keys on the wolfcrypt CPE below.
|
||||
'purl': lambda v: wolfssl_project_purl('wolfssl', v) + '#wolfcrypt',
|
||||
'cpe': lambda v: f'cpe:2.3:a:wolfssl:wolfcrypt:{v}:*:*:*:*:*:*:*',
|
||||
'cpe': lambda v: cpe23_uri('wolfssl', 'wolfcrypt', v),
|
||||
},
|
||||
'libz': {
|
||||
'name': 'zlib',
|
||||
|
|
@ -326,8 +406,9 @@ DEP_META = {
|
|||
# pkg:github resolves in OSV / GHSA / Snyk / Trivy without the
|
||||
# vendor:product mapping a pkg:generic PURL would force. zlib tags
|
||||
# its releases `vX.Y.Z`, so the bare pkg-config version needs the `v`.
|
||||
'purl': lambda v: github_purl('madler', 'zlib', f'v{v}'),
|
||||
'cpe': lambda v: f'cpe:2.3:a:zlib:zlib:{v}:*:*:*:*:*:*:*',
|
||||
'purl': lambda v: github_purl(
|
||||
'madler', 'zlib', f'v{identifier_version(v)}'),
|
||||
'cpe': lambda v: cpe23_uri('zlib', 'zlib', v),
|
||||
},
|
||||
# openssl, declared as a dependency by the OpenSSL-compat products
|
||||
# (wolfProvider, wolfEngine) that link libcrypto/libssl alongside wolfSSL.
|
||||
|
|
@ -342,8 +423,9 @@ DEP_META = {
|
|||
'license': 'Apache-2.0',
|
||||
'download': 'https://github.com/openssl/openssl',
|
||||
'pkgconfig': 'openssl',
|
||||
'purl': lambda v: github_purl('openssl', 'openssl', f'openssl-{v}'),
|
||||
'cpe': lambda v: f'cpe:2.3:a:openssl:openssl:{v}:*:*:*:*:*:*:*',
|
||||
'purl': lambda v: github_purl(
|
||||
'openssl', 'openssl', f'openssl-{identifier_version(v)}'),
|
||||
'cpe': lambda v: cpe23_uri('openssl', 'openssl', v),
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -988,6 +1070,8 @@ def cdx_dep_component(name, pkg_version, key, dep_version_overrides=None):
|
|||
'externalReferences': [{'type': 'vcs', 'url': meta['download']}],
|
||||
}
|
||||
if version:
|
||||
# Pass the full version: constructors apply identifier_version().
|
||||
# version / versionInfo keep the local string (BUILD_METADATA).
|
||||
comp['version'] = version
|
||||
comp['purl'] = meta['purl'](version)
|
||||
# Both identifiers are version-bearing, so neither can be emitted
|
||||
|
|
@ -1435,6 +1519,53 @@ def _resolve_dep_versions(enabled_deps, overrides):
|
|||
return overrides
|
||||
|
||||
|
||||
def _inherit_wolfcrypt_version(enabled_deps, overrides, name, version):
|
||||
"""Give wolfcrypt a version so its CPE and PURL are not dropped.
|
||||
|
||||
wolfcrypt has no pkg-config file of its own, so `dep_version` can never
|
||||
resolve it. Prefer an explicit `--dep-version wolfcrypt=`, else the
|
||||
wolfssl dependency's version, else (for wolfSSL's own SBOM) the package
|
||||
version.
|
||||
|
||||
MUST be called AFTER `_resolve_dep_versions`. The wolfssl version
|
||||
normally arrives from pkg-config rather than from `--dep-version`, so
|
||||
inheriting before the resolve step only ever saw an explicit override: a
|
||||
downstream embedder such as wolfBoot that did not pass
|
||||
`--dep-version wolfssl=` emitted a wolfcrypt component with no version,
|
||||
no purl and no cpe, and still exited 0.
|
||||
|
||||
Tests the value rather than the key, because `_resolve_dep_versions`
|
||||
caches a None for every dep pkg-config cannot resolve, so by this point
|
||||
'wolfcrypt' is always present in `overrides`.
|
||||
"""
|
||||
if 'wolfcrypt' not in enabled_deps or overrides.get('wolfcrypt'):
|
||||
return overrides
|
||||
if overrides.get('wolfssl'):
|
||||
overrides['wolfcrypt'] = overrides['wolfssl']
|
||||
elif name.lower() == 'wolfssl':
|
||||
overrides['wolfcrypt'] = version
|
||||
return overrides
|
||||
def _enabled_deps(dep_flags):
|
||||
"""Return the enabled dependency keys, rejecting values outside {yes, no}.
|
||||
|
||||
Treating anything that is not 'yes' as 'no' loses a dependency silently.
|
||||
A build fragment wiring `--dep-wolfssl=$(HAVE_WOLFSSL)` where the variable
|
||||
expands to 1, true, on or Y would drop a CVE-bearing component from the
|
||||
SBOM with no diagnostic, which is the one failure mode a CRA tool must
|
||||
never have. Fail loudly instead.
|
||||
"""
|
||||
enabled = []
|
||||
for key, flag_name, value in dep_flags:
|
||||
normalized = (value or '').strip().lower()
|
||||
if normalized not in ('yes', 'no'):
|
||||
sys.exit(f"ERROR: {flag_name} expects 'yes' or 'no', got {value!r}. "
|
||||
f"A value outside that set would silently drop {key} from "
|
||||
f"the SBOM.")
|
||||
if normalized == 'yes':
|
||||
enabled.append(key)
|
||||
return enabled
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Generate CycloneDX and SPDX SBOMs for wolfssl. '
|
||||
|
|
@ -1618,28 +1749,19 @@ def main():
|
|||
"https://example.com/sbom/wolfssl-X.Y.Z.spdx.json or "
|
||||
"urn:uuid:00000000-0000-0000-0000-000000000000.")
|
||||
|
||||
enabled_deps = [
|
||||
key for key, flag in [
|
||||
('wolfssl', args.dep_wolfssl),
|
||||
('wolfcrypt', args.dep_wolfcrypt),
|
||||
('openssl', args.dep_openssl),
|
||||
('libz', args.dep_libz),
|
||||
]
|
||||
if flag.lower() == 'yes'
|
||||
]
|
||||
enabled_deps = _enabled_deps([
|
||||
('wolfssl', '--dep-wolfssl', args.dep_wolfssl),
|
||||
('wolfcrypt', '--dep-wolfcrypt', args.dep_wolfcrypt),
|
||||
('openssl', '--dep-openssl', args.dep_openssl),
|
||||
('libz', '--dep-libz', args.dep_libz),
|
||||
])
|
||||
dep_version_overrides = _parse_dep_version_overrides(args.dep_version)
|
||||
# wolfcrypt has no pkg-config. Inherit a version so the CPE/PURL are
|
||||
# not dropped: prefer an explicit --dep-version wolfcrypt=, else the
|
||||
# wolfssl dep version, else (for wolfSSL's own SBOM) the package version.
|
||||
if 'wolfcrypt' in enabled_deps and 'wolfcrypt' not in dep_version_overrides:
|
||||
if dep_version_overrides.get('wolfssl'):
|
||||
dep_version_overrides['wolfcrypt'] = dep_version_overrides['wolfssl']
|
||||
elif args.name.lower() == 'wolfssl':
|
||||
dep_version_overrides['wolfcrypt'] = args.version
|
||||
# Resolve each enabled dependency's version once, here, and feed the
|
||||
# result to both the CDX and SPDX emitters via the overrides map (see
|
||||
# _resolve_dep_versions for the once-per-dep pkg-config rationale).
|
||||
_resolve_dep_versions(enabled_deps, dep_version_overrides)
|
||||
_inherit_wolfcrypt_version(enabled_deps, dep_version_overrides,
|
||||
args.name, args.version)
|
||||
|
||||
if args.license_override:
|
||||
license_id = args.license_override
|
||||
|
|
@ -1756,8 +1878,28 @@ def main():
|
|||
|
||||
dt, timestamp = build_timestamp()
|
||||
year = dt.year
|
||||
serial = derived_uuid(args.name, args.version, 'serial')
|
||||
doc_ns_uuid = derived_uuid(args.name, args.version, 'document')
|
||||
# SPDX 2.3 6.5 requires documentNamespace to be unique, and a CycloneDX
|
||||
# version:1 sharing a serialNumber over differing content is
|
||||
# self-contradictory. Every body-affecting input goes into the digest;
|
||||
# see config_identity for the invariant.
|
||||
identity_file_names = (
|
||||
[fe['name'] for fe in file_entries] if file_entries
|
||||
else (srcs_basenames or [])
|
||||
)
|
||||
config_id = config_identity(
|
||||
lib_hash, license_id, build_props, enabled_deps,
|
||||
dep_version_overrides,
|
||||
supplier=args.supplier,
|
||||
component_type=args.component_type,
|
||||
license_text=license_text,
|
||||
hash_kind=hash_kind,
|
||||
hash_source=hash_source,
|
||||
file_names=identity_file_names,
|
||||
wolfssl_subset=wolfssl_subset,
|
||||
subset_basis=subset_basis,
|
||||
)
|
||||
serial = derived_uuid(args.name, args.version, 'serial', config_id)
|
||||
doc_ns_uuid = derived_uuid(args.name, args.version, 'document', config_id)
|
||||
|
||||
cdx = generate_cdx(
|
||||
args.name, args.version, args.supplier,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
# build-time dependency on a separate wolfSSL checkout.
|
||||
#
|
||||
# ---------------------------------------------------------------------------
|
||||
# The including Makefile.am MUST set, before `include scripts/sbom.am`:
|
||||
# The including Makefile.am MUST set, before `include tools/sbom/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
|
||||
|
|
@ -44,15 +44,23 @@
|
|||
# captured (e.g. wolfEngine: $(abs_builddir)/include/config.h;
|
||||
# wolfCLU: $(abs_builddir)/src/config.h).
|
||||
# Default: $(abs_builddir)/config.h.
|
||||
# SBOM_HOSTCC Host C compiler used to expand the build macros (the
|
||||
# -D/-U/-I tokens only, never the configured cross $(CC)),
|
||||
# so the SBOM is byte-reproducible across toolchains.
|
||||
# Default: cc.
|
||||
#
|
||||
# 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 next to this fragment in the vendored `tools/sbom/`
|
||||
# directory unless SBOM_GEN is overridden. GEN_SBOM is accepted as a legacy
|
||||
# alias. python3, pyspdxtools and git come from configure (AC_PATH_PROG); git
|
||||
# is used only to derive SOURCE_DATE_EPOCH.
|
||||
# gen-sbom is located in the vendored directory (SBOM_VENDOR_DIR, default
|
||||
# $(srcdir)/tools/sbom), or, if not vendored, in a wolfSSL source tree via
|
||||
# WOLFSSL_DIR (the wolfSSH-style route); SBOM_GEN overrides both. NOTE: this
|
||||
# fragment cannot locate itself at make time -- Automake's `include` is textual,
|
||||
# so $(MAKEFILE_LIST) resolves to the top Makefile, not this file -- which is why
|
||||
# the vendored directory is named explicitly rather than derived from the
|
||||
# fragment's own path. 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
|
||||
|
|
@ -65,7 +73,16 @@ SBOM_BIN_NAME ?= $(SBOM_PKGNAME)
|
|||
SBOM_DEP_WOLFSSL ?= no
|
||||
SBOM_DEP_OPENSSL ?= no
|
||||
SBOM_CONFIG_H ?= $(abs_builddir)/config.h
|
||||
SBOM_AM_DIR ?= $(dir $(lastword $(MAKEFILE_LIST)))
|
||||
# Host C compiler used to capture the build macros (matching the Make/CMake
|
||||
# paths' HOSTCC), NOT the configured $(CC): on a cross build $(CC) would bake
|
||||
# the cross compiler's target-specific predefined macros into the SBOM, so the
|
||||
# same product built through a different toolchain would produce a different
|
||||
# document. Only the -D/-U/-I tokens are fed to it (see the capture below), so
|
||||
# it never sees target arch flags it cannot parse.
|
||||
SBOM_HOSTCC ?= cc
|
||||
# Directory the wolfGlass tooling (gen-sbom) is vendored into. Products that
|
||||
# vendor elsewhere override this (or SBOM_GEN directly).
|
||||
SBOM_VENDOR_DIR ?= $(srcdir)/tools/sbom
|
||||
|
||||
SBOM_CDX = $(SBOM_PKGNAME)-$(PACKAGE_VERSION).cdx.json
|
||||
SBOM_SPDX = $(SBOM_PKGNAME)-$(PACKAGE_VERSION).spdx.json
|
||||
|
|
@ -74,8 +91,12 @@ SBOM_SPDX_TV = $(SBOM_PKGNAME)-$(PACKAGE_VERSION).spdx
|
|||
# equals $(datadir)/doc/$(PACKAGE) by default).
|
||||
sbomdir = $(docdir)
|
||||
|
||||
# Prefer the vendored sibling copy. Callers may override SBOM_GEN explicitly.
|
||||
SBOM_GEN := $(or $(SBOM_GEN),$(GEN_SBOM),$(abspath $(SBOM_AM_DIR)/gen-sbom))
|
||||
# Prefer the vendored copy; else fall back to a wolfSSL source tree via
|
||||
# WOLFSSL_DIR. Empty if neither exists -- the `test -f "$(SBOM_GEN)"` check in
|
||||
# the recipe then fails with a clear error. Callers may override SBOM_GEN.
|
||||
SBOM_GEN ?= $(abspath $(firstword \
|
||||
$(wildcard $(SBOM_VENDOR_DIR)/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.
|
||||
|
|
@ -92,16 +113,18 @@ SBOM_LIB_GLOBS = \
|
|||
|
||||
# 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`.
|
||||
# init block) before `include tools/sbom/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 (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),
|
||||
# configured build macros (from SBOM_OPTIONS_H if set, else the -D/-U/-I tokens
|
||||
# of AM_CPPFLAGS/AM_CFLAGS/CFLAGS + config.h, expanded through the HOST compiler
|
||||
# SBOM_HOSTCC so the SBOM is reproducible across cross toolchains; 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
|
||||
|
|
@ -147,8 +170,11 @@ sbom:
|
|||
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) \
|
||||
sbom_cpp=""; \
|
||||
for f in $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS); do \
|
||||
case "$$f" in -D*|-U*|-I*) sbom_cpp="$$sbom_cpp $$f";; esac; \
|
||||
done; \
|
||||
$(SBOM_HOSTCC) -dM -E $(DEFAULT_INCLUDES) $$sbom_cpp \
|
||||
$(if $(wildcard $(SBOM_CONFIG_H)),-include $(SBOM_CONFIG_H)) \
|
||||
-x c /dev/null > "$$_defines"; \
|
||||
fi; \
|
||||
|
|
|
|||
Loading…
Reference in New Issue