Files
git.stella-ops.org/src/Scanner/__Libraries/StellaOps.Scanner.Reachability/ReachabilityGraphBuilder.cs
master 536f6249a6
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Add SBOM, symbols, traces, and VEX files for CVE-2022-21661 SQLi case
- Created CycloneDX and SPDX SBOM files for both reachable and unreachable images.
- Added symbols.json detailing function entry and sink points in the WordPress code.
- Included runtime traces for function calls in both reachable and unreachable scenarios.
- Developed OpenVEX files indicating vulnerability status and justification for both cases.
- Updated README for evaluator harness to guide integration with scanner output.
2025-11-08 20:53:45 +02:00

122 lines
4.2 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
namespace StellaOps.Scanner.Reachability;
public sealed class ReachabilityGraphBuilder
{
private const string GraphSchemaVersion = "1.0";
private readonly HashSet<string> nodes = new(StringComparer.Ordinal);
private readonly HashSet<ReachabilityEdge> edges = new();
public ReachabilityGraphBuilder AddNode(string symbolId)
{
if (!string.IsNullOrWhiteSpace(symbolId))
{
nodes.Add(symbolId.Trim());
}
return this;
}
public ReachabilityGraphBuilder AddEdge(string from, string to, string kind = "call")
{
if (string.IsNullOrWhiteSpace(from) || string.IsNullOrWhiteSpace(to))
{
return this;
}
var edge = new ReachabilityEdge(from.Trim(), to.Trim(), string.IsNullOrWhiteSpace(kind) ? "call" : kind.Trim());
edges.Add(edge);
nodes.Add(edge.From);
nodes.Add(edge.To);
return this;
}
public string BuildJson(bool indented = true)
{
var payload = new ReachabilityGraphPayload
{
SchemaVersion = GraphSchemaVersion,
Nodes = nodes.Select(id => new ReachabilityNode(id)).ToList(),
Edges = edges.Select(edge => new ReachabilityEdgePayload(edge.From, edge.To, edge.Kind)).ToList()
};
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = indented
};
return JsonSerializer.Serialize(payload, options);
}
public static ReachabilityGraphBuilder FromFixture(string variantPath)
{
ArgumentException.ThrowIfNullOrWhiteSpace(variantPath);
var builder = new ReachabilityGraphBuilder();
foreach (var fileName in new[] { "callgraph.static.json", "callgraph.framework.json" })
{
var path = Path.Combine(variantPath, fileName);
if (!File.Exists(path))
{
continue;
}
using var stream = File.OpenRead(path);
using var document = JsonDocument.Parse(stream);
var root = document.RootElement;
if (root.TryGetProperty("nodes", out var nodesElement) && nodesElement.ValueKind == JsonValueKind.Array)
{
foreach (var node in nodesElement.EnumerateArray())
{
var sid = node.TryGetProperty("sid", out var sidElement)
? sidElement.GetString()
: node.GetProperty("id").GetString();
builder.AddNode(sid ?? string.Empty);
}
}
if (root.TryGetProperty("edges", out var edgesElement) && edgesElement.ValueKind == JsonValueKind.Array)
{
foreach (var edge in edgesElement.EnumerateArray())
{
var from = edge.TryGetProperty("from", out var fromEl)
? fromEl.GetString()
: edge.GetProperty("source").GetString();
var to = edge.TryGetProperty("to", out var toEl)
? toEl.GetString()
: edge.GetProperty("target").GetString();
var kind = edge.TryGetProperty("kind", out var kindEl)
? kindEl.GetString()
: edge.TryGetProperty("type", out var typeEl)
? typeEl.GetString()
: "call";
builder.AddEdge(from ?? string.Empty, to ?? string.Empty, kind ?? "call");
}
}
}
return builder;
}
private sealed record ReachabilityEdge(string From, string To, string Kind);
private sealed record ReachabilityNode(string Sid);
private sealed record ReachabilityEdgePayload(string From, string To, string Kind);
private sealed record ReachabilityGraphPayload
{
public string SchemaVersion { get; set; } = GraphSchemaVersion;
public List<ReachabilityNode> Nodes { get; set; } = new();
public List<ReachabilityEdgePayload> Edges { get; set; } = new();
}
}