Some checks failed
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
Manifest Integrity / Validate Schema Integrity (push) Has been cancelled
Manifest Integrity / Validate Contract Documents (push) Has been cancelled
Manifest Integrity / Validate Pack Fixtures (push) Has been cancelled
Manifest Integrity / Audit SHA256SUMS Files (push) Has been cancelled
Manifest Integrity / Verify Merkle Roots (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
197 lines
7.0 KiB
C#
197 lines
7.0 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text.Json;
|
|
using System.Threading.Tasks;
|
|
using StellaOps.Scanner.Reachability;
|
|
using Xunit;
|
|
|
|
namespace StellaOps.Scanner.Reachability.Tests;
|
|
|
|
public class ReachabilityUnionWriterTests
|
|
{
|
|
[Fact]
|
|
public async Task WritesDeterministicNdjson()
|
|
{
|
|
var writer = new ReachabilityUnionWriter();
|
|
using var temp = new TempDir();
|
|
|
|
var graph = new ReachabilityUnionGraph(
|
|
Nodes: new[]
|
|
{
|
|
new ReachabilityUnionNode("sym:dotnet:B", "dotnet", "method"),
|
|
new ReachabilityUnionNode("sym:dotnet:A", "dotnet", "method")
|
|
},
|
|
Edges: new[]
|
|
{
|
|
new ReachabilityUnionEdge("sym:dotnet:A", "sym:dotnet:B", "call")
|
|
});
|
|
|
|
var result = await writer.WriteAsync(graph, temp.Path, "analysis-x");
|
|
|
|
var meta = await JsonDocument.ParseAsync(File.OpenRead(result.MetaPath));
|
|
var files = meta.RootElement.GetProperty("files").EnumerateArray().ToList();
|
|
Assert.Equal(2, files.Count); // nodes + edges
|
|
|
|
// Deterministic order
|
|
var nodeLines = await File.ReadAllLinesAsync(Path.Combine(temp.Path, "reachability_graphs/analysis-x/nodes.ndjson"));
|
|
Assert.Contains(nodeLines, l => l.Contains("sym:dotnet:A"));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task WritesNodePurlAndSymbolDigest()
|
|
{
|
|
var writer = new ReachabilityUnionWriter();
|
|
using var temp = new TempDir();
|
|
|
|
var graph = new ReachabilityUnionGraph(
|
|
Nodes: new[]
|
|
{
|
|
new ReachabilityUnionNode(
|
|
"sym:dotnet:A",
|
|
"dotnet",
|
|
"method",
|
|
"TestMethod",
|
|
null,
|
|
null,
|
|
Purl: "pkg:nuget/TestPackage@1.0.0",
|
|
SymbolDigest: "sha256:abc123")
|
|
},
|
|
Edges: Array.Empty<ReachabilityUnionEdge>());
|
|
|
|
var result = await writer.WriteAsync(graph, temp.Path, "analysis-purl");
|
|
|
|
var nodeLines = await File.ReadAllLinesAsync(result.Nodes.Path);
|
|
Assert.Single(nodeLines);
|
|
Assert.Contains("\"purl\":\"pkg:nuget/TestPackage@1.0.0\"", nodeLines[0]);
|
|
Assert.Contains("\"symbol_digest\":\"sha256:abc123\"", nodeLines[0]);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task WritesEdgePurlAndSymbolDigest()
|
|
{
|
|
var writer = new ReachabilityUnionWriter();
|
|
using var temp = new TempDir();
|
|
|
|
var graph = new ReachabilityUnionGraph(
|
|
Nodes: new[]
|
|
{
|
|
new ReachabilityUnionNode("sym:dotnet:A", "dotnet", "method"),
|
|
new ReachabilityUnionNode("sym:dotnet:B", "dotnet", "method")
|
|
},
|
|
Edges: new[]
|
|
{
|
|
new ReachabilityUnionEdge(
|
|
"sym:dotnet:A",
|
|
"sym:dotnet:B",
|
|
"call",
|
|
"high",
|
|
null,
|
|
Purl: "pkg:nuget/TargetPackage@2.0.0",
|
|
SymbolDigest: "sha256:def456")
|
|
});
|
|
|
|
var result = await writer.WriteAsync(graph, temp.Path, "analysis-edge-purl");
|
|
|
|
var edgeLines = await File.ReadAllLinesAsync(result.Edges.Path);
|
|
Assert.Single(edgeLines);
|
|
Assert.Contains("\"purl\":\"pkg:nuget/TargetPackage@2.0.0\"", edgeLines[0]);
|
|
Assert.Contains("\"symbol_digest\":\"sha256:def456\"", edgeLines[0]);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task WritesEdgeCandidates()
|
|
{
|
|
var writer = new ReachabilityUnionWriter();
|
|
using var temp = new TempDir();
|
|
|
|
var graph = new ReachabilityUnionGraph(
|
|
Nodes: new[]
|
|
{
|
|
new ReachabilityUnionNode("sym:binary:main", "binary", "function"),
|
|
new ReachabilityUnionNode("sym:binary:openssl_connect", "binary", "function")
|
|
},
|
|
Edges: new[]
|
|
{
|
|
new ReachabilityUnionEdge(
|
|
"sym:binary:main",
|
|
"sym:binary:openssl_connect",
|
|
"call",
|
|
"medium",
|
|
null,
|
|
Purl: null,
|
|
SymbolDigest: null,
|
|
Candidates: new List<ReachabilityEdgeCandidate>
|
|
{
|
|
new("pkg:deb/ubuntu/openssl@3.0.2", "sha256:abc", 0.8),
|
|
new("pkg:deb/debian/openssl@3.0.2", "sha256:def", 0.6)
|
|
})
|
|
});
|
|
|
|
var result = await writer.WriteAsync(graph, temp.Path, "analysis-candidates");
|
|
|
|
var edgeLines = await File.ReadAllLinesAsync(result.Edges.Path);
|
|
Assert.Single(edgeLines);
|
|
Assert.Contains("\"candidates\":", edgeLines[0]);
|
|
Assert.Contains("pkg:deb/ubuntu/openssl@3.0.2", edgeLines[0]);
|
|
Assert.Contains("pkg:deb/debian/openssl@3.0.2", edgeLines[0]);
|
|
Assert.Contains("\"score\":0.8", edgeLines[0]);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task WritesSymbolMetadataAndCodeBlockHash()
|
|
{
|
|
var writer = new ReachabilityUnionWriter();
|
|
using var temp = new TempDir();
|
|
|
|
var graph = new ReachabilityUnionGraph(
|
|
Nodes: new[]
|
|
{
|
|
new ReachabilityUnionNode(
|
|
"sym:binary:foo",
|
|
"binary",
|
|
"function",
|
|
"ssl3_read_bytes",
|
|
CodeBlockHash: "sha256:deadbeef",
|
|
Symbol: new ReachabilitySymbol("_Z15ssl3_read_bytes", "ssl3_read_bytes", "DWARF", 0.98))
|
|
},
|
|
Edges: Array.Empty<ReachabilityUnionEdge>());
|
|
|
|
var result = await writer.WriteAsync(graph, temp.Path, "analysis-symbol");
|
|
|
|
var nodeLines = await File.ReadAllLinesAsync(result.Nodes.Path);
|
|
Assert.Single(nodeLines);
|
|
Assert.Contains("\"code_block_hash\":\"sha256:deadbeef\"", nodeLines[0]);
|
|
Assert.Contains("\"symbol\":{\"mangled\":\"_Z15ssl3_read_bytes\",\"demangled\":\"ssl3_read_bytes\",\"source\":\"DWARF\",\"confidence\":0.98}", nodeLines[0]);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task OmitsPurlAndSymbolDigestWhenNull()
|
|
{
|
|
var writer = new ReachabilityUnionWriter();
|
|
using var temp = new TempDir();
|
|
|
|
var graph = new ReachabilityUnionGraph(
|
|
Nodes: new[]
|
|
{
|
|
new ReachabilityUnionNode("sym:dotnet:A", "dotnet", "method")
|
|
},
|
|
Edges: new[]
|
|
{
|
|
new ReachabilityUnionEdge("sym:dotnet:A", "sym:dotnet:A", "call")
|
|
});
|
|
|
|
var result = await writer.WriteAsync(graph, temp.Path, "analysis-null-purl");
|
|
|
|
var nodeLines = await File.ReadAllLinesAsync(result.Nodes.Path);
|
|
Assert.DoesNotContain("purl", nodeLines[0]);
|
|
Assert.DoesNotContain("symbol_digest", nodeLines[0]);
|
|
|
|
var edgeLines = await File.ReadAllLinesAsync(result.Edges.Path);
|
|
Assert.DoesNotContain("purl", edgeLines[0]);
|
|
Assert.DoesNotContain("symbol_digest", edgeLines[0]);
|
|
Assert.DoesNotContain("candidates", edgeLines[0]);
|
|
}
|
|
}
|