Gaps fill up, fixes, ui restructuring
This commit is contained in:
@@ -15,5 +15,8 @@ public enum BundleContentType
|
||||
ProofBundle,
|
||||
TrustRoot,
|
||||
TimeAnchor,
|
||||
TriageSuppress,
|
||||
ExecutionEvidence,
|
||||
BeaconAttestation,
|
||||
Other
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
|
||||
@@ -10,5 +10,6 @@ public enum ExportSegment
|
||||
Reachability,
|
||||
Guards,
|
||||
Runtime,
|
||||
Policy
|
||||
Policy,
|
||||
TriageSuppress
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user