//! The external-evidence provider port. //! //! A sibling product (tramiton, for firmware) may already hold authoritative //! analysis for an artifact. An [`EvidenceProvider`] lets compliance-scanner //! *reconcile* that evidence — a build plan, an SBOM, a VEX document, a //! reproducible-build lock, an attestation — instead of recomputing it. The key //! used to match is the artifact content digest (a firmware sha256, which equals //! [`crate::models::Artifact::content_hash`]). //! //! Concrete providers live in the agent (a tramiton CLI shell-out today, a cloud //! client later) plus a deterministic mock for tests, so nothing here depends on //! an external binary. use std::path::Path; use crate::error::CoreError; use crate::models::{Artifact, ExternalSystem}; /// A single reconcilable evidence document fetched from a sibling product. #[derive(Debug, Clone)] pub struct EvidenceDocument { /// What the document is: `build_plan` | `sbom` | `vex` | `lock` | `attestation`. pub kind: String, /// The document's format (e.g. `cyclonedx-1.5`, `openvex-0.2.0`, `toml`, `json`). pub format: String, /// The raw document payload. pub content: String, } /// The evidence a provider could return for a target's artifact. #[derive(Debug, Clone, Default)] pub struct ReconciledEvidence { /// The sibling's project identifier, if resolved. pub project_id: Option, /// The subject content digest the evidence pertains to. pub subject_sha256: Option, /// The documents fetched (any of build plan / SBOM / VEX / lock / attestation). pub documents: Vec, } /// A source of externally-held, reconcilable evidence for an artifact. #[allow(async_fn_in_trait)] pub trait EvidenceProvider: Send + Sync { /// Which sibling product this provider integrates. fn system(&self) -> ExternalSystem; /// Whether this provider can handle the given artifact + working path /// (e.g. tramiton handles firmware images / embedded source trees). fn handles(&self, artifact: &Artifact, working_path: Option<&Path>) -> bool; /// Reconcile existing evidence for the artifact, keyed by its content digest. /// Returns `Ok(None)` when the provider has nothing for this artifact. async fn reconcile( &self, artifact: &Artifact, working_path: Option<&Path>, ) -> Result, CoreError>; }