Skip to content

SBOM & VEX export

An SBOM lists what is installed. A VEX document states, per vulnerability, whether you are actually affected. monsys generates both per host, from data the agent already reports — no extra scanner, no separate tool.

This is the pairing the EU Cyber Resilience Act (CRA Annex I §5) and NIS2 auditors ask for: the component inventory and the exploitability signal that turns a raw CVE list into an actionable one.

Earlier the SBOM only covered application dependencies. It now has three layers, so it represents the whole host:

Layer Source purl example
Application dependencies package.json, requirements.txt, composer.lock, go.sum, … pkg:npm/lodash@4.17.21
OS packages latest inventory snapshot (apt/dpkg/rpm/dnf/apk/zypper/pacman) pkg:deb/ubuntu/openssl@3.0.13?distro=ubuntu
Running kernel kernels_running pkg:deb/ubuntu/linux-kernel@6.8.0-100-generic?arch=x86_64&distro=ubuntu

Component types are set correctly in both formats: the kernel is OPERATING-SYSTEM (SPDX primaryPackagePurpose) / operating-system (CycloneDX type), OS packages are applications/libraries, app deps are libraries.

GET /api/v1/agents/{id}/sbom?format=spdx-json # SPDX 2.3 (default)
GET /api/v1/agents/{id}/sbom?format=cyclonedx-json # CycloneDX 1.5

Both return a single JSON document with a Content-Disposition attachment name like monsys-sbom-<host>-<date>.spdx.json.

The SBOM conforms to CISA’s 2026 Minimum Elements for a Software Bill of Materials. Every required element is emitted:

  • Author, tool name and version. SPDX creationInfo.creators carries Tool: monsys.ai-<version> and Organization: GoTrust BV. CycloneDX sets metadata.tools (with the running hub version) and metadata.authors.
  • Data format and version. SPDX 2.3 and CycloneDX 1.5, stated in the document header of each format.
  • Timestamp and SBOM version. RFC 3339 timestamp; SBOM version 1.
  • Generation context. Declared as after-build (runtime host inventory): CycloneDX metadata.lifecycles phase operations, mirrored in the SPDX creation comment.
  • Component producer, name, version, identifiers, license, dependency relationship. OS and kernel components carry the distributor as producer (SPDX supplier, CycloneDX publisher); every component has a purl identifier; licenses use SPDX IDs where known. CycloneDX ships an explicit dependencies graph rooted at the host.
  • Explicitly identifying unknown information. Component file hashes are not collected (monsys does metadata-only host inventory with no artifact access), so Component Hash is recorded as unknown rather than fabricated. Producer is NOASSERTION where the publisher is genuinely unknown.

Each SBOM is signed inline with the hub’s Ed25519 key, satisfying the CISA “SBOM Author Signature” element. CycloneDX uses its native JSF signature block; SPDX carries the signature in a document-level annotation. The signature is computed over the RFC 8785 (JCS) canonical form of the document with the signature holder removed, so anyone can verify it offline against the public key published at /api/v1/keys (the same key that signs the Audit Pack).

The VEX document is derived from the CVE findings the dashboard already shows — application (inventory_dependency_cves), OS packages (os_package_cves) and the backport-aware kernel pipeline (kernel_cve_findings) — and grounded on operator decisions (cve_suppressions) and the kernel’s distro/mitigation state.

monsys is honest by default (the human-in-the-loop principle): a CVE that matched an installed version is reported affected unless there is positive evidence otherwise. We never silently downgrade to not_affected.

Evidence OpenVEX status CycloneDX-VEX state
Operator suppressed the CVE (risk accepted / false positive) not_affected (with your reason as impact_statement) not_affected
Kernel fix backported / finding resolved fixed resolved
Kernel mitigation active (e.g. Spectre/Meltdown control) not_affected (inline_mitigations_already_exist) not_affected (protected_at_runtime)
Installed at a vulnerable version, no fix/mitigation affected (with an action_statement) exploitable
Kernel state unknown under_investigation in_triage

Every affected statement carries a concrete action_statement (e.g. “Upgrade openssl to 3.0.14 or later.”); every not_affected carries a justification or impact statement, as the specs require.

GET /api/v1/agents/{id}/vex?format=openvex # OpenVEX 0.2.0 (default)
GET /api/v1/agents/{id}/vex?format=cyclonedx-vex # CycloneDX-VEX 1.5

Open a server → CVEs tab → the Audit export bar. Four one-click downloads: SBOM (SPDX / CycloneDX) and VEX (OpenVEX / CycloneDX-VEX).

The monthly Audit Pack bundles a vex_statement line per host per vulnerability, using the exact same derivation as the download above — so the signed evidence matches what your operators can pull on demand. Like the rest of the pack it is Ed25519-signed and offline-verifiable.

Everything is generated inside the hub from data the agent already reports. Building the SBOM/VEX sends nothing to third parties. (The underlying CVE matching uses OSV.dev with only (ecosystem, name, version) triples — see OS-package CVE matching.)