- Implemented Attestation Chain API client with methods for verifying, fetching, and managing attestation chains. - Created models for Attestation Chain, including DSSE envelope structures and verification results. - Developed Triage Evidence API client for fetching finding evidence, including methods for evidence retrieval by CVE and component. - Added models for Triage Evidence, encapsulating evidence responses, entry points, boundary proofs, and VEX evidence. - Introduced mock implementations for both API clients to facilitate testing and development.
138 lines
5.1 KiB
C#
138 lines
5.1 KiB
C#
using System.IO;
|
|
using System.Threading.Tasks;
|
|
using StellaOps.Cryptography;
|
|
using StellaOps.Scanner.Reachability;
|
|
using StellaOps.Scanner.Reachability.Gates;
|
|
using Xunit;
|
|
|
|
namespace StellaOps.Scanner.Reachability.Tests;
|
|
|
|
public class RichGraphWriterTests
|
|
{
|
|
[Fact]
|
|
public async Task WritesCanonicalGraphAndMeta()
|
|
{
|
|
var writer = new RichGraphWriter(CryptoHashFactory.CreateDefault());
|
|
using var temp = new TempDir();
|
|
|
|
var union = new ReachabilityUnionGraph(
|
|
Nodes: new[]
|
|
{
|
|
new ReachabilityUnionNode("sym:dotnet:B", "dotnet", "method", "B"),
|
|
new ReachabilityUnionNode("sym:dotnet:A", "dotnet", "method", "A")
|
|
},
|
|
Edges: new[]
|
|
{
|
|
new ReachabilityUnionEdge("sym:dotnet:A", "sym:dotnet:B", "call", "high")
|
|
});
|
|
|
|
var rich = RichGraphBuilder.FromUnion(union, "test-analyzer", "1.0.0");
|
|
var result = await writer.WriteAsync(rich, temp.Path, "analysis-1");
|
|
|
|
Assert.True(File.Exists(result.GraphPath));
|
|
Assert.True(File.Exists(result.MetaPath));
|
|
var json = await File.ReadAllTextAsync(result.GraphPath);
|
|
Assert.Contains("richgraph-v1", json);
|
|
Assert.Contains(":", result.GraphHash); // hash format: algorithm:digest
|
|
Assert.Equal(2, result.NodeCount);
|
|
Assert.Equal(1, result.EdgeCount);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task CarriesSymbolMetadataToRichGraph()
|
|
{
|
|
var writer = new RichGraphWriter(CryptoHashFactory.CreateDefault());
|
|
using var temp = new TempDir();
|
|
|
|
var union = new ReachabilityUnionGraph(
|
|
Nodes: new[]
|
|
{
|
|
new ReachabilityUnionNode(
|
|
"sym:binary:target",
|
|
"binary",
|
|
"function",
|
|
"ssl_read",
|
|
CodeBlockHash: "sha256:blockhash",
|
|
Symbol: new ReachabilitySymbol("_Zssl_read", "ssl_read", "DWARF", 0.9))
|
|
},
|
|
Edges: Array.Empty<ReachabilityUnionEdge>());
|
|
|
|
var rich = RichGraphBuilder.FromUnion(union, "test-analyzer", "1.0.0");
|
|
var result = await writer.WriteAsync(rich, temp.Path, "analysis-symbol-rich");
|
|
|
|
var json = await File.ReadAllTextAsync(result.GraphPath);
|
|
Assert.Contains("\"code_block_hash\":\"sha256:blockhash\"", json);
|
|
Assert.Contains("\"symbol\":{\"mangled\":\"_Zssl_read\",\"demangled\":\"ssl_read\",\"source\":\"DWARF\",\"confidence\":0.9}", json);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task WritesGatesOnEdgesWhenPresent()
|
|
{
|
|
var writer = new RichGraphWriter(CryptoHashFactory.CreateDefault());
|
|
using var temp = new TempDir();
|
|
|
|
var union = new ReachabilityUnionGraph(
|
|
Nodes: new[]
|
|
{
|
|
new ReachabilityUnionNode("sym:dotnet:B", "dotnet", "method", "B"),
|
|
new ReachabilityUnionNode("sym:dotnet:A", "dotnet", "method", "A")
|
|
},
|
|
Edges: new[]
|
|
{
|
|
new ReachabilityUnionEdge("sym:dotnet:A", "sym:dotnet:B", "call", "high")
|
|
});
|
|
|
|
var rich = RichGraphBuilder.FromUnion(union, "test-analyzer", "1.0.0");
|
|
var gate = new DetectedGate
|
|
{
|
|
Type = GateType.AuthRequired,
|
|
Detail = "Auth required: ASP.NET Core Authorize attribute",
|
|
GuardSymbol = "sym:dotnet:B",
|
|
Confidence = 0.95,
|
|
DetectionMethod = "annotation:\\[Authorize\\]"
|
|
};
|
|
|
|
rich = rich with
|
|
{
|
|
Edges = new[]
|
|
{
|
|
rich.Edges[0] with { Gates = new[] { gate }, GateMultiplierBps = 3000 }
|
|
}
|
|
};
|
|
|
|
var result = await writer.WriteAsync(rich, temp.Path, "analysis-gates");
|
|
var json = await File.ReadAllTextAsync(result.GraphPath);
|
|
|
|
Assert.Contains("\"gate_multiplier_bps\":3000", json);
|
|
Assert.Contains("\"gates\":[", json);
|
|
Assert.Contains("\"type\":\"authRequired\"", json);
|
|
Assert.Contains("\"guard_symbol\":\"sym:dotnet:B\"", json);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task UsesBlake3HashForDefaultProfile()
|
|
{
|
|
// WIT-013: Verify BLAKE3 is used for graph hashing
|
|
var writer = new RichGraphWriter(CryptoHashFactory.CreateDefault());
|
|
using var temp = new TempDir();
|
|
|
|
var union = new ReachabilityUnionGraph(
|
|
Nodes: new[]
|
|
{
|
|
new ReachabilityUnionNode("sym:dotnet:A", "dotnet", "method", "A")
|
|
},
|
|
Edges: Array.Empty<ReachabilityUnionEdge>());
|
|
|
|
var rich = RichGraphBuilder.FromUnion(union, "test-analyzer", "1.0.0");
|
|
var result = await writer.WriteAsync(rich, temp.Path, "analysis-blake3");
|
|
|
|
// Default profile (world) uses BLAKE3
|
|
Assert.StartsWith("blake3:", result.GraphHash);
|
|
Assert.Equal(64 + 7, result.GraphHash.Length); // "blake3:" (7) + 64 hex chars
|
|
|
|
// Verify meta.json also contains the blake3-prefixed hash
|
|
var metaJson = await File.ReadAllTextAsync(result.MetaPath);
|
|
Assert.Contains("\"graph_hash\":\"blake3:", metaJson);
|
|
}
|
|
}
|