mod sbom¶
- module sbom¶
Planned CycloneDX 1.5 SBOM from a stack lock (pre-install inventory).
Built with the official
cyclonedx_bomcrate (same models ascargo-cyclonedx/ CycloneDX Rust Cargo). Documents are serialized as JSON 1.5 with serial numbers, tool metadata, lifecycle phase, and declared dependency edges — not a post-build compliance scan.Functions
- fn artifact_facts_for_lock(lock: &StackLock) -> HashMap<String, ArtifactFacts>¶
Read the easyconfigs a lock names, for the facts only they carry.
This is the one place the module touches the filesystem, and it earns it: a lock records which easyconfig was selected, and the checksums and source URLs that make the document verifiable live in that file rather than in the lock. A path that cannot be read or parsed contributes nothing rather than failing the document, since an SBOM missing one component’s hashes is worth more than no SBOM, and the count of what was read is reported to the caller.
- fn build_dep_map_from_universe(lock: &StackLock, universe: &Universe) -> HashMap<String, Vec<String>>¶
Build dep map name -> build-time dependency names (
builddependencies) from universe candidates matching the lock.
- fn dep_map_from_universe(lock: &StackLock, universe: &Universe) -> HashMap<String, Vec<String>>¶
Build dep map name -> runtime dependency names from universe candidates matching the lock. Build-time deps are intentionally omitted here so SBOM
dependsOnedges stay role-specific; usebuild_dep_map_from_universefor the build-time list (same shape, separate map).
- fn lock_to_bom(lock: &StackLock, runtime_dep_map: Option<&HashMap<String, Vec<String>>>, build_dep_map: Option<&HashMap<String, Vec<String>>>) -> Bom¶
Typed CycloneDX BOM (1.5 models) — preferred when callers want validation.
- fn lock_to_bom_with_facts(lock: &StackLock, facts: SbomFacts<'_>) -> Bom¶
Build the BOM from a lock plus whatever else the caller knows.
Everything beyond the lock is optional, and what is absent is left absent rather than guessed: a component with no stated checksum carries no
hashes, and a plan with nothing unresolved is the only one that claimscomplete.
- fn lock_to_cyclonedx(lock: &StackLock) -> Value¶
Build a CycloneDX JSON document from a lock only (no dependency map).
Without declared edges each component gets an empty
dependsOnlist — never all-to-all co-stack edges (those create invalid cyclic BOMs).
- fn lock_to_cyclonedx_with_deps(lock: &StackLock, selected_dep_map: Option<&HashMap<String, Vec<String>>>) -> Value¶
Preferred: when the selected candidates (or full universe selection map) are known, emit dependsOn from each package’s declared EasyBuild-style dependency list intersected with co-selected lock members. When
selected_dep_mapisNone, each package’sdependsOnis empty (unknown), not all-to-all.
- fn lock_to_cyclonedx_with_facts(lock: &StackLock, facts: SbomFacts<'_>) -> Value¶
JSON document from a lock plus caller-supplied facts.
- fn lock_to_cyclonedx_with_runtime_and_build(lock: &StackLock, runtime_dep_map: Option<&HashMap<String, Vec<String>>>, build_dep_map: Option<&HashMap<String, Vec<String>>>) -> Value¶
Like
lock_to_cyclonedx_with_deps, also records build-time edges as a component property (eb_stack:buildDependsOn) while runtime edges fill the CycloneDXdependenciesgraph.
Structs and Unions
- struct ArtifactFacts¶
What an easyconfig states about the artifact one component builds from.
A lock records a selection, not an artifact. These are the fields that make the difference between an inventory and a document someone can verify: which bytes were expected, and where they were fetched from.
- checksums: Vec<String>¶
Checksums the easyconfig states, in the order it states them. Only 64-character hex values are emitted as SHA-256; anything else is carried as a property rather than asserted as a hash of the wrong kind.
- source_urls: Vec<String>¶
Source URLs the easyconfig downloads from.
- patches: Vec<String>¶
Patch filenames applied on top of the source.
- struct SbomFacts<'a>¶
Everything a caller can tell the SBOM builder beyond the lock itself.
Grouped into one struct so the builder keeps a single entry point as more of the spec is filled in, rather than growing another positional argument per field.
- runtime_dep_map: Option<&'a HashMap<String, Vec<String>>>¶
Runtime edges, name to names, which become the
dependenciesgraph.
- build_dep_map: Option<&'a HashMap<String, Vec<String>>>¶
Build edges, kept as a property because CycloneDX
dependenciesdoes not distinguish build from runtime in 1.5.
- artifacts: Option<&'a HashMap<String, ArtifactFacts>>¶
Per-package artifact facts, keyed by package name.
- unresolved: Option<&'a [String]>¶
Requirements the plan could not resolve. Their presence is what makes the document’s
compositionssayincompleterather thancomplete.
- input_hashes: Option<&'a HashMap<String, String>>¶
Input hash per package name, from
crate::input_hash. Used as theuidof the task that builds it, which is what CycloneDX means by a unique identifier for a resource instance: two plans producing the same hash describe the same build.
- build_environment: Option<&'a BTreeMap<String, String>>¶
The build environment the plan targets: optimisation flags, compute capabilities, the EasyBuild version. A build is reproducible only against a stated environment, since the same recipe compiled with different flags is a different binary, and an SBOM that omits it describes something nobody can reproduce (doi:10.1109/ms.2021.3073045).