modelCard, with every claim
backed by evidence.occurrences[] and evidence.identity[].
Emit a CycloneDX ML-BOM
Spec version
Target CycloneDX 1.7
The
modelCard shape is identical in both versions, so only the declared
specVersion and $schema differ. The 1.6→1.7 delta is four
externalReference types AIROM never emits. Pick 1.6 unless a consumer
demands otherwise.Document shape
bom.json (abridged)
metadata.component, and never duplicated in
components[]. Scan provenance travels in metadata.properties under the
airom: prefix (airom:source.type, airom:source.target,
airom:source.digest, airom:source.git.remote, airom:source.git.commit,
airom:source.git.dirty, airom:source.k8s.context, airom:tool.commit).
bom-ref identity
Every component’sbom-ref is its AIROM component ID (airom: followed by a
stable hex digest). It is the anchor for every intra-document reference:
dependencies[].ref, modelCard.modelParameters.datasets[].ref, and the
airom:rel.* property values all point at a bom-ref.
Kind → component type
The 13 AIROM component kinds map onto CycloneDX’s coarsercomponent.type. The
exact kind always survives in the airom:kind property, so nothing is lost.
Components typed
data also carry a data[] facet: dataset → data[].type: "dataset", ai-config → "configuration", prompt → "other".
modelCard
AmodelCard is emitted for machine-learning-model components — but only when
there is something to put in it. If AIROM knows no task, no architecture, no
model card, and no trained-on edge, the key is absent rather than empty.
A hosted model with a modelCard
trained-on edges become modelCard.modelParameters.datasets[].ref, pointing at
the dataset component’s bom-ref.
airom:param.temperature = "0.2 @ src/rag.py:42". A bound generation
parameter carries its call site inline, so the provenance of a decoding setting
survives into the BOM. An unbound param carries just its value.airom:model.paramCount, airom:model.quantization,
airom:model.contextLength, airom:model.format, airom:model.baseModel, and
airom:pickle.risk / airom:pickle.imports for static pickle-walk findings.
vulnerabilities[] — the risk overlay
Artifact risks — a poisoned pickle, a Keras Lambda layer, an
unsafe-load call site — project into the top-level vulnerabilities[] array. Each is a
non-CVE id (e.g. AIROM-RISK-PICKLE-IMPORT) with source.name: airom,
ratings[].method: other (AIROM claims no CVSS), and affects[].ref pointing at the
affected component’s bom-ref.
a risk in vulnerabilities[]
definitions / declarations — the compliance overlay
When you pass --compliance, the framework mapping projects into
CycloneDX’s native attestation model: definitions.standards[] declares the framework and
its requirements[] (controls), and declarations records AIROM as a first-party
assessor with one claim + attestation.map[] entry per control.
a control's verdict in declarations
claim and attestation.map entry with no
conformance.score — AIROM never asserts a figure it cannot back with evidence.
Evidence
Every component that has evidence carries it natively.evidence on a component
occurrences[] is every sighting: location (required), plus line, symbol
and additionalContext (the snippet) when known. A whole-file sighting omits
line entirely rather than claiming line 0.
identity[] is what AIROM concluded and why. Note that identity entries can
disagree — a low-confidence competing concludedValue sits alongside the
winning one rather than being silently discarded.
AIROM’s 8 detection methods map to CycloneDX evidence techniques by identical
string, with one exception: config-analysis has no enum value, so it degrades
to technique: "other" with value: "config-analysis" as a recovery marker.
dependencies[] — what AIROM will and won’t claim
The writer maps depends-on edges to dependencies[] {ref, dependsOn}, and
root edges naturally reference the metadata.component bom-ref.
If a custom pack supplied via --rules declares depends-on edges, the writer
emits them. Every other relationship type is lossy in CycloneDX and travels as a
property on the edge’s source component:
The property value is
"<to-bom-ref>@<confidence>", e.g. airom:rel.uses =
"airom:1a2b3c4d5e6f7788@0.9". Type, endpoints and confidence round-trip; the
edge’s own evidence occurrences do not survive. Use json or yaml if you need
them.
purl and hashes
Local weight files get real purls and SHA-256 hashes — their identity is their bytes:Determinism
Components arrive sorted by ID and stay that way. Properties are sorted by name then value.dependencies[] refs and their dependsOn lists are sorted. Two
scans of an unchanged tree produce byte-identical BOMs, so a checked-in
bom.json diffs cleanly.
Reference
Full field mapping
Every internal field and its route into each format is specified in
docs/mapping.md in the repository.SARIF output
For code-scanning UIs rather than BOM exchange.