sprints and audit work
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace StellaOps.Scanner.WebService.Contracts;
|
||||
|
||||
/// <summary>
|
||||
/// Response for GET /scans/{scanId}/layers endpoint.
|
||||
/// </summary>
|
||||
public sealed record LayerListResponseDto
|
||||
{
|
||||
[JsonPropertyName("scanId")]
|
||||
public required string ScanId { get; init; }
|
||||
|
||||
[JsonPropertyName("imageDigest")]
|
||||
public required string ImageDigest { get; init; }
|
||||
|
||||
[JsonPropertyName("layers")]
|
||||
public required IReadOnlyList<LayerSummaryDto> Layers { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Summary of a single layer.
|
||||
/// </summary>
|
||||
public sealed record LayerSummaryDto
|
||||
{
|
||||
[JsonPropertyName("digest")]
|
||||
public required string Digest { get; init; }
|
||||
|
||||
[JsonPropertyName("order")]
|
||||
public required int Order { get; init; }
|
||||
|
||||
[JsonPropertyName("hasSbom")]
|
||||
public required bool HasSbom { get; init; }
|
||||
|
||||
[JsonPropertyName("componentCount")]
|
||||
public required int ComponentCount { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Response for GET /scans/{scanId}/composition-recipe endpoint.
|
||||
/// </summary>
|
||||
public sealed record CompositionRecipeResponseDto
|
||||
{
|
||||
[JsonPropertyName("scanId")]
|
||||
public required string ScanId { get; init; }
|
||||
|
||||
[JsonPropertyName("imageDigest")]
|
||||
public required string ImageDigest { get; init; }
|
||||
|
||||
[JsonPropertyName("createdAt")]
|
||||
public required string CreatedAt { get; init; }
|
||||
|
||||
[JsonPropertyName("recipe")]
|
||||
public required CompositionRecipeDto Recipe { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The composition recipe.
|
||||
/// </summary>
|
||||
public sealed record CompositionRecipeDto
|
||||
{
|
||||
[JsonPropertyName("version")]
|
||||
public required string Version { get; init; }
|
||||
|
||||
[JsonPropertyName("generatorName")]
|
||||
public required string GeneratorName { get; init; }
|
||||
|
||||
[JsonPropertyName("generatorVersion")]
|
||||
public required string GeneratorVersion { get; init; }
|
||||
|
||||
[JsonPropertyName("layers")]
|
||||
public required IReadOnlyList<CompositionRecipeLayerDto> Layers { get; init; }
|
||||
|
||||
[JsonPropertyName("merkleRoot")]
|
||||
public required string MerkleRoot { get; init; }
|
||||
|
||||
[JsonPropertyName("aggregatedSbomDigests")]
|
||||
public required AggregatedSbomDigestsDto AggregatedSbomDigests { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A layer in the composition recipe.
|
||||
/// </summary>
|
||||
public sealed record CompositionRecipeLayerDto
|
||||
{
|
||||
[JsonPropertyName("digest")]
|
||||
public required string Digest { get; init; }
|
||||
|
||||
[JsonPropertyName("order")]
|
||||
public required int Order { get; init; }
|
||||
|
||||
[JsonPropertyName("fragmentDigest")]
|
||||
public required string FragmentDigest { get; init; }
|
||||
|
||||
[JsonPropertyName("sbomDigests")]
|
||||
public required LayerSbomDigestsDto SbomDigests { get; init; }
|
||||
|
||||
[JsonPropertyName("componentCount")]
|
||||
public required int ComponentCount { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Digests for a layer's SBOMs.
|
||||
/// </summary>
|
||||
public sealed record LayerSbomDigestsDto
|
||||
{
|
||||
[JsonPropertyName("cyclonedx")]
|
||||
public required string CycloneDx { get; init; }
|
||||
|
||||
[JsonPropertyName("spdx")]
|
||||
public required string Spdx { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Digests for aggregated SBOMs.
|
||||
/// </summary>
|
||||
public sealed record AggregatedSbomDigestsDto
|
||||
{
|
||||
[JsonPropertyName("cyclonedx")]
|
||||
public required string CycloneDx { get; init; }
|
||||
|
||||
[JsonPropertyName("spdx")]
|
||||
public string? Spdx { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Result of composition recipe verification.
|
||||
/// </summary>
|
||||
public sealed record CompositionRecipeVerificationResponseDto
|
||||
{
|
||||
[JsonPropertyName("valid")]
|
||||
public required bool Valid { get; init; }
|
||||
|
||||
[JsonPropertyName("merkleRootMatch")]
|
||||
public required bool MerkleRootMatch { get; init; }
|
||||
|
||||
[JsonPropertyName("layerDigestsMatch")]
|
||||
public required bool LayerDigestsMatch { get; init; }
|
||||
|
||||
[JsonPropertyName("errors")]
|
||||
public IReadOnlyList<string>? Errors { get; init; }
|
||||
}
|
||||
@@ -243,6 +243,71 @@ internal sealed record ScanCompletedEventPayload : OrchestratorEventPayload
|
||||
[JsonPropertyName("report")]
|
||||
[JsonPropertyOrder(10)]
|
||||
public ReportDocumentDto Report { get; init; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// VEX gate evaluation summary (Sprint: SPRINT_20260106_003_002, Task: T024).
|
||||
/// </summary>
|
||||
[JsonPropertyName("vexGate")]
|
||||
[JsonPropertyOrder(11)]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public VexGateSummaryPayload? VexGate { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// VEX gate evaluation summary for scan completion events.
|
||||
/// Sprint: SPRINT_20260106_003_002_SCANNER_vex_gate_service, Task: T024
|
||||
/// </summary>
|
||||
internal sealed record VexGateSummaryPayload
|
||||
{
|
||||
/// <summary>
|
||||
/// Total findings evaluated by the gate.
|
||||
/// </summary>
|
||||
[JsonPropertyName("totalFindings")]
|
||||
[JsonPropertyOrder(0)]
|
||||
public int TotalFindings { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Findings that passed (cleared by VEX evidence).
|
||||
/// </summary>
|
||||
[JsonPropertyName("passed")]
|
||||
[JsonPropertyOrder(1)]
|
||||
public int Passed { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Findings with warnings (partial evidence).
|
||||
/// </summary>
|
||||
[JsonPropertyName("warned")]
|
||||
[JsonPropertyOrder(2)]
|
||||
public int Warned { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Findings that were blocked (require attention).
|
||||
/// </summary>
|
||||
[JsonPropertyName("blocked")]
|
||||
[JsonPropertyOrder(3)]
|
||||
public int Blocked { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the gate was bypassed for this scan.
|
||||
/// </summary>
|
||||
[JsonPropertyName("bypassed")]
|
||||
[JsonPropertyOrder(4)]
|
||||
public bool Bypassed { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Policy version used for evaluation.
|
||||
/// </summary>
|
||||
[JsonPropertyName("policyVersion")]
|
||||
[JsonPropertyOrder(5)]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string? PolicyVersion { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// When the gate evaluation was performed.
|
||||
/// </summary>
|
||||
[JsonPropertyName("evaluatedAt")]
|
||||
[JsonPropertyOrder(6)]
|
||||
public DateTimeOffset EvaluatedAt { get; init; }
|
||||
}
|
||||
|
||||
internal sealed record ReportDeltaPayload
|
||||
|
||||
@@ -0,0 +1,322 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// RationaleContracts.cs
|
||||
// Sprint: SPRINT_20260106_001_001_LB_verdict_rationale_renderer
|
||||
// Task: VRR-020 - Integrate VerdictRationaleRenderer into Scanner.WebService
|
||||
// Description: DTOs for verdict rationale endpoint responses.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace StellaOps.Scanner.WebService.Contracts;
|
||||
|
||||
/// <summary>
|
||||
/// Response for verdict rationale request.
|
||||
/// </summary>
|
||||
public sealed record VerdictRationaleResponseDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Finding identifier.
|
||||
/// </summary>
|
||||
[JsonPropertyName("finding_id")]
|
||||
public required string FindingId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Unique rationale ID (content-addressed).
|
||||
/// </summary>
|
||||
[JsonPropertyName("rationale_id")]
|
||||
public required string RationaleId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Schema version.
|
||||
/// </summary>
|
||||
[JsonPropertyName("schema_version")]
|
||||
public string SchemaVersion { get; init; } = "1.0";
|
||||
|
||||
/// <summary>
|
||||
/// Line 1: Evidence summary.
|
||||
/// </summary>
|
||||
[JsonPropertyName("evidence")]
|
||||
public required RationaleEvidenceDto Evidence { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Line 2: Policy clause that triggered the decision.
|
||||
/// </summary>
|
||||
[JsonPropertyName("policy_clause")]
|
||||
public required RationalePolicyClauseDto PolicyClause { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Line 3: Attestations and proofs.
|
||||
/// </summary>
|
||||
[JsonPropertyName("attestations")]
|
||||
public required RationaleAttestationsDto Attestations { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Line 4: Final decision with recommendation.
|
||||
/// </summary>
|
||||
[JsonPropertyName("decision")]
|
||||
public required RationaleDecisionDto Decision { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// When the rationale was generated.
|
||||
/// </summary>
|
||||
[JsonPropertyName("generated_at")]
|
||||
public required DateTimeOffset GeneratedAt { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Input digests for reproducibility verification.
|
||||
/// </summary>
|
||||
[JsonPropertyName("input_digests")]
|
||||
public required RationaleInputDigestsDto InputDigests { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Line 1: Evidence summary DTO.
|
||||
/// </summary>
|
||||
public sealed record RationaleEvidenceDto
|
||||
{
|
||||
/// <summary>
|
||||
/// CVE identifier.
|
||||
/// </summary>
|
||||
[JsonPropertyName("cve")]
|
||||
public required string Cve { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Component PURL.
|
||||
/// </summary>
|
||||
[JsonPropertyName("component_purl")]
|
||||
public required string ComponentPurl { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Component version.
|
||||
/// </summary>
|
||||
[JsonPropertyName("component_version")]
|
||||
public string? ComponentVersion { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Vulnerable function (if reachability analyzed).
|
||||
/// </summary>
|
||||
[JsonPropertyName("vulnerable_function")]
|
||||
public string? VulnerableFunction { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Entry point from which vulnerable code is reachable.
|
||||
/// </summary>
|
||||
[JsonPropertyName("entry_point")]
|
||||
public string? EntryPoint { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Human-readable formatted text.
|
||||
/// </summary>
|
||||
[JsonPropertyName("text")]
|
||||
public required string Text { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Line 2: Policy clause DTO.
|
||||
/// </summary>
|
||||
public sealed record RationalePolicyClauseDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Policy clause ID.
|
||||
/// </summary>
|
||||
[JsonPropertyName("clause_id")]
|
||||
public required string ClauseId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Rule description.
|
||||
/// </summary>
|
||||
[JsonPropertyName("rule_description")]
|
||||
public required string RuleDescription { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Conditions that matched.
|
||||
/// </summary>
|
||||
[JsonPropertyName("conditions")]
|
||||
public required IReadOnlyList<string> Conditions { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Human-readable formatted text.
|
||||
/// </summary>
|
||||
[JsonPropertyName("text")]
|
||||
public required string Text { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Line 3: Attestations DTO.
|
||||
/// </summary>
|
||||
public sealed record RationaleAttestationsDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Path witness reference.
|
||||
/// </summary>
|
||||
[JsonPropertyName("path_witness")]
|
||||
public RationaleAttestationRefDto? PathWitness { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// VEX statement references.
|
||||
/// </summary>
|
||||
[JsonPropertyName("vex_statements")]
|
||||
public IReadOnlyList<RationaleAttestationRefDto>? VexStatements { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Provenance attestation reference.
|
||||
/// </summary>
|
||||
[JsonPropertyName("provenance")]
|
||||
public RationaleAttestationRefDto? Provenance { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Human-readable formatted text.
|
||||
/// </summary>
|
||||
[JsonPropertyName("text")]
|
||||
public required string Text { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attestation reference DTO.
|
||||
/// </summary>
|
||||
public sealed record RationaleAttestationRefDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Attestation ID.
|
||||
/// </summary>
|
||||
[JsonPropertyName("id")]
|
||||
public required string Id { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Attestation type.
|
||||
/// </summary>
|
||||
[JsonPropertyName("type")]
|
||||
public required string Type { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Content digest.
|
||||
/// </summary>
|
||||
[JsonPropertyName("digest")]
|
||||
public string? Digest { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Summary description.
|
||||
/// </summary>
|
||||
[JsonPropertyName("summary")]
|
||||
public string? Summary { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Line 4: Decision DTO.
|
||||
/// </summary>
|
||||
public sealed record RationaleDecisionDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Final verdict (Affected, Not Affected, etc.).
|
||||
/// </summary>
|
||||
[JsonPropertyName("verdict")]
|
||||
public required string Verdict { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Risk score (0-1).
|
||||
/// </summary>
|
||||
[JsonPropertyName("score")]
|
||||
public double? Score { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Recommended action.
|
||||
/// </summary>
|
||||
[JsonPropertyName("recommendation")]
|
||||
public required string Recommendation { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Mitigation guidance.
|
||||
/// </summary>
|
||||
[JsonPropertyName("mitigation")]
|
||||
public RationaleMitigationDto? Mitigation { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Human-readable formatted text.
|
||||
/// </summary>
|
||||
[JsonPropertyName("text")]
|
||||
public required string Text { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mitigation guidance DTO.
|
||||
/// </summary>
|
||||
public sealed record RationaleMitigationDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Recommended action.
|
||||
/// </summary>
|
||||
[JsonPropertyName("action")]
|
||||
public required string Action { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Additional details.
|
||||
/// </summary>
|
||||
[JsonPropertyName("details")]
|
||||
public string? Details { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Input digests for reproducibility.
|
||||
/// </summary>
|
||||
public sealed record RationaleInputDigestsDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Verdict attestation digest.
|
||||
/// </summary>
|
||||
[JsonPropertyName("verdict_digest")]
|
||||
public required string VerdictDigest { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Policy snapshot digest.
|
||||
/// </summary>
|
||||
[JsonPropertyName("policy_digest")]
|
||||
public string? PolicyDigest { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Evidence bundle digest.
|
||||
/// </summary>
|
||||
[JsonPropertyName("evidence_digest")]
|
||||
public string? EvidenceDigest { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Request for rationale in specific format.
|
||||
/// </summary>
|
||||
public sealed record RationaleFormatRequestDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Desired format: json, markdown, plaintext.
|
||||
/// </summary>
|
||||
[JsonPropertyName("format")]
|
||||
public string Format { get; init; } = "json";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plain text rationale response.
|
||||
/// </summary>
|
||||
public sealed record RationalePlainTextResponseDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Finding identifier.
|
||||
/// </summary>
|
||||
[JsonPropertyName("finding_id")]
|
||||
public required string FindingId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Rationale ID.
|
||||
/// </summary>
|
||||
[JsonPropertyName("rationale_id")]
|
||||
public required string RationaleId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Format of the content.
|
||||
/// </summary>
|
||||
[JsonPropertyName("format")]
|
||||
public required string Format { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Rendered content.
|
||||
/// </summary>
|
||||
[JsonPropertyName("content")]
|
||||
public required string Content { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// VexGateContracts.cs
|
||||
// Sprint: SPRINT_20260106_003_002_SCANNER_vex_gate_service
|
||||
// Task: T021
|
||||
// Description: DTOs for VEX gate results API endpoints.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace StellaOps.Scanner.WebService.Contracts;
|
||||
|
||||
/// <summary>
|
||||
/// Response for GET /scans/{scanId}/gate-results.
|
||||
/// </summary>
|
||||
public sealed record VexGateResultsResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// Scan identifier.
|
||||
/// </summary>
|
||||
[JsonPropertyName("scanId")]
|
||||
public required string ScanId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Summary of gate evaluation results.
|
||||
/// </summary>
|
||||
[JsonPropertyName("gateSummary")]
|
||||
public required VexGateSummaryDto GateSummary { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Individual gated findings.
|
||||
/// </summary>
|
||||
[JsonPropertyName("gatedFindings")]
|
||||
public required IReadOnlyList<GatedFindingDto> GatedFindings { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Policy version used for evaluation.
|
||||
/// </summary>
|
||||
[JsonPropertyName("policyVersion")]
|
||||
public string? PolicyVersion { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether gate was bypassed for this scan.
|
||||
/// </summary>
|
||||
[JsonPropertyName("bypassed")]
|
||||
public bool Bypassed { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Summary of VEX gate evaluation.
|
||||
/// </summary>
|
||||
public sealed record VexGateSummaryDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Total number of findings evaluated.
|
||||
/// </summary>
|
||||
[JsonPropertyName("totalFindings")]
|
||||
public int TotalFindings { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of findings that passed (cleared by VEX evidence).
|
||||
/// </summary>
|
||||
[JsonPropertyName("passed")]
|
||||
public int Passed { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of findings with warnings (partial evidence).
|
||||
/// </summary>
|
||||
[JsonPropertyName("warned")]
|
||||
public int Warned { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of findings blocked (requires attention).
|
||||
/// </summary>
|
||||
[JsonPropertyName("blocked")]
|
||||
public int Blocked { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// When the evaluation was performed (UTC ISO-8601).
|
||||
/// </summary>
|
||||
[JsonPropertyName("evaluatedAt")]
|
||||
public DateTimeOffset EvaluatedAt { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Percentage of findings that passed.
|
||||
/// </summary>
|
||||
[JsonPropertyName("passRate")]
|
||||
public double PassRate => TotalFindings > 0 ? (double)Passed / TotalFindings : 0;
|
||||
|
||||
/// <summary>
|
||||
/// Percentage of findings that were blocked.
|
||||
/// </summary>
|
||||
[JsonPropertyName("blockRate")]
|
||||
public double BlockRate => TotalFindings > 0 ? (double)Blocked / TotalFindings : 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A finding with its gate evaluation result.
|
||||
/// </summary>
|
||||
public sealed record GatedFindingDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Finding identifier.
|
||||
/// </summary>
|
||||
[JsonPropertyName("findingId")]
|
||||
public required string FindingId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// CVE or vulnerability identifier.
|
||||
/// </summary>
|
||||
[JsonPropertyName("cve")]
|
||||
public required string Cve { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Package URL of the affected component.
|
||||
/// </summary>
|
||||
[JsonPropertyName("purl")]
|
||||
public string? Purl { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gate decision: Pass, Warn, or Block.
|
||||
/// </summary>
|
||||
[JsonPropertyName("decision")]
|
||||
public required string Decision { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Human-readable explanation of the decision.
|
||||
/// </summary>
|
||||
[JsonPropertyName("rationale")]
|
||||
public required string Rationale { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// ID of the policy rule that matched.
|
||||
/// </summary>
|
||||
[JsonPropertyName("policyRuleMatched")]
|
||||
public required string PolicyRuleMatched { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Supporting evidence for the decision.
|
||||
/// </summary>
|
||||
[JsonPropertyName("evidence")]
|
||||
public required GateEvidenceDto Evidence { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// References to VEX statements that contributed to this decision.
|
||||
/// </summary>
|
||||
[JsonPropertyName("contributingStatements")]
|
||||
public IReadOnlyList<VexStatementRefDto>? ContributingStatements { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evidence supporting a gate decision.
|
||||
/// </summary>
|
||||
public sealed record GateEvidenceDto
|
||||
{
|
||||
/// <summary>
|
||||
/// VEX status from vendor or authoritative source.
|
||||
/// </summary>
|
||||
[JsonPropertyName("vendorStatus")]
|
||||
public string? VendorStatus { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Justification type from VEX statement.
|
||||
/// </summary>
|
||||
[JsonPropertyName("justification")]
|
||||
public string? Justification { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the vulnerable code is reachable.
|
||||
/// </summary>
|
||||
[JsonPropertyName("isReachable")]
|
||||
public bool IsReachable { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether compensating controls mitigate the vulnerability.
|
||||
/// </summary>
|
||||
[JsonPropertyName("hasCompensatingControl")]
|
||||
public bool HasCompensatingControl { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Confidence score in the gate decision (0.0 to 1.0).
|
||||
/// </summary>
|
||||
[JsonPropertyName("confidenceScore")]
|
||||
public double ConfidenceScore { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Severity level from the advisory.
|
||||
/// </summary>
|
||||
[JsonPropertyName("severityLevel")]
|
||||
public string? SeverityLevel { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the vulnerability is exploitable.
|
||||
/// </summary>
|
||||
[JsonPropertyName("isExploitable")]
|
||||
public bool IsExploitable { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Backport hints detected.
|
||||
/// </summary>
|
||||
[JsonPropertyName("backportHints")]
|
||||
public IReadOnlyList<string>? BackportHints { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reference to a VEX statement.
|
||||
/// </summary>
|
||||
public sealed record VexStatementRefDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Statement identifier.
|
||||
/// </summary>
|
||||
[JsonPropertyName("statementId")]
|
||||
public required string StatementId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Issuer identifier.
|
||||
/// </summary>
|
||||
[JsonPropertyName("issuerId")]
|
||||
public required string IssuerId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// VEX status declared in the statement.
|
||||
/// </summary>
|
||||
[JsonPropertyName("status")]
|
||||
public required string Status { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// When the statement was issued (UTC ISO-8601).
|
||||
/// </summary>
|
||||
[JsonPropertyName("timestamp")]
|
||||
public DateTimeOffset Timestamp { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Trust weight of this statement (0.0 to 1.0).
|
||||
/// </summary>
|
||||
[JsonPropertyName("trustWeight")]
|
||||
public double TrustWeight { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Query parameters for filtering gate results.
|
||||
/// </summary>
|
||||
public sealed record VexGateResultsQuery
|
||||
{
|
||||
/// <summary>
|
||||
/// Filter by gate decision (Pass, Warn, Block).
|
||||
/// </summary>
|
||||
public string? Decision { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Filter by minimum confidence score.
|
||||
/// </summary>
|
||||
public double? MinConfidence { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Maximum number of results to return.
|
||||
/// </summary>
|
||||
public int? Limit { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Offset for pagination.
|
||||
/// </summary>
|
||||
public int? Offset { get; init; }
|
||||
}
|
||||
Reference in New Issue
Block a user