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:
master
2026-01-19 09:02:59 +02:00
parent 8c4bf54aed
commit 17419ba7c4
809 changed files with 170738 additions and 12244 deletions

View File

@@ -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