using StellaOps.Scanner.Analyzers.Lang.DotNet.Internal.Capabilities;
namespace StellaOps.Scanner.Analyzers.Lang.DotNet.Tests.Internal;
///
/// Tests for .
///
public sealed class DotNetCapabilityScannerTests
{
private const string TestFile = "Test.cs";
#region ScanFile - General Tests
[Fact]
public void ScanFile_NullContent_ReturnsEmpty()
{
var result = DotNetCapabilityScanner.ScanFile(null!, TestFile);
Assert.Empty(result);
}
[Fact]
public void ScanFile_EmptyContent_ReturnsEmpty()
{
var result = DotNetCapabilityScanner.ScanFile("", TestFile);
Assert.Empty(result);
}
[Fact]
public void ScanFile_WhitespaceContent_ReturnsEmpty()
{
var result = DotNetCapabilityScanner.ScanFile(" \n\t\n ", TestFile);
Assert.Empty(result);
}
[Fact]
public void ScanFile_NoPatterns_ReturnsEmpty()
{
const string code = @"
namespace Test
{
public class Program
{
public static void Main() => Console.WriteLine(""Hello"");
}
}";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Empty(result);
}
[Fact]
public void ScanFile_NormalizesBackslashesInPath()
{
const string code = @"Process.Start(""notepad.exe"");";
var result = DotNetCapabilityScanner.ScanFile(code, @"C:\src\Test.cs");
Assert.Single(result);
Assert.Equal("C:/src/Test.cs", result[0].SourceFile);
}
[Fact]
public void ScanFile_DeduplicatesSamePatternOnSameLine()
{
const string code = @"Process.Start(""cmd""); Process.Start(""notepad"");";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
// Same pattern on same line should be deduplicated
Assert.Single(result);
}
#endregion
#region ScanFile - Comment Stripping
[Fact]
public void ScanFile_IgnoresSingleLineComments()
{
const string code = @"
// Process.Start(""cmd"");
public void Method() { }";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Empty(result);
}
[Fact]
public void ScanFile_IgnoresMultiLineComments()
{
const string code = @"
/*
Process.Start(""cmd"");
File.Delete(""file.txt"");
*/
public void Method() { }";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Empty(result);
}
#endregion
#region ScanFile - Exec Patterns
[Fact]
public void ScanFile_DetectsProcessStart()
{
const string code = @"Process.Start(""notepad.exe"");";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.Exec, result[0].Kind);
Assert.Equal("Process.Start", result[0].Pattern);
Assert.Equal(CapabilityRisk.Critical, result[0].Risk);
Assert.Equal(1.0f, result[0].Confidence);
}
[Fact]
public void ScanFile_DetectsNewProcessStartInfo()
{
const string code = @"var psi = new ProcessStartInfo(""cmd.exe"");";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.Exec, result[0].Kind);
Assert.Equal("ProcessStartInfo", result[0].Pattern);
Assert.Equal(CapabilityRisk.Critical, result[0].Risk);
}
[Fact]
public void ScanFile_DetectsProcessStartInfoObjectInitializer()
{
const string code = @"var psi = new ProcessStartInfo { FileName = ""cmd.exe"" };";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.Exec, result[0].Kind);
}
[Fact]
public void ScanFile_DetectsUseShellExecuteTrue()
{
const string code = @"psi.UseShellExecute = true;";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.Exec, result[0].Kind);
Assert.Equal("UseShellExecute=true", result[0].Pattern);
Assert.Equal(CapabilityRisk.Critical, result[0].Risk);
}
#endregion
#region ScanFile - Filesystem Patterns
[Fact]
public void ScanFile_DetectsFileReadAllText()
{
const string code = @"var content = File.ReadAllText(""file.txt"");";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.Filesystem, result[0].Kind);
Assert.Equal("File.ReadAll/WriteAll", result[0].Pattern);
Assert.Equal(CapabilityRisk.Medium, result[0].Risk);
}
[Fact]
public void ScanFile_DetectsFileWriteAllText()
{
const string code = @"File.WriteAllText(""file.txt"", content);";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.Filesystem, result[0].Kind);
Assert.Equal("File.ReadAll/WriteAll", result[0].Pattern);
}
[Fact]
public void ScanFile_DetectsFileDelete()
{
const string code = @"File.Delete(""file.txt"");";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.Filesystem, result[0].Kind);
Assert.Equal("File/Directory.Delete", result[0].Pattern);
Assert.Equal(CapabilityRisk.High, result[0].Risk);
}
[Fact]
public void ScanFile_DetectsDirectoryDelete()
{
const string code = @"Directory.Delete(""dir"", true);";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.Filesystem, result[0].Kind);
Assert.Equal("File/Directory.Delete", result[0].Pattern);
Assert.Equal(CapabilityRisk.High, result[0].Risk);
}
[Fact]
public void ScanFile_DetectsFileCopy()
{
const string code = @"File.Copy(""src.txt"", ""dest.txt"");";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.Filesystem, result[0].Kind);
Assert.Equal("File/Directory operations", result[0].Pattern);
Assert.Equal(CapabilityRisk.Medium, result[0].Risk);
}
[Fact]
public void ScanFile_DetectsNewFileStream()
{
const string code = @"using var fs = new FileStream(""file.bin"", FileMode.Open);";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.Filesystem, result[0].Kind);
Assert.Equal("FileStream", result[0].Pattern);
}
[Fact]
public void ScanFile_DetectsSetAccessControl()
{
const string code = @"fileInfo.SetAccessControl(security);";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.Filesystem, result[0].Kind);
Assert.Equal("SetAccessControl", result[0].Pattern);
Assert.Equal(CapabilityRisk.High, result[0].Risk);
}
#endregion
#region ScanFile - Network Patterns
[Fact]
public void ScanFile_DetectsNewHttpClient()
{
const string code = @"using var client = new HttpClient();";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.Network, result[0].Kind);
Assert.Equal("HttpClient", result[0].Pattern);
Assert.Equal(CapabilityRisk.Medium, result[0].Risk);
}
[Fact]
public void ScanFile_DetectsGetAsync()
{
const string code = @"var response = await client.GetAsync(url);";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.Network, result[0].Kind);
Assert.Equal("HttpClient", result[0].Pattern);
}
[Fact]
public void ScanFile_DetectsNewWebClient()
{
const string code = @"using var client = new WebClient();";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.Network, result[0].Kind);
Assert.Equal("WebClient", result[0].Pattern);
}
[Fact]
public void ScanFile_DetectsNewSocket()
{
const string code = @"var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.Network, result[0].Kind);
Assert.Equal("Socket/TcpClient", result[0].Pattern);
}
[Fact]
public void ScanFile_DetectsNewTcpClient()
{
const string code = @"var tcp = new TcpClient(""localhost"", 8080);";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.Network, result[0].Kind);
Assert.Equal("Socket/TcpClient", result[0].Pattern);
}
[Fact]
public void ScanFile_DetectsWebRequestCreate()
{
const string code = @"var request = WebRequest.Create(url);";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.Network, result[0].Kind);
Assert.Equal("WebRequest", result[0].Pattern);
}
#endregion
#region ScanFile - Environment Patterns
[Fact]
public void ScanFile_DetectsEnvironmentGetEnvironmentVariable()
{
const string code = @"var path = Environment.GetEnvironmentVariable(""PATH"");";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.Environment, result[0].Kind);
Assert.Equal("Environment.GetEnvironmentVariable", result[0].Pattern);
Assert.Equal(CapabilityRisk.Medium, result[0].Risk);
}
[Fact]
public void ScanFile_DetectsEnvironmentSetEnvironmentVariable()
{
const string code = @"Environment.SetEnvironmentVariable(""MY_VAR"", ""value"");";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.Environment, result[0].Kind);
Assert.Equal("Environment.SetEnvironmentVariable", result[0].Pattern);
Assert.Equal(CapabilityRisk.High, result[0].Risk);
}
[Fact]
public void ScanFile_DetectsEnvironmentGetEnvironmentVariables()
{
const string code = @"var envVars = Environment.GetEnvironmentVariables();";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.Environment, result[0].Kind);
Assert.Equal("Environment.GetEnvironmentVariables", result[0].Pattern);
}
#endregion
#region ScanFile - Serialization Patterns (Critical for deserialization attacks)
[Fact]
public void ScanFile_DetectsBinaryFormatter()
{
const string code = @"var formatter = new BinaryFormatter();";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.Serialization, result[0].Kind);
Assert.Equal("BinaryFormatter", result[0].Pattern);
Assert.Equal(CapabilityRisk.Critical, result[0].Risk);
}
[Fact]
public void ScanFile_DetectsObjectStateFormatter()
{
const string code = @"var formatter = new ObjectStateFormatter();";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.Serialization, result[0].Kind);
Assert.Equal("ObjectStateFormatter", result[0].Pattern);
Assert.Equal(CapabilityRisk.Critical, result[0].Risk);
}
[Fact]
public void ScanFile_DetectsNetDataContractSerializer()
{
const string code = @"var serializer = new NetDataContractSerializer();";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.Serialization, result[0].Kind);
Assert.Equal("NetDataContractSerializer", result[0].Pattern);
Assert.Equal(CapabilityRisk.Critical, result[0].Risk);
}
[Fact]
public void ScanFile_DetectsLosFormatter()
{
const string code = @"var formatter = new LosFormatter();";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.Serialization, result[0].Kind);
Assert.Equal("LosFormatter", result[0].Pattern);
Assert.Equal(CapabilityRisk.Critical, result[0].Risk);
}
[Fact]
public void ScanFile_DetectsSoapFormatter()
{
const string code = @"var formatter = new SoapFormatter();";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.Serialization, result[0].Kind);
Assert.Equal("SoapFormatter", result[0].Pattern);
Assert.Equal(CapabilityRisk.Critical, result[0].Risk);
}
[Fact]
public void ScanFile_DetectsDataContractSerializer()
{
const string code = @"var serializer = new DataContractSerializer(typeof(MyClass));";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.Serialization, result[0].Kind);
Assert.Equal("DataContractSerializer", result[0].Pattern);
Assert.Equal(CapabilityRisk.Medium, result[0].Risk);
}
[Fact]
public void ScanFile_DetectsJsonDeserialize()
{
const string code = @"var obj = JsonSerializer.Deserialize(json);";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.Serialization, result[0].Kind);
Assert.Equal("JsonSerializer.Deserialize", result[0].Pattern);
Assert.Equal(CapabilityRisk.Low, result[0].Risk);
}
#endregion
#region ScanFile - Crypto Patterns
[Fact]
public void ScanFile_DetectsAesCreate()
{
const string code = @"using var aes = Aes.Create();";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.Crypto, result[0].Kind);
Assert.Equal("Cryptography", result[0].Pattern);
Assert.Equal(CapabilityRisk.Low, result[0].Risk);
}
[Fact]
public void ScanFile_DetectsRsaCreate()
{
const string code = @"using var rsa = RSA.Create();";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.Crypto, result[0].Kind);
Assert.Equal("Asymmetric crypto", result[0].Pattern);
}
#endregion
#region ScanFile - Database Patterns
[Fact]
public void ScanFile_DetectsNewSqlConnection()
{
const string code = @"using var conn = new SqlConnection(connectionString);";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.Database, result[0].Kind);
Assert.Equal("SqlConnection", result[0].Pattern);
Assert.Equal(CapabilityRisk.Medium, result[0].Risk);
}
[Fact]
public void ScanFile_DetectsNewSqlCommand()
{
const string code = @"var cmd = new SqlCommand(""SELECT * FROM Users"", conn);";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.Database, result[0].Kind);
Assert.Equal("SqlCommand", result[0].Pattern);
}
[Fact]
public void ScanFile_DetectsExecuteNonQuery()
{
const string code = @"cmd.ExecuteNonQuery();";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.Database, result[0].Kind);
Assert.Equal("Execute*", result[0].Pattern);
}
[Fact]
public void ScanFile_DetectsExecuteReader()
{
const string code = @"using var reader = cmd.ExecuteReader();";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.Database, result[0].Kind);
}
#endregion
#region ScanFile - Dynamic Code Patterns
[Fact]
public void ScanFile_DetectsDynamicMethod()
{
const string code = @"var dm = new DynamicMethod(""Test"", typeof(int), null);";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.DynamicCode, result[0].Kind);
Assert.Equal("DynamicMethod", result[0].Pattern);
Assert.Equal(CapabilityRisk.Critical, result[0].Risk);
}
[Fact]
public void ScanFile_DetectsILGenerator()
{
const string code = @"var il = dm.GetILGenerator();";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.DynamicCode, result[0].Kind);
Assert.Equal("ILGenerator", result[0].Pattern);
Assert.Equal(CapabilityRisk.Critical, result[0].Risk);
}
[Fact]
public void ScanFile_DetectsCSharpScript()
{
const string code = @"var result = await CSharpScript.EvaluateAsync(code);";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.DynamicCode, result[0].Kind);
Assert.Equal("CSharpScript", result[0].Pattern);
Assert.Equal(CapabilityRisk.Critical, result[0].Risk);
}
[Fact]
public void ScanFile_DetectsTypeBuilder()
{
const string code = @"var tb = mb.DefineType(""MyType"");";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
// TypeBuilder check expects "TypeBuilder" in the line
Assert.Empty(result); // DefineType doesn't match TypeBuilder pattern
}
#endregion
#region ScanFile - Reflection Patterns
[Fact]
public void ScanFile_DetectsAssemblyLoad()
{
const string code = @"var assembly = Assembly.Load(""MyAssembly"");";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.Reflection, result[0].Kind);
Assert.Equal("Assembly.Load", result[0].Pattern);
Assert.Equal(CapabilityRisk.High, result[0].Risk);
}
[Fact]
public void ScanFile_DetectsAssemblyLoadFrom()
{
const string code = @"var assembly = Assembly.LoadFrom(""plugin.dll"");";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.Reflection, result[0].Kind);
Assert.Equal("Assembly.Load", result[0].Pattern);
}
[Fact]
public void ScanFile_DetectsAssemblyLoadFile()
{
const string code = @"var assembly = Assembly.LoadFile(path);";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.Reflection, result[0].Kind);
}
[Fact]
public void ScanFile_DetectsTypeInvokeMember()
{
const string code = @"type.InvokeMember(""Method"", BindingFlags.InvokeMethod, null, obj, args);";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.Reflection, result[0].Kind);
Assert.Equal("Type.InvokeMember", result[0].Pattern);
Assert.Equal(CapabilityRisk.High, result[0].Risk);
}
[Fact]
public void ScanFile_DetectsActivatorCreateInstance()
{
const string code = @"var obj = Activator.CreateInstance(type);";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.Reflection, result[0].Kind);
Assert.Equal("Activator.CreateInstance", result[0].Pattern);
Assert.Equal(CapabilityRisk.Medium, result[0].Risk);
}
#endregion
#region ScanFile - Native Code Patterns
[Fact]
public void ScanFile_DetectsDllImport()
{
const string code = @"[DllImport(""kernel32.dll"")]";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.NativeCode, result[0].Kind);
Assert.Equal("DllImport", result[0].Pattern);
Assert.Equal(CapabilityRisk.High, result[0].Risk);
}
[Fact]
public void ScanFile_DetectsLibraryImport()
{
const string code = @"[LibraryImport(""user32.dll"")]";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.NativeCode, result[0].Kind);
Assert.Equal("LibraryImport", result[0].Pattern);
Assert.Equal(CapabilityRisk.High, result[0].Risk);
}
[Fact]
public void ScanFile_DetectsMarshalPtrToStructure()
{
const string code = @"var obj = Marshal.PtrToStructure(ptr);";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.NativeCode, result[0].Kind);
Assert.Equal("Marshal operations", result[0].Pattern);
}
[Fact]
public void ScanFile_DetectsMarshalAllocHGlobal()
{
const string code = @"var ptr = Marshal.AllocHGlobal(size);";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.NativeCode, result[0].Kind);
}
[Fact]
public void ScanFile_DetectsNativeLibraryLoad()
{
const string code = @"var lib = NativeLibrary.Load(""mylib.dll"");";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.NativeCode, result[0].Kind);
Assert.Equal("NativeLibrary.Load", result[0].Pattern);
}
[Fact]
public void ScanFile_DetectsIntPtrOperations()
{
const string code = @"var ptr = new IntPtr(address);";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.NativeCode, result[0].Kind);
Assert.Equal("IntPtr operations", result[0].Pattern);
Assert.Equal(CapabilityRisk.Medium, result[0].Risk);
}
#endregion
#region ScanFile - Unsafe Patterns
[Fact]
public void ScanFile_DetectsUnsafeBlock()
{
const string code = @"unsafe { var ptr = &value; }";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.NativeCode, result[0].Kind);
Assert.Equal("unsafe block", result[0].Pattern);
Assert.Equal(CapabilityRisk.Critical, result[0].Risk);
}
[Fact]
public void ScanFile_DetectsFixedStatement()
{
const string code = @"fixed (byte* ptr = array) { }";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.NativeCode, result[0].Kind);
Assert.Equal("fixed statement", result[0].Pattern);
Assert.Equal(CapabilityRisk.High, result[0].Risk);
}
[Fact]
public void ScanFile_DetectsStackalloc()
{
const string code = @"Span buffer = stackalloc byte[256];";
var result = DotNetCapabilityScanner.ScanFile(code, TestFile);
Assert.Single(result);
Assert.Equal(CapabilityKind.NativeCode, result[0].Kind);
Assert.Equal("stackalloc", result[0].Pattern);
Assert.Equal(CapabilityRisk.High, result[0].Risk);
}
#endregion
#region DotNetCapabilityEvidence Tests
[Fact]
public void Evidence_DeduplicationKey_IsCorrect()
{
var evidence = new DotNetCapabilityEvidence(
CapabilityKind.Exec,
"Test.cs",
10,
"Process.Start");
Assert.Equal("Exec|Test.cs|10|Process.Start", evidence.DeduplicationKey);
}
[Fact]
public void Evidence_ConfidenceIsClamped()
{
var evidence1 = new DotNetCapabilityEvidence(
CapabilityKind.Exec, "Test.cs", 1, "pattern",
confidence: 2.0f);
var evidence2 = new DotNetCapabilityEvidence(
CapabilityKind.Exec, "Test.cs", 1, "pattern",
confidence: -1.0f);
Assert.Equal(1.0f, evidence1.Confidence);
Assert.Equal(0.0f, evidence2.Confidence);
}
[Fact]
public void Evidence_CreateMetadata_IncludesAllFields()
{
var evidence = new DotNetCapabilityEvidence(
CapabilityKind.Exec,
"Test.cs",
10,
"Process.Start",
snippet: "Process.Start(\"cmd.exe\");",
confidence: 0.95f,
risk: CapabilityRisk.Critical);
var metadata = evidence.CreateMetadata().ToDictionary(kv => kv.Key, kv => kv.Value);
Assert.Equal("exec", metadata["capability.kind"]);
Assert.Equal("Test.cs:10", metadata["capability.source"]);
Assert.Equal("Process.Start", metadata["capability.pattern"]);
Assert.Equal("critical", metadata["capability.risk"]);
Assert.Equal("0.95", metadata["capability.confidence"]);
Assert.Contains("Process.Start", metadata["capability.snippet"]);
}
[Fact]
public void Evidence_ToLanguageEvidence_ReturnsCorrectFormat()
{
var evidence = new DotNetCapabilityEvidence(
CapabilityKind.Exec,
"Test.cs",
10,
"Process.Start");
var langEvidence = evidence.ToLanguageEvidence();
Assert.Equal(LanguageEvidenceKind.Metadata, langEvidence.Kind);
Assert.Equal("Test.cs", langEvidence.Source);
Assert.Equal("line:10", langEvidence.Locator);
Assert.Equal("Exec:Process.Start", langEvidence.Value);
}
#endregion
}