Files
git.stella-ops.org/docs/api/evidence-api-reference.md
2025-12-24 21:45:46 +02:00

10 KiB

Evidence API Reference

This document provides the complete API reference for the StellaOps unified evidence model.

Interfaces

IEvidence

Base interface for all evidence records.

namespace StellaOps.Evidence.Core;

public interface IEvidence
{
    /// <summary>
    /// Content-addressed evidence identifier (e.g., "sha256:abc123...").
    /// </summary>
    string EvidenceId { get; }

    /// <summary>
    /// The type of evidence this record represents.
    /// </summary>
    EvidenceType Type { get; }

    /// <summary>
    /// The subject (node ID) this evidence is about.
    /// </summary>
    string SubjectNodeId { get; }

    /// <summary>
    /// When the evidence was created (UTC).
    /// </summary>
    DateTimeOffset CreatedAt { get; }

    /// <summary>
    /// Cryptographic signatures attesting to this evidence.
    /// </summary>
    IReadOnlyList<EvidenceSignature> Signatures { get; }

    /// <summary>
    /// Origin and provenance information.
    /// </summary>
    EvidenceProvenance? Provenance { get; }

    /// <summary>
    /// Type-specific properties as key-value pairs.
    /// </summary>
    IReadOnlyDictionary<string, string> Properties { get; }
}

IEvidenceStore

Storage interface for evidence persistence.

namespace StellaOps.Evidence.Core;

public interface IEvidenceStore
{
    /// <summary>
    /// Retrieves evidence by its content-addressed ID.
    /// </summary>
    Task<IEvidence?> GetAsync(string evidenceId, CancellationToken ct = default);

    /// <summary>
    /// Retrieves all evidence records for a given subject.
    /// </summary>
    Task<IReadOnlyList<IEvidence>> GetBySubjectAsync(
        string subjectNodeId, 
        CancellationToken ct = default);

    /// <summary>
    /// Retrieves all evidence records of a specific type.
    /// </summary>
    Task<IReadOnlyList<IEvidence>> GetByTypeAsync(
        EvidenceType type, 
        CancellationToken ct = default);

    /// <summary>
    /// Stores an evidence record.
    /// </summary>
    Task StoreAsync(IEvidence evidence, CancellationToken ct = default);

    /// <summary>
    /// Checks if evidence with the given ID exists.
    /// </summary>
    Task<bool> ExistsAsync(string evidenceId, CancellationToken ct = default);
}

IEvidenceAdapter

Adapter interface for converting module-specific types to evidence.

namespace StellaOps.Evidence.Core.Adapters;

public interface IEvidenceAdapter<TInput>
{
    /// <summary>
    /// Converts a module-specific input to one or more evidence records.
    /// </summary>
    IReadOnlyList<IEvidence> ToEvidence(TInput input);
}

Records

EvidenceRecord

Standard implementation of IEvidence.

namespace StellaOps.Evidence.Core;

public sealed record EvidenceRecord : IEvidence
{
    public required string EvidenceId { get; init; }
    public required EvidenceType Type { get; init; }
    public required string SubjectNodeId { get; init; }
    public required DateTimeOffset CreatedAt { get; init; }
    public IReadOnlyList<EvidenceSignature> Signatures { get; init; } = [];
    public EvidenceProvenance? Provenance { get; init; }
    public IReadOnlyDictionary<string, string> Properties { get; init; } = 
        new Dictionary<string, string>();
}

EvidenceSignature

Cryptographic signature attached to evidence.

namespace StellaOps.Evidence.Core;

public sealed record EvidenceSignature
{
    /// <summary>
    /// Identifier of the signer (key ID, tool name, etc.).
    /// </summary>
    public required string SignerId { get; init; }

    /// <summary>
    /// Signing algorithm (e.g., "Ed25519", "ES256").
    /// </summary>
    public required string Algorithm { get; init; }

    /// <summary>
    /// Base64-encoded signature bytes.
    /// </summary>
    public required string SignatureBase64 { get; init; }

    /// <summary>
    /// When the signature was created (UTC).
    /// </summary>
    public required DateTimeOffset SignedAt { get; init; }

    /// <summary>
    /// Type of entity that produced the signature.
    /// </summary>
    public required SignerType SignerType { get; init; }
}

EvidenceProvenance

Origin and provenance information.

namespace StellaOps.Evidence.Core;

public sealed record EvidenceProvenance
{
    /// <summary>
    /// Source that produced this evidence (e.g., "grype", "trivy").
    /// </summary>
    public required string Source { get; init; }

    /// <summary>
    /// Version of the source tool.
    /// </summary>
    public string? SourceVersion { get; init; }

    /// <summary>
    /// URI where original data was obtained.
    /// </summary>
    public string? SourceUri { get; init; }

    /// <summary>
    /// Digest of the original content.
    /// </summary>
    public string? ContentDigest { get; init; }
}

Enumerations

EvidenceType

namespace StellaOps.Evidence.Core;

