Files
git.stella-ops.org/src/Policy/StellaOps.Policy.Engine/WhatIfSimulation/WhatIfSimulationModels.cs
StellaOps Bot 05da719048
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
up
2025-11-28 09:41:08 +02:00

372 lines
11 KiB
C#

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);