Add SBOM, symbols, traces, and VEX files for CVE-2022-21661 SQLi case
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
- 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.
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using StellaOps.Replay.Core;
|
||||
|
||||
namespace StellaOps.Scanner.Reachability;
|
||||
|
||||
/// <summary>
|
||||
/// Helper that projects reachability artifacts into the replay manifest.
|
||||
/// </summary>
|
||||
public sealed class ReachabilityReplayWriter
|
||||
{
|
||||
/// <summary>
|
||||
/// Attaches reachability graphs and runtime traces to the supplied replay manifest.
|
||||
/// </summary>
|
||||
public void AttachEvidence(
|
||||
ReplayManifest manifest,
|
||||
IEnumerable<ReachabilityReplayGraph>? graphs,
|
||||
IEnumerable<ReachabilityReplayTrace>? traces)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(manifest);
|
||||
|
||||
WriteGraphs(manifest, graphs);
|
||||
WriteTraces(manifest, traces);
|
||||
}
|
||||
|
||||
private static void WriteGraphs(ReplayManifest manifest, IEnumerable<ReachabilityReplayGraph>? graphs)
|
||||
{
|
||||
if (graphs is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var sanitized = graphs
|
||||
.Where(graph => graph is not null)
|
||||
.Select(graph => NormalizeGraph(graph!))
|
||||
.Where(graph => graph is not null)
|
||||
.Select(graph => graph!)
|
||||
.DistinctBy(graph => (graph.Kind, graph.CasUri, graph.Sha256, graph.Analyzer, graph.Version))
|
||||
.OrderBy(graph => graph.CasUri, StringComparer.Ordinal)
|
||||
.ThenBy(graph => graph.Kind, StringComparer.Ordinal)
|
||||
.ToList();
|
||||
|
||||
foreach (var graph in sanitized)
|
||||
{
|
||||
manifest.AddReachabilityGraph(new ReplayReachabilityGraphReference
|
||||
{
|
||||
Kind = graph.Kind,
|
||||
CasUri = graph.CasUri,
|
||||
Sha256 = graph.Sha256,
|
||||
Analyzer = graph.Analyzer,
|
||||
Version = graph.Version
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteTraces(ReplayManifest manifest, IEnumerable<ReachabilityReplayTrace>? traces)
|
||||
{
|
||||
if (traces is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var normalized = traces
|
||||
.Where(trace => trace is not null)
|
||||
.Select(trace => NormalizeTrace(trace!))
|
||||
.Where(trace => trace is not null)
|
||||
.Select(trace => trace!)
|
||||
.ToList();
|
||||
var collapsed = normalized
|
||||
.GroupBy(trace => (trace.Source, trace.CasUri, trace.Sha256))
|
||||
.Select(group => group.OrderBy(t => t.RecordedAt).First())
|
||||
.OrderBy(trace => trace.RecordedAt)
|
||||
.ThenBy(trace => trace.CasUri, StringComparer.Ordinal)
|
||||
.ToList();
|
||||
|
||||
foreach (var trace in collapsed)
|
||||
{
|
||||
manifest.AddReachabilityTrace(new ReplayReachabilityTraceReference
|
||||
{
|
||||
Source = trace.Source,
|
||||
CasUri = trace.CasUri,
|
||||
Sha256 = trace.Sha256,
|
||||
RecordedAt = trace.RecordedAt
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static NormalizedGraph? NormalizeGraph(ReachabilityReplayGraph graph)
|
||||
{
|
||||
var casUri = Normalize(graph.CasUri);
|
||||
if (string.IsNullOrEmpty(casUri))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new NormalizedGraph(
|
||||
Kind: Normalize(graph.Kind) ?? "static",
|
||||
CasUri: casUri,
|
||||
Sha256: NormalizeHash(graph.Sha256),
|
||||
Analyzer: Normalize(graph.Analyzer) ?? string.Empty,
|
||||
Version: Normalize(graph.Version) ?? string.Empty);
|
||||
}
|
||||
|
||||
private static NormalizedTrace? NormalizeTrace(ReachabilityReplayTrace trace)
|
||||
{
|
||||
var casUri = Normalize(trace.CasUri);
|
||||
if (string.IsNullOrEmpty(casUri))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new NormalizedTrace(
|
||||
Source: Normalize(trace.Source) ?? string.Empty,
|
||||
CasUri: casUri,
|
||||
Sha256: NormalizeHash(trace.Sha256),
|
||||
RecordedAt: trace.RecordedAt.ToUniversalTime());
|
||||
}
|
||||
|
||||
private static string? Normalize(string? value)
|
||||
=> string.IsNullOrWhiteSpace(value) ? null : value.Trim();
|
||||
|
||||
private static string NormalizeHash(string? hash)
|
||||
=> string.IsNullOrWhiteSpace(hash) ? string.Empty : hash.Trim().ToLowerInvariant();
|
||||
|
||||
private sealed record NormalizedGraph(
|
||||
string Kind,
|
||||
string CasUri,
|
||||
string Sha256,
|
||||
string Analyzer,
|
||||
string Version);
|
||||
|
||||
private sealed record NormalizedTrace(
|
||||
string Source,
|
||||
string CasUri,
|
||||
string Sha256,
|
||||
DateTimeOffset RecordedAt);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Describes a CAS-backed reachability graph emitted by Scanner.
|
||||
/// </summary>
|
||||
public sealed record ReachabilityReplayGraph(
|
||||
string? Kind,
|
||||
string? CasUri,
|
||||
string? Sha256,
|
||||
string? Analyzer,
|
||||
string? Version);
|
||||
|
||||
/// <summary>
|
||||
/// Describes a runtime trace artifact emitted by Scanner/Zastava.
|
||||
/// </summary>
|
||||
public sealed record ReachabilityReplayTrace(
|
||||
string? Source,
|
||||
string? CasUri,
|
||||
string? Sha256,
|
||||
DateTimeOffset RecordedAt);
|
||||
@@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Text.Json" Version="10.0.0-preview.7.25380.108" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\__Libraries\StellaOps.Replay.Core\StellaOps.Replay.Core.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -3,11 +3,11 @@ using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Cryptography;
|
||||
|
||||
namespace StellaOps.Scanner.Surface.FS;
|
||||
|
||||
@@ -20,12 +20,14 @@ public sealed class FileSurfaceManifestStore :
|
||||
{
|
||||
private readonly ILogger<FileSurfaceManifestStore> _logger;
|
||||
private readonly SurfaceManifestPathBuilder _pathBuilder;
|
||||
private readonly ICryptoHash _hash;
|
||||
private readonly SemaphoreSlim _publishGate = new(1, 1);
|
||||
|
||||
public FileSurfaceManifestStore(
|
||||
IOptions<SurfaceCacheOptions> cacheOptions,
|
||||
IOptions<SurfaceManifestStoreOptions> storeOptions,
|
||||
ILogger<FileSurfaceManifestStore> logger)
|
||||
ILogger<FileSurfaceManifestStore> logger,
|
||||
ICryptoHash hash)
|
||||
{
|
||||
if (cacheOptions is null)
|
||||
{
|
||||
@@ -38,6 +40,7 @@ public sealed class FileSurfaceManifestStore :
|
||||
}
|
||||
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_hash = hash ?? throw new ArgumentNullException(nameof(hash));
|
||||
_pathBuilder = new SurfaceManifestPathBuilder(cacheOptions.Value, storeOptions.Value);
|
||||
}
|
||||
|
||||
@@ -183,11 +186,10 @@ public sealed class FileSurfaceManifestStore :
|
||||
};
|
||||
}
|
||||
|
||||
private static string ComputeDigest(ReadOnlySpan<byte> bytes)
|
||||
private string ComputeDigest(ReadOnlySpan<byte> bytes)
|
||||
{
|
||||
using var sha = SHA256.Create();
|
||||
var hash = sha.ComputeHash(bytes);
|
||||
return $"sha256:{Convert.ToHexString(hash).ToLowerInvariant()}";
|
||||
var digest = _hash.ComputeHash(bytes, HashAlgorithms.Sha256);
|
||||
return $"sha256:{Convert.ToHexString(digest).ToLowerInvariant()}";
|
||||
}
|
||||
|
||||
private static SurfaceManifestArtifact NormalizeArtifact(SurfaceManifestArtifact artifact)
|
||||
|
||||
@@ -16,12 +16,15 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="10.0.0-preview.7.25380.108" />
|
||||
<PackageReference Include="System.Collections.Immutable" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="10.0.0-rc.2.25502.107" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\\..\\..\\__Libraries\\StellaOps.Cryptography\\StellaOps.Cryptography.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
Reference in New Issue
Block a user