// =============================================================================
// IAttestationParser.cs
// Attestation parsing abstraction for DSSE/in-toto attestations
// Part of Step 2: Evidence Collection (Task T6)
// =============================================================================
namespace StellaOps.AirGap.Importer.Reconciliation.Parsers;
///
/// Interface for parsing DSSE-wrapped in-toto attestations.
///
public interface IAttestationParser
{
///
/// Parses a DSSE envelope from the given file path.
///
/// Path to the attestation file.
/// Cancellation token.
/// Parsed attestation result.
Task ParseAsync(string filePath, CancellationToken cancellationToken = default);
///
/// Parses a DSSE envelope from a stream.
///
/// Stream containing the attestation content.
/// Cancellation token.
/// Parsed attestation result.
Task ParseAsync(Stream stream, CancellationToken cancellationToken = default);
///
/// Detects if a file is a DSSE attestation.
///
/// Path to the file.
/// True if the file appears to be a DSSE attestation.
bool IsAttestation(string filePath);
}
///
/// Result of parsing an attestation document.
///
public sealed record AttestationParseResult
{
///
/// Whether parsing was successful.
///
public bool IsSuccess { get; init; }
///
/// Error message if parsing failed.
///
public string? ErrorMessage { get; init; }
///
/// The parsed DSSE envelope.
///
public DsseEnvelope? Envelope { get; init; }
///
/// The parsed in-toto statement (payload).
///
public InTotoStatement? Statement { get; init; }
///
/// Creates a successful parse result.
///
public static AttestationParseResult Success(DsseEnvelope envelope, InTotoStatement statement)
{
return new AttestationParseResult
{
IsSuccess = true,
Envelope = envelope,
Statement = statement
};
}
///
/// Creates a failed parse result.
///
public static AttestationParseResult Failure(string errorMessage)
{
return new AttestationParseResult
{
IsSuccess = false,
ErrorMessage = errorMessage
};
}
}
///
/// Represents a DSSE (Dead Simple Signing Envelope).
///
public sealed record DsseEnvelope
{
///
/// Payload type (typically "application/vnd.in-toto+json").
///
public required string PayloadType { get; init; }
///
/// Base64-encoded payload.
///
public required string Payload { get; init; }
///
/// Signatures on the envelope.
///
public IReadOnlyList Signatures { get; init; } = [];
}
///
/// Represents a signature in a DSSE envelope.
///
public sealed record DsseSignature
{
///
/// Key identifier (e.g., key ID or certificate fingerprint).
///
public string? KeyId { get; init; }
///
/// Base64-encoded signature.
///
public required string Sig { get; init; }
///
/// Certificate chain (if present).
///
public string? Cert { get; init; }
}
///
/// Represents an in-toto statement (attestation payload).
///
public sealed record InTotoStatement
{
///
/// Statement type (typically "https://in-toto.io/Statement/v1").
///
public required string Type { get; init; }
///
/// Predicate type URI (e.g., "https://slsa.dev/provenance/v1").
///
public required string PredicateType { get; init; }
///
/// Subjects (artifacts) this statement applies to.
///
public IReadOnlyList Subjects { get; init; } = [];
///
/// Raw predicate JSON for further processing.
///
public string? PredicateJson { get; init; }
}
///
/// Represents a subject in an in-toto statement.
///
public sealed record InTotoSubject
{
///
/// Subject name (typically a file path or artifact reference).
///
public string? Name { get; init; }
///
/// Subject digests (algorithm -> hash).
///
public IReadOnlyDictionary Digest { get; init; } = new Dictionary();
///
/// Gets the normalized SHA-256 digest if available.
///
public string? GetSha256Digest()
{
if (Digest.TryGetValue("sha256", out var hash))
{
return "sha256:" + hash.ToLowerInvariant();
}
return null;
}
}
///
/// Well-known predicate types for attestations.
///
public static class PredicateTypes
{
public const string SlsaProvenanceV1 = "https://slsa.dev/provenance/v1";
public const string SlsaProvenanceV02 = "https://slsa.dev/provenance/v0.2";
public const string InTotoLink = "https://in-toto.io/Link/v1";
public const string Spdx = "https://spdx.dev/Document";
public const string CycloneDx = "https://cyclonedx.org/bom";
public const string OpenVex = "https://openvex.dev/ns/v0.2.0";
public const string Csaf = "https://docs.oasis-open.org/csaf/csaf/v2.0";
public const string ScorecardV2 = "https://ossf.github.io/scorecard/v2";
public const string VulnerabilityReport = "https://cosign.sigstore.dev/attestation/vuln/v1";
}