Refactor SurfaceCacheValidator to simplify oldest entry calculation
Add global using for Xunit in test project Enhance ImportValidatorTests with async validation and quarantine checks Implement FileSystemQuarantineServiceTests for quarantine functionality Add integration tests for ImportValidator to check monotonicity Create BundleVersionTests to validate version parsing and comparison logic Implement VersionMonotonicityCheckerTests for monotonicity checks and activation logic
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace StellaOps.Scanner.WebService.Contracts;
|
||||
|
||||
/// <summary>
|
||||
/// Call graph submission request (CallGraphV1 schema).
|
||||
/// </summary>
|
||||
public sealed record CallGraphV1Dto(
|
||||
[property: JsonPropertyName("schema")] string Schema,
|
||||
[property: JsonPropertyName("scanKey")] string ScanKey,
|
||||
[property: JsonPropertyName("language")] string Language,
|
||||
[property: JsonPropertyName("nodes")] IReadOnlyList<CallGraphNodeDto> Nodes,
|
||||
[property: JsonPropertyName("edges")] IReadOnlyList<CallGraphEdgeDto> Edges,
|
||||
[property: JsonPropertyName("artifacts")] IReadOnlyList<CallGraphArtifactDto>? Artifacts = null,
|
||||
[property: JsonPropertyName("entrypoints")] IReadOnlyList<CallGraphEntrypointDto>? Entrypoints = null);
|
||||
|
||||
/// <summary>
|
||||
/// Artifact in a call graph.
|
||||
/// </summary>
|
||||
public sealed record CallGraphArtifactDto(
|
||||
[property: JsonPropertyName("artifactKey")] string ArtifactKey,
|
||||
[property: JsonPropertyName("kind")] string? Kind = null,
|
||||
[property: JsonPropertyName("sha256")] string? Sha256 = null);
|
||||
|
||||
/// <summary>
|
||||
/// Node in a call graph.
|
||||
/// </summary>
|
||||
public sealed record CallGraphNodeDto(
|
||||
[property: JsonPropertyName("nodeId")] string NodeId,
|
||||
[property: JsonPropertyName("symbolKey")] string SymbolKey,
|
||||
[property: JsonPropertyName("artifactKey")] string? ArtifactKey = null,
|
||||
[property: JsonPropertyName("visibility")] string? Visibility = null,
|
||||
[property: JsonPropertyName("isEntrypointCandidate")] bool IsEntrypointCandidate = false);
|
||||
|
||||
/// <summary>
|
||||
/// Edge in a call graph.
|
||||
/// </summary>
|
||||
public sealed record CallGraphEdgeDto(
|
||||
[property: JsonPropertyName("from")] string From,
|
||||
[property: JsonPropertyName("to")] string To,
|
||||
[property: JsonPropertyName("kind")] string Kind = "static",
|
||||
[property: JsonPropertyName("reason")] string? Reason = null,
|
||||
[property: JsonPropertyName("weight")] double Weight = 1.0);
|
||||
|
||||
/// <summary>
|
||||
/// Entrypoint in a call graph.
|
||||
/// </summary>
|
||||
public sealed record CallGraphEntrypointDto(
|
||||
[property: JsonPropertyName("nodeId")] string NodeId,
|
||||
[property: JsonPropertyName("kind")] string Kind,
|
||||
[property: JsonPropertyName("route")] string? Route = null,
|
||||
[property: JsonPropertyName("framework")] string? Framework = null);
|
||||
|
||||
/// <summary>
|
||||
/// Response when call graph is accepted.
|
||||
/// </summary>
|
||||
public sealed record CallGraphAcceptedResponseDto(
|
||||
[property: JsonPropertyName("callgraphId")] string CallgraphId,
|
||||
[property: JsonPropertyName("nodeCount")] int NodeCount,
|
||||
[property: JsonPropertyName("edgeCount")] int EdgeCount,
|
||||
[property: JsonPropertyName("digest")] string Digest);
|
||||
|
||||
/// <summary>
|
||||
/// Existing call graph reference (for duplicate detection).
|
||||
/// </summary>
|
||||
public sealed record ExistingCallGraphDto(
|
||||
[property: JsonPropertyName("id")] string Id,
|
||||
[property: JsonPropertyName("digest")] string Digest,
|
||||
[property: JsonPropertyName("createdAt")] DateTimeOffset CreatedAt);
|
||||
@@ -129,10 +129,22 @@ public sealed record DsseEnvelopeDto
|
||||
public sealed record DsseSignatureDto
|
||||
{
|
||||
[JsonPropertyName("keyid")]
|
||||
[JsonPropertyOrder(0)]
|
||||
public string KeyId { get; init; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("sig")]
|
||||
[JsonPropertyOrder(1)]
|
||||
public string Sig { get; init; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("algorithm")]
|
||||
[JsonPropertyOrder(2)]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string? Algorithm { get; init; }
|
||||
|
||||
[JsonPropertyName("signature")]
|
||||
[JsonPropertyOrder(3)]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string? Signature { get; init; }
|
||||
}
|
||||
|
||||
public sealed record ProofSpineVerificationDto
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace StellaOps.Scanner.WebService.Contracts;
|
||||
|
||||
/// <summary>
|
||||
/// Request to trigger reachability computation.
|
||||
/// </summary>
|
||||
public sealed record ComputeReachabilityRequestDto(
|
||||
[property: JsonPropertyName("forceRecompute")] bool ForceRecompute = false,
|
||||
[property: JsonPropertyName("entrypoints")] IReadOnlyList<string>? Entrypoints = null,
|
||||
[property: JsonPropertyName("targets")] IReadOnlyList<string>? Targets = null);
|
||||
|
||||
/// <summary>
|
||||
/// Response from triggering reachability computation.
|
||||
/// </summary>
|
||||
public sealed record ComputeReachabilityResponseDto(
|
||||
[property: JsonPropertyName("jobId")] string JobId,
|
||||
[property: JsonPropertyName("status")] string Status,
|
||||
[property: JsonPropertyName("estimatedDuration")] string? EstimatedDuration = null);
|
||||
|
||||
/// <summary>
|
||||
/// Component reachability status.
|
||||
/// </summary>
|
||||
public sealed record ComponentReachabilityDto(
|
||||
[property: JsonPropertyName("purl")] string Purl,
|
||||
[property: JsonPropertyName("status")] string Status,
|
||||
[property: JsonPropertyName("confidence")] double Confidence,
|
||||
[property: JsonPropertyName("latticeState")] string? LatticeState = null,
|
||||
[property: JsonPropertyName("why")] IReadOnlyList<string>? Why = null);
|
||||
|
||||
/// <summary>
|
||||
/// List of component reachability results.
|
||||
/// </summary>
|
||||
public sealed record ComponentReachabilityListDto(
|
||||
[property: JsonPropertyName("items")] IReadOnlyList<ComponentReachabilityDto> Items,
|
||||
[property: JsonPropertyName("total")] int Total);
|
||||
|
||||
/// <summary>
|
||||
/// Vulnerability finding with reachability.
|
||||
/// </summary>
|
||||
public sealed record ReachabilityFindingDto(
|
||||
[property: JsonPropertyName("cveId")] string CveId,
|
||||
[property: JsonPropertyName("purl")] string Purl,
|
||||
[property: JsonPropertyName("status")] string Status,
|
||||
[property: JsonPropertyName("confidence")] double Confidence,
|
||||
[property: JsonPropertyName("latticeState")] string? LatticeState = null,
|
||||
[property: JsonPropertyName("severity")] string? Severity = null,
|
||||
[property: JsonPropertyName("affectedVersions")] string? AffectedVersions = null);
|
||||
|
||||
/// <summary>
|
||||
/// List of reachability findings.
|
||||
/// </summary>
|
||||
public sealed record ReachabilityFindingListDto(
|
||||
[property: JsonPropertyName("items")] IReadOnlyList<ReachabilityFindingDto> Items,
|
||||
[property: JsonPropertyName("total")] int Total);
|
||||
|
||||
/// <summary>
|
||||
/// Explanation reason with code and impact.
|
||||
/// </summary>
|
||||
public sealed record ExplanationReasonDto(
|
||||
[property: JsonPropertyName("code")] string Code,
|
||||
[property: JsonPropertyName("description")] string Description,
|
||||
[property: JsonPropertyName("impact")] double? Impact = null);
|
||||
|
||||
/// <summary>
|
||||
/// Static analysis evidence.
|
||||
/// </summary>
|
||||
public sealed record StaticAnalysisEvidenceDto(
|
||||
[property: JsonPropertyName("callgraphDigest")] string? CallgraphDigest = null,
|
||||
[property: JsonPropertyName("pathLength")] int? PathLength = null,
|
||||
[property: JsonPropertyName("edgeTypes")] IReadOnlyList<string>? EdgeTypes = null);
|
||||
|
||||
/// <summary>
|
||||
/// Runtime evidence.
|
||||
/// </summary>
|
||||
public sealed record RuntimeEvidenceDto(
|
||||
[property: JsonPropertyName("observed")] bool Observed,
|
||||
[property: JsonPropertyName("hitCount")] int HitCount = 0,
|
||||
[property: JsonPropertyName("lastObserved")] DateTimeOffset? LastObserved = null);
|
||||
|
||||
/// <summary>
|
||||
/// Policy evaluation result.
|
||||
/// </summary>
|
||||
public sealed record PolicyEvaluationEvidenceDto(
|
||||
[property: JsonPropertyName("policyDigest")] string? PolicyDigest = null,
|
||||
[property: JsonPropertyName("verdict")] string? Verdict = null,
|
||||
[property: JsonPropertyName("verdictReason")] string? VerdictReason = null);
|
||||
|
||||
/// <summary>
|
||||
/// Evidence chain for explanation.
|
||||
/// </summary>
|
||||
public sealed record EvidenceChainDto(
|
||||
[property: JsonPropertyName("staticAnalysis")] StaticAnalysisEvidenceDto? StaticAnalysis = null,
|
||||
[property: JsonPropertyName("runtimeEvidence")] RuntimeEvidenceDto? RuntimeEvidence = null,
|
||||
[property: JsonPropertyName("policyEvaluation")] PolicyEvaluationEvidenceDto? PolicyEvaluation = null);
|
||||
|
||||
/// <summary>
|
||||
/// Full reachability explanation.
|
||||
/// </summary>
|
||||
public sealed record ReachabilityExplanationDto(
|
||||
[property: JsonPropertyName("cveId")] string CveId,
|
||||
[property: JsonPropertyName("purl")] string Purl,
|
||||
[property: JsonPropertyName("status")] string Status,
|
||||
[property: JsonPropertyName("confidence")] double Confidence,
|
||||
[property: JsonPropertyName("latticeState")] string? LatticeState = null,
|
||||
[property: JsonPropertyName("pathWitness")] IReadOnlyList<string>? PathWitness = null,
|
||||
[property: JsonPropertyName("why")] IReadOnlyList<ExplanationReasonDto>? Why = null,
|
||||
[property: JsonPropertyName("evidence")] EvidenceChainDto? Evidence = null,
|
||||
[property: JsonPropertyName("spineId")] string? SpineId = null);
|
||||
@@ -102,30 +102,3 @@ public sealed record ReportSummaryDto
|
||||
[JsonPropertyOrder(4)]
|
||||
public int Quieted { get; init; }
|
||||
}
|
||||
|
||||
public sealed record DsseEnvelopeDto
|
||||
{
|
||||
[JsonPropertyName("payloadType")]
|
||||
[JsonPropertyOrder(0)]
|
||||
public string PayloadType { get; init; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("payload")]
|
||||
[JsonPropertyOrder(1)]
|
||||
public string Payload { get; init; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("signatures")]
|
||||
[JsonPropertyOrder(2)]
|
||||
public IReadOnlyList<DsseSignatureDto> Signatures { get; init; } = Array.Empty<DsseSignatureDto>();
|
||||
}
|
||||
|
||||
public sealed record DsseSignatureDto
|
||||
{
|
||||
[JsonPropertyName("keyId")]
|
||||
public string KeyId { get; init; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("algorithm")]
|
||||
public string Algorithm { get; init; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("signature")]
|
||||
public string Signature { get; init; } = string.Empty;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace StellaOps.Scanner.WebService.Contracts;
|
||||
|
||||
/// <summary>
|
||||
/// Response when SBOM is accepted.
|
||||
/// </summary>
|
||||
public sealed record SbomAcceptedResponseDto(
|
||||
[property: JsonPropertyName("sbomId")] string SbomId,
|
||||
[property: JsonPropertyName("format")] string Format,
|
||||
[property: JsonPropertyName("componentCount")] int ComponentCount,
|
||||
[property: JsonPropertyName("digest")] string Digest);
|
||||
|
||||
/// <summary>
|
||||
/// SBOM format types.
|
||||
/// </summary>
|
||||
public static class SbomFormats
|
||||
{
|
||||
public const string CycloneDx = "cyclonedx";
|
||||
public const string Spdx = "spdx";
|
||||
}
|
||||
Reference in New Issue
Block a user