feat(eidas): Implement eIDAS Crypto Plugin with dependency injection and signing capabilities

- Added ServiceCollectionExtensions for eIDAS crypto providers.
- Implemented EidasCryptoProvider for handling eIDAS-compliant signatures.
- Created LocalEidasProvider for local signing using PKCS#12 keystores.
- Defined SignatureLevel and SignatureFormat enums for eIDAS compliance.
- Developed TrustServiceProviderClient for remote signing via TSP.
- Added configuration support for eIDAS options in the project file.
- Implemented unit tests for SM2 compliance and crypto operations.
- Introduced dependency injection extensions for SM software and remote plugins.
This commit is contained in:
master
2025-12-23 14:06:48 +02:00
parent ef933db0d8
commit 84d97fd22c
51 changed files with 4353 additions and 747 deletions

View File

@@ -4,7 +4,7 @@ using Microsoft.Extensions.Logging;
using StellaOps.Attestor;
using StellaOps.Scanner.Core.Configuration;
using StellaOps.Scanner.Reachability;
using StellaOps.Scanner.Reachability.Models;
using StellaOps.Attestor;
using StellaOps.Signals.Storage;
namespace StellaOps.Scanner.Worker.Orchestration;
@@ -42,7 +42,7 @@ public class PoEOrchestrator
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>List of generated PoE hashes</returns>
public async Task<IReadOnlyList<PoEResult>> GeneratePoEArtifactsAsync(
ScanContext context,
PoEScanContext context,
IReadOnlyList<VulnerabilityMatch> vulnerabilities,
PoEConfiguration configuration,
CancellationToken cancellationToken = default)
@@ -129,8 +129,8 @@ public class PoEOrchestrator
/// Generate a single PoE artifact for a subgraph.
/// </summary>
private async Task<PoEResult> GenerateSinglePoEAsync(
Subgraph subgraph,
ScanContext context,
PoESubgraph subgraph,
PoEScanContext context,
PoEConfiguration configuration,
CancellationToken cancellationToken)
{
@@ -201,7 +201,7 @@ public class PoEOrchestrator
);
}
private string[] GenerateReproSteps(ScanContext context, Subgraph subgraph)
private string[] GenerateReproSteps(PoEScanContext context, PoESubgraph subgraph)
{
return new[]
{

View File

@@ -9,7 +9,7 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StellaOps.Scanner.Core.Configuration;
using StellaOps.Scanner.Core.Contracts;
using StellaOps.Scanner.Reachability.Models;
using StellaOps.Attestor;
using StellaOps.Scanner.Worker.Orchestration;
namespace StellaOps.Scanner.Worker.Processing.PoE;
@@ -138,7 +138,7 @@ public sealed class PoEGenerationStageExecutor : IScanStageExecutor
}
}
private ScanContext BuildScanContext(ScanJobContext context)
private PoEScanContext BuildScanContext(ScanJobContext context)
{
// Extract scan metadata from job context
var scanId = context.ScanId;
@@ -169,7 +169,7 @@ public sealed class PoEGenerationStageExecutor : IScanStageExecutor
// Get configuration path
var configPath = "etc/scanner.yaml"; // Default
return new ScanContext(
return new PoEScanContext(
ScanId: scanId,
GraphHash: graphHash ?? "blake3:unknown",
BuildId: buildId ?? "gnu-build-id:unknown",

View File

@@ -33,5 +33,7 @@
<ProjectReference Include="../StellaOps.Scanner.Analyzers.Native/StellaOps.Scanner.Analyzers.Native.csproj" />
<ProjectReference Include="../../Unknowns/__Libraries/StellaOps.Unknowns.Core/StellaOps.Unknowns.Core.csproj" />
<ProjectReference Include="../../BinaryIndex/__Libraries/StellaOps.BinaryIndex.Core/StellaOps.BinaryIndex.Core.csproj" />
<ProjectReference Include="../../Attestor/StellaOps.Attestor/StellaOps.Attestor.Core/StellaOps.Attestor.Core.csproj" />
<ProjectReference Include="../../Signals/StellaOps.Signals/StellaOps.Signals.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,170 @@
namespace StellaOps.Scanner.ProofIntegration;
using Microsoft.Extensions.Logging;
using StellaOps.Attestor.ProofChain.Generators;
using StellaOps.Attestor.ProofChain.Models;
using StellaOps.Attestor.ProofChain.Statements;
using StellaOps.Concelier.ProofService;
/// <summary>
/// Generates VEX verdicts with cryptographic proof references.
/// Integrates Scanner vulnerability detection with proof-driven backport detection.
/// </summary>
public sealed class ProofAwareVexGenerator
{
private readonly ILogger<ProofAwareVexGenerator> _logger;
private readonly BackportProofService _proofService;
public ProofAwareVexGenerator(
ILogger<ProofAwareVexGenerator> logger,
BackportProofService proofService)
{
_logger = logger;
_proofService = proofService;
}
/// <summary>
/// Generate VEX verdict with proof for a vulnerability finding.
/// </summary>
/// <param name="finding">Vulnerability finding from scanner</param>
/// <param name="sbomEntryId">SBOM entry ID for the component</param>
/// <param name="policyVersion">Policy version used for decisioning</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>VEX verdict statement with embedded proof reference</returns>
public async Task<VexVerdictWithProof> GenerateVexWithProofAsync(
VulnerabilityFinding finding,
string sbomEntryId,
string policyVersion,
CancellationToken cancellationToken = default)
{
_logger.LogInformation(
"Generating proof-carrying VEX verdict for {CveId} in {Package}",
finding.CveId, finding.PackagePurl);
// Step 1: Generate cryptographic proof using four-tier detection
var proof = await _proofService.GenerateProofAsync(
finding.CveId,
finding.PackagePurl,
cancellationToken);
if (proof == null)
{
_logger.LogWarning(
"No proof generated for {CveId} in {Package}, using fallback verdict",
finding.CveId, finding.PackagePurl);
// Fallback: Generate VEX without proof
return GenerateFallbackVex(finding, sbomEntryId, policyVersion);
}
_logger.LogInformation(
"Generated proof {ProofId} with confidence {Confidence:P0} for {CveId}",
proof.ProofId, proof.Confidence, finding.CveId);
// Step 2: Generate VEX verdict with proof reference
var reasoningId = GenerateReasoningId(finding, proof);
var (statement, proofPayload) = VexProofIntegrator.GenerateWithProofMetadata(
proof,
sbomEntryId,
policyVersion,
reasoningId);
return new VexVerdictWithProof
{
Statement = statement,
ProofPayload = proofPayload,
Proof = proof,
GeneratedAt = DateTimeOffset.UtcNow
};
}
/// <summary>
/// Generate VEX verdicts for multiple findings in batch.
/// </summary>
public async Task<IReadOnlyList<VexVerdictWithProof>> GenerateBatchVexWithProofAsync(
IEnumerable<VulnerabilityFinding> findings,
string policyVersion,
Func<VulnerabilityFinding, string> sbomEntryIdResolver,
CancellationToken cancellationToken = default)
{
var tasks = findings.Select(finding =>
{
var sbomEntryId = sbomEntryIdResolver(finding);
return GenerateVexWithProofAsync(finding, sbomEntryId, policyVersion, cancellationToken);
});
var results = await Task.WhenAll(tasks);
return results.ToList();
}
/// <summary>
/// Retrieve existing proof for a CVE + package combination.
/// Useful for audit replay and verification.
/// </summary>
public async Task<ProofBlob?> RetrieveProofAsync(
string cveId,
string packagePurl,
CancellationToken cancellationToken = default)
{
return await _proofService.GenerateProofAsync(cveId, packagePurl, cancellationToken);
}
private VexVerdictWithProof GenerateFallbackVex(
VulnerabilityFinding finding,
string sbomEntryId,
string policyVersion)
{
// Generate basic VEX without proof
// This is used when no evidence is available (e.g., newly disclosed CVE)
var unknownProof = BackportProofGenerator.Unknown(
finding.CveId,
finding.PackagePurl,
"no_evidence_available",
Array.Empty<ProofEvidence>());
var reasoningId = $"reasoning:{finding.CveId}:{finding.PackagePurl}";
var (statement, proofPayload) = VexProofIntegrator.GenerateWithProofMetadata(
unknownProof,
sbomEntryId,
policyVersion,
reasoningId);
return new VexVerdictWithProof
{
Statement = statement,
ProofPayload = proofPayload,
Proof = unknownProof,
GeneratedAt = DateTimeOffset.UtcNow
};
}
private string GenerateReasoningId(VulnerabilityFinding finding, ProofBlob proof)
{
// Reasoning ID format: reasoning:{cve}:{method}:{snapshot}
return $"reasoning:{finding.CveId}:{proof.Method}:{proof.SnapshotId}";
}
}
/// <summary>
/// Vulnerability finding from scanner.
/// </summary>
public sealed record VulnerabilityFinding
{
public required string CveId { get; init; }
public required string PackagePurl { get; init; }
public required string PackageName { get; init; }
public required string PackageVersion { get; init; }
public required string Severity { get; init; }
}
/// <summary>
/// VEX verdict with associated proof.
/// </summary>
public sealed record VexVerdictWithProof
{
public required VexVerdictStatement Statement { get; init; }
public required VexVerdictProofPayload ProofPayload { get; init; }
public required ProofBlob Proof { get; init; }
public required DateTimeOffset GeneratedAt { get; init; }
}

View File

@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Concelier\__Libraries\StellaOps.Concelier.ProofService\StellaOps.Concelier.ProofService.csproj" />
<ProjectReference Include="..\..\..\Attestor\__Libraries\StellaOps.Attestor.ProofChain\StellaOps.Attestor.ProofChain.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,6 +1,6 @@
// Copyright (c) StellaOps. Licensed under AGPL-3.0-or-later.
using StellaOps.Scanner.Reachability.Models;
using StellaOps.Attestor;
namespace StellaOps.Scanner.Reachability;
@@ -22,7 +22,7 @@ public interface IReachabilityResolver
/// <exception cref="SubgraphExtractionException">
/// Thrown when resolution fails due to missing data, invalid graph, or configuration errors.
/// </exception>
Task<Subgraph?> ResolveAsync(
Task<PoESubgraph?> ResolveAsync(
ReachabilityResolutionRequest request,
CancellationToken cancellationToken = default
);
@@ -36,7 +36,7 @@ public interface IReachabilityResolver
/// <returns>
/// Dictionary mapping vuln_id to resolved subgraph (or null if unreachable).
/// </returns>
Task<IReadOnlyDictionary<string, Subgraph?>> ResolveBatchAsync(
Task<IReadOnlyDictionary<string, PoESubgraph?>> ResolveBatchAsync(
IReadOnlyList<ReachabilityResolutionRequest> requests,
CancellationToken cancellationToken = default
);

View File

@@ -1,239 +0,0 @@
// Copyright (c) StellaOps. Licensed under AGPL-3.0-or-later.
using System.Text.Json.Serialization;
namespace StellaOps.Scanner.Reachability.Models;
/// <summary>
/// Represents a function identifier in a subgraph with module, symbol, address, and optional source location.
/// </summary>
/// <param name="ModuleHash">SHA-256 hash of the module/library containing this function</param>
/// <param name="Symbol">Human-readable symbol name (e.g., "main()", "Foo.bar()")</param>
/// <param name="Addr">Hexadecimal address (e.g., "0x401000")</param>
/// <param name="File">Optional source file path</param>
/// <param name="Line">Optional source line number</param>
[method: JsonConstructor]
public record FunctionId(
[property: JsonPropertyName("moduleHash")] string ModuleHash,
[property: JsonPropertyName("symbol")] string Symbol,
[property: JsonPropertyName("addr")] string Addr,
[property: JsonPropertyName("file")] string? File = null,
[property: JsonPropertyName("line")] int? Line = null
)
{
/// <summary>
/// Gets the canonical identifier for this function (symbol_id or code_id).
/// </summary>
[JsonIgnore]
public string Id => Symbol;
}
/// <summary>
/// Represents a call edge between two functions with optional guard predicates.
/// </summary>
/// <param name="Caller">Calling function identifier</param>
/// <param name="Callee">Called function identifier</param>
/// <param name="Guards">Guard predicates controlling this edge (e.g., ["feature:dark-mode", "platform:linux"])</param>
/// <param name="Confidence">Confidence score for this edge [0.0, 1.0]</param>
[method: JsonConstructor]
public record Edge(
[property: JsonPropertyName("from")] string Caller,
[property: JsonPropertyName("to")] string Callee,
[property: JsonPropertyName("guards")] string[] Guards,
[property: JsonPropertyName("confidence")] double Confidence = 1.0
);
/// <summary>
/// Represents a minimal subgraph showing call paths from entry points to vulnerable sinks.
/// </summary>
/// <param name="BuildId">Deterministic build identifier (e.g., "gnu-build-id:5f0c7c3c...")</param>
/// <param name="ComponentRef">PURL package reference (e.g., "pkg:maven/log4j@2.14.1")</param>
/// <param name="VulnId">CVE identifier (e.g., "CVE-2021-44228")</param>
/// <param name="Nodes">Function nodes in the subgraph</param>
/// <param name="Edges">Call edges in the subgraph</param>
/// <param name="EntryRefs">Entry point node IDs (where execution begins)</param>
/// <param name="SinkRefs">Vulnerable sink node IDs (CVE-affected functions)</param>
/// <param name="PolicyDigest">SHA-256 hash of policy version used during extraction</param>
/// <param name="ToolchainDigest">SHA-256 hash of scanner version/toolchain</param>
[method: JsonConstructor]
public record Subgraph(
[property: JsonPropertyName("buildId")] string BuildId,
[property: JsonPropertyName("componentRef")] string ComponentRef,
[property: JsonPropertyName("vulnId")] string VulnId,
[property: JsonPropertyName("nodes")] IReadOnlyList<FunctionId> Nodes,
[property: JsonPropertyName("edges")] IReadOnlyList<Edge> Edges,
[property: JsonPropertyName("entryRefs")] string[] EntryRefs,
[property: JsonPropertyName("sinkRefs")] string[] SinkRefs,
[property: JsonPropertyName("policyDigest")] string PolicyDigest,
[property: JsonPropertyName("toolchainDigest")] string ToolchainDigest
);
/// <summary>
/// Metadata for Proof of Exposure artifact generation.
/// </summary>
/// <param name="GeneratedAt">Timestamp when PoE was generated</param>
/// <param name="AnalyzerName">Analyzer identifier (e.g., "stellaops-scanner")</param>
/// <param name="AnalyzerVersion">Semantic version (e.g., "1.2.0")</param>
/// <param name="ToolchainDigest">SHA-256 hash of analyzer binary/container</param>
/// <param name="PolicyDigest">SHA-256 hash of policy document</param>
/// <param name="ReproSteps">Minimal steps to reproduce this PoE</param>
[method: JsonConstructor]
public record ProofMetadata(
[property: JsonPropertyName("generatedAt")] DateTime GeneratedAt,
[property: JsonPropertyName("analyzer")] AnalyzerInfo Analyzer,
[property: JsonPropertyName("policy")] PolicyInfo Policy,
[property: JsonPropertyName("reproSteps")] string[] ReproSteps
);
/// <summary>
/// Analyzer information for PoE provenance.
/// </summary>
[method: JsonConstructor]
public record AnalyzerInfo(
[property: JsonPropertyName("name")] string Name,
[property: JsonPropertyName("version")] string Version,
[property: JsonPropertyName("toolchainDigest")] string ToolchainDigest
);
/// <summary>
/// Policy information for PoE provenance.
/// </summary>
[method: JsonConstructor]
public record PolicyInfo(
[property: JsonPropertyName("policyId")] string PolicyId,
[property: JsonPropertyName("policyDigest")] string PolicyDigest,
[property: JsonPropertyName("evaluatedAt")] DateTime EvaluatedAt
);
/// <summary>
/// Complete Proof of Exposure artifact.
/// </summary>
/// <param name="Schema">Schema version (e.g., "stellaops.dev/poe@v1")</param>
/// <param name="Subgraph">Minimal subgraph with call paths</param>
/// <param name="Metadata">Provenance and reproduction metadata</param>
/// <param name="GraphHash">Parent richgraph-v1 BLAKE3 hash</param>
/// <param name="SbomRef">Optional reference to SBOM artifact</param>
/// <param name="VexClaimUri">Optional reference to VEX claim</param>
[method: JsonConstructor]
public record ProofOfExposure(
[property: JsonPropertyName("@type")] string Type,
[property: JsonPropertyName("schema")] string Schema,
[property: JsonPropertyName("subject")] SubjectInfo Subject,
[property: JsonPropertyName("subgraph")] SubgraphData SubgraphData,
[property: JsonPropertyName("metadata")] ProofMetadata Metadata,
[property: JsonPropertyName("evidence")] EvidenceInfo Evidence
);
/// <summary>
/// Subject information identifying what this PoE is about.
/// </summary>
[method: JsonConstructor]
public record SubjectInfo(
[property: JsonPropertyName("buildId")] string BuildId,
[property: JsonPropertyName("componentRef")] string ComponentRef,
[property: JsonPropertyName("vulnId")] string VulnId,
[property: JsonPropertyName("imageDigest")] string? ImageDigest = null
);
/// <summary>
/// Subgraph data structure for PoE JSON.
/// </summary>
[method: JsonConstructor]
public record SubgraphData(
[property: JsonPropertyName("nodes")] NodeData[] Nodes,
[property: JsonPropertyName("edges")] EdgeData[] Edges,
[property: JsonPropertyName("entryRefs")] string[] EntryRefs,
[property: JsonPropertyName("sinkRefs")] string[] SinkRefs
);
/// <summary>
/// Node data for PoE JSON serialization.
/// </summary>
[method: JsonConstructor]
public record NodeData(
[property: JsonPropertyName("id")] string Id,
[property: JsonPropertyName("moduleHash")] string ModuleHash,
[property: JsonPropertyName("symbol")] string Symbol,
[property: JsonPropertyName("addr")] string Addr,
[property: JsonPropertyName("file")] string? File = null,
[property: JsonPropertyName("line")] int? Line = null
);
/// <summary>
/// Edge data for PoE JSON serialization.
/// </summary>
[method: JsonConstructor]
public record EdgeData(
[property: JsonPropertyName("from")] string From,
[property: JsonPropertyName("to")] string To,
[property: JsonPropertyName("guards")] string[]? Guards = null,
[property: JsonPropertyName("confidence")] double Confidence = 1.0
);
/// <summary>
/// Evidence links to related artifacts.
/// </summary>
[method: JsonConstructor]
public record EvidenceInfo(
[property: JsonPropertyName("graphHash")] string GraphHash,
[property: JsonPropertyName("sbomRef")] string? SbomRef = null,
[property: JsonPropertyName("vexClaimUri")] string? VexClaimUri = null,
[property: JsonPropertyName("runtimeFactsUri")] string? RuntimeFactsUri = null
);
/// <summary>
/// Represents a matched vulnerability for PoE generation.
/// </summary>
/// <param name="VulnId">Vulnerability identifier (CVE, GHSA, etc.)</param>
/// <param name="ComponentRef">Component package URL (PURL)</param>
/// <param name="IsReachable">Whether the vulnerability is reachable from entry points</param>
/// <param name="Severity">Vulnerability severity (Critical, High, Medium, Low, Info)</param>
[method: JsonConstructor]
public record VulnerabilityMatch(
[property: JsonPropertyName("vulnId")] string VulnId,
[property: JsonPropertyName("componentRef")] string ComponentRef,
[property: JsonPropertyName("isReachable")] bool IsReachable,
[property: JsonPropertyName("severity")] string Severity
);
/// <summary>
/// Scan context for PoE generation.
/// </summary>
/// <param name="ScanId">Unique scan identifier</param>
/// <param name="GraphHash">BLAKE3 hash of the reachability graph</param>
/// <param name="BuildId">GNU build ID or equivalent</param>
/// <param name="ImageDigest">Container image digest</param>
/// <param name="PolicyId">Policy identifier</param>
/// <param name="PolicyDigest">Policy content digest</param>
/// <param name="ScannerVersion">Scanner version</param>
/// <param name="ConfigPath">Scanner configuration path</param>
[method: JsonConstructor]
public record ScanContext(
[property: JsonPropertyName("scanId")] string ScanId,
[property: JsonPropertyName("graphHash")] string GraphHash,
[property: JsonPropertyName("buildId")] string BuildId,
[property: JsonPropertyName("imageDigest")] string ImageDigest,
[property: JsonPropertyName("policyId")] string PolicyId,
[property: JsonPropertyName("policyDigest")] string PolicyDigest,
[property: JsonPropertyName("scannerVersion")] string ScannerVersion,
[property: JsonPropertyName("configPath")] string ConfigPath
);
/// <summary>
/// Result from PoE generation for a single vulnerability.
/// </summary>
/// <param name="VulnId">Vulnerability identifier</param>
/// <param name="ComponentRef">Component package URL</param>
/// <param name="PoEHash">Content hash of the PoE artifact</param>
/// <param name="PoERef">CAS reference to the PoE artifact</param>
/// <param name="IsSigned">Whether the PoE is cryptographically signed</param>
/// <param name="PathCount">Number of paths in the subgraph</param>
[method: JsonConstructor]
public record PoEResult(
[property: JsonPropertyName("vulnId")] string VulnId,
[property: JsonPropertyName("componentRef")] string ComponentRef,
[property: JsonPropertyName("poeHash")] string PoEHash,
[property: JsonPropertyName("poeRef")] string? PoERef,
[property: JsonPropertyName("isSigned")] bool IsSigned,
[property: JsonPropertyName("pathCount")] int? PathCount = null
);

View File

@@ -2,7 +2,7 @@
using System.Collections.Concurrent;
using Microsoft.Extensions.Logging;
using StellaOps.Scanner.Reachability.Models;
using StellaOps.Attestor;
namespace StellaOps.Scanner.Reachability;
@@ -29,7 +29,7 @@ public class SubgraphExtractor : IReachabilityResolver
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public async Task<Subgraph?> ResolveAsync(
public async Task<PoESubgraph?> ResolveAsync(
ReachabilityResolutionRequest request,
CancellationToken cancellationToken = default)
{
@@ -129,14 +129,14 @@ public class SubgraphExtractor : IReachabilityResolver
}
}
public async Task<IReadOnlyDictionary<string, Subgraph?>> ResolveBatchAsync(
public async Task<IReadOnlyDictionary<string, PoESubgraph?>> ResolveBatchAsync(
IReadOnlyList<ReachabilityResolutionRequest> requests,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(requests);
if (requests.Count == 0)
return new Dictionary<string, Subgraph?>();
return new Dictionary<string, PoESubgraph?>();
// Verify all requests are for the same graph
var graphHash = requests[0].GraphHash;
@@ -151,7 +151,7 @@ public class SubgraphExtractor : IReachabilityResolver
"Batch resolving {Count} subgraphs for graph {GraphHash}",
requests.Count, graphHash);
var results = new ConcurrentDictionary<string, Subgraph?>();
var results = new ConcurrentDictionary<string, PoESubgraph?>();
// Process requests in parallel (limit concurrency to avoid memory pressure)
var parallelOptions = new ParallelOptions
@@ -297,7 +297,7 @@ public class SubgraphExtractor : IReachabilityResolver
/// <summary>
/// Build subgraph from selected paths.
/// </summary>
private Subgraph BuildSubgraphFromPaths(
private PoESubgraph BuildSubgraphFromPaths(
List<CallPath> paths,
string buildId,
string componentRef,
@@ -343,7 +343,7 @@ public class SubgraphExtractor : IReachabilityResolver
Line: null
)).ToList();
return new Subgraph(
return new PoESubgraph(
BuildId: buildId,
ComponentRef: componentRef,
VulnId: vulnId,
@@ -359,7 +359,7 @@ public class SubgraphExtractor : IReachabilityResolver
/// <summary>
/// Normalize subgraph for deterministic ordering.
/// </summary>
private Subgraph NormalizeSubgraph(Subgraph subgraph)
private PoESubgraph NormalizeSubgraph(PoESubgraph subgraph)
{
// Sort nodes by symbol
var sortedNodes = subgraph.Nodes
@@ -473,7 +473,7 @@ public class SubgraphExtractor : IReachabilityResolver
/// <summary>
/// Represents a call path from entry to sink.
/// </summary>
internal record CallPath(
public record CallPath(
string PathId,
List<string> Nodes,
List<Edge> Edges,

View File

@@ -12,7 +12,7 @@ using StellaOps.Attestor;
using StellaOps.Scanner.Core.Configuration;
using StellaOps.Scanner.Core.Contracts;
using StellaOps.Scanner.Reachability;
using StellaOps.Scanner.Reachability.Models;
using StellaOps.Attestor;
using StellaOps.Scanner.Worker.Orchestration;
using StellaOps.Scanner.Worker.Processing;
using StellaOps.Scanner.Worker.Processing.PoE;
@@ -115,7 +115,7 @@ public class PoEGenerationStageExecutorTests : IDisposable
_resolverMock
.Setup(x => x.ResolveBatchAsync(It.IsAny<IReadOnlyList<ReachabilityResolutionRequest>>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new Dictionary<string, Subgraph?> { ["CVE-2021-44228"] = subgraph });
.ReturnsAsync(new Dictionary<string, PoESubgraph?> { ["CVE-2021-44228"] = subgraph });
_emitterMock
.Setup(x => x.EmitPoEAsync(It.IsAny<Subgraph>(), It.IsAny<ProofMetadata>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<CancellationToken>()))
@@ -169,7 +169,7 @@ public class PoEGenerationStageExecutorTests : IDisposable
_resolverMock
.Setup(x => x.ResolveBatchAsync(It.Is<IReadOnlyList<ReachabilityResolutionRequest>>(r => r.Count == 1), It.IsAny<CancellationToken>()))
.ReturnsAsync(new Dictionary<string, Subgraph?> { ["CVE-2021-44228"] = subgraph });
.ReturnsAsync(new Dictionary<string, PoESubgraph?> { ["CVE-2021-44228"] = subgraph });
_emitterMock
.Setup(x => x.EmitPoEAsync(It.IsAny<Subgraph>(), It.IsAny<ProofMetadata>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<CancellationToken>()))
@@ -219,7 +219,7 @@ public class PoEGenerationStageExecutorTests : IDisposable
_resolverMock
.Setup(x => x.ResolveBatchAsync(It.IsAny<IReadOnlyList<ReachabilityResolutionRequest>>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new Dictionary<string, Subgraph?>
.ReturnsAsync(new Dictionary<string, PoESubgraph?>
{
["CVE-2021-44228"] = subgraph1,
["CVE-2023-12345"] = subgraph2
@@ -270,7 +270,7 @@ public class PoEGenerationStageExecutorTests : IDisposable
_resolverMock
.Setup(x => x.ResolveBatchAsync(It.IsAny<IReadOnlyList<ReachabilityResolutionRequest>>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new Dictionary<string, Subgraph?> { ["CVE-2021-44228"] = subgraph });
.ReturnsAsync(new Dictionary<string, PoESubgraph?> { ["CVE-2021-44228"] = subgraph });
_emitterMock
.Setup(x => x.EmitPoEAsync(It.IsAny<Subgraph>(), It.IsAny<ProofMetadata>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<CancellationToken>()))
@@ -306,9 +306,9 @@ public class PoEGenerationStageExecutorTests : IDisposable
);
}
private Subgraph CreateTestSubgraph(string vulnId, string componentRef)
private PoESubgraph CreateTestSubgraph(string vulnId, string componentRef)
{
return new Subgraph(
return new PoESubgraph(
BuildId: "gnu-build-id:test",
ComponentRef: componentRef,
VulnId: vulnId,