doctor enhancements, setup, enhancements, ui functionality and design consolidation and , test projects fixes , product advisory attestation/rekor and delta verfications enhancements
This commit is contained in:
@@ -0,0 +1,445 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
// Copyright (c) 2026 StellaOps
|
||||
// Sprint: SPRINT_20260118_030_LIB_verdict_rekor_gate_api
|
||||
// Task: TASK-030-006 - Gate Decision API Endpoint
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace StellaOps.Policy.Gateway.Contracts;
|
||||
|
||||
/// <summary>
|
||||
/// Request for score-based gate evaluation.
|
||||
/// Used by CI/CD pipelines to evaluate individual findings.
|
||||
/// </summary>
|
||||
public sealed record ScoreGateEvaluateRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Finding identifier (CVE@PURL format).
|
||||
/// Example: "CVE-2024-1234@pkg:npm/lodash@4.17.20"
|
||||
/// </summary>
|
||||
[JsonPropertyName("finding_id")]
|
||||
public required string FindingId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// CVSS base score [0, 10].
|
||||
/// </summary>
|
||||
[JsonPropertyName("cvss_base")]
|
||||
public double CvssBase { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// CVSS version (3.0, 3.1, 4.0). Defaults to 3.1.
|
||||
/// </summary>
|
||||
[JsonPropertyName("cvss_version")]
|
||||
public string? CvssVersion { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// EPSS probability [0, 1].
|
||||
/// </summary>
|
||||
[JsonPropertyName("epss")]
|
||||
public double Epss { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// EPSS model date (ISO 8601 date).
|
||||
/// </summary>
|
||||
[JsonPropertyName("epss_model_date")]
|
||||
public DateOnly? EpssModelDate { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Reachability level: "none", "package", "function", "caller".
|
||||
/// </summary>
|
||||
[JsonPropertyName("reachability")]
|
||||
public string? Reachability { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Exploit maturity: "none", "poc", "functional", "high".
|
||||
/// </summary>
|
||||
[JsonPropertyName("exploit_maturity")]
|
||||
public string? ExploitMaturity { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Patch proof confidence [0, 1].
|
||||
/// </summary>
|
||||
[JsonPropertyName("patch_proof_confidence")]
|
||||
public double PatchProofConfidence { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// VEX status: "affected", "not_affected", "fixed", "under_investigation".
|
||||
/// </summary>
|
||||
[JsonPropertyName("vex_status")]
|
||||
public string? VexStatus { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// VEX statement source/issuer.
|
||||
/// </summary>
|
||||
[JsonPropertyName("vex_source")]
|
||||
public string? VexSource { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether to anchor the verdict to Rekor transparency log.
|
||||
/// Default: false (async anchoring).
|
||||
/// </summary>
|
||||
[JsonPropertyName("anchor_to_rekor")]
|
||||
public bool AnchorToRekor { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether to include the full verdict bundle in the response.
|
||||
/// </summary>
|
||||
[JsonPropertyName("include_verdict")]
|
||||
public bool IncludeVerdict { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional policy profile to use ("advisory", "legacy", "custom").
|
||||
/// Default: "advisory".
|
||||
/// </summary>
|
||||
[JsonPropertyName("policy_profile")]
|
||||
public string? PolicyProfile { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Response from score-based gate evaluation.
|
||||
/// </summary>
|
||||
public sealed record ScoreGateEvaluateResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// Gate action: "pass", "warn", "block".
|
||||
/// </summary>
|
||||
[JsonPropertyName("action")]
|
||||
public required string Action { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Final score [0, 1].
|
||||
/// </summary>
|
||||
[JsonPropertyName("score")]
|
||||
public required double Score { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Threshold that triggered the action.
|
||||
/// </summary>
|
||||
[JsonPropertyName("threshold")]
|
||||
public required double Threshold { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Human-readable reason for the gate decision.
|
||||
/// </summary>
|
||||
[JsonPropertyName("reason")]
|
||||
public required string Reason { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Verdict bundle ID (SHA256 digest).
|
||||
/// </summary>
|
||||
[JsonPropertyName("verdict_bundle_id")]
|
||||
public required string VerdictBundleId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Rekor entry UUID (if anchored).
|
||||
/// </summary>
|
||||
[JsonPropertyName("rekor_uuid")]
|
||||
public string? RekorUuid { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Rekor log index (if anchored).
|
||||
/// </summary>
|
||||
[JsonPropertyName("rekor_log_index")]
|
||||
public long? RekorLogIndex { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// When the verdict was computed (ISO 8601).
|
||||
/// </summary>
|
||||
[JsonPropertyName("computed_at")]
|
||||
public required DateTimeOffset ComputedAt { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Matched rules that influenced the decision.
|
||||
/// </summary>
|
||||
[JsonPropertyName("matched_rules")]
|
||||
public IReadOnlyList<string> MatchedRules { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Suggestions for resolving the gate decision.
|
||||
/// </summary>
|
||||
[JsonPropertyName("suggestions")]
|
||||
public IReadOnlyList<string> Suggestions { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// CI/CD exit code: 0=pass, 1=warn, 2=block.
|
||||
/// </summary>
|
||||
[JsonPropertyName("exit_code")]
|
||||
public required int ExitCode { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Score breakdown by dimension (optional).
|
||||
/// </summary>
|
||||
[JsonPropertyName("breakdown")]
|
||||
public IReadOnlyList<ScoreDimensionBreakdown>? Breakdown { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Full verdict bundle JSON (if include_verdict=true).
|
||||
/// </summary>
|
||||
[JsonPropertyName("verdict_bundle")]
|
||||
public object? VerdictBundle { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Per-dimension score breakdown.
|
||||
/// </summary>
|
||||
public sealed record ScoreDimensionBreakdown
|
||||
{
|
||||
/// <summary>
|
||||
/// Dimension name.
|
||||
/// </summary>
|
||||
[JsonPropertyName("dimension")]
|
||||
public required string Dimension { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Dimension symbol.
|
||||
/// </summary>
|
||||
[JsonPropertyName("symbol")]
|
||||
public required string Symbol { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Raw input value.
|
||||
/// </summary>
|
||||
[JsonPropertyName("value")]
|
||||
public required double Value { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Weight applied.
|
||||
/// </summary>
|
||||
[JsonPropertyName("weight")]
|
||||
public required double Weight { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Contribution to final score.
|
||||
/// </summary>
|
||||
[JsonPropertyName("contribution")]
|
||||
public required double Contribution { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this is a subtractive dimension.
|
||||
/// </summary>
|
||||
[JsonPropertyName("is_subtractive")]
|
||||
public bool IsSubtractive { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gate action types.
|
||||
/// </summary>
|
||||
public static class ScoreGateActions
|
||||
{
|
||||
public const string Pass = "pass";
|
||||
public const string Warn = "warn";
|
||||
public const string Block = "block";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// CI exit codes for gate evaluation.
|
||||
/// </summary>
|
||||
public static class ScoreGateExitCodes
|
||||
{
|
||||
/// <summary>Gate passed.</summary>
|
||||
public const int Pass = 0;
|
||||
|
||||
/// <summary>Gate warned.</summary>
|
||||
public const int Warn = 1;
|
||||
|
||||
/// <summary>Gate blocked.</summary>
|
||||
public const int Block = 2;
|
||||
}
|
||||
|
||||
#region Batch Evaluation Contracts
|
||||
|
||||
/// <summary>
|
||||
/// Request for batch score-based gate evaluation.
|
||||
/// Sprint: SPRINT_20260118_030_LIB_verdict_rekor_gate_api
|
||||
/// Task: TASK-030-007 - Batch Gate Evaluation API
|
||||
/// </summary>
|
||||
public sealed record ScoreGateBatchEvaluateRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// List of findings to evaluate.
|
||||
/// </summary>
|
||||
[JsonPropertyName("findings")]
|
||||
public required IReadOnlyList<ScoreGateEvaluateRequest> Findings { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Batch evaluation options.
|
||||
/// </summary>
|
||||
[JsonPropertyName("options")]
|
||||
public ScoreGateBatchOptions? Options { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Options for batch gate evaluation.
|
||||
/// </summary>
|
||||
public sealed record ScoreGateBatchOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Stop evaluation on first block.
|
||||
/// Default: false
|
||||
/// </summary>
|
||||
[JsonPropertyName("fail_fast")]
|
||||
public bool FailFast { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Include full verdict bundles in response.
|
||||
/// Default: false
|
||||
/// </summary>
|
||||
[JsonPropertyName("include_verdicts")]
|
||||
public bool IncludeVerdicts { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Anchor each verdict to Rekor (slower but auditable).
|
||||
/// Default: false
|
||||
/// </summary>
|
||||
[JsonPropertyName("anchor_to_rekor")]
|
||||
public bool AnchorToRekor { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Policy profile to use for all evaluations.
|
||||
/// Default: "advisory"
|
||||
/// </summary>
|
||||
[JsonPropertyName("policy_profile")]
|
||||
public string? PolicyProfile { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Maximum parallelism for evaluation.
|
||||
/// Default: 10
|
||||
/// </summary>
|
||||
[JsonPropertyName("max_parallelism")]
|
||||
public int MaxParallelism { get; init; } = 10;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Response from batch gate evaluation.
|
||||
/// </summary>
|
||||
public sealed record ScoreGateBatchEvaluateResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// Summary statistics for the batch evaluation.
|
||||
/// </summary>
|
||||
[JsonPropertyName("summary")]
|
||||
public required ScoreGateBatchSummary Summary { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Overall action: worst-case across all findings.
|
||||
/// "block" if any blocked, "warn" if any warned but none blocked, "pass" otherwise.
|
||||
/// </summary>
|
||||
[JsonPropertyName("overall_action")]
|
||||
public required string OverallAction { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// CI/CD exit code based on overall action.
|
||||
/// </summary>
|
||||
[JsonPropertyName("exit_code")]
|
||||
public required int ExitCode { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Individual decisions for each finding.
|
||||
/// </summary>
|
||||
[JsonPropertyName("decisions")]
|
||||
public required IReadOnlyList<ScoreGateBatchDecision> Decisions { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Evaluation duration in milliseconds.
|
||||
/// </summary>
|
||||
[JsonPropertyName("duration_ms")]
|
||||
public required long DurationMs { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether evaluation was stopped early due to fail-fast.
|
||||
/// </summary>
|
||||
[JsonPropertyName("fail_fast_triggered")]
|
||||
public bool FailFastTriggered { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Summary statistics for batch evaluation.
|
||||
/// </summary>
|
||||
public sealed record ScoreGateBatchSummary
|
||||
{
|
||||
/// <summary>
|
||||
/// Total number of findings evaluated.
|
||||
/// </summary>
|
||||
[JsonPropertyName("total")]
|
||||
public required int Total { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of findings that passed.
|
||||
/// </summary>
|
||||
[JsonPropertyName("passed")]
|
||||
public required int Passed { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of findings that warned.
|
||||
/// </summary>
|
||||
[JsonPropertyName("warned")]
|
||||
public required int Warned { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of findings that blocked.
|
||||
/// </summary>
|
||||
[JsonPropertyName("blocked")]
|
||||
public required int Blocked { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of findings that errored.
|
||||
/// </summary>
|
||||
[JsonPropertyName("errored")]
|
||||
public int Errored { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Individual decision in a batch evaluation.
|
||||
/// </summary>
|
||||
public sealed record ScoreGateBatchDecision
|
||||
{
|
||||
/// <summary>
|
||||
/// Finding identifier.
|
||||
/// </summary>
|
||||
[JsonPropertyName("finding_id")]
|
||||
public required string FindingId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gate action: "pass", "warn", "block", or "error".
|
||||
/// </summary>
|
||||
[JsonPropertyName("action")]
|
||||
public required string Action { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Final score [0, 1].
|
||||
/// </summary>
|
||||
[JsonPropertyName("score")]
|
||||
public double? Score { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Threshold that triggered the action.
|
||||
/// </summary>
|
||||
[JsonPropertyName("threshold")]
|
||||
public double? Threshold { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Reason for the decision.
|
||||
/// </summary>
|
||||
[JsonPropertyName("reason")]
|
||||
public string? Reason { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Verdict bundle ID if created.
|
||||
/// </summary>
|
||||
[JsonPropertyName("verdict_bundle_id")]
|
||||
public string? VerdictBundleId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Full verdict bundle (if include_verdicts=true).
|
||||
/// </summary>
|
||||
[JsonPropertyName("verdict_bundle")]
|
||||
public object? VerdictBundle { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Error message if evaluation failed.
|
||||
/// </summary>
|
||||
[JsonPropertyName("error")]
|
||||
public string? Error { get; init; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
Reference in New Issue
Block a user