Add comprehensive tests for PathConfidenceScorer, PathEnumerator, ShellSymbolicExecutor, and SymbolicState
- Implemented unit tests for PathConfidenceScorer to evaluate path scoring under various conditions, including empty constraints, known and unknown constraints, environmental dependencies, and custom weights. - Developed tests for PathEnumerator to ensure correct path enumeration from simple scripts, handling known environments, and respecting maximum paths and depth limits. - Created tests for ShellSymbolicExecutor to validate execution of shell scripts, including handling of commands, branching, and environment tracking. - Added tests for SymbolicState to verify state management, variable handling, constraint addition, and environment dependency collection.
This commit is contained in:
@@ -0,0 +1,254 @@
|
||||
// Licensed to StellaOps under the AGPL-3.0-or-later license.
|
||||
|
||||
using System.Collections.Immutable;
|
||||
using StellaOps.Scanner.EntryTrace.Binary;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Scanner.EntryTrace.Tests.Binary;
|
||||
|
||||
/// <summary>
|
||||
/// Unit tests for <see cref="IFingerprintIndex"/> implementations.
|
||||
/// </summary>
|
||||
public sealed class FingerprintIndexTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task InMemoryIndex_Add_IncreasesCount()
|
||||
{
|
||||
// Arrange
|
||||
var index = new InMemoryFingerprintIndex();
|
||||
var fingerprint = CreateFingerprint("test-001");
|
||||
|
||||
// Act
|
||||
await index.AddAsync(fingerprint, "pkg:npm/lodash@4.17.21", "lodash", null);
|
||||
|
||||
// Assert
|
||||
var stats = index.GetStatistics();
|
||||
Assert.Equal(1, stats.TotalFingerprints);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InMemoryIndex_LookupExact_FindsMatch()
|
||||
{
|
||||
// Arrange
|
||||
var index = new InMemoryFingerprintIndex();
|
||||
var fingerprint = CreateFingerprint("test-001");
|
||||
|
||||
await index.AddAsync(fingerprint, "pkg:npm/lodash@4.17.21", "_.map", null);
|
||||
|
||||
// Act
|
||||
var matches = await index.LookupAsync(fingerprint);
|
||||
|
||||
// Assert
|
||||
Assert.Single(matches);
|
||||
Assert.Equal("_.map", matches[0].FunctionName);
|
||||
Assert.Equal("pkg:npm/lodash@4.17.21", matches[0].SourcePackage);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InMemoryIndex_LookupExactAsync_FindsMatch()
|
||||
{
|
||||
// Arrange
|
||||
var index = new InMemoryFingerprintIndex();
|
||||
var fingerprint = CreateFingerprint("test-001");
|
||||
|
||||
await index.AddAsync(fingerprint, "pkg:npm/lodash@4.17.21", "_.map", null);
|
||||
|
||||
// Act
|
||||
var match = await index.LookupExactAsync(fingerprint);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(match);
|
||||
Assert.Equal("_.map", match.FunctionName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InMemoryIndex_LookupSimilar_LimitsResults()
|
||||
{
|
||||
// Arrange
|
||||
var index = new InMemoryFingerprintIndex();
|
||||
|
||||
// Add many fingerprints
|
||||
for (var i = 0; i < 20; i++)
|
||||
{
|
||||
var fp = CreateFingerprint($"test-{i:D3}");
|
||||
await index.AddAsync(fp, $"pkg:npm/lib{i}@1.0.0", $"func_{i}", null);
|
||||
}
|
||||
|
||||
var queryFp = CreateFingerprint("query");
|
||||
|
||||
// Act
|
||||
var matches = await index.LookupAsync(queryFp, minSimilarity: 0.1f, maxResults: 5);
|
||||
|
||||
// Assert
|
||||
Assert.True(matches.Length <= 5);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InMemoryIndex_Clear_RemovesAll()
|
||||
{
|
||||
// Arrange
|
||||
var index = new InMemoryFingerprintIndex();
|
||||
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
var fp = CreateFingerprint($"test-{i:D3}");
|
||||
await index.AddAsync(fp, $"pkg:npm/lib{i}@1.0.0", $"func_{i}", null);
|
||||
}
|
||||
|
||||
// Act
|
||||
await index.ClearAsync();
|
||||
|
||||
// Assert
|
||||
var stats = index.GetStatistics();
|
||||
Assert.Equal(0, stats.TotalFingerprints);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InMemoryIndex_Statistics_TracksPackages()
|
||||
{
|
||||
// Arrange
|
||||
var index = new InMemoryFingerprintIndex();
|
||||
|
||||
var fp1 = CreateFingerprint("test-001");
|
||||
var fp2 = CreateFingerprint("test-002");
|
||||
var fp3 = CreateFingerprint("test-003");
|
||||
|
||||
await index.AddAsync(fp1, "pkg:npm/lodash@4.17.21", "func_a", null);
|
||||
await index.AddAsync(fp2, "pkg:npm/lodash@4.17.21", "func_b", null);
|
||||
await index.AddAsync(fp3, "pkg:npm/express@4.18.0", "func_c", null);
|
||||
|
||||
// Act
|
||||
var stats = index.GetStatistics();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(3, stats.TotalFingerprints);
|
||||
Assert.Equal(2, stats.TotalPackages);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task VulnerableIndex_TracksVulnerabilities()
|
||||
{
|
||||
// Arrange
|
||||
var index = new VulnerableFingerprintIndex();
|
||||
var fp = CreateFingerprint("test-001");
|
||||
|
||||
// Act
|
||||
await index.AddVulnerableAsync(
|
||||
fp,
|
||||
"pkg:npm/lodash@4.17.20",
|
||||
"_.template",
|
||||
"CVE-2021-23337",
|
||||
"4.17.0-4.17.20",
|
||||
VulnerabilitySeverity.High);
|
||||
|
||||
// Assert
|
||||
var matches = await index.LookupAsync(fp);
|
||||
Assert.Single(matches);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task VulnerableIndex_CheckVulnerable_ReturnsMatch()
|
||||
{
|
||||
// Arrange
|
||||
var index = new VulnerableFingerprintIndex();
|
||||
var fp = CreateFingerprint("test-001");
|
||||
|
||||
await index.AddVulnerableAsync(
|
||||
fp,
|
||||
"pkg:npm/lodash@4.17.20",
|
||||
"_.template",
|
||||
"CVE-2021-23337",
|
||||
"4.17.0-4.17.20",
|
||||
VulnerabilitySeverity.High);
|
||||
|
||||
// Act
|
||||
var match = await index.CheckVulnerableAsync(fp, 0x1000);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(match);
|
||||
Assert.Equal("CVE-2021-23337", match.VulnerabilityId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task VulnerableIndex_Statistics_TracksVulns()
|
||||
{
|
||||
// Arrange
|
||||
var index = new VulnerableFingerprintIndex();
|
||||
var fp1 = CreateFingerprint("test-001");
|
||||
var fp2 = CreateFingerprint("test-002");
|
||||
|
||||
await index.AddVulnerableAsync(
|
||||
fp1, "pkg:npm/lodash@4.17.20", "_.template", "CVE-2021-23337", "4.17.x", VulnerabilitySeverity.High);
|
||||
await index.AddVulnerableAsync(
|
||||
fp2, "pkg:npm/moment@2.29.0", "moment.locale", "CVE-2022-24785", "2.29.x", VulnerabilitySeverity.Medium);
|
||||
|
||||
// Act
|
||||
var stats = index.GetStatistics();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(2, stats.TotalFingerprints);
|
||||
Assert.True(stats.TotalVulnerabilities >= 2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InMemoryIndex_AddBatch_AddsMultiple()
|
||||
{
|
||||
// Arrange
|
||||
var index = new InMemoryFingerprintIndex();
|
||||
|
||||
var matches = Enumerable.Range(0, 10)
|
||||
.Select(i => new FingerprintMatch(
|
||||
Fingerprint: CreateFingerprint($"test-{i:D3}"),
|
||||
FunctionName: $"func_{i}",
|
||||
SourcePackage: "pkg:npm/test@1.0.0",
|
||||
SourceVersion: "1.0.0",
|
||||
SourceFile: null,
|
||||
SourceLine: null,
|
||||
VulnerabilityIds: ImmutableArray<string>.Empty,
|
||||
Similarity: 1.0f,
|
||||
MatchedAt: DateTimeOffset.UtcNow))
|
||||
.ToList();
|
||||
|
||||
// Act
|
||||
foreach (var match in matches)
|
||||
{
|
||||
await index.AddAsync(match);
|
||||
}
|
||||
|
||||
// Assert
|
||||
var stats = index.GetStatistics();
|
||||
Assert.Equal(10, stats.TotalFingerprints);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InMemoryIndex_Count_ReturnsCorrectValue()
|
||||
{
|
||||
// Arrange
|
||||
var index = new InMemoryFingerprintIndex();
|
||||
|
||||
// Assert initial
|
||||
Assert.Equal(0, index.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InMemoryIndex_IndexedPackages_ReturnsEmptyInitially()
|
||||
{
|
||||
// Arrange
|
||||
var index = new InMemoryFingerprintIndex();
|
||||
|
||||
// Assert
|
||||
Assert.Empty(index.IndexedPackages);
|
||||
}
|
||||
|
||||
private static CodeFingerprint CreateFingerprint(string id)
|
||||
{
|
||||
return new CodeFingerprint(
|
||||
Id: id,
|
||||
Algorithm: FingerprintAlgorithm.BasicBlockHash,
|
||||
Hash: ImmutableArray.Create<byte>(0x01, 0x02, 0x03, 0x04),
|
||||
FunctionSize: 100,
|
||||
BasicBlockCount: 5,
|
||||
InstructionCount: 20,
|
||||
Metadata: ImmutableDictionary<string, string>.Empty);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user