public enum EvidenceType
{
    Unknown = 0,
    Sbom = 1,
    Vulnerability = 2,
    Vex = 3,
    Attestation = 4,
    PolicyDecision = 5,
    ScanResult = 6,
    Provenance = 7,
    Signature = 8,
    ProofSegment = 9,
    Exception = 10,
    Advisory = 11,
    CveMatch = 12,
    ReachabilityResult = 13
}

SignerType

namespace StellaOps.Evidence.Core;

public enum SignerType
{
    Unknown = 0,
    Tool = 1,
    Human = 2,
    Authority = 3,
    Vendor = 4,
    Service = 5
}

Adapters

EvidenceStatementAdapter

Converts EvidenceStatement from Attestor module.

Input: EvidenceStatementInput

public sealed record EvidenceStatementInput
{
    public required string StatementId { get; init; }
    public required string SubjectDigest { get; init; }
    public required string StatementType { get; init; }
    public required string PredicateType { get; init; }
    public required DateTimeOffset IssuedAt { get; init; }
    public IReadOnlyList<EvidenceSignatureInput>? Signatures { get; init; }
    public IReadOnlyDictionary<string, string>? Metadata { get; init; }
}

Output: Single IEvidence record with Type = Attestation.


ProofSegmentAdapter

Converts ProofSegment from Scanner module.

Input: ProofSegmentInput

public sealed record ProofSegmentInput
{
    public required string SegmentId { get; init; }
    public required string SubjectNodeId { get; init; }
    public required string SegmentType { get; init; }
    public required string Status { get; init; }
    public required DateTimeOffset CreatedAt { get; init; }
    public string? PreviousSegmentId { get; init; }
    public string? PayloadDigest { get; init; }
    public IReadOnlyList<EvidenceSignatureInput>? Signatures { get; init; }
    public IReadOnlyDictionary<string, string>? Properties { get; init; }
}

Output: Single IEvidence record with Type = ProofSegment.


VexObservationAdapter

Converts VexObservation from Excititor module.

Input: VexObservationInput

public sealed record VexObservationInput
{
    public required string SubjectDigest { get; init; }
    public VexObservationUpstreamInput? Upstream { get; init; }
    public IReadOnlyList<VexObservationStatementInput>? Statements { get; init; }
    public IReadOnlyDictionary<string, string>? Properties { get; init; }
}

public sealed record VexObservationUpstreamInput
{
    public required string VexDocumentId { get; init; }
    public required string VendorName { get; init; }
    public required DateTimeOffset PublishedAt { get; init; }
    public string? DocumentDigest { get; init; }
}

public sealed record VexObservationStatementInput
{
    public required string VulnerabilityId { get; init; }
    public required string ProductId { get; init; }
    public required string Status { get; init; }
    public required DateTimeOffset Timestamp { get; init; }
    public string? Justification { get; init; }
}

Output: Multiple IEvidence records:

  • 1 record with Type = Provenance (from upstream)
  • N records with Type = Vex (one per statement)

ExceptionApplicationAdapter

Converts ExceptionApplication from Policy module.

Input: ExceptionApplicationInput

public sealed record ExceptionApplicationInput
{
    public required string ApplicationId { get; init; }
    public required string TenantId { get; init; }
    public required string ExceptionId { get; init; }
    public required string FindingId { get; init; }
    public required DateTimeOffset AppliedAt { get; init; }
    public DateTimeOffset? ExpiresAt { get; init; }
    public string? Reason { get; init; }
    public string? AppliedBy { get; init; }
    public IReadOnlyDictionary<string, string>? Properties { get; init; }
}

Output: Single IEvidence record with Type = Exception.


Implementations

InMemoryEvidenceStore

Thread-safe in-memory evidence store for testing and caching.

var store = new InMemoryEvidenceStore();

// Store evidence
await store.StoreAsync(record);

// Retrieve by ID
var evidence = await store.GetAsync("sha256:abc123...");

// Query by subject
var subjectEvidence = await store.GetBySubjectAsync("pkg:npm/lodash@4.17.21");

// Query by type
var vexRecords = await store.GetByTypeAsync(EvidenceType.Vex);

// Check existence
var exists = await store.ExistsAsync("sha256:abc123...");

Thread Safety: Uses ConcurrentDictionary for all operations.


Common Property Keys

Standard property keys used across evidence types:

Key Used By Description
cve Vulnerability, CveMatch CVE identifier
severity Vulnerability Severity level (CRITICAL, HIGH, etc.)
cvss Vulnerability CVSS score
status Vex, ProofSegment Current status
justification Vex, Exception Reason for status
productId Vex Affected product identifier
exceptionId Exception Parent exception ID
findingId Exception Finding being excepted
tenantId Exception Tenant context
segmentType ProofSegment Type of proof segment
previousSegmentId ProofSegment Chain link to previous segment
payloadDigest ProofSegment Content digest
predicateType Attestation In-toto predicate type URI
statementType Attestation Statement type identifier