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

- 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:
master
2025-11-08 20:53:45 +02:00
parent 515975edc5
commit 536f6249a6
837 changed files with 37279 additions and 14675 deletions

View File

@@ -0,0 +1,61 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using FluentAssertions;
using StellaOps.Replay.Core;
using StellaOps.Scanner.Reachability;
using Xunit;
namespace StellaOps.Reachability.FixtureTests;
public sealed class ReachabilityReplayWriterTests
{
[Fact]
public void AttachEvidence_AppendsGraphsAndTracesDeterministically()
{
var manifest = new ReplayManifest
{
Scan = new ReplayScanMetadata { Id = "scan-123", Time = DateTimeOffset.Parse("2025-10-15T10:00:00Z", CultureInfo.InvariantCulture) }
};
var graphs = new List<ReachabilityReplayGraph>
{
new("static", "cas://graph/B", "ABCDEF", "scanner-jvm", "1.0.0"),
new("framework", "cas://graph/A", "abcdef", "scanner-jvm", "1.0.0"),
new("static", "cas://graph/B", "ABCDEF", "scanner-jvm", "1.0.0") // duplicate
};
var traces = new List<ReachabilityReplayTrace>
{
new("zastava", "cas://trace/1", "FFEE", DateTimeOffset.Parse("2025-10-15T09:00:00+02:00", CultureInfo.InvariantCulture)),
new("zastava", "cas://trace/2", "ffee", DateTimeOffset.Parse("2025-10-15T09:05:00Z", CultureInfo.InvariantCulture)),
new("zastava", "cas://trace/1", "FFEE", DateTimeOffset.Parse("2025-10-15T09:00:00Z", CultureInfo.InvariantCulture)) // duplicate once normalized
};
var writer = new ReachabilityReplayWriter();
writer.AttachEvidence(manifest, graphs, traces);
manifest.Reachability.Should().NotBeNull();
manifest.Reachability!.Graphs.Should().HaveCount(2);
manifest.Reachability.Graphs[0].CasUri.Should().Be("cas://graph/A");
manifest.Reachability.Graphs[0].Sha256.Should().Be("abcdef");
manifest.Reachability.Graphs[1].CasUri.Should().Be("cas://graph/B");
manifest.Reachability.Graphs[1].Kind.Should().Be("static");
manifest.Reachability.RuntimeTraces.Should().HaveCount(2);
manifest.Reachability.RuntimeTraces[0].RecordedAt.Should().Be(DateTimeOffset.Parse("2025-10-15T07:00:00Z"));
manifest.Reachability.RuntimeTraces[0].Sha256.Should().Be("ffee");
manifest.Reachability.RuntimeTraces[1].CasUri.Should().Be("cas://trace/2");
}
[Fact]
public void AttachEvidence_DoesNotCreateSectionWhenEmpty()
{
var manifest = new ReplayManifest();
var writer = new ReachabilityReplayWriter();
writer.AttachEvidence(manifest, Array.Empty<ReachabilityReplayGraph>(), Array.Empty<ReachabilityReplayTrace>());
manifest.Reachability.Should().BeNull();
}
}

View File

