Gaps fill up, fixes, ui restructuring

This commit is contained in:
master
2026-02-19 22:10:54 +02:00
parent b5829dce5c
commit 04cacdca8a
331 changed files with 42859 additions and 2174 deletions

View File

@@ -15,5 +15,8 @@ public enum BundleContentType
ProofBundle,
TrustRoot,
TimeAnchor,
TriageSuppress,
ExecutionEvidence,
BeaconAttestation,
Other
}

View File

@@ -78,6 +78,24 @@ public sealed record AuditBundleWriteRequest
/// </summary>
public byte[]? ScoringRules { get; init; }
/// <summary>
/// Triage-suppress predicates and source DSSEs (optional).
/// Sprint: SPRINT_20260219_012 (MWS-03)
/// </summary>
public byte[]? TriageSuppressEvidence { get; init; }
/// <summary>
/// Execution evidence predicates (DSSE envelope, optional).
/// Sprint: SPRINT_20260219_013 (SEE-04)
/// </summary>
public byte[]? ExecutionEvidence { get; init; }
/// <summary>
/// Beacon attestation predicates (DSSE envelope, optional).
/// Sprint: SPRINT_20260219_014 (BEA-04)
/// </summary>
public byte[]? BeaconAttestation { get; init; }
/// <summary>
/// Time anchor for replay context (optional).
/// </summary>

View File

@@ -38,6 +38,9 @@ public sealed partial class AuditBundleWriter
AddOptionalEntry(entries, files, archiveEntries, "proof/proof-bundle.json", request.ProofBundle, BundleContentType.ProofBundle);
var trustRootsDigest = AddOptionalEntry(entries, files, archiveEntries, "trust/trust-roots.json", request.TrustRoots, BundleContentType.TrustRoot);
var scoringDigest = AddOptionalEntry(entries, files, archiveEntries, "scoring-rules.json", request.ScoringRules, BundleContentType.Other);
AddOptionalEntry(entries, files, archiveEntries, "predicates/triage-suppress.json", request.TriageSuppressEvidence, BundleContentType.TriageSuppress);
AddOptionalEntry(entries, files, archiveEntries, "predicates/execution-evidence.json", request.ExecutionEvidence, BundleContentType.ExecutionEvidence);
AddOptionalEntry(entries, files, archiveEntries, "predicates/beacon-attestation.json", request.BeaconAttestation, BundleContentType.BeaconAttestation);
var timeAnchor = AddTimeAnchorEntry(entries, files, archiveEntries, request.TimeAnchor);

View File

@@ -32,4 +32,11 @@ public sealed partial class AuditPackExportService
? null
: await _repository.GetProofChainAsync(scanId, ct).ConfigureAwait(false);
}
private async Task<TriageSuppressExportBundle?> GetTriageSuppressEvidenceAsync(string scanId, CancellationToken ct)
{
return _repository is null
? null
: await _repository.GetTriageSuppressEvidenceAsync(scanId, ct).ConfigureAwait(false);
}
}

View File

@@ -43,6 +43,30 @@ public sealed partial class AuditPackExportService
.ConfigureAwait(false);
}
}
if (request.IncludeTriageSuppress)
{
var triageSuppress = await GetTriageSuppressEvidenceAsync(request.ScanId, ct)
.ConfigureAwait(false);
if (triageSuppress is not null)
{
// Suppression predicates
await AddJsonToZipAsync(archive, "predicates/triage-suppress.json",
triageSuppress.Suppressions, ct).ConfigureAwait(false);
// Source DSSEs: micro-witness envelopes that triggered suppression
if (triageSuppress.WitnessSources.Count > 0)
{
await AddJsonToZipAsync(archive, "predicates/triage-suppress-witness-sources.json",
triageSuppress.WitnessSources, ct).ConfigureAwait(false);
}
// Source DSSEs: VEX consensus records that contributed to suppression
if (triageSuppress.VexConsensusSources.Count > 0)
{
await AddJsonToZipAsync(archive, "predicates/triage-suppress-vex-sources.json",
triageSuppress.VexConsensusSources, ct).ConfigureAwait(false);
}
}
}
}
memoryStream.Position = 0;

View File

@@ -17,6 +17,7 @@ public sealed partial class AuditPackExportService
Segments = [.. request.Segments.Select(s => s.ToString())],
IncludesAttestations = request.IncludeAttestations,
IncludesProofChain = request.IncludeProofChain,
IncludesTriageSuppress = request.IncludeTriageSuppress,
Version = "1.0"
};
}
@@ -31,6 +32,7 @@ public sealed partial class AuditPackExportService
ExportSegment.Guards => "guards/guard-analysis.json",
ExportSegment.Runtime => "runtime/runtime-signals.json",
ExportSegment.Policy => "policy/policy-evaluation.json",
ExportSegment.TriageSuppress => "predicates/triage-suppress.json",
_ => $"segments/{segment.ToString().ToLowerInvariant()}.json"
};
}

View File

@@ -12,5 +12,6 @@ public sealed record ExportManifest
public required IReadOnlyList<string> Segments { get; init; }
public bool IncludesAttestations { get; init; }
public bool IncludesProofChain { get; init; }
public bool IncludesTriageSuppress { get; init; }
public required string Version { get; init; }
}

View File

@@ -11,5 +11,13 @@ public sealed record ExportRequest
public required IReadOnlyList<ExportSegment> Segments { get; init; }
public bool IncludeAttestations { get; init; }
public bool IncludeProofChain { get; init; }
/// <summary>
/// When true, include triage-suppress predicates and their source DSSEs
/// (micro-witness + VEX consensus) in the export for auditability.
/// Sprint: SPRINT_20260219_012 (MWS-03)
/// </summary>
public bool IncludeTriageSuppress { get; init; }
public required string Filename { get; init; }
}

View File

@@ -10,5 +10,6 @@ public enum ExportSegment
Reachability,
Guards,
Runtime,
Policy
Policy,
TriageSuppress
}

View File

@@ -8,4 +8,11 @@ public interface IAuditPackRepository
Task<byte[]?> GetSegmentDataAsync(string scanId, ExportSegment segment, CancellationToken ct);
Task<IReadOnlyList<object>> GetAttestationsAsync(string scanId, CancellationToken ct);
Task<object?> GetProofChainAsync(string scanId, CancellationToken ct);
/// <summary>
/// Retrieves triage-suppress predicates and their source DSSEs (micro-witness + VEX consensus)
/// for inclusion in audit packs. Returns null if no triage-suppress evidence exists.
/// Sprint: SPRINT_20260219_012 (MWS-03)
/// </summary>
Task<TriageSuppressExportBundle?> GetTriageSuppressEvidenceAsync(string scanId, CancellationToken ct);
}

View File

@@ -0,0 +1,72 @@
using System.Text.Json.Serialization;
namespace StellaOps.AuditPack.Services;
/// <summary>
/// Bundle of triage-suppress evidence for audit pack export.
/// Contains the suppression predicate DSSE plus the triggering source DSSEs
/// (micro-witness and VEX consensus) for independent auditor verification.
/// Sprint: SPRINT_20260219_012 (MWS-03)
/// </summary>
public sealed record TriageSuppressExportBundle
{
/// <summary>
/// Suppression predicate DSSEs (stella.ops/triageSuppress@v1).
/// </summary>
[JsonPropertyName("suppressions")]
public required IReadOnlyList<TriageSuppressDsseEntry> Suppressions { get; init; }
/// <summary>
/// Source micro-witness DSSEs that triggered each suppression.
/// </summary>
[JsonPropertyName("witness_sources")]
public required IReadOnlyList<SourceDsseEntry> WitnessSources { get; init; }
/// <summary>
/// Source VEX consensus DSSEs that contributed to each suppression.
/// </summary>
[JsonPropertyName("vex_consensus_sources")]
public required IReadOnlyList<SourceDsseEntry> VexConsensusSources { get; init; }
}
/// <summary>
/// A triage-suppress DSSE envelope entry for export.
/// </summary>
public sealed record TriageSuppressDsseEntry
{
[JsonPropertyName("canonical_id")]
public required string CanonicalId { get; init; }
[JsonPropertyName("cve")]
public required string Cve { get; init; }
[JsonPropertyName("suppress_reason")]
public required string SuppressReason { get; init; }
[JsonPropertyName("dsse_digest")]
public required string DsseDigest { get; init; }
[JsonPropertyName("envelope")]
public required object Envelope { get; init; }
[JsonPropertyName("evaluated_at")]
public required DateTimeOffset EvaluatedAt { get; init; }
}
/// <summary>
/// A source DSSE entry (witness or VEX consensus) that triggered a suppression.
/// </summary>
public sealed record SourceDsseEntry
{
[JsonPropertyName("predicate_type")]
public required string PredicateType { get; init; }
[JsonPropertyName("dsse_digest")]
public required string DsseDigest { get; init; }
[JsonPropertyName("envelope")]
public required object Envelope { get; init; }
[JsonPropertyName("signed_at")]
public required DateTimeOffset SignedAt { get; init; }
}

View File

@@ -76,6 +76,9 @@ internal sealed class FakeAuditPackRepository : IAuditPackRepository
public Task<object?> GetProofChainAsync(string scanId, CancellationToken ct)
=> Task.FromResult<object?>(new { proof = "chain", scanId });
public Task<TriageSuppressExportBundle?> GetTriageSuppressEvidenceAsync(string scanId, CancellationToken ct)
=> Task.FromResult<TriageSuppressExportBundle?>(null);
}
internal sealed class FixedTimeProvider(DateTimeOffset now) : TimeProvider