Complete batch 012 (golden set diff) and 013 (advisory chat), fix build errors
Sprints completed: - SPRINT_20260110_012_* (golden set diff layer - 10 sprints) - SPRINT_20260110_013_* (advisory chat - 4 sprints) Build fixes applied: - Fix namespace conflicts with Microsoft.Extensions.Options.Options.Create - Fix VexDecisionReachabilityIntegrationTests API drift (major rewrite) - Fix VexSchemaValidationTests FluentAssertions method name - Fix FixChainGateIntegrationTests ambiguous type references - Fix AdvisoryAI test files required properties and namespace aliases - Add stub types for CveMappingController (ICveSymbolMappingService) - Fix VerdictBuilderService static context issue Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,275 @@
|
||||
// Licensed under AGPL-3.0-or-later. Copyright (C) 2026 StellaOps Contributors.
|
||||
|
||||
using System.Collections.Immutable;
|
||||
using FluentAssertions;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.BinaryIndex.Diff.Tests.Unit;
|
||||
|
||||
[Trait("Category", "Unit")]
|
||||
public sealed class DiffEvidenceTests
|
||||
{
|
||||
[Fact]
|
||||
public void FunctionRemoved_CreatesCorrectEvidence()
|
||||
{
|
||||
// Act
|
||||
var evidence = DiffEvidence.FunctionRemoved("vuln_func");
|
||||
|
||||
// Assert
|
||||
evidence.Type.Should().Be(DiffEvidenceType.FunctionRemoved);
|
||||
evidence.FunctionName.Should().Be("vuln_func");
|
||||
evidence.Weight.Should().Be(0.9m);
|
||||
evidence.Description.Should().Contain("vuln_func");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FunctionRenamed_CreatesCorrectEvidence()
|
||||
{
|
||||
// Act
|
||||
var evidence = DiffEvidence.FunctionRenamed("old_name", "new_name", 0.85m);
|
||||
|
||||
// Assert
|
||||
evidence.Type.Should().Be(DiffEvidenceType.FunctionRenamed);
|
||||
evidence.FunctionName.Should().Be("old_name");
|
||||
evidence.Data["OldName"].Should().Be("old_name");
|
||||
evidence.Data["NewName"].Should().Be("new_name");
|
||||
evidence.Data["Similarity"].Should().Be("0.850");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CfgStructureChanged_CreatesCorrectEvidence()
|
||||
{
|
||||
// Act
|
||||
var evidence = DiffEvidence.CfgStructureChanged("func", "hash1", "hash2");
|
||||
|
||||
// Assert
|
||||
evidence.Type.Should().Be(DiffEvidenceType.CfgStructureChanged);
|
||||
evidence.FunctionName.Should().Be("func");
|
||||
evidence.Data["PreHash"].Should().Be("hash1");
|
||||
evidence.Data["PostHash"].Should().Be("hash2");
|
||||
evidence.Weight.Should().Be(0.5m);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VulnerableEdgeRemoved_CreatesCorrectEvidence()
|
||||
{
|
||||
// Arrange
|
||||
var edges = ImmutableArray.Create("bb0->bb1", "bb1->bb2");
|
||||
|
||||
// Act
|
||||
var evidence = DiffEvidence.VulnerableEdgeRemoved("func", edges);
|
||||
|
||||
// Assert
|
||||
evidence.Type.Should().Be(DiffEvidenceType.VulnerableEdgeRemoved);
|
||||
evidence.Weight.Should().Be(1.0m);
|
||||
evidence.Data["EdgeCount"].Should().Be("2");
|
||||
evidence.Data["EdgesRemoved"].Should().Contain("bb0->bb1");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SinkMadeUnreachable_CreatesCorrectEvidence()
|
||||
{
|
||||
// Arrange
|
||||
var sinks = ImmutableArray.Create("memcpy", "strcpy");
|
||||
|
||||
// Act
|
||||
var evidence = DiffEvidence.SinkMadeUnreachable("func", sinks);
|
||||
|
||||
// Assert
|
||||
evidence.Type.Should().Be(DiffEvidenceType.SinkMadeUnreachable);
|
||||
evidence.Weight.Should().Be(0.95m);
|
||||
evidence.Data["SinkCount"].Should().Be("2");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TaintGateAdded_CreatesCorrectEvidence()
|
||||
{
|
||||
// Act
|
||||
var evidence = DiffEvidence.TaintGateAdded("func", "BoundCheck", "len < bufsize");
|
||||
|
||||
// Assert
|
||||
evidence.Type.Should().Be(DiffEvidenceType.TaintGateAdded);
|
||||
evidence.Data["GateType"].Should().Be("BoundCheck");
|
||||
evidence.Data["Condition"].Should().Be("len < bufsize");
|
||||
evidence.Weight.Should().Be(0.85m);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SemanticDivergence_CreatesCorrectEvidence()
|
||||
{
|
||||
// Act
|
||||
var evidence = DiffEvidence.SemanticDivergence("func", 0.45m);
|
||||
|
||||
// Assert
|
||||
evidence.Type.Should().Be(DiffEvidenceType.SemanticDivergence);
|
||||
evidence.Data["Similarity"].Should().Be("0.450");
|
||||
evidence.Weight.Should().Be(0.6m);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IdenticalBinaries_CreatesCorrectEvidence()
|
||||
{
|
||||
// Act
|
||||
var evidence = DiffEvidence.IdenticalBinaries("sha256:abc123");
|
||||
|
||||
// Assert
|
||||
evidence.Type.Should().Be(DiffEvidenceType.IdenticalBinaries);
|
||||
evidence.Data["Digest"].Should().Be("sha256:abc123");
|
||||
evidence.Weight.Should().Be(1.0m);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(DiffEvidenceType.FunctionRemoved)]
|
||||
[InlineData(DiffEvidenceType.FunctionRenamed)]
|
||||
[InlineData(DiffEvidenceType.CfgStructureChanged)]
|
||||
[InlineData(DiffEvidenceType.VulnerableEdgeRemoved)]
|
||||
[InlineData(DiffEvidenceType.VulnerableBlockModified)]
|
||||
[InlineData(DiffEvidenceType.SinkMadeUnreachable)]
|
||||
[InlineData(DiffEvidenceType.TaintGateAdded)]
|
||||
[InlineData(DiffEvidenceType.ConstantChanged)]
|
||||
[InlineData(DiffEvidenceType.SemanticDivergence)]
|
||||
[InlineData(DiffEvidenceType.IdenticalBinaries)]
|
||||
public void DiffEvidenceType_AllValuesAreDefined(DiffEvidenceType type)
|
||||
{
|
||||
// Assert
|
||||
Enum.IsDefined(type).Should().BeTrue();
|
||||
}
|
||||
}
|
||||
|
||||
[Trait("Category", "Unit")]
|
||||
public sealed class DiffOptionsTests
|
||||
{
|
||||
[Fact]
|
||||
public void Default_HasSensibleDefaults()
|
||||
{
|
||||
// Act
|
||||
var options = DiffOptions.Default;
|
||||
|
||||
// Assert
|
||||
options.IncludeSemanticAnalysis.Should().BeFalse();
|
||||
options.IncludeReachabilityAnalysis.Should().BeTrue();
|
||||
options.SemanticThreshold.Should().Be(0.85m);
|
||||
options.FixedConfidenceThreshold.Should().Be(0.80m);
|
||||
options.DetectRenames.Should().BeTrue();
|
||||
options.FunctionTimeout.Should().Be(TimeSpan.FromSeconds(30));
|
||||
options.TotalTimeout.Should().Be(TimeSpan.FromMinutes(10));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DiffOptions_CanBeCustomized()
|
||||
{
|
||||
// Act
|
||||
var options = new DiffOptions
|
||||
{
|
||||
IncludeSemanticAnalysis = true,
|
||||
SemanticThreshold = 0.95m,
|
||||
DetectRenames = false
|
||||
};
|
||||
|
||||
// Assert
|
||||
options.IncludeSemanticAnalysis.Should().BeTrue();
|
||||
options.SemanticThreshold.Should().Be(0.95m);
|
||||
options.DetectRenames.Should().BeFalse();
|
||||
}
|
||||
}
|
||||
|
||||
[Trait("Category", "Unit")]
|
||||
public sealed class DiffMetadataTests
|
||||
{
|
||||
[Fact]
|
||||
public void CurrentEngineVersion_IsSet()
|
||||
{
|
||||
// Assert
|
||||
DiffMetadata.CurrentEngineVersion.Should().NotBeNullOrEmpty();
|
||||
DiffMetadata.CurrentEngineVersion.Should().Be("1.0.0");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DiffMetadata_StoresAllProperties()
|
||||
{
|
||||
// Arrange
|
||||
var comparedAt = DateTimeOffset.UtcNow;
|
||||
var duration = TimeSpan.FromSeconds(5);
|
||||
var options = DiffOptions.Default;
|
||||
|
||||
// Act
|
||||
var metadata = new DiffMetadata
|
||||
{
|
||||
ComparedAt = comparedAt,
|
||||
EngineVersion = DiffMetadata.CurrentEngineVersion,
|
||||
Duration = duration,
|
||||
Options = options
|
||||
};
|
||||
|
||||
// Assert
|
||||
metadata.ComparedAt.Should().Be(comparedAt);
|
||||
metadata.EngineVersion.Should().Be("1.0.0");
|
||||
metadata.Duration.Should().Be(duration);
|
||||
metadata.Options.Should().Be(options);
|
||||
}
|
||||
}
|
||||
|
||||
[Trait("Category", "Unit")]
|
||||
public sealed class SingleBinaryCheckResultTests
|
||||
{
|
||||
[Fact]
|
||||
public void NotVulnerable_CreatesCorrectResult()
|
||||
{
|
||||
// Arrange
|
||||
var binaryDigest = "sha256:abc123";
|
||||
var goldenSetId = "CVE-2024-1234";
|
||||
var checkedAt = DateTimeOffset.UtcNow;
|
||||
var duration = TimeSpan.FromMilliseconds(50);
|
||||
|
||||
// Act
|
||||
var result = SingleBinaryCheckResult.NotVulnerable(
|
||||
binaryDigest, goldenSetId, checkedAt, duration);
|
||||
|
||||
// Assert
|
||||
result.IsVulnerable.Should().BeFalse();
|
||||
result.Confidence.Should().Be(0.9m);
|
||||
result.BinaryDigest.Should().Be(binaryDigest);
|
||||
result.GoldenSetId.Should().Be(goldenSetId);
|
||||
result.FunctionResults.Should().BeEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
[Trait("Category", "Unit")]
|
||||
public sealed class FunctionRenameTests
|
||||
{
|
||||
[Fact]
|
||||
public void FunctionRename_StoresAllProperties()
|
||||
{
|
||||
// Act
|
||||
var rename = new FunctionRename
|
||||
{
|
||||
OriginalName = "old_func",
|
||||
NewName = "new_func",
|
||||
Confidence = 0.92m,
|
||||
Similarity = 0.92m
|
||||
};
|
||||
|
||||
// Assert
|
||||
rename.OriginalName.Should().Be("old_func");
|
||||
rename.NewName.Should().Be("new_func");
|
||||
rename.Confidence.Should().Be(0.92m);
|
||||
rename.Similarity.Should().Be(0.92m);
|
||||
}
|
||||
}
|
||||
|
||||
[Trait("Category", "Unit")]
|
||||
public sealed class RenameDetectionOptionsTests
|
||||
{
|
||||
[Fact]
|
||||
public void Default_HasSensibleDefaults()
|
||||
{
|
||||
// Act
|
||||
var options = RenameDetectionOptions.Default;
|
||||
|
||||
// Assert
|
||||
options.MinSimilarity.Should().Be(0.7m);
|
||||
options.UseCfgHash.Should().BeTrue();
|
||||
options.UseBlockHashes.Should().BeTrue();
|
||||
options.UseStringRefs.Should().BeTrue();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user