part #2
This commit is contained in:
@@ -477,3 +477,22 @@ public interface IEvidenceStorageService
|
||||
ReachabilityEvidenceJob job,
|
||||
CancellationToken ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// No-op call graph snapshot provider for environments without call graph infrastructure.
|
||||
/// </summary>
|
||||
internal sealed class NullCallGraphSnapshotProvider : ICallGraphSnapshotProvider
|
||||
{
|
||||
public Task<CallGraphSnapshot?> GetOrComputeAsync(string imageDigest, bool forceRecompute, CancellationToken ct)
|
||||
=> Task.FromResult<CallGraphSnapshot?>(null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// No-op evidence storage for environments without evidence infrastructure.
|
||||
/// </summary>
|
||||
internal sealed class NullEvidenceStorageService : IEvidenceStorageService
|
||||
{
|
||||
public Task<(string BundleId, string EvidenceUri)> StoreReachabilityStackAsync(
|
||||
ReachabilityStack stack, ReachabilityEvidenceJob job, CancellationToken ct)
|
||||
=> Task.FromResult(("null", "null://no-evidence-store"));
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using StellaOps.Scanner.Explainability.Assumptions;
|
||||
using StellaOps.Scanner.Reachability.Binary;
|
||||
using StellaOps.Scanner.Reachability.Jobs;
|
||||
using StellaOps.Scanner.Reachability.Runtime;
|
||||
@@ -43,14 +44,20 @@ public static class ServiceCollectionExtensions
|
||||
// VEX Integration
|
||||
services.TryAddSingleton<IVexStatusDeterminer, VexStatusDeterminer>();
|
||||
|
||||
// Call graph snapshot provider (required by job executor)
|
||||
services.TryAddSingleton<ICallGraphSnapshotProvider, NullCallGraphSnapshotProvider>();
|
||||
|
||||
// Evidence storage (required by job executor)
|
||||
services.TryAddSingleton<IEvidenceStorageService, NullEvidenceStorageService>();
|
||||
|
||||
// Job Executor
|
||||
services.TryAddScoped<IReachabilityEvidenceJobExecutor, ReachabilityEvidenceJobExecutor>();
|
||||
|
||||
// Runtime Collection (optional - requires eBPF infrastructure)
|
||||
services.TryAddSingleton<IRuntimeReachabilityCollector, EbpfRuntimeReachabilityCollector>();
|
||||
// Runtime Collection (optional - requires eBPF infrastructure; null default for environments without eBPF)
|
||||
services.TryAddSingleton<IRuntimeReachabilityCollector, NullRuntimeReachabilityCollector>();
|
||||
|
||||
// Binary Patch Verification (requires Ghidra infrastructure)
|
||||
services.TryAddSingleton<IBinaryPatchVerifier, BinaryPatchVerifier>();
|
||||
// Binary Patch Verification (requires Ghidra infrastructure; null default for environments without Ghidra)
|
||||
services.TryAddSingleton<IBinaryPatchVerifier, NullBinaryPatchVerifier>();
|
||||
|
||||
return services;
|
||||
}
|
||||
@@ -71,9 +78,11 @@ public static class ServiceCollectionExtensions
|
||||
services.TryAddSingleton(cveSymbolMappingFactory);
|
||||
services.TryAddSingleton<IReachabilityStackEvaluator, ReachabilityStackEvaluator>();
|
||||
services.TryAddSingleton<IVexStatusDeterminer, VexStatusDeterminer>();
|
||||
services.TryAddSingleton<ICallGraphSnapshotProvider, NullCallGraphSnapshotProvider>();
|
||||
services.TryAddSingleton<IEvidenceStorageService, NullEvidenceStorageService>();
|
||||
services.TryAddScoped<IReachabilityEvidenceJobExecutor, ReachabilityEvidenceJobExecutor>();
|
||||
services.TryAddSingleton<IRuntimeReachabilityCollector, EbpfRuntimeReachabilityCollector>();
|
||||
services.TryAddSingleton<IBinaryPatchVerifier, BinaryPatchVerifier>();
|
||||
services.TryAddSingleton<IRuntimeReachabilityCollector, NullRuntimeReachabilityCollector>();
|
||||
services.TryAddSingleton<IBinaryPatchVerifier, NullBinaryPatchVerifier>();
|
||||
|
||||
return services;
|
||||
}
|
||||
@@ -140,3 +149,65 @@ public sealed class ReachabilityEvidenceOptions
|
||||
/// </summary>
|
||||
public int MaxJobTimeoutSeconds { get; set; } = 300;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Null implementation of IBinaryPatchVerifier for environments without Ghidra.
|
||||
/// </summary>
|
||||
internal sealed class NullBinaryPatchVerifier : IBinaryPatchVerifier
|
||||
{
|
||||
public Task<PatchVerificationResult> VerifyPatchAsync(PatchVerificationRequest request, CancellationToken ct = default)
|
||||
=> Task.FromResult(new PatchVerificationResult
|
||||
{
|
||||
Success = false,
|
||||
Status = PatchStatus.Unknown,
|
||||
FunctionResults = [],
|
||||
Layer2 = new ReachabilityLayer2
|
||||
{
|
||||
IsResolved = false,
|
||||
Confidence = ConfidenceLevel.Low,
|
||||
Reason = "Binary patch verification not available (Ghidra not configured)"
|
||||
},
|
||||
Confidence = 0m,
|
||||
Error = "Binary patch verification not available (Ghidra not configured)"
|
||||
});
|
||||
|
||||
public Task<FunctionPatchResult> CompareFunctionAsync(string vulnerableBinaryPath, string targetBinaryPath, string symbolName, CancellationToken ct = default)
|
||||
=> Task.FromResult(new FunctionPatchResult
|
||||
{
|
||||
SymbolName = symbolName,
|
||||
Success = false,
|
||||
IsPatched = false,
|
||||
Similarity = 0m,
|
||||
Confidence = 0m,
|
||||
Error = "Binary patch verification not available (Ghidra not configured)"
|
||||
});
|
||||
|
||||
public bool IsSupported(string binaryPath) => false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Null implementation of IRuntimeReachabilityCollector for environments without eBPF.
|
||||
/// </summary>
|
||||
internal sealed class NullRuntimeReachabilityCollector : IRuntimeReachabilityCollector
|
||||
{
|
||||
public bool IsAvailable => false;
|
||||
public string Platform => "none";
|
||||
|
||||
public Task<RuntimeReachabilityResult> ObserveAsync(RuntimeObservationRequest request, CancellationToken ct = default)
|
||||
=> Task.FromResult(new RuntimeReachabilityResult
|
||||
{
|
||||
Success = false,
|
||||
Layer3 = new ReachabilityLayer3
|
||||
{
|
||||
IsGated = false,
|
||||
Outcome = GatingOutcome.NotGated,
|
||||
Confidence = ConfidenceLevel.Low,
|
||||
Description = "Runtime observation not available (eBPF not configured)"
|
||||
},
|
||||
Source = ObservationSource.None,
|
||||
Error = "Runtime observation not available (eBPF not configured)"
|
||||
});
|
||||
|
||||
public Task<IReadOnlyList<SymbolObservation>> CheckObservationsAsync(string containerId, IReadOnlyList<string> symbols, CancellationToken ct = default)
|
||||
=> Task.FromResult<IReadOnlyList<SymbolObservation>>([]);
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ public sealed class NullEpssSignalPublisher : IEpssSignalPublisher
|
||||
{
|
||||
public static readonly NullEpssSignalPublisher Instance = new();
|
||||
|
||||
private NullEpssSignalPublisher() { }
|
||||
public NullEpssSignalPublisher() { }
|
||||
|
||||
public Task<EpssSignalPublishResult> PublishAsync(EpssSignal signal, CancellationToken cancellationToken = default)
|
||||
=> Task.FromResult(new EpssSignalPublishResult { Success = true, MessageId = "null" });
|
||||
|
||||
@@ -95,7 +95,13 @@ public static class ServiceCollectionExtensions
|
||||
services.AddSingleton<EpssCsvStreamParser>();
|
||||
services.AddScoped<IEpssRepository, PostgresEpssRepository>();
|
||||
services.AddSingleton<EpssOnlineSource>();
|
||||
services.AddSingleton<EpssBundleSource>();
|
||||
services.AddSingleton<EpssBundleSource>(sp =>
|
||||
{
|
||||
var opts = sp.GetRequiredService<IOptions<ScannerStorageOptions>>().Value;
|
||||
// Default to /app/epss for bundle path; overrideable via config.
|
||||
var path = Environment.GetEnvironmentVariable("EPSS_BUNDLE_PATH") ?? "/app/epss";
|
||||
return new EpssBundleSource(path);
|
||||
});
|
||||
// Note: EpssChangeDetector is a static class, no DI registration needed
|
||||
|
||||
// EPSS provider with optional Valkey cache layer (Sprint: SPRINT_3410_0002_0001, Task: EPSS-SCAN-005)
|
||||
|
||||
Reference in New Issue
Block a user