180 lines
6.3 KiB
C#
180 lines
6.3 KiB
C#
using ZB.MOM.WW.ScadaBridge.ScriptAnalysis;
|
|
|
|
namespace ZB.MOM.WW.ScadaBridge.ScriptAnalysis.Tests;
|
|
|
|
/// <summary>
|
|
/// M3.1: adversarial + legitimate cases for the fused trust validator. Reject
|
|
/// cases exercise the semantic pass (alias / using static / global::), the
|
|
/// reflection-gateway hardening pass, and the namespace deny-list union; clean
|
|
/// cases pin the allowed exceptions (Tasks, CancellationToken, Diagnostics
|
|
/// other than Process).
|
|
/// </summary>
|
|
public class ScriptTrustValidatorTests
|
|
{
|
|
// ---- Reject (non-empty violations) --------------------------------------
|
|
|
|
[Fact]
|
|
public void Rejects_SystemIo_Using()
|
|
{
|
|
var code = "using System.IO; var x = File.ReadAllText(\"/etc/passwd\");";
|
|
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code));
|
|
}
|
|
|
|
[Fact]
|
|
public void Rejects_SystemIo_GlobalQualified()
|
|
{
|
|
var code = "var x = global::System.IO.File.ReadAllText(\"x\");";
|
|
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code));
|
|
}
|
|
|
|
[Fact]
|
|
public void Rejects_SystemIo_Aliased()
|
|
{
|
|
var code = "using IO = System.IO; var f = IO.File.ReadAllText(\"x\");";
|
|
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code));
|
|
}
|
|
|
|
[Fact]
|
|
public void Rejects_SystemIo_UsingStatic()
|
|
{
|
|
var code = "using static System.IO.File; var s = ReadAllText(\"x\");";
|
|
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code));
|
|
}
|
|
|
|
[Fact]
|
|
public void Rejects_ReflectionGateway_OffPermittedType()
|
|
{
|
|
var code = "var t = typeof(string).Assembly.GetType(\"System.IO.File\");";
|
|
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code));
|
|
}
|
|
|
|
[Fact]
|
|
public void Rejects_Dynamic_Keyword()
|
|
{
|
|
var code = "dynamic d = 5; d.Foo();";
|
|
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code));
|
|
}
|
|
|
|
[Fact]
|
|
public void Rejects_Activator_CreateInstance()
|
|
{
|
|
var code = "var o = Activator.CreateInstance(typeof(string));";
|
|
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code));
|
|
}
|
|
|
|
[Fact]
|
|
public void Rejects_RuntimeInteropServices()
|
|
{
|
|
var code = "using System.Runtime.InteropServices; var h = Marshal.SizeOf<int>();";
|
|
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code));
|
|
}
|
|
|
|
[Fact]
|
|
public void Rejects_MicrosoftWin32()
|
|
{
|
|
var code = "using Microsoft.Win32; var k = Registry.LocalMachine;";
|
|
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code));
|
|
}
|
|
|
|
[Fact]
|
|
public void Rejects_Threading_Thread_Sleep()
|
|
{
|
|
var code = "System.Threading.Thread.Sleep(10);";
|
|
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code));
|
|
}
|
|
|
|
[Fact]
|
|
public void Rejects_All_SystemNet()
|
|
{
|
|
var code = "System.Net.WebClient w = null;";
|
|
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code));
|
|
}
|
|
|
|
[Fact]
|
|
public void Rejects_ReflectionGateway_OffAllowedTaskType()
|
|
{
|
|
// Guards the reflection-first ordering: a gateway member hung off an
|
|
// allowed System.Threading.Tasks type must still be rejected even though
|
|
// the chain's namespace prefix is an allowed exception.
|
|
var code = "var a = System.Threading.Tasks.Task.CompletedTask.GetType().Assembly;";
|
|
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code));
|
|
}
|
|
|
|
[Fact]
|
|
public void Rejects_ForbiddenIo_NestedInAllowedTaskRunLambda()
|
|
{
|
|
// A forbidden System.IO reference buried inside an allowed Task.Run lambda.
|
|
// The allowed-exception prefix on the outer member access must NOT shadow
|
|
// the nested forbidden reference — Pass 2 must descend into the lambda.
|
|
var code = "await System.Threading.Tasks.Task.Run(() => System.IO.File.ReadAllText(\"x\"));";
|
|
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code));
|
|
}
|
|
|
|
[Fact]
|
|
public void Rejects_ForbiddenMutex_AsGenericArg_UnderAllowedTasksPrefix()
|
|
{
|
|
// System.Threading.Mutex (forbidden) appears as a generic argument of an
|
|
// allowed System.Threading.Tasks.TaskCompletionSource<T>. The allowed
|
|
// outer name must not shadow the forbidden generic arg.
|
|
var code = "System.Threading.Tasks.TaskCompletionSource<System.Threading.Mutex> tcs = null;";
|
|
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code));
|
|
}
|
|
|
|
[Fact]
|
|
public void Rejects_DirectThreadingMutex_NotThreadSleep()
|
|
{
|
|
// A direct forbidden System.Threading type (not Thread.Sleep) — pins that
|
|
// the broad System.Threading deny-list catches more than the one cased test.
|
|
var code = "var m = new System.Threading.Mutex();";
|
|
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code));
|
|
}
|
|
|
|
[Fact]
|
|
public void Rejects_ForbiddenFileInfo_AsGenericArg()
|
|
{
|
|
// System.IO.FileInfo (forbidden) as a generic argument of an allowed
|
|
// System.Collections.Generic.List<T>.
|
|
var code = "System.Collections.Generic.List<System.IO.FileInfo> x = null;";
|
|
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code));
|
|
}
|
|
|
|
[Fact]
|
|
public void Rejects_NameOf_ForbiddenType()
|
|
{
|
|
// Conservative fail-safe: naming a forbidden type inside nameof(...) is
|
|
// deliberately flagged (a script has no business naming it even there).
|
|
var code = "var s = nameof(System.IO.File);";
|
|
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code));
|
|
}
|
|
|
|
// ---- Clean (empty violations) -------------------------------------------
|
|
|
|
[Fact]
|
|
public void Allows_TasksDelay()
|
|
{
|
|
var code = "await System.Threading.Tasks.Task.Delay(1);";
|
|
Assert.Empty(ScriptTrustValidator.FindViolations(code));
|
|
}
|
|
|
|
[Fact]
|
|
public void Allows_DiagnosticsStopwatch_NotProcess()
|
|
{
|
|
var code = "var sw = System.Diagnostics.Stopwatch.StartNew();";
|
|
Assert.Empty(ScriptTrustValidator.FindViolations(code));
|
|
}
|
|
|
|
[Fact]
|
|
public void Allows_CancellationTokenSource()
|
|
{
|
|
var code = "var c = new System.Threading.CancellationTokenSource();";
|
|
Assert.Empty(ScriptTrustValidator.FindViolations(code));
|
|
}
|
|
|
|
[Fact]
|
|
public void Allows_LinqAndMath()
|
|
{
|
|
var code = "var n = System.Linq.Enumerable.Range(0,3).Sum(); var m = System.Math.Max(1,2);";
|
|
Assert.Empty(ScriptTrustValidator.FindViolations(code));
|
|
}
|
|
}
|