Architecture

Vexcalibur separates package inventory parsing, vulnerability source access, provider-neutral finding data, and VEX rendering.

Current Flow

The generation path separates untrusted SBOM parsing, vulnerability source access, provider-neutral findings, and CycloneDX rendering:

CycloneDX JSON/XML file       GitHub Dependency Graph SBOM
        |                                 |
        v                                 v
vexcalibur.sbom              vexcalibur.github_sbom
        |                                 |
        +----------------+----------------+
                         |
                         v
ComponentIdentity values with package URLs
        |
        v
Selected VulnerabilitySource
        |                         |
        | public opt-in or        | no network
        | private mirror          |
        v                         v
OSV-compatible API          Local findings JSON
        |                         |
        +------------+------------+
                     |
                     v
          VulnerabilityFinding values
                     |
                     v
              vexcalibur.vex
                     |
                     v
          CycloneDX 1.6 VEX JSON

In text form: Vexcalibur parses a local CycloneDX SBOM or fetches a GitHub Dependency Graph SBOM, converts that package inventory into component identities, collects provider-neutral findings from one selected source, then renders a CycloneDX 1.6 VEX JSON document.

  1. vexcalibur generate accepts either a CycloneDX JSON/XML SBOM path or --github-repo OWNER/REPO.

  2. vexcalibur.sbom validates the raw document shape, parses CycloneDX JSON with cyclonedx-python-lib, parses CycloneDX XML with defusedxml, and extracts component identities with package URLs.

  3. vexcalibur.github_sbom requests, polls, and downloads a GitHub Dependency Graph SPDX JSON report when --github-repo is selected, then extracts package URL references from that response into the same component identity shape.

  4. The selected VulnerabilitySource produces provider-neutral VulnerabilityFinding objects:

    • vexcalibur.sources.osv.OsvSource converts versioned component identities into OSV package queries, then maps OSV responses into findings.

    • vexcalibur.sources.local.LocalFindingsSource reads local findings JSON and maps each finding to a normalized component reference or unique package URL.

  5. vexcalibur.vex renders deterministic CycloneDX 1.6 VEX JSON.

Trust Boundary

SBOMs can disclose internal package names, versions, ecosystem choices, and dependency graph details. Vexcalibur therefore treats public vulnerability services as an explicit trust boundary.

Commands that would send package URLs or SBOM-derived inventories to https://api.osv.dev fail unless the caller passes --allow-public-osv. Private mirrors use --osv-url. Offline workflows use --findings-file and do not construct an OSV client.

Fetching an SBOM from GitHub is also a network operation, but it is an input source decision rather than permission to send the resulting package inventory to a public vulnerability service. --github-repo cannot be combined with --offline, and public OSV still requires --allow-public-osv after a GitHub SBOM has been fetched.

The same policy applies to library callers that use OsvSource or inject an OsvClient: Vexcalibur checks the client’s effective base URL when it is knowable and rejects public OSV unless the caller opted in.

Source Providers

Provider-specific code belongs under vexcalibur.sources. Provider clients should handle source-specific request formats, response validation, pagination, and policy checks. Source adapters implement the provider-neutral VulnerabilitySource protocol from vexcalibur.domain and return shared VulnerabilityFinding objects. Workflow modules should orchestrate providers through that protocol rather than duplicating provider parsing or source policy.

Sources use VulnerabilitySourceInputError for component-shape problems that prevent a query from being built; generation surfaces those as SBOM input errors. Provider-specific configuration, network, parsing, and local-file failures should subclass VulnerabilitySourceError while keeping their more specific exception types so CLI and action callers can report accurate categories.

OSV is the first network provider because it has a maintained public API and can also be mirrored internally. Local findings are the first offline provider. The architecture should leave room for additional sources without making Vexcalibur Python-specific or OSV-specific.

VEX Rendering

The current renderer emits CycloneDX 1.6 JSON. It groups findings by vulnerability ID, source, analysis state, and analysis detail, then references affected components by their normalized component refs.

Findings are marked in_triage by default. That default means “detected by a source and awaiting manual exploitability analysis”; it does not claim that a component is exploitable.

See CycloneDX VEX output for the output contract.

Compatibility

The package installs a vexy executable for selected legacy workflow compatibility. That compatibility layer maps supported legacy-style input and output flags onto the same generation workflow described above, so source-mode validation and the public OSV opt-in boundary stay shared with the primary vexcalibur generate command.

The supported compatibility subset emits CycloneDX 1.6 JSON VEX. It accepts the legacy -c/--config flag for command-line compatibility, but it does not parse legacy data-source credentials or re-enable OSS Index. Legacy CycloneDX XML VEX output and CycloneDX 1.4 VEX output are intentionally outside the current compatibility contract.