Refactor code structure for improved readability and maintainability; optimize performance in key functions.
This commit is contained in:
@@ -0,0 +1,395 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FluentAssertions;
|
||||
using StellaOps.Scanner.Reachability.MiniMap;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Scanner.Reachability.Tests.MiniMap;
|
||||
|
||||
public class MiniMapExtractorTests
|
||||
{
|
||||
private readonly MiniMapExtractor _extractor = new();
|
||||
|
||||
[Fact]
|
||||
public void Extract_ReachableComponent_ReturnsPaths()
|
||||
{
|
||||
var graph = CreateGraphWithPaths();
|
||||
|
||||
var result = _extractor.Extract(graph, "pkg:npm/vulnerable@1.0.0");
|
||||
|
||||
result.State.Should().Be(ReachabilityState.StaticReachable);
|
||||
result.Paths.Should().NotBeEmpty();
|
||||
result.Entrypoints.Should().NotBeEmpty();
|
||||
result.Confidence.Should().BeGreaterThan(0.5m);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Extract_UnreachableComponent_ReturnsEmptyPaths()
|
||||
{
|
||||
var graph = CreateGraphWithoutPaths();
|
||||
|
||||
var result = _extractor.Extract(graph, "pkg:npm/isolated@1.0.0");
|
||||
|
||||
result.State.Should().Be(ReachabilityState.StaticUnreachable);
|
||||
result.Paths.Should().BeEmpty();
|
||||
result.Confidence.Should().Be(0.9m); // High confidence in unreachability
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Extract_WithRuntimeEvidence_ReturnsConfirmedReachable()
|
||||
{
|
||||
var graph = CreateGraphWithRuntimeEvidence();
|
||||
|
||||
var result = _extractor.Extract(graph, "pkg:npm/vulnerable@1.0.0");
|
||||
|
||||
result.State.Should().Be(ReachabilityState.ConfirmedReachable);
|
||||
result.Paths.Should().Contain(p => p.HasRuntimeEvidence);
|
||||
result.Confidence.Should().BeGreaterThan(0.8m);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Extract_NonExistentComponent_ReturnsNotFoundMap()
|
||||
{
|
||||
var graph = CreateGraphWithPaths();
|
||||
|
||||
var result = _extractor.Extract(graph, "pkg:npm/nonexistent@1.0.0");
|
||||
|
||||
result.State.Should().Be(ReachabilityState.Unknown);
|
||||
result.Confidence.Should().Be(0m);
|
||||
result.VulnerableComponent.Id.Should().Be("pkg:npm/nonexistent@1.0.0");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Extract_RespectMaxPaths_LimitsResults()
|
||||
{
|
||||
var graph = CreateGraphWithManyPaths();
|
||||
|
||||
var result = _extractor.Extract(graph, "pkg:npm/vulnerable@1.0.0", maxPaths: 5);
|
||||
|
||||
result.Paths.Count.Should().BeLessOrEqualTo(5);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Extract_ClassifiesEntrypointKinds_Correctly()
|
||||
{
|
||||
var graph = CreateGraphWithDifferentEntrypoints();
|
||||
|
||||
var result = _extractor.Extract(graph, "pkg:npm/vulnerable@1.0.0");
|
||||
|
||||
result.Entrypoints.Should().Contain(e => e.Kind == EntrypointKind.HttpEndpoint);
|
||||
result.Entrypoints.Should().Contain(e => e.Kind == EntrypointKind.MainFunction);
|
||||
}
|
||||
|
||||
private static RichGraph CreateGraphWithPaths()
|
||||
{
|
||||
var nodes = new List<RichGraphNode>
|
||||
{
|
||||
new(
|
||||
Id: "entrypoint:main",
|
||||
SymbolId: "main",
|
||||
CodeId: null,
|
||||
Purl: null,
|
||||
Lang: "javascript",
|
||||
Kind: "main",
|
||||
Display: "main()",
|
||||
BuildId: null,
|
||||
Evidence: null,
|
||||
Attributes: null,
|
||||
SymbolDigest: null),
|
||||
new(
|
||||
Id: "function:process",
|
||||
SymbolId: "process",
|
||||
CodeId: null,
|
||||
Purl: null,
|
||||
Lang: "javascript",
|
||||
Kind: "function",
|
||||
Display: "process()",
|
||||
BuildId: null,
|
||||
Evidence: null,
|
||||
Attributes: null,
|
||||
SymbolDigest: null),
|
||||
new(
|
||||
Id: "vuln:component",
|
||||
SymbolId: "vulnerable",
|
||||
CodeId: null,
|
||||
Purl: "pkg:npm/vulnerable@1.0.0",
|
||||
Lang: "javascript",
|
||||
Kind: "function",
|
||||
Display: "vulnerable()",
|
||||
BuildId: null,
|
||||
Evidence: null,
|
||||
Attributes: null,
|
||||
SymbolDigest: null)
|
||||
};
|
||||
|
||||
var edges = new List<RichGraphEdge>
|
||||
{
|
||||
new(
|
||||
From: "entrypoint:main",
|
||||
To: "function:process",
|
||||
Kind: "call",
|
||||
Purl: null,
|
||||
SymbolDigest: null,
|
||||
Evidence: null,
|
||||
Confidence: 0.9,
|
||||
Candidates: null),
|
||||
new(
|
||||
From: "function:process",
|
||||
To: "vuln:component",
|
||||
Kind: "call",
|
||||
Purl: "pkg:npm/vulnerable@1.0.0",
|
||||
SymbolDigest: null,
|
||||
Evidence: null,
|
||||
Confidence: 0.9,
|
||||
Candidates: null)
|
||||
};
|
||||
|
||||
return new RichGraph(
|
||||
Nodes: nodes,
|
||||
Edges: edges,
|
||||
Roots: Array.Empty<RichGraphRoot>(),
|
||||
Analyzer: new RichGraphAnalyzer("test", "1.0", null));
|
||||
}
|
||||
|
||||
private static RichGraph CreateGraphWithoutPaths()
|
||||
{
|
||||
var nodes = new List<RichGraphNode>
|
||||
{
|
||||
new(
|
||||
Id: "entrypoint:main",
|
||||
SymbolId: "main",
|
||||
CodeId: null,
|
||||
Purl: null,
|
||||
Lang: "javascript",
|
||||
Kind: "main",
|
||||
Display: "main()",
|
||||
BuildId: null,
|
||||
Evidence: null,
|
||||
Attributes: null,
|
||||
SymbolDigest: null),
|
||||
new(
|
||||
Id: "isolated:component",
|
||||
SymbolId: "isolated",
|
||||
CodeId: null,
|
||||
Purl: "pkg:npm/isolated@1.0.0",
|
||||
Lang: "javascript",
|
||||
Kind: "function",
|
||||
Display: "isolated()",
|
||||
BuildId: null,
|
||||
Evidence: null,
|
||||
Attributes: null,
|
||||
SymbolDigest: null)
|
||||
};
|
||||
|
||||
// No edges - isolated component
|
||||
var edges = new List<RichGraphEdge>();
|
||||
|
||||
return new RichGraph(
|
||||
Nodes: nodes,
|
||||
Edges: edges,
|
||||
Roots: Array.Empty<RichGraphRoot>(),
|
||||
Analyzer: new RichGraphAnalyzer("test", "1.0", null));
|
||||
}
|
||||
|
||||
private static RichGraph CreateGraphWithRuntimeEvidence()
|
||||
{
|
||||
var nodes = new List<RichGraphNode>
|
||||
{
|
||||
new(
|
||||
Id: "entrypoint:main",
|
||||
SymbolId: "main",
|
||||
CodeId: null,
|
||||
Purl: null,
|
||||
Lang: "javascript",
|
||||
Kind: "main",
|
||||
Display: "main()",
|
||||
BuildId: null,
|
||||
Evidence: null,
|
||||
Attributes: null,
|
||||
SymbolDigest: null),
|
||||
new(
|
||||
Id: "vuln:component",
|
||||
SymbolId: "vulnerable",
|
||||
CodeId: null,
|
||||
Purl: "pkg:npm/vulnerable@1.0.0",
|
||||
Lang: "javascript",
|
||||
Kind: "function",
|
||||
Display: "vulnerable()",
|
||||
BuildId: null,
|
||||
Evidence: null,
|
||||
Attributes: null,
|
||||
SymbolDigest: null)
|
||||
};
|
||||
|
||||
var edges = new List<RichGraphEdge>
|
||||
{
|
||||
new(
|
||||
From: "entrypoint:main",
|
||||
To: "vuln:component",
|
||||
Kind: "call",
|
||||
Purl: "pkg:npm/vulnerable@1.0.0",
|
||||
SymbolDigest: null,
|
||||
Evidence: new[] { "runtime", "static" },
|
||||
Confidence: 0.95,
|
||||
Candidates: null)
|
||||
};
|
||||
|
||||
return new RichGraph(
|
||||
Nodes: nodes,
|
||||
Edges: edges,
|
||||
Roots: Array.Empty<RichGraphRoot>(),
|
||||
Analyzer: new RichGraphAnalyzer("test", "1.0", null));
|
||||
}
|
||||
|
||||
private static RichGraph CreateGraphWithManyPaths()
|
||||
{
|
||||
var nodes = new List<RichGraphNode>
|
||||
{
|
||||
new(
|
||||
Id: "entrypoint:main",
|
||||
SymbolId: "main",
|
||||
CodeId: null,
|
||||
Purl: null,
|
||||
Lang: "javascript",
|
||||
Kind: "main",
|
||||
Display: "main()",
|
||||
BuildId: null,
|
||||
Evidence: null,
|
||||
Attributes: null,
|
||||
SymbolDigest: null),
|
||||
new(
|
||||
Id: "vuln:component",
|
||||
SymbolId: "vulnerable",
|
||||
CodeId: null,
|
||||
Purl: "pkg:npm/vulnerable@1.0.0",
|
||||
Lang: "javascript",
|
||||
Kind: "function",
|
||||
Display: "vulnerable()",
|
||||
BuildId: null,
|
||||
Evidence: null,
|
||||
Attributes: null,
|
||||
SymbolDigest: null)
|
||||
};
|
||||
|
||||
// Add intermediate nodes to create multiple paths
|
||||
for (int i = 1; i <= 10; i++)
|
||||
{
|
||||
nodes.Add(new RichGraphNode(
|
||||
Id: $"function:intermediate{i}",
|
||||
SymbolId: $"intermediate{i}",
|
||||
CodeId: null,
|
||||
Purl: null,
|
||||
Lang: "javascript",
|
||||
Kind: "function",
|
||||
Display: $"intermediate{i}()",
|
||||
BuildId: null,
|
||||
Evidence: null,
|
||||
Attributes: null,
|
||||
SymbolDigest: null));
|
||||
}
|
||||
|
||||
var edges = new List<RichGraphEdge>();
|
||||
|
||||
// Create multiple paths from main to vuln through different intermediates
|
||||
for (int i = 1; i <= 10; i++)
|
||||
{
|
||||
edges.Add(new RichGraphEdge(
|
||||
From: "entrypoint:main",
|
||||
To: $"function:intermediate{i}",
|
||||
Kind: "call",
|
||||
Purl: null,
|
||||
SymbolDigest: null,
|
||||
Evidence: null,
|
||||
Confidence: 0.9,
|
||||
Candidates: null));
|
||||
|
||||
edges.Add(new RichGraphEdge(
|
||||
From: $"function:intermediate{i}",
|
||||
To: "vuln:component",
|
||||
Kind: "call",
|
||||
Purl: "pkg:npm/vulnerable@1.0.0",
|
||||
SymbolDigest: null,
|
||||
Evidence: null,
|
||||
Confidence: 0.9,
|
||||
Candidates: null));
|
||||
}
|
||||
|
||||
return new RichGraph(
|
||||
Nodes: nodes,
|
||||
Edges: edges,
|
||||
Roots: Array.Empty<RichGraphRoot>(),
|
||||
Analyzer: new RichGraphAnalyzer("test", "1.0", null));
|
||||
}
|
||||
|
||||
private static RichGraph CreateGraphWithDifferentEntrypoints()
|
||||
{
|
||||
var nodes = new List<RichGraphNode>
|
||||
{
|
||||
new(
|
||||
Id: "entrypoint:http",
|
||||
SymbolId: "handleRequest",
|
||||
CodeId: null,
|
||||
Purl: null,
|
||||
Lang: "javascript",
|
||||
Kind: "entrypoint",
|
||||
Display: "handleRequest()",
|
||||
BuildId: null,
|
||||
Evidence: null,
|
||||
Attributes: new Dictionary<string, string> { ["http_method"] = "POST" },
|
||||
SymbolDigest: null),
|
||||
new(
|
||||
Id: "entrypoint:main",
|
||||
SymbolId: "main",
|
||||
CodeId: null,
|
||||
Purl: null,
|
||||
Lang: "javascript",
|
||||
Kind: "main",
|
||||
Display: "main()",
|
||||
BuildId: null,
|
||||
Evidence: null,
|
||||
Attributes: null,
|
||||
SymbolDigest: null),
|
||||
new(
|
||||
Id: "vuln:component",
|
||||
SymbolId: "vulnerable",
|
||||
CodeId: null,
|
||||
Purl: "pkg:npm/vulnerable@1.0.0",
|
||||
Lang: "javascript",
|
||||
Kind: "function",
|
||||
Display: "vulnerable()",
|
||||
BuildId: null,
|
||||
Evidence: null,
|
||||
Attributes: null,
|
||||
SymbolDigest: null)
|
||||
};
|
||||
|
||||
var edges = new List<RichGraphEdge>
|
||||
{
|
||||
new(
|
||||
From: "entrypoint:http",
|
||||
To: "vuln:component",
|
||||
Kind: "call",
|
||||
Purl: "pkg:npm/vulnerable@1.0.0",
|
||||
SymbolDigest: null,
|
||||
Evidence: null,
|
||||
Confidence: 0.9,
|
||||
Candidates: null),
|
||||
new(
|
||||
From: "entrypoint:main",
|
||||
To: "vuln:component",
|
||||
Kind: "call",
|
||||
Purl: "pkg:npm/vulnerable@1.0.0",
|
||||
SymbolDigest: null,
|
||||
Evidence: null,
|
||||
Confidence: 0.9,
|
||||
Candidates: null)
|
||||
};
|
||||
|
||||
return new RichGraph(
|
||||
Nodes: nodes,
|
||||
Edges: edges,
|
||||
Roots: Array.Empty<RichGraphRoot>(),
|
||||
Analyzer: new RichGraphAnalyzer("test", "1.0", null));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user