@@ -0,0 +1,129 @@
using System.Text.Json;
using FluentAssertions;
using Xunit;
namespace StellaOps.Reachability.FixtureTests;
public class ReachbenchFixtureTests
{
private static readonly string RepoRoot = LocateRepoRoot();
private static readonly string FixtureRoot = Path.Combine(
RepoRoot, "tests", "reachability", "fixtures", "reachbench-2025-expanded");
private static readonly string CasesRoot = Path.Combine(FixtureRoot, "cases");
[Fact]
public void IndexListsAllCases()
{
Directory.Exists(FixtureRoot).Should().BeTrue("reachbench fixtures should exist under tests/reachability/fixtures");
File.Exists(Path.Combine(FixtureRoot, "INDEX.json")).Should().BeTrue("the reachbench index must be present");
using var indexStream = File.OpenRead(Path.Combine(FixtureRoot, "INDEX.json"));
using var document = JsonDocument.Parse(indexStream);
var names = new List<string>();
var found = false;
JsonElement casesElement = default;
foreach (var property in document.RootElement.EnumerateObject())
{
names.Add(property.Name);
if (property.NameEquals("cases"))
{
casesElement = property.Value;
found = true;
}
}
found.Should().BeTrue($"INDEX.json should contain 'cases'. Properties present: {string.Join(",", names)}");
casesElement.ValueKind.Should().Be(JsonValueKind.Array);
casesElement.GetArrayLength().Should().BeGreaterOrEqualTo(20, "expanded pack should carry broad coverage");
foreach (var entry in casesElement.EnumerateArray())
{
var id = entry.GetProperty("id").GetString();
id.Should().NotBeNullOrEmpty();
var rel = entry.TryGetProperty("path", out var relProp)
? relProp.GetString()
: Path.Combine("cases", id!);
rel.Should().NotBeNullOrEmpty();
var path = Path.Combine(FixtureRoot, rel!);
Directory.Exists(path).Should().BeTrue($"case '{id}' folder '{rel}' should exist");
}
}
public static IEnumerable<object[]> CaseVariantData()
{
foreach (var caseDir in Directory.EnumerateDirectories(CasesRoot))
{
var caseId = Path.GetFileName(caseDir);
yield return new object[] { caseId!, Path.Combine(caseDir, "images", "reachable") };
yield return new object[] { caseId!, Path.Combine(caseDir, "images", "unreachable") };
}
}
[Theory]
[MemberData(nameof(CaseVariantData))]
public void CaseVariantContainsExpectedArtifacts(string caseId, string variantPath)
{
Directory.Exists(variantPath).Should().BeTrue();
var requiredFiles = new[]
{
"manifest.json",
"sbom.cdx.json",
"sbom.spdx.json",
"symbols.json",
"callgraph.static.json",
"callgraph.framework.json",
"reachgraph.truth.json",
"vex.openvex.json",
"attestation.dsse.json"
};
foreach (var file in requiredFiles)
{
File.Exists(Path.Combine(variantPath, file)).Should().BeTrue($"{caseId}:{Path.GetFileName(variantPath)} missing {file}");
}
var truthPath = Path.Combine(variantPath, "reachgraph.truth.json");
using var truthStream = File.OpenRead(truthPath);
using var truthDoc = JsonDocument.Parse(truthStream);
truthDoc.RootElement.GetProperty("schema_version").GetString().Should().NotBeNullOrEmpty();
truthDoc.RootElement.GetProperty("paths").ValueKind.Should().Be(JsonValueKind.Array);
}
[Theory]
[MemberData(nameof(CaseVariantData))]
public void CaseGroundTruthMatchesVariants(string caseId, string variantPath)
{
var caseJsonPath = Path.Combine(Path.GetDirectoryName(Path.GetDirectoryName(variantPath))!, "case.json");
File.Exists(caseJsonPath).Should().BeTrue();
using var caseStream = File.OpenRead(caseJsonPath);
using var caseDoc = JsonDocument.Parse(caseStream);
var groundTruth = caseDoc.RootElement.GetProperty("ground_truth");
var variantKey = variantPath.EndsWith("reachable", StringComparison.OrdinalIgnoreCase)
? "reachable_variant"
: "unreachable_variant";
var variant = groundTruth.GetProperty(variantKey);
variant.GetProperty("status").GetString().Should().NotBeNullOrEmpty($"{caseId}:{variantKey} should set status");
variant.TryGetProperty("evidence", out var evidence).Should().BeTrue($"{caseId}:{variantKey} should define evidence");
evidence.TryGetProperty("paths", out var pathsProp).Should().BeTrue();
pathsProp.ValueKind.Should().Be(JsonValueKind.Array);
}
private static string LocateRepoRoot()
{
var current = new DirectoryInfo(AppContext.BaseDirectory);
while (current != null)
{
if (File.Exists(Path.Combine(current.FullName, "Directory.Build.props")))
{
return current.FullName;
}
current = current.Parent;
}
throw new InvalidOperationException("Cannot locate repository root (missing Directory.Build.props).");
}
}

View File

@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>preview</LangVersion>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="xunit" Version="2.7.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="System.Text.Json" Version="10.0.0-preview.7.24405.7" />
</ItemGroup>
<ItemGroup>
<Content Include="..\\..\\fixtures\\**\\*">
<Link>fixtures\\%(RecursiveDir)%(Filename)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Scanner\__Libraries\StellaOps.Scanner.Reachability\StellaOps.Scanner.Reachability.csproj" />
<ProjectReference Include="..\..\..\src\__Libraries\StellaOps.Replay.Core\StellaOps.Replay.Core.csproj" />
</ItemGroup>
</Project>