up
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Mirror Thin Bundle Sign & Verify / mirror-sign (push) Has been cancelled
api-governance / spectral-lint (push) Has been cancelled

This commit is contained in:
StellaOps Bot
2025-11-24 07:52:25 +02:00
parent 5970f0d9bd
commit 150b3730ef
215 changed files with 8119 additions and 740 deletions

View File

@@ -0,0 +1,92 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using StellaOps.Scanner.Core.Contracts;
using StellaOps.Scanner.EntryTrace;
using StellaOps.Scanner.Reachability;
namespace StellaOps.Scanner.Worker.Processing.Reachability;
/// <summary>
/// Builds a reachability union graph from the EntryTrace graph if available.
/// </summary>
public sealed class ReachabilityBuildStageExecutor : IScanStageExecutor
{
private readonly ILogger<ReachabilityBuildStageExecutor> _logger;
public ReachabilityBuildStageExecutor(ILogger<ReachabilityBuildStageExecutor> logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public string StageName => ScanStageNames.ComposeArtifacts;
public ValueTask ExecuteAsync(ScanJobContext context, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(context);
if (!context.Analysis.TryGet<EntryTraceGraph>(ScanAnalysisKeys.EntryTraceGraph, out var entryTrace) || entryTrace is null)
{
_logger.LogDebug("No EntryTrace graph present; reachability union graph not built.");
return ValueTask.CompletedTask;
}
var nodeMap = entryTrace.Nodes.ToDictionary(n => n.Id);
var unionNodes = new List<ReachabilityUnionNode>(entryTrace.Nodes.Length);
foreach (var node in entryTrace.Nodes)
{
var symbolId = ComputeSymbolId("shell", node.DisplayName, node.Kind.ToString());
var source = node.Evidence is null
? null
: new ReachabilitySource("static", "entrytrace", node.Evidence.Path);
unionNodes.Add(new ReachabilityUnionNode(
SymbolId: symbolId,
Lang: "shell",
Kind: node.Kind.ToString().ToLowerInvariant(),
Display: node.DisplayName,
Source: source));
}
var unionEdges = new List<ReachabilityUnionEdge>(entryTrace.Edges.Length);
foreach (var edge in entryTrace.Edges)
{
if (!nodeMap.TryGetValue(edge.FromNodeId, out var fromNode) || !nodeMap.TryGetValue(edge.ToNodeId, out var toNode))
{
continue;
}
var fromId = ComputeSymbolId("shell", fromNode.DisplayName, fromNode.Kind.ToString());
var toId = ComputeSymbolId("shell", toNode.DisplayName, toNode.Kind.ToString());
unionEdges.Add(new ReachabilityUnionEdge(
From: fromId,
To: toId,
EdgeType: "call",
Confidence: "high",
Source: new ReachabilitySource("static", "entrytrace", edge.Relationship)));
}
var unionGraph = new ReachabilityUnionGraph(unionNodes, unionEdges);
context.Analysis.Set(ScanAnalysisKeys.ReachabilityUnionGraph, unionGraph);
_logger.LogInformation("Reachability union graph built from EntryTrace: nodes={NodeCount} edges={EdgeCount}", unionNodes.Count, unionEdges.Count);
return ValueTask.CompletedTask;
}
private static string ComputeSymbolId(string lang, string display, string kind)
{
using var sha = SHA256.Create();
var input = Encoding.UTF8.GetBytes((display ?? string.Empty) + "|" + (kind ?? string.Empty));
var hash = sha.ComputeHash(input);
var base64 = Convert.ToBase64String(hash)
.TrimEnd('=')
.Replace('+', '-')
.Replace('/', '_');
return $"sym:{lang}:{base64}";
}
}

View File

@@ -0,0 +1,43 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using StellaOps.Scanner.Core.Contracts;
using StellaOps.Scanner.Reachability;
namespace StellaOps.Scanner.Worker.Processing.Reachability;
/// <summary>
/// Emits reachability union graphs to CAS during the EmitReports stage when present in the analysis store.
/// </summary>
public sealed class ReachabilityPublishStageExecutor : IScanStageExecutor
{
private readonly IReachabilityUnionPublisherService _publisher;
private readonly ILogger<ReachabilityPublishStageExecutor> _logger;
public ReachabilityPublishStageExecutor(
IReachabilityUnionPublisherService publisher,
ILogger<ReachabilityPublishStageExecutor> logger)
{
_publisher = publisher ?? throw new ArgumentNullException(nameof(publisher));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public string StageName => ScanStageNames.EmitReports;
public async ValueTask ExecuteAsync(ScanJobContext context, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(context);
if (!context.Analysis.TryGet<ReachabilityUnionGraph>(ScanAnalysisKeys.ReachabilityUnionGraph, out var graph) || graph is null)
{
_logger.LogDebug("No reachability union graph present; skipping publish.");
return;
}
var publishResult = await _publisher.PublishAsync(graph, context.ScanId, cancellationToken).ConfigureAwait(false);
context.Analysis.Set(ScanAnalysisKeys.ReachabilityUnionCas, publishResult);
_logger.LogInformation("Published reachability union graph to CAS: sha256={Sha} records={Records}", publishResult.Sha256, publishResult.Records);
}
}

View File

@@ -1,21 +1,28 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using StellaOps.Scanner.Reachability;
namespace StellaOps.Scanner.Worker.Processing;
public sealed class ScanJobProcessor
{
private readonly IReadOnlyDictionary<string, IScanStageExecutor> _executors;
private readonly ScanProgressReporter _progressReporter;
private readonly ILogger<ScanJobProcessor> _logger;
public ScanJobProcessor(IEnumerable<IScanStageExecutor> executors, ScanProgressReporter progressReporter, ILogger<ScanJobProcessor> logger)
{
_progressReporter = progressReporter ?? throw new ArgumentNullException(nameof(progressReporter));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
private readonly IReadOnlyDictionary<string, IScanStageExecutor> _executors;
private readonly ScanProgressReporter _progressReporter;
private readonly ILogger<ScanJobProcessor> _logger;
private readonly IReachabilityUnionPublisherService _reachabilityPublisher;
public ScanJobProcessor(
IEnumerable<IScanStageExecutor> executors,
ScanProgressReporter progressReporter,
IReachabilityUnionPublisherService reachabilityPublisher,
ILogger<ScanJobProcessor> logger)
{
_progressReporter = progressReporter ?? throw new ArgumentNullException(nameof(progressReporter));
_reachabilityPublisher = reachabilityPublisher ?? throw new ArgumentNullException(nameof(reachabilityPublisher));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
var map = new Dictionary<string, IScanStageExecutor>(StringComparer.OrdinalIgnoreCase);
foreach (var executor in executors ?? Array.Empty<IScanStageExecutor>())
@@ -42,12 +49,14 @@ public sealed class ScanJobProcessor
_executors = map;
}
public async ValueTask ExecuteAsync(ScanJobContext context, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(context);
foreach (var stage in ScanStageNames.Ordered)
{
public async ValueTask ExecuteAsync(ScanJobContext context, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(context);
// Placeholder: reachability publisher will be fed once lifter outputs are routed here.
_ = _reachabilityPublisher;
foreach (var stage in ScanStageNames.Ordered)
{
cancellationToken.ThrowIfCancellationRequested();
if (!_executors.TryGetValue(stage, out var executor))
@@ -55,11 +64,11 @@ public sealed class ScanJobProcessor
continue;
}
await _progressReporter.ExecuteStageAsync(
context,
stage,
executor.ExecuteAsync,
cancellationToken).ConfigureAwait(false);
}
}
}
await _progressReporter.ExecuteStageAsync(
context,
stage,
executor.ExecuteAsync,
cancellationToken).ConfigureAwait(false);
}
}
}

View File

@@ -8,6 +8,7 @@ using Microsoft.Extensions.Options;
using StellaOps.Auth.Client;
using StellaOps.Configuration;
using StellaOps.Scanner.Cache;
using StellaOps.Scanner.Reachability;
using StellaOps.Scanner.Analyzers.OS.Plugin;
using StellaOps.Scanner.Analyzers.Lang.Plugin;
using StellaOps.Scanner.EntryTrace;
@@ -24,6 +25,7 @@ using StellaOps.Scanner.Worker.Processing;
using StellaOps.Scanner.Worker.Processing.Surface;
using StellaOps.Scanner.Storage.Extensions;
using StellaOps.Scanner.Storage;
using Reachability = StellaOps.Scanner.Worker.Processing.Reachability;
var builder = Host.CreateApplicationBuilder(args);
@@ -56,6 +58,9 @@ builder.Services.AddSingleton<IDelayScheduler, SystemDelayScheduler>();
builder.Services.AddEntryTraceAnalyzer();
builder.Services.AddSingleton<IEntryTraceExecutionService, EntryTraceExecutionService>();
builder.Services.AddSingleton<ReachabilityUnionWriter>();
builder.Services.AddSingleton<ReachabilityUnionPublisher>();
builder.Services.AddSingleton<IReachabilityUnionPublisherService, ReachabilityUnionPublisherService>();
var storageSection = builder.Configuration.GetSection("ScannerStorage");
var connectionString = storageSection.GetValue<string>("Mongo:ConnectionString");
@@ -78,6 +83,8 @@ builder.Services.AddSingleton<ILanguageAnalyzerPluginCatalog, LanguageAnalyzerPl
builder.Services.AddSingleton<IScanAnalyzerDispatcher, CompositeScanAnalyzerDispatcher>();
builder.Services.AddSingleton<IScanStageExecutor, RegistrySecretStageExecutor>();
builder.Services.AddSingleton<IScanStageExecutor, AnalyzerStageExecutor>();
builder.Services.AddSingleton<IScanStageExecutor, Reachability.ReachabilityBuildStageExecutor>();
builder.Services.AddSingleton<IScanStageExecutor, Reachability.ReachabilityPublishStageExecutor>();
builder.Services.AddSingleton<ScannerWorkerHostedService>();
builder.Services.AddHostedService(sp => sp.GetRequiredService<ScannerWorkerHostedService>());

View File

@@ -21,6 +21,7 @@
<ProjectReference Include="../__Libraries/StellaOps.Scanner.Analyzers.Lang/StellaOps.Scanner.Analyzers.Lang.csproj" />
<ProjectReference Include="../__Libraries/StellaOps.Scanner.EntryTrace/StellaOps.Scanner.EntryTrace.csproj" />
<ProjectReference Include="../__Libraries/StellaOps.Scanner.Cache/StellaOps.Scanner.Cache.csproj" />
<ProjectReference Include="../__Libraries/StellaOps.Scanner.Reachability/StellaOps.Scanner.Reachability.csproj" />
<ProjectReference Include="../__Libraries/StellaOps.Scanner.Surface.Env/StellaOps.Scanner.Surface.Env.csproj" />
<ProjectReference Include="../__Libraries/StellaOps.Scanner.Surface.Validation/StellaOps.Scanner.Surface.Validation.csproj" />
<ProjectReference Include="../__Libraries/StellaOps.Scanner.Surface.Secrets/StellaOps.Scanner.Surface.Secrets.csproj" />