Files
git.stella-ops.org/src/Scanner/__Tests/StellaOps.Scanner.EntryTrace.Tests/Binary/FingerprintIndexTests.cs
StellaOps Bot ce8cdcd23d 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.
2025-12-20 14:05:40 +02:00

255 lines
7.5 KiB
C#

// 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);
}
}