up
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,371 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace StellaOps.Policy.Engine.WhatIfSimulation;
|
||||
|
||||
/// <summary>
|
||||
/// Request for what-if simulation supporting hypothetical SBOM diffs and draft policies.
|
||||
/// </summary>
|
||||
public sealed record WhatIfSimulationRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Tenant identifier.
|
||||
/// </summary>
|
||||
[JsonPropertyName("tenant_id")]
|
||||
public required string TenantId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Base snapshot ID to apply diffs to.
|
||||
/// </summary>
|
||||
[JsonPropertyName("base_snapshot_id")]
|
||||
public required string BaseSnapshotId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Active policy pack ID to use as baseline.
|
||||
/// If DraftPolicy is provided, this will be compared against.
|
||||
/// </summary>
|
||||
[JsonPropertyName("baseline_pack_id")]
|
||||
public string? BaselinePackId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Baseline policy version. If null, uses active version.
|
||||
/// </summary>
|
||||
[JsonPropertyName("baseline_pack_version")]
|
||||
public int? BaselinePackVersion { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Draft policy to simulate (not yet activated).
|
||||
/// If null, uses baseline policy.
|
||||
/// </summary>
|
||||
[JsonPropertyName("draft_policy")]
|
||||
public WhatIfDraftPolicy? DraftPolicy { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// SBOM diffs to apply hypothetically.
|
||||
/// </summary>
|
||||
[JsonPropertyName("sbom_diffs")]
|
||||
public ImmutableArray<WhatIfSbomDiff> SbomDiffs { get; init; } = ImmutableArray<WhatIfSbomDiff>.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Specific component PURLs to evaluate. If empty, evaluates affected by diffs.
|
||||
/// </summary>
|
||||
[JsonPropertyName("target_purls")]
|
||||
public ImmutableArray<string> TargetPurls { get; init; } = ImmutableArray<string>.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum number of components to evaluate.
|
||||
/// </summary>
|
||||
[JsonPropertyName("limit")]
|
||||
public int Limit { get; init; } = 1000;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to include detailed explanations for each decision.
|
||||
/// </summary>
|
||||
[JsonPropertyName("include_explanations")]
|
||||
public bool IncludeExplanations { get; init; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Correlation ID for tracing.
|
||||
/// </summary>
|
||||
[JsonPropertyName("correlation_id")]
|
||||
public string? CorrelationId { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draft policy definition for simulation.
|
||||
/// </summary>
|
||||
public sealed record WhatIfDraftPolicy
|
||||
{
|
||||
/// <summary>
|
||||
/// Draft policy pack ID.
|
||||
/// </summary>
|
||||
[JsonPropertyName("pack_id")]
|
||||
public required string PackId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Draft policy version.
|
||||
/// </summary>
|
||||
[JsonPropertyName("version")]
|
||||
public int Version { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Raw YAML policy definition to compile and evaluate.
|
||||
/// If provided, this is compiled on-the-fly.
|
||||
/// </summary>
|
||||
[JsonPropertyName("policy_yaml")]
|
||||
public string? PolicyYaml { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Pre-compiled bundle digest if available.
|
||||
/// </summary>
|
||||
[JsonPropertyName("bundle_digest")]
|
||||
public string? BundleDigest { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hypothetical SBOM modification for what-if simulation.
|
||||
/// </summary>
|
||||
public sealed record WhatIfSbomDiff
|
||||
{
|
||||
/// <summary>
|
||||
/// Type of modification: add, remove, upgrade, downgrade.
|
||||
/// </summary>
|
||||
[JsonPropertyName("operation")]
|
||||
public required string Operation { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Component PURL being modified.
|
||||
/// </summary>
|
||||
[JsonPropertyName("purl")]
|
||||
public required string Purl { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// New version for upgrade/downgrade operations.
|
||||
/// </summary>
|
||||
[JsonPropertyName("new_version")]
|
||||
public string? NewVersion { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Original version (for reference in upgrades/downgrades).
|
||||
/// </summary>
|
||||
[JsonPropertyName("original_version")]
|
||||
public string? OriginalVersion { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Hypothetical advisory IDs affecting this component.
|
||||
/// </summary>
|
||||
[JsonPropertyName("advisory_ids")]
|
||||
public ImmutableArray<string> AdvisoryIds { get; init; } = ImmutableArray<string>.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Hypothetical VEX status for this component.
|
||||
/// </summary>
|
||||
[JsonPropertyName("vex_status")]
|
||||
public string? VexStatus { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Hypothetical reachability state.
|
||||
/// </summary>
|
||||
[JsonPropertyName("reachability")]
|
||||
public string? Reachability { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Response from what-if simulation.
|
||||
/// </summary>
|
||||
public sealed record WhatIfSimulationResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// Simulation identifier.
|
||||
/// </summary>
|
||||
[JsonPropertyName("simulation_id")]
|
||||
public required string SimulationId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Tenant identifier.
|
||||
/// </summary>
|
||||
[JsonPropertyName("tenant_id")]
|
||||
public required string TenantId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Base snapshot ID used.
|
||||
/// </summary>
|
||||
[JsonPropertyName("base_snapshot_id")]
|
||||
public required string BaseSnapshotId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Baseline policy used for comparison.
|
||||
/// </summary>
|
||||
[JsonPropertyName("baseline_policy")]
|
||||
public required WhatIfPolicyRef BaselinePolicy { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Simulated policy (draft or modified).
|
||||
/// </summary>
|
||||
[JsonPropertyName("simulated_policy")]
|
||||
public WhatIfPolicyRef? SimulatedPolicy { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Decision changes between baseline and simulation.
|
||||
/// </summary>
|
||||
[JsonPropertyName("decision_changes")]
|
||||
public required ImmutableArray<WhatIfDecisionChange> DecisionChanges { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Summary of changes.
|
||||
/// </summary>
|
||||
[JsonPropertyName("summary")]
|
||||
public required WhatIfSummary Summary { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// When the simulation was executed.
|
||||
/// </summary>
|
||||
[JsonPropertyName("executed_at")]
|
||||
public required DateTimeOffset ExecutedAt { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Execution duration in milliseconds.
|
||||
/// </summary>
|
||||
[JsonPropertyName("duration_ms")]
|
||||
public long DurationMs { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Correlation ID.
|
||||
/// </summary>
|
||||
[JsonPropertyName("correlation_id")]
|
||||
public string? CorrelationId { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Policy reference in simulation.
|
||||
/// </summary>
|
||||
public sealed record WhatIfPolicyRef(
|
||||
[property: JsonPropertyName("pack_id")] string PackId,
|
||||
[property: JsonPropertyName("version")] int Version,
|
||||
[property: JsonPropertyName("bundle_digest")] string? BundleDigest,
|
||||
[property: JsonPropertyName("is_draft")] bool IsDraft);
|
||||
|
||||
/// <summary>
|
||||
/// A decision change detected in what-if simulation.
|
||||
/// </summary>
|
||||
public sealed record WhatIfDecisionChange
|
||||
{
|
||||
/// <summary>
|
||||
/// Component PURL.
|
||||
/// </summary>
|
||||
[JsonPropertyName("purl")]
|
||||
public required string Purl { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Advisory ID if applicable.
|
||||
/// </summary>
|
||||
[JsonPropertyName("advisory_id")]
|
||||
public string? AdvisoryId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Type of change: new, removed, status_changed, severity_changed.
|
||||
/// </summary>
|
||||
[JsonPropertyName("change_type")]
|
||||
public required string ChangeType { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Baseline decision.
|
||||
/// </summary>
|
||||
[JsonPropertyName("baseline")]
|
||||
public WhatIfDecision? Baseline { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Simulated decision.
|
||||
/// </summary>
|
||||
[JsonPropertyName("simulated")]
|
||||
public WhatIfDecision? Simulated { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// SBOM diff that caused this change, if any.
|
||||
/// </summary>
|
||||
[JsonPropertyName("caused_by_diff")]
|
||||
public WhatIfSbomDiff? CausedByDiff { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Explanation for the change.
|
||||
/// </summary>
|
||||
[JsonPropertyName("explanation")]
|
||||
public WhatIfExplanation? Explanation { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A decision in what-if simulation.
|
||||
/// </summary>
|
||||
public sealed record WhatIfDecision(
|
||||
[property: JsonPropertyName("status")] string Status,
|
||||
[property: JsonPropertyName("severity")] string? Severity,
|
||||
[property: JsonPropertyName("rule_name")] string? RuleName,
|
||||
[property: JsonPropertyName("priority")] int? Priority,
|
||||
[property: JsonPropertyName("exception_applied")] bool ExceptionApplied);
|
||||
|
||||
/// <summary>
|
||||
/// Explanation for a what-if decision.
|
||||
/// </summary>
|
||||
public sealed record WhatIfExplanation
|
||||
{
|
||||
/// <summary>
|
||||
/// Rules that matched.
|
||||
/// </summary>
|
||||
[JsonPropertyName("matched_rules")]
|
||||
public ImmutableArray<string> MatchedRules { get; init; } = ImmutableArray<string>.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Key factors in the decision.
|
||||
/// </summary>
|
||||
[JsonPropertyName("factors")]
|
||||
public ImmutableArray<string> Factors { get; init; } = ImmutableArray<string>.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// VEX evidence considered.
|
||||
/// </summary>
|
||||
[JsonPropertyName("vex_evidence")]
|
||||
public string? VexEvidence { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Reachability state.
|
||||
/// </summary>
|
||||
[JsonPropertyName("reachability")]
|
||||
public string? Reachability { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Summary of what-if simulation results.
|
||||
/// </summary>
|
||||
public sealed record WhatIfSummary
|
||||
{
|
||||
/// <summary>
|
||||
/// Total components evaluated.
|
||||
/// </summary>
|
||||
[JsonPropertyName("total_evaluated")]
|
||||
public int TotalEvaluated { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Components with changed decisions.
|
||||
/// </summary>
|
||||
[JsonPropertyName("total_changed")]
|
||||
public int TotalChanged { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Components newly affected.
|
||||
/// </summary>
|
||||
[JsonPropertyName("newly_affected")]
|
||||
public int NewlyAffected { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Components no longer affected.
|
||||
/// </summary>
|
||||
[JsonPropertyName("no_longer_affected")]
|
||||
public int NoLongerAffected { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Status changes by type.
|
||||
/// </summary>
|
||||
[JsonPropertyName("status_changes")]
|
||||
public required ImmutableDictionary<string, int> StatusChanges { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Severity changes by type (e.g., "low_to_high").
|
||||
/// </summary>
|
||||
[JsonPropertyName("severity_changes")]
|
||||
public required ImmutableDictionary<string, int> SeverityChanges { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Impact assessment.
|
||||
/// </summary>
|
||||
[JsonPropertyName("impact")]
|
||||
public required WhatIfImpact Impact { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Impact assessment from what-if simulation.
|
||||
/// </summary>
|
||||
public sealed record WhatIfImpact(
|
||||
[property: JsonPropertyName("risk_delta")] string RiskDelta, // increased, decreased, unchanged
|
||||
[property: JsonPropertyName("blocked_count_delta")] int BlockedCountDelta,
|
||||
[property: JsonPropertyName("warning_count_delta")] int WarningCountDelta,
|
||||
[property: JsonPropertyName("recommendation")] string? Recommendation);
|
||||
Reference in New Issue
Block a user