Refactor code structure for improved readability and maintainability; optimize performance in key functions.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// FindingEvidenceContracts.cs
|
||||
// Sprint: SPRINT_3800_0001_0001_evidence_api_models
|
||||
// Description: Unified evidence API response contracts for findings.
|
||||
// Sprint: SPRINT_4300_0001_0002_findings_evidence_api
|
||||
// Description: Evidence API response contracts for explainable triage.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
@@ -11,447 +11,188 @@ using System.Text.Json.Serialization;
|
||||
namespace StellaOps.Scanner.WebService.Contracts;
|
||||
|
||||
/// <summary>
|
||||
/// Unified evidence response for a finding, combining reachability, boundary,
|
||||
/// VEX evidence, and score explanation.
|
||||
/// Consolidated evidence response for a finding.
|
||||
/// Matches the advisory contract for explainable triage UX.
|
||||
/// </summary>
|
||||
public sealed record FindingEvidenceResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// Unique identifier for the finding.
|
||||
/// Unique finding identifier.
|
||||
/// </summary>
|
||||
[JsonPropertyName("finding_id")]
|
||||
public string FindingId { get; init; } = string.Empty;
|
||||
public required string FindingId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// CVE identifier (e.g., "CVE-2021-44228").
|
||||
/// CVE or vulnerability identifier.
|
||||
/// </summary>
|
||||
[JsonPropertyName("cve")]
|
||||
public string Cve { get; init; } = string.Empty;
|
||||
public required string Cve { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Component where the vulnerability was found.
|
||||
/// Affected component details.
|
||||
/// </summary>
|
||||
[JsonPropertyName("component")]
|
||||
public ComponentRef? Component { get; init; }
|
||||
public required ComponentInfo Component { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Reachable call path from entrypoint to vulnerable sink.
|
||||
/// Each element is a fully-qualified name (FQN).
|
||||
/// Reachable path from entrypoint to vulnerable code.
|
||||
/// </summary>
|
||||
[JsonPropertyName("reachable_path")]
|
||||
public IReadOnlyList<string>? ReachablePath { get; init; }
|
||||
public IReadOnlyList<string> ReachablePath { get; init; } = Array.Empty<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Entrypoint proof (how the code is exposed).
|
||||
/// Entrypoint details (HTTP route, CLI command, etc.).
|
||||
/// </summary>
|
||||
[JsonPropertyName("entrypoint")]
|
||||
public EntrypointProof? Entrypoint { get; init; }
|
||||
public EntrypointInfo? Entrypoint { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Boundary proof (surface exposure and controls).
|
||||
/// </summary>
|
||||
[JsonPropertyName("boundary")]
|
||||
public BoundaryProofDto? Boundary { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// VEX (Vulnerability Exploitability eXchange) evidence.
|
||||
/// VEX exploitability status.
|
||||
/// </summary>
|
||||
[JsonPropertyName("vex")]
|
||||
public VexEvidenceDto? Vex { get; init; }
|
||||
public VexStatusInfo? Vex { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Score explanation with additive risk breakdown.
|
||||
/// </summary>
|
||||
[JsonPropertyName("score_explain")]
|
||||
public ScoreExplanationDto? ScoreExplain { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// When the finding was last observed.
|
||||
/// When this evidence was last observed/generated.
|
||||
/// </summary>
|
||||
[JsonPropertyName("last_seen")]
|
||||
public DateTimeOffset LastSeen { get; init; }
|
||||
public required DateTimeOffset LastSeen { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// When the evidence expires (for VEX/attestation freshness).
|
||||
/// </summary>
|
||||
[JsonPropertyName("expires_at")]
|
||||
public DateTimeOffset? ExpiresAt { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the evidence is stale (expired or near-expiry).
|
||||
/// </summary>
|
||||
[JsonPropertyName("is_stale")]
|
||||
public bool IsStale { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// References to DSSE/in-toto attestations backing this evidence.
|
||||
/// Content-addressed references to attestations.
|
||||
/// </summary>
|
||||
[JsonPropertyName("attestation_refs")]
|
||||
public IReadOnlyList<string>? AttestationRefs { get; init; }
|
||||
public IReadOnlyList<string> AttestationRefs { get; init; } = Array.Empty<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Risk score with explanation.
|
||||
/// </summary>
|
||||
[JsonPropertyName("score")]
|
||||
public ScoreInfo? Score { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Boundary exposure information.
|
||||
/// </summary>
|
||||
[JsonPropertyName("boundary")]
|
||||
public BoundaryInfo? Boundary { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Evidence freshness and TTL.
|
||||
/// </summary>
|
||||
[JsonPropertyName("freshness")]
|
||||
public FreshnessInfo Freshness { get; init; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reference to a component (package) by PURL and version.
|
||||
/// </summary>
|
||||
public sealed record ComponentRef
|
||||
public sealed record ComponentInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Package URL (PURL) identifier.
|
||||
/// </summary>
|
||||
[JsonPropertyName("purl")]
|
||||
public string Purl { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Package name.
|
||||
/// </summary>
|
||||
[JsonPropertyName("name")]
|
||||
public string Name { get; init; } = string.Empty;
|
||||
public required string Name { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Package version.
|
||||
/// </summary>
|
||||
[JsonPropertyName("version")]
|
||||
public string Version { get; init; } = string.Empty;
|
||||
public required string Version { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Package type/ecosystem (npm, maven, nuget, etc.).
|
||||
/// </summary>
|
||||
[JsonPropertyName("type")]
|
||||
public string Type { get; init; } = string.Empty;
|
||||
[JsonPropertyName("purl")]
|
||||
public string? Purl { get; init; }
|
||||
|
||||
[JsonPropertyName("ecosystem")]
|
||||
public string? Ecosystem { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Proof of how code is exposed as an entrypoint.
|
||||
/// </summary>
|
||||
public sealed record EntrypointProof
|
||||
public sealed record EntrypointInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Type of entrypoint (http_handler, grpc_method, cli_command, etc.).
|
||||
/// </summary>
|
||||
[JsonPropertyName("type")]
|
||||
public string Type { get; init; } = string.Empty;
|
||||
public required string Type { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Route or path (e.g., "/api/v1/users", "grpc.UserService.GetUser").
|
||||
/// </summary>
|
||||
[JsonPropertyName("route")]
|
||||
public string? Route { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// HTTP method if applicable (GET, POST, etc.).
|
||||
/// </summary>
|
||||
[JsonPropertyName("method")]
|
||||
public string? Method { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Authentication requirement (none, optional, required).
|
||||
/// </summary>
|
||||
[JsonPropertyName("auth")]
|
||||
public string? Auth { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Execution phase (startup, runtime, shutdown).
|
||||
/// </summary>
|
||||
[JsonPropertyName("phase")]
|
||||
public string? Phase { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Fully qualified name of the entrypoint symbol.
|
||||
/// </summary>
|
||||
[JsonPropertyName("fqn")]
|
||||
public string Fqn { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Source file location.
|
||||
/// </summary>
|
||||
[JsonPropertyName("location")]
|
||||
public SourceLocation? Location { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source file location reference.
|
||||
/// </summary>
|
||||
public sealed record SourceLocation
|
||||
public sealed record VexStatusInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// File path relative to repository root.
|
||||
/// </summary>
|
||||
[JsonPropertyName("file")]
|
||||
public string File { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Line number (1-indexed).
|
||||
/// </summary>
|
||||
[JsonPropertyName("line")]
|
||||
public int? Line { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Column number (1-indexed).
|
||||
/// </summary>
|
||||
[JsonPropertyName("column")]
|
||||
public int? Column { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Boundary proof describing surface exposure and controls.
|
||||
/// </summary>
|
||||
public sealed record BoundaryProofDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Kind of boundary (network, file, ipc, etc.).
|
||||
/// </summary>
|
||||
[JsonPropertyName("kind")]
|
||||
public string Kind { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Surface descriptor (what is exposed).
|
||||
/// </summary>
|
||||
[JsonPropertyName("surface")]
|
||||
public SurfaceDescriptor? Surface { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Exposure descriptor (how it's exposed).
|
||||
/// </summary>
|
||||
[JsonPropertyName("exposure")]
|
||||
public ExposureDescriptor? Exposure { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Authentication descriptor.
|
||||
/// </summary>
|
||||
[JsonPropertyName("auth")]
|
||||
public AuthDescriptor? Auth { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Security controls in place.
|
||||
/// </summary>
|
||||
[JsonPropertyName("controls")]
|
||||
public IReadOnlyList<ControlDescriptor>? Controls { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// When the boundary was last verified.
|
||||
/// </summary>
|
||||
[JsonPropertyName("last_seen")]
|
||||
public DateTimeOffset LastSeen { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Confidence score (0.0 to 1.0).
|
||||
/// </summary>
|
||||
[JsonPropertyName("confidence")]
|
||||
public double Confidence { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Describes what attack surface is exposed.
|
||||
/// </summary>
|
||||
public sealed record SurfaceDescriptor
|
||||
{
|
||||
/// <summary>
|
||||
/// Type of surface (api, web, cli, library).
|
||||
/// </summary>
|
||||
[JsonPropertyName("type")]
|
||||
public string Type { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Protocol (http, https, grpc, tcp).
|
||||
/// </summary>
|
||||
[JsonPropertyName("protocol")]
|
||||
public string? Protocol { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Port number if network-exposed.
|
||||
/// </summary>
|
||||
[JsonPropertyName("port")]
|
||||
public int? Port { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Describes how the surface is exposed.
|
||||
/// </summary>
|
||||
public sealed record ExposureDescriptor
|
||||
{
|
||||
/// <summary>
|
||||
/// Exposure level (public, internal, private).
|
||||
/// </summary>
|
||||
[JsonPropertyName("level")]
|
||||
public string Level { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the exposure is internet-facing.
|
||||
/// </summary>
|
||||
[JsonPropertyName("internet_facing")]
|
||||
public bool InternetFacing { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Network zone (dmz, internal, trusted).
|
||||
/// </summary>
|
||||
[JsonPropertyName("zone")]
|
||||
public string? Zone { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Describes authentication requirements.
|
||||
/// </summary>
|
||||
public sealed record AuthDescriptor
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether authentication is required.
|
||||
/// </summary>
|
||||
[JsonPropertyName("required")]
|
||||
public bool Required { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Authentication type (jwt, oauth2, basic, api_key).
|
||||
/// </summary>
|
||||
[JsonPropertyName("type")]
|
||||
public string? Type { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Required roles/scopes.
|
||||
/// </summary>
|
||||
[JsonPropertyName("roles")]
|
||||
public IReadOnlyList<string>? Roles { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Describes a security control.
|
||||
/// </summary>
|
||||
public sealed record ControlDescriptor
|
||||
{
|
||||
/// <summary>
|
||||
/// Type of control (rate_limit, waf, input_validation, etc.).
|
||||
/// </summary>
|
||||
[JsonPropertyName("type")]
|
||||
public string Type { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the control is active.
|
||||
/// </summary>
|
||||
[JsonPropertyName("active")]
|
||||
public bool Active { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Control configuration details.
|
||||
/// </summary>
|
||||
[JsonPropertyName("config")]
|
||||
public string? Config { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// VEX (Vulnerability Exploitability eXchange) evidence.
|
||||
/// </summary>
|
||||
public sealed record VexEvidenceDto
|
||||
{
|
||||
/// <summary>
|
||||
/// VEX status (not_affected, affected, fixed, under_investigation).
|
||||
/// </summary>
|
||||
[JsonPropertyName("status")]
|
||||
public string Status { get; init; } = string.Empty;
|
||||
public required string Status { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Justification for the status.
|
||||
/// </summary>
|
||||
[JsonPropertyName("justification")]
|
||||
public string? Justification { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Impact statement explaining why not affected.
|
||||
/// </summary>
|
||||
[JsonPropertyName("impact")]
|
||||
public string? Impact { get; init; }
|
||||
[JsonPropertyName("timestamp")]
|
||||
public DateTimeOffset? Timestamp { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Action statement (remediation steps).
|
||||
/// </summary>
|
||||
[JsonPropertyName("action")]
|
||||
public string? Action { get; init; }
|
||||
[JsonPropertyName("issuer")]
|
||||
public string? Issuer { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reference to the VEX document/attestation.
|
||||
/// </summary>
|
||||
[JsonPropertyName("attestation_ref")]
|
||||
public string? AttestationRef { get; init; }
|
||||
public sealed record ScoreInfo
|
||||
{
|
||||
[JsonPropertyName("risk_score")]
|
||||
public required int RiskScore { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// When the VEX statement was issued.
|
||||
/// </summary>
|
||||
[JsonPropertyName("issued_at")]
|
||||
public DateTimeOffset? IssuedAt { get; init; }
|
||||
[JsonPropertyName("contributions")]
|
||||
public IReadOnlyList<ScoreContribution> Contributions { get; init; } = Array.Empty<ScoreContribution>();
|
||||
}
|
||||
|
||||
public sealed record ScoreContribution
|
||||
{
|
||||
[JsonPropertyName("factor")]
|
||||
public required string Factor { get; init; }
|
||||
|
||||
[JsonPropertyName("value")]
|
||||
public required int Value { get; init; }
|
||||
|
||||
[JsonPropertyName("reason")]
|
||||
public string? Reason { get; init; }
|
||||
}
|
||||
|
||||
public sealed record BoundaryInfo
|
||||
{
|
||||
[JsonPropertyName("surface")]
|
||||
public required string Surface { get; init; }
|
||||
|
||||
[JsonPropertyName("exposure")]
|
||||
public required string Exposure { get; init; }
|
||||
|
||||
[JsonPropertyName("auth")]
|
||||
public AuthInfo? Auth { get; init; }
|
||||
|
||||
[JsonPropertyName("controls")]
|
||||
public IReadOnlyList<string> Controls { get; init; } = Array.Empty<string>();
|
||||
}
|
||||
|
||||
public sealed record AuthInfo
|
||||
{
|
||||
[JsonPropertyName("mechanism")]
|
||||
public required string Mechanism { get; init; }
|
||||
|
||||
[JsonPropertyName("required_scopes")]
|
||||
public IReadOnlyList<string> RequiredScopes { get; init; } = Array.Empty<string>();
|
||||
}
|
||||
|
||||
public sealed record FreshnessInfo
|
||||
{
|
||||
[JsonPropertyName("is_stale")]
|
||||
public bool IsStale { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// When the VEX statement expires.
|
||||
/// </summary>
|
||||
[JsonPropertyName("expires_at")]
|
||||
public DateTimeOffset? ExpiresAt { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Source of the VEX statement (vendor, first-party, third-party).
|
||||
/// </summary>
|
||||
[JsonPropertyName("source")]
|
||||
public string? Source { get; init; }
|
||||
[JsonPropertyName("ttl_remaining_hours")]
|
||||
public int? TtlRemainingHours { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Score explanation with additive breakdown of risk factors.
|
||||
/// </summary>
|
||||
public sealed record ScoreExplanationDto
|
||||
public sealed record BatchEvidenceRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Kind of scoring algorithm (stellaops_risk_v1, cvss_v4, etc.).
|
||||
/// </summary>
|
||||
[JsonPropertyName("kind")]
|
||||
public string Kind { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Final computed risk score.
|
||||
/// </summary>
|
||||
[JsonPropertyName("risk_score")]
|
||||
public double RiskScore { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Individual score contributions.
|
||||
/// </summary>
|
||||
[JsonPropertyName("contributions")]
|
||||
public IReadOnlyList<ScoreContributionDto>? Contributions { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// When the score was computed.
|
||||
/// </summary>
|
||||
[JsonPropertyName("last_seen")]
|
||||
public DateTimeOffset LastSeen { get; init; }
|
||||
[JsonPropertyName("finding_ids")]
|
||||
public required IReadOnlyList<string> FindingIds { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Individual contribution to the risk score.
|
||||
/// </summary>
|
||||
public sealed record ScoreContributionDto
|
||||
public sealed record BatchEvidenceResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// Factor name (cvss_base, epss, reachability, gate_multiplier, etc.).
|
||||
/// </summary>
|
||||
[JsonPropertyName("factor")]
|
||||
public string Factor { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Weight applied to this factor (0.0 to 1.0).
|
||||
/// </summary>
|
||||
[JsonPropertyName("weight")]
|
||||
public double Weight { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Raw value before weighting.
|
||||
/// </summary>
|
||||
[JsonPropertyName("raw_value")]
|
||||
public double RawValue { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Weighted contribution to final score.
|
||||
/// </summary>
|
||||
[JsonPropertyName("contribution")]
|
||||
public double Contribution { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Human-readable explanation of this factor.
|
||||
/// </summary>
|
||||
[JsonPropertyName("explanation")]
|
||||
public string? Explanation { get; init; }
|
||||
[JsonPropertyName("findings")]
|
||||
public required IReadOnlyList<FindingEvidenceResponse> Findings { get; init; }
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace StellaOps.Scanner.WebService.Contracts;
|
||||
@@ -11,6 +12,153 @@ public sealed record SbomAcceptedResponseDto(
|
||||
[property: JsonPropertyName("componentCount")] int ComponentCount,
|
||||
[property: JsonPropertyName("digest")] string Digest);
|
||||
|
||||
/// <summary>
|
||||
/// Request payload for BYOS SBOM uploads.
|
||||
/// </summary>
|
||||
public sealed record SbomUploadRequestDto
|
||||
{
|
||||
[JsonPropertyName("artifactRef")]
|
||||
public string ArtifactRef { get; init; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("artifactDigest")]
|
||||
public string? ArtifactDigest { get; init; }
|
||||
|
||||
[JsonPropertyName("sbom")]
|
||||
public JsonElement? Sbom { get; init; }
|
||||
|
||||
[JsonPropertyName("sbomBase64")]
|
||||
public string? SbomBase64 { get; init; }
|
||||
|
||||
[JsonPropertyName("format")]
|
||||
public string? Format { get; init; }
|
||||
|
||||
[JsonPropertyName("source")]
|
||||
public SbomUploadSourceDto? Source { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provenance metadata for a BYOS SBOM upload.
|
||||
/// </summary>
|
||||
public sealed record SbomUploadSourceDto
|
||||
{
|
||||
[JsonPropertyName("tool")]
|
||||
public string? Tool { get; init; }
|
||||
|
||||
[JsonPropertyName("version")]
|
||||
public string? Version { get; init; }
|
||||
|
||||
[JsonPropertyName("ciContext")]
|
||||
public SbomUploadCiContextDto? CiContext { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// CI metadata attached to a BYOS SBOM upload.
|
||||
/// </summary>
|
||||
public sealed record SbomUploadCiContextDto
|
||||
{
|
||||
[JsonPropertyName("buildId")]
|
||||
public string? BuildId { get; init; }
|
||||
|
||||
[JsonPropertyName("repository")]
|
||||
public string? Repository { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Response payload for BYOS SBOM uploads.
|
||||
/// </summary>
|
||||
public sealed record SbomUploadResponseDto
|
||||
{
|
||||
[JsonPropertyName("sbomId")]
|
||||
public string SbomId { get; init; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("artifactRef")]
|
||||
public string ArtifactRef { get; init; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("artifactDigest")]
|
||||
public string? ArtifactDigest { get; init; }
|
||||
|
||||
[JsonPropertyName("digest")]
|
||||
public string Digest { get; init; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("format")]
|
||||
public string Format { get; init; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("formatVersion")]
|
||||
public string FormatVersion { get; init; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("validationResult")]
|
||||
public SbomValidationSummaryDto ValidationResult { get; init; } = new();
|
||||
|
||||
[JsonPropertyName("analysisJobId")]
|
||||
public string AnalysisJobId { get; init; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("uploadedAtUtc")]
|
||||
public DateTimeOffset UploadedAtUtc { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validation summary for a BYOS SBOM upload.
|
||||
/// </summary>
|
||||
public sealed record SbomValidationSummaryDto
|
||||
{
|
||||
[JsonPropertyName("valid")]
|
||||
public bool Valid { get; init; }
|
||||
|
||||
[JsonPropertyName("qualityScore")]
|
||||
public double QualityScore { get; init; }
|
||||
|
||||
[JsonPropertyName("warnings")]
|
||||
public IReadOnlyList<string> Warnings { get; init; } = Array.Empty<string>();
|
||||
|
||||
[JsonPropertyName("errors")]
|
||||
public IReadOnlyList<string> Errors { get; init; } = Array.Empty<string>();
|
||||
|
||||
[JsonPropertyName("componentCount")]
|
||||
public int ComponentCount { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Upload record returned for BYOS queries.
|
||||
/// </summary>
|
||||
public sealed record SbomUploadRecordDto
|
||||
{
|
||||
[JsonPropertyName("sbomId")]
|
||||
public string SbomId { get; init; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("artifactRef")]
|
||||
public string ArtifactRef { get; init; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("artifactDigest")]
|
||||
public string? ArtifactDigest { get; init; }
|
||||
|
||||
[JsonPropertyName("digest")]
|
||||
public string Digest { get; init; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("format")]
|
||||
public string Format { get; init; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("formatVersion")]
|
||||
public string FormatVersion { get; init; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("analysisJobId")]
|
||||
public string AnalysisJobId { get; init; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("componentCount")]
|
||||
public int ComponentCount { get; init; }
|
||||
|
||||
[JsonPropertyName("qualityScore")]
|
||||
public double QualityScore { get; init; }
|
||||
|
||||
[JsonPropertyName("warnings")]
|
||||
public IReadOnlyList<string> Warnings { get; init; } = Array.Empty<string>();
|
||||
|
||||
[JsonPropertyName("source")]
|
||||
public SbomUploadSourceDto? Source { get; init; }
|
||||
|
||||
[JsonPropertyName("createdAtUtc")]
|
||||
public DateTimeOffset CreatedAtUtc { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SBOM format types.
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user