feat: Implement IsolatedReplayContext for deterministic audit replay
- Added IsolatedReplayContext class to provide an isolated environment for replaying audit bundles without external calls. - Introduced methods for initializing the context, verifying input digests, and extracting inputs for policy evaluation. - Created supporting interfaces and options for context configuration. feat: Create ReplayExecutor for executing policy re-evaluation and verdict comparison - Developed ReplayExecutor class to handle the execution of replay processes, including input verification and verdict comparison. - Implemented detailed drift detection and error handling during replay execution. - Added interfaces for policy evaluation and replay execution options. feat: Add ScanSnapshotFetcher for fetching scan data and snapshots - Introduced ScanSnapshotFetcher class to retrieve necessary scan data and snapshots for audit bundle creation. - Implemented methods to fetch scan metadata, advisory feeds, policy snapshots, and VEX statements. - Created supporting interfaces for scan data, feed snapshots, and policy snapshots.
This commit is contained in:
292
src/Policy/StellaOps.Policy.Gateway/Contracts/DeltaContracts.cs
Normal file
292
src/Policy/StellaOps.Policy.Gateway/Contracts/DeltaContracts.cs
Normal file
@@ -0,0 +1,292 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
// Sprint: SPRINT_4100_0004_0001 - Security State Delta & Verdict
|
||||
// Task: T6 - Add Delta API endpoints
|
||||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using StellaOps.Policy.Deltas;
|
||||
|
||||
namespace StellaOps.Policy.Gateway.Contracts;
|
||||
|
||||
/// <summary>
|
||||
/// Request to compute a security state delta.
|
||||
/// </summary>
|
||||
public sealed record ComputeDeltaRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Artifact digest (required).
|
||||
/// </summary>
|
||||
[Required]
|
||||
public required string ArtifactDigest { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Artifact name (optional).
|
||||
/// </summary>
|
||||
public string? ArtifactName { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Artifact tag (optional).
|
||||
/// </summary>
|
||||
public string? ArtifactTag { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Target snapshot ID (required).
|
||||
/// </summary>
|
||||
[Required]
|
||||
public required string TargetSnapshotId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Explicit baseline snapshot ID (optional).
|
||||
/// If not provided, baseline selection strategy is used.
|
||||
/// </summary>
|
||||
public string? BaselineSnapshotId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Baseline selection strategy (optional, defaults to LastApproved).
|
||||
/// Values: PreviousBuild, LastApproved, ProductionDeployed, BranchBase
|
||||
/// </summary>
|
||||
public string? BaselineStrategy { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Response from computing a security state delta.
|
||||
/// </summary>
|
||||
public sealed record ComputeDeltaResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// The computed delta ID.
|
||||
/// </summary>
|
||||
public required string DeltaId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Baseline snapshot ID used.
|
||||
/// </summary>
|
||||
public required string BaselineSnapshotId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Target snapshot ID.
|
||||
/// </summary>
|
||||
public required string TargetSnapshotId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// When the delta was computed.
|
||||
/// </summary>
|
||||
public required DateTimeOffset ComputedAt { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Summary statistics.
|
||||
/// </summary>
|
||||
public required DeltaSummaryDto Summary { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of drivers identified.
|
||||
/// </summary>
|
||||
public int DriverCount { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Summary statistics DTO.
|
||||
/// </summary>
|
||||
public sealed record DeltaSummaryDto
|
||||
{
|
||||
public int TotalChanges { get; init; }
|
||||
public int RiskIncreasing { get; init; }
|
||||
public int RiskDecreasing { get; init; }
|
||||
public int Neutral { get; init; }
|
||||
public decimal RiskScore { get; init; }
|
||||
public required string RiskDirection { get; init; }
|
||||
|
||||
public static DeltaSummaryDto FromModel(DeltaSummary summary) => new()
|
||||
{
|
||||
TotalChanges = summary.TotalChanges,
|
||||
RiskIncreasing = summary.RiskIncreasing,
|
||||
RiskDecreasing = summary.RiskDecreasing,
|
||||
Neutral = summary.Neutral,
|
||||
RiskScore = summary.RiskScore,
|
||||
RiskDirection = summary.RiskDirection
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Full delta response DTO.
|
||||
/// </summary>
|
||||
public sealed record DeltaResponse
|
||||
{
|
||||
public required string DeltaId { get; init; }
|
||||
public required DateTimeOffset ComputedAt { get; init; }
|
||||
public required string BaselineSnapshotId { get; init; }
|
||||
public required string TargetSnapshotId { get; init; }
|
||||
public required ArtifactRefDto Artifact { get; init; }
|
||||
public required SbomDeltaDto Sbom { get; init; }
|
||||
public required ReachabilityDeltaDto Reachability { get; init; }
|
||||
public required VexDeltaDto Vex { get; init; }
|
||||
public required PolicyDeltaDto Policy { get; init; }
|
||||
public required UnknownsDeltaDto Unknowns { get; init; }
|
||||
public required IReadOnlyList<DeltaDriverDto> Drivers { get; init; }
|
||||
public required DeltaSummaryDto Summary { get; init; }
|
||||
|
||||
public static DeltaResponse FromModel(SecurityStateDelta delta) => new()
|
||||
{
|
||||
DeltaId = delta.DeltaId,
|
||||
ComputedAt = delta.ComputedAt,
|
||||
BaselineSnapshotId = delta.BaselineSnapshotId,
|
||||
TargetSnapshotId = delta.TargetSnapshotId,
|
||||
Artifact = ArtifactRefDto.FromModel(delta.Artifact),
|
||||
Sbom = SbomDeltaDto.FromModel(delta.Sbom),
|
||||
Reachability = ReachabilityDeltaDto.FromModel(delta.Reachability),
|
||||
Vex = VexDeltaDto.FromModel(delta.Vex),
|
||||
Policy = PolicyDeltaDto.FromModel(delta.Policy),
|
||||
Unknowns = UnknownsDeltaDto.FromModel(delta.Unknowns),
|
||||
Drivers = delta.Drivers.Select(DeltaDriverDto.FromModel).ToList(),
|
||||
Summary = DeltaSummaryDto.FromModel(delta.Summary)
|
||||
};
|
||||
}
|
||||
|
||||
public sealed record ArtifactRefDto
|
||||
{
|
||||
public required string Digest { get; init; }
|
||||
public string? Name { get; init; }
|
||||
public string? Tag { get; init; }
|
||||
|
||||
public static ArtifactRefDto FromModel(ArtifactRef artifact) => new()
|
||||
{
|
||||
Digest = artifact.Digest,
|
||||
Name = artifact.Name,
|
||||
Tag = artifact.Tag
|
||||
};
|
||||
}
|
||||
|
||||
public sealed record SbomDeltaDto
|
||||
{
|
||||
public int PackagesAdded { get; init; }
|
||||
public int PackagesRemoved { get; init; }
|
||||
public int PackagesModified { get; init; }
|
||||
|
||||
public static SbomDeltaDto FromModel(SbomDelta sbom) => new()
|
||||
{
|
||||
PackagesAdded = sbom.PackagesAdded,
|
||||
PackagesRemoved = sbom.PackagesRemoved,
|
||||
PackagesModified = sbom.PackagesModified
|
||||
};
|
||||
}
|
||||
|
||||
public sealed record ReachabilityDeltaDto
|
||||
{
|
||||
public int NewReachable { get; init; }
|
||||
public int NewUnreachable { get; init; }
|
||||
public int ChangedReachability { get; init; }
|
||||
|
||||
public static ReachabilityDeltaDto FromModel(ReachabilityDelta reach) => new()
|
||||
{
|
||||
NewReachable = reach.NewReachable,
|
||||
NewUnreachable = reach.NewUnreachable,
|
||||
ChangedReachability = reach.ChangedReachability
|
||||
};
|
||||
}
|
||||
|
||||
public sealed record VexDeltaDto
|
||||
{
|
||||
public int NewVexStatements { get; init; }
|
||||
public int RevokedVexStatements { get; init; }
|
||||
public int CoverageIncrease { get; init; }
|
||||
public int CoverageDecrease { get; init; }
|
||||
|
||||
public static VexDeltaDto FromModel(VexDelta vex) => new()
|
||||
{
|
||||
NewVexStatements = vex.NewVexStatements,
|
||||
RevokedVexStatements = vex.RevokedVexStatements,
|
||||
CoverageIncrease = vex.CoverageIncrease,
|
||||
CoverageDecrease = vex.CoverageDecrease
|
||||
};
|
||||
}
|
||||
|
||||
public sealed record PolicyDeltaDto
|
||||
{
|
||||
public int NewViolations { get; init; }
|
||||
public int ResolvedViolations { get; init; }
|
||||
public int PolicyVersionChanged { get; init; }
|
||||
|
||||
public static PolicyDeltaDto FromModel(PolicyDelta policy) => new()
|
||||
{
|
||||
NewViolations = policy.NewViolations,
|
||||
ResolvedViolations = policy.ResolvedViolations,
|
||||
PolicyVersionChanged = policy.PolicyVersionChanged
|
||||
};
|
||||
}
|
||||
|
||||
public sealed record UnknownsDeltaDto
|
||||
{
|
||||
public int NewUnknowns { get; init; }
|
||||
public int ResolvedUnknowns { get; init; }
|
||||
public int TotalBaselineUnknowns { get; init; }
|
||||
public int TotalTargetUnknowns { get; init; }
|
||||
|
||||
public static UnknownsDeltaDto FromModel(UnknownsDelta unknowns) => new()
|
||||
{
|
||||
NewUnknowns = unknowns.NewUnknowns,
|
||||
ResolvedUnknowns = unknowns.ResolvedUnknowns,
|
||||
TotalBaselineUnknowns = unknowns.TotalBaselineUnknowns,
|
||||
TotalTargetUnknowns = unknowns.TotalTargetUnknowns
|
||||
};
|
||||
}
|
||||
|
||||
public sealed record DeltaDriverDto
|
||||
{
|
||||
public required string Type { get; init; }
|
||||
public required string Severity { get; init; }
|
||||
public required string Description { get; init; }
|
||||
public string? CveId { get; init; }
|
||||
public string? Purl { get; init; }
|
||||
|
||||
public static DeltaDriverDto FromModel(DeltaDriver driver) => new()
|
||||
{
|
||||
Type = driver.Type,
|
||||
Severity = driver.Severity.ToString().ToLowerInvariant(),
|
||||
Description = driver.Description,
|
||||
CveId = driver.CveId,
|
||||
Purl = driver.Purl
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Request to evaluate a delta verdict.
|
||||
/// </summary>
|
||||
public sealed record EvaluateDeltaRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Exception IDs to apply.
|
||||
/// </summary>
|
||||
public IReadOnlyList<string>? Exceptions { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delta verdict response DTO.
|
||||
/// </summary>
|
||||
public sealed record DeltaVerdictResponse
|
||||
{
|
||||
public required string VerdictId { get; init; }
|
||||
public required string DeltaId { get; init; }
|
||||
public required DateTimeOffset EvaluatedAt { get; init; }
|
||||
public required string Status { get; init; }
|
||||
public required string RecommendedGate { get; init; }
|
||||
public int RiskPoints { get; init; }
|
||||
public required IReadOnlyList<DeltaDriverDto> BlockingDrivers { get; init; }
|
||||
public required IReadOnlyList<DeltaDriverDto> WarningDrivers { get; init; }
|
||||
public required IReadOnlyList<string> AppliedExceptions { get; init; }
|
||||
public string? Explanation { get; init; }
|
||||
public required IReadOnlyList<string> Recommendations { get; init; }
|
||||
|
||||
public static DeltaVerdictResponse FromModel(DeltaVerdict verdict) => new()
|
||||
{
|
||||
VerdictId = verdict.VerdictId,
|
||||
DeltaId = verdict.DeltaId,
|
||||
EvaluatedAt = verdict.EvaluatedAt,
|
||||
Status = verdict.Status.ToString().ToLowerInvariant(),
|
||||
RecommendedGate = verdict.RecommendedGate.ToString(),
|
||||
RiskPoints = verdict.RiskPoints,
|
||||
BlockingDrivers = verdict.BlockingDrivers.Select(DeltaDriverDto.FromModel).ToList(),
|
||||
WarningDrivers = verdict.WarningDrivers.Select(DeltaDriverDto.FromModel).ToList(),
|
||||
AppliedExceptions = verdict.AppliedExceptions.ToList(),
|
||||
Explanation = verdict.Explanation,
|
||||
Recommendations = verdict.Recommendations.ToList()
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user