docs(tests): fix CommentChecker findings across tests/ — 13,392 → 0 (repo-wide scan clean)
Comment/XML-doc changes only: - add summaries to ~10k test methods, fixtures, fakes and helpers (+ <param> on theories, <returns> on async Task methods, <inheritdoc/> on interface implementations the checker can resolve) - strip ~2.6k project-tracking references (WP*, P*-*, row-NN, Task N, #NN, Component-0NN) from comments, keeping the explanatory text; reword false positives in comments only - real docs on fakes implementing BCL/library interfaces (IDisposable, ILogger, IOptionsMonitor, IAsyncLifetime) - reword the last 5 src/ false positives (CWE-1236, failures-1, year-0001, M365) Only non-comment edits: four one-line nested classes split across lines so members can carry docs. Build 0 errors / 0 warnings.
This commit is contained in:
+22
@@ -17,8 +17,10 @@ public class CachingScriptMetadataResolverTests
|
||||
{
|
||||
public int MissingCalls;
|
||||
public int ReferenceCalls;
|
||||
/// <inheritdoc />
|
||||
public override bool ResolveMissingAssemblies => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override PortableExecutableReference? ResolveMissingAssembly(
|
||||
MetadataReference definition, AssemblyIdentity referenceIdentity)
|
||||
{
|
||||
@@ -26,6 +28,7 @@ public class CachingScriptMetadataResolverTests
|
||||
return MetadataReference.CreateFromFile(typeof(object).Assembly.Location);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ImmutableArray<PortableExecutableReference> ResolveReference(
|
||||
string reference, string? baseFilePath, MetadataReferenceProperties properties)
|
||||
{
|
||||
@@ -33,7 +36,9 @@ public class CachingScriptMetadataResolverTests
|
||||
return [MetadataReference.CreateFromFile(typeof(object).Assembly.Location)];
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object? other) => ReferenceEquals(this, other);
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode() => 0;
|
||||
}
|
||||
|
||||
@@ -41,8 +46,10 @@ public class CachingScriptMetadataResolverTests
|
||||
private sealed class NullReturningResolver : MetadataReferenceResolver
|
||||
{
|
||||
public int MissingCalls;
|
||||
/// <inheritdoc />
|
||||
public override bool ResolveMissingAssemblies => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override PortableExecutableReference? ResolveMissingAssembly(
|
||||
MetadataReference definition, AssemblyIdentity referenceIdentity)
|
||||
{
|
||||
@@ -50,11 +57,14 @@ public class CachingScriptMetadataResolverTests
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ImmutableArray<PortableExecutableReference> ResolveReference(
|
||||
string reference, string? baseFilePath, MetadataReferenceProperties properties)
|
||||
=> [];
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object? other) => ReferenceEquals(this, other);
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode() => 0;
|
||||
}
|
||||
|
||||
@@ -62,6 +72,7 @@ public class CachingScriptMetadataResolverTests
|
||||
private static readonly MetadataReference SomeDefinition =
|
||||
MetadataReference.CreateFromFile(typeof(object).Assembly.Location);
|
||||
|
||||
/// <summary>Verifies that resolving the same identity twice resolves once and shares the instance.</summary>
|
||||
[Fact]
|
||||
public void ResolveMissingAssembly_SameIdentityTwice_ResolvesOnceAndSharesInstance()
|
||||
{
|
||||
@@ -75,6 +86,7 @@ public class CachingScriptMetadataResolverTests
|
||||
Assert.Same(first, second); // one AssemblyMetadata/PEReader/native block, not two
|
||||
}
|
||||
|
||||
/// <summary>Verifies that distinct identities are resolved independently.</summary>
|
||||
[Fact]
|
||||
public void ResolveMissingAssembly_DistinctIdentities_ResolveIndependently()
|
||||
{
|
||||
@@ -133,6 +145,7 @@ public class CachingScriptMetadataResolverTests
|
||||
Assert.Equal(1, inner.MissingCalls);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that resolving a reference twice with the same arguments resolves once and shares the instances.</summary>
|
||||
[Fact]
|
||||
public void ResolveReference_SameArgsTwice_ResolvesOnceAndSharesInstances()
|
||||
{
|
||||
@@ -146,10 +159,12 @@ public class CachingScriptMetadataResolverTests
|
||||
Assert.Same(first[0], second[0]);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the shared instance is a process-wide singleton.</summary>
|
||||
[Fact]
|
||||
public void Instance_IsProcessWideSingleton()
|
||||
=> Assert.Same(CachingScriptMetadataResolver.Instance, CachingScriptMetadataResolver.Instance);
|
||||
|
||||
/// <summary>Verifies that the resolve-missing-assemblies flag is delegated to the inner resolver.</summary>
|
||||
[Fact]
|
||||
public void ResolveMissingAssemblies_DelegatesToInner()
|
||||
{
|
||||
@@ -204,18 +219,25 @@ public class CachingScriptMetadataResolverTests
|
||||
{
|
||||
private readonly MetadataReferenceResolver _inner;
|
||||
public long MissingCalls;
|
||||
/// <summary>Initializes the probe around an inner resolver.</summary>
|
||||
/// <param name="inner">The resolver to delegate to.</param>
|
||||
public ProbeResolver(MetadataReferenceResolver inner) => _inner = inner;
|
||||
/// <inheritdoc />
|
||||
public override bool ResolveMissingAssemblies => _inner.ResolveMissingAssemblies;
|
||||
/// <inheritdoc />
|
||||
public override PortableExecutableReference? ResolveMissingAssembly(
|
||||
MetadataReference definition, AssemblyIdentity referenceIdentity)
|
||||
{
|
||||
Interlocked.Increment(ref MissingCalls);
|
||||
return _inner.ResolveMissingAssembly(definition, referenceIdentity);
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public override ImmutableArray<PortableExecutableReference> ResolveReference(
|
||||
string reference, string? baseFilePath, MetadataReferenceProperties properties)
|
||||
=> _inner.ResolveReference(reference, baseFilePath, properties);
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object? other) => ReferenceEquals(this, other);
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode() => 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ public class FlowShapePredictorTests
|
||||
private static string? AnySiteScript(string instanceCode, string scriptName)
|
||||
=> scriptName == "ProcessRecipeDownload" ? DelmiaSiteScript : null;
|
||||
|
||||
/// <summary>Verifies that delmia method plus site script predicts the full swimlane shape.</summary>
|
||||
[Fact]
|
||||
public void Delmia_MethodPlusSiteScript_PredictsTheFullSwimlaneShape()
|
||||
{
|
||||
@@ -104,6 +105,7 @@ public class FlowShapePredictorTests
|
||||
Assert.True(flow.Edges[^1].CrossesBoundary);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that constant route target is not input dependent and qualifies site titles.</summary>
|
||||
[Fact]
|
||||
public void ConstantRouteTarget_IsNotInputDependent_AndQualifiesSiteTitles()
|
||||
{
|
||||
@@ -124,6 +126,7 @@ public class FlowShapePredictorTests
|
||||
Assert.StartsWith("Z28061Sim.", write.Title);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that resolver returning null leaves the site run unexpanded.</summary>
|
||||
[Fact]
|
||||
public void ResolverReturningNull_LeavesTheSiteRunUnexpanded()
|
||||
{
|
||||
@@ -137,6 +140,7 @@ public class FlowShapePredictorTests
|
||||
], flow.Nodes.Select(n => n.Kind).ToList());
|
||||
}
|
||||
|
||||
/// <summary>Verifies that no recognised call yields only request and response.</summary>
|
||||
[Fact]
|
||||
public void NoRecognisedCall_YieldsOnlyRequestAndResponse()
|
||||
{
|
||||
@@ -150,6 +154,7 @@ public class FlowShapePredictorTests
|
||||
Assert.False(edge.CrossesBoundary);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that recursion stops at max depth.</summary>
|
||||
[Fact]
|
||||
public void Recursion_StopsAtMaxDepth()
|
||||
{
|
||||
@@ -168,6 +173,7 @@ public class FlowShapePredictorTests
|
||||
Assert.True(deep.Nodes.Count > shallow.Nodes.Count);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that malformed code never throws and notes the parse error.</summary>
|
||||
[Fact]
|
||||
public void MalformedCode_NeverThrows_AndNotesTheParseError()
|
||||
{
|
||||
@@ -182,6 +188,7 @@ public class FlowShapePredictorTests
|
||||
Assert.Equal(FlowShapePredictor.ParseErrorNote, root.Note);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that predict site script roots at the script run and recognises attribute ops.</summary>
|
||||
[Fact]
|
||||
public void PredictSiteScript_RootsAtTheScriptRun_AndRecognisesAttributeOps()
|
||||
{
|
||||
@@ -208,6 +215,7 @@ public class FlowShapePredictorTests
|
||||
Assert.Equal(FlowLane.External, flow.Nodes[4].Lane);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that outbound calls sit on the external lane sync in chain cached and notify branch async.</summary>
|
||||
[Fact]
|
||||
public void OutboundCalls_SitOnTheExternalLane_SyncInChain_CachedAndNotifyBranchAsync()
|
||||
{
|
||||
@@ -246,6 +254,7 @@ public class FlowShapePredictorTests
|
||||
Assert.All(flow.Edges, e => Assert.False(e.CrossesBoundary));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that cached call is an async api call and notify to resolves the list.</summary>
|
||||
[Fact]
|
||||
public void CachedCall_IsAnAsyncApiCall_AndNotifyTo_ResolvesTheList()
|
||||
{
|
||||
@@ -268,6 +277,7 @@ public class FlowShapePredictorTests
|
||||
Assert.Equal([(flow.Nodes[0].Id, true), (flow.Nodes[0].Id, true)], flow.Edges.Select(e => (e.From, e.Async)).ToList());
|
||||
}
|
||||
|
||||
/// <summary>Verifies that outbound calls with computed targets are input dependent.</summary>
|
||||
[Fact]
|
||||
public void OutboundCalls_WithComputedTargets_AreInputDependent()
|
||||
{
|
||||
@@ -286,6 +296,7 @@ public class FlowShapePredictorTests
|
||||
Assert.Equal("Notify.Send(list)", outbound[2].Title);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that inbound method database call crosses from central to external.</summary>
|
||||
[Fact]
|
||||
public void InboundMethod_DatabaseCall_CrossesFromCentralToExternal()
|
||||
{
|
||||
@@ -304,6 +315,7 @@ public class FlowShapePredictorTests
|
||||
Assert.False(into.Async);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that input dependent attribute name is flagged.</summary>
|
||||
[Fact]
|
||||
public void InputDependentAttributeName_IsFlagged()
|
||||
{
|
||||
@@ -314,7 +326,7 @@ public class FlowShapePredictorTests
|
||||
Assert.Equal("name", write.Title);
|
||||
}
|
||||
|
||||
/// <summary>Gitea #36: the reset-aware overload predicts the reset write + echo wait ahead of the batch.</summary>
|
||||
/// <summary>The reset-aware overload predicts the reset write + echo wait ahead of the batch.</summary>
|
||||
[Fact]
|
||||
public void ResetAwareWriteBatchAndWait_PredictsResetWriteAndEchoWaitBeforeTheBatch()
|
||||
{
|
||||
|
||||
@@ -3,25 +3,28 @@ using ZB.MOM.WW.ScadaBridge.ScriptAnalysis;
|
||||
namespace ZB.MOM.WW.ScadaBridge.ScriptAnalysis.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// M3.1: parse + compile gate tests. The "representative real script" corpus is
|
||||
/// Parse + compile gate tests. The "representative real script" corpus is
|
||||
/// the PRIMARY guard that <see cref="ScriptCompileSurface"/> faithfully mirrors
|
||||
/// the runtime <c>ScriptGlobals</c> surface — if a member or signature drifts,
|
||||
/// the corpus stops binding and this test fails.
|
||||
/// </summary>
|
||||
public class RoslynScriptCompilerTests
|
||||
{
|
||||
/// <summary>Verifies that parse diagnostics non empty for syntax error.</summary>
|
||||
[Fact]
|
||||
public void ParseDiagnostics_NonEmpty_ForSyntaxError()
|
||||
{
|
||||
Assert.NotEmpty(RoslynScriptCompiler.ParseDiagnostics("var x = ;"));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that parse diagnostics empty for valid syntax.</summary>
|
||||
[Fact]
|
||||
public void ParseDiagnostics_Empty_ForValidSyntax()
|
||||
{
|
||||
Assert.Empty(RoslynScriptCompiler.ParseDiagnostics("var x = 1;"));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that compile non empty for undefined symbol.</summary>
|
||||
[Fact]
|
||||
public void Compile_NonEmpty_ForUndefinedSymbol()
|
||||
{
|
||||
@@ -29,6 +32,7 @@ public class RoslynScriptCompilerTests
|
||||
Assert.NotEmpty(RoslynScriptCompiler.Compile(code, typeof(ScriptCompileSurface)));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that compile empty for representative real script.</summary>
|
||||
[Fact]
|
||||
public void Compile_Empty_ForRepresentativeRealScript()
|
||||
{
|
||||
@@ -67,6 +71,7 @@ public class RoslynScriptCompilerTests
|
||||
Assert.Empty(diagnostics);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that compile empty for a recipe file parsing script.</summary>
|
||||
[Fact]
|
||||
public void Compile_Empty_ForARecipeFileParsingScript()
|
||||
{
|
||||
@@ -89,6 +94,7 @@ public class RoslynScriptCompilerTests
|
||||
Assert.Empty(RoslynScriptCompiler.Compile(code, typeof(ScriptCompileSurface)));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that compile empty for the mes alarm status script shape.</summary>
|
||||
[Fact]
|
||||
public void Compile_Empty_ForTheMesAlarmStatusScriptShape()
|
||||
{
|
||||
@@ -139,6 +145,7 @@ public class RoslynScriptCompilerTests
|
||||
Assert.Empty(RoslynScriptCompiler.Compile(code, typeof(ScriptCompileSurface)));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that compile empty for trigger expression.</summary>
|
||||
[Fact]
|
||||
public void Compile_Empty_ForTriggerExpression()
|
||||
{
|
||||
@@ -149,6 +156,7 @@ public class RoslynScriptCompilerTests
|
||||
Assert.Empty(diagnostics);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that compile empty for wait async and wait for async.</summary>
|
||||
[Fact]
|
||||
public void Compile_Empty_ForWaitAsyncAndWaitForAsync()
|
||||
{
|
||||
@@ -171,6 +179,7 @@ public class RoslynScriptCompilerTests
|
||||
Assert.Empty(diagnostics);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that compiles cleanly for the log global.</summary>
|
||||
[Fact]
|
||||
public void CompilesCleanly_ForTheLogGlobal()
|
||||
{
|
||||
@@ -190,6 +199,7 @@ public class RoslynScriptCompilerTests
|
||||
Assert.Empty(diagnostics);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that compile twice with shared resolver still compiles cleanly.</summary>
|
||||
[Fact]
|
||||
public void Compile_TwiceWithSharedResolver_StillCompilesCleanly()
|
||||
{
|
||||
|
||||
@@ -3,7 +3,7 @@ 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
|
||||
/// 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
|
||||
@@ -11,6 +11,8 @@ namespace ZB.MOM.WW.ScadaBridge.ScriptAnalysis.Tests;
|
||||
/// </summary>
|
||||
public class ScriptTrustValidatorTests
|
||||
{
|
||||
/// <summary>Verifies that runtime escape APIs are rejected.</summary>
|
||||
/// <param name="code">The script source containing a runtime escape API call.</param>
|
||||
[Theory]
|
||||
[InlineData("object o = new object(); System.Runtime.CompilerServices.Unsafe.As<object, string>(ref o);")]
|
||||
[InlineData("System.Runtime.CompilerServices.RuntimeHelpers.GetUninitializedObject(typeof(object));")]
|
||||
@@ -22,6 +24,8 @@ public class ScriptTrustValidatorTests
|
||||
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that compiler-support constructs are allowed.</summary>
|
||||
/// <param name="code">The script source using compiler-support APIs.</param>
|
||||
[Theory]
|
||||
[InlineData("await System.Threading.Tasks.Task.Delay(1);")]
|
||||
[InlineData("string Name([System.Runtime.CompilerServices.CallerMemberName] string name = \"\") => name;")]
|
||||
@@ -32,6 +36,7 @@ public class ScriptTrustValidatorTests
|
||||
|
||||
// ---- Reject (non-empty violations) --------------------------------------
|
||||
|
||||
/// <summary>Verifies that a <c>using</c> of the System.IO namespace is rejected.</summary>
|
||||
[Fact]
|
||||
public void Rejects_SystemIo_Using()
|
||||
{
|
||||
@@ -39,6 +44,7 @@ public class ScriptTrustValidatorTests
|
||||
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a global-qualified System.IO reference is rejected.</summary>
|
||||
[Fact]
|
||||
public void Rejects_SystemIo_GlobalQualified()
|
||||
{
|
||||
@@ -46,6 +52,7 @@ public class ScriptTrustValidatorTests
|
||||
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that an aliased System.IO reference is rejected.</summary>
|
||||
[Fact]
|
||||
public void Rejects_SystemIo_Aliased()
|
||||
{
|
||||
@@ -53,6 +60,7 @@ public class ScriptTrustValidatorTests
|
||||
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a <c>using static</c> of a System.IO type is rejected.</summary>
|
||||
[Fact]
|
||||
public void Rejects_SystemIo_UsingStatic()
|
||||
{
|
||||
@@ -60,6 +68,7 @@ public class ScriptTrustValidatorTests
|
||||
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that reflection-gateway members are rejected on a permitted type.</summary>
|
||||
[Fact]
|
||||
public void Rejects_ReflectionGateway_OffPermittedType()
|
||||
{
|
||||
@@ -67,6 +76,7 @@ public class ScriptTrustValidatorTests
|
||||
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the <c>dynamic</c> keyword is rejected.</summary>
|
||||
[Fact]
|
||||
public void Rejects_Dynamic_Keyword()
|
||||
{
|
||||
@@ -74,6 +84,7 @@ public class ScriptTrustValidatorTests
|
||||
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that <c>Activator.CreateInstance</c> is rejected.</summary>
|
||||
[Fact]
|
||||
public void Rejects_Activator_CreateInstance()
|
||||
{
|
||||
@@ -81,6 +92,7 @@ public class ScriptTrustValidatorTests
|
||||
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that System.Runtime.InteropServices usage is rejected.</summary>
|
||||
[Fact]
|
||||
public void Rejects_RuntimeInteropServices()
|
||||
{
|
||||
@@ -88,6 +100,7 @@ public class ScriptTrustValidatorTests
|
||||
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that Microsoft.Win32 usage is rejected.</summary>
|
||||
[Fact]
|
||||
public void Rejects_MicrosoftWin32()
|
||||
{
|
||||
@@ -95,6 +108,7 @@ public class ScriptTrustValidatorTests
|
||||
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that <c>Thread.Sleep</c> is rejected.</summary>
|
||||
[Fact]
|
||||
public void Rejects_Threading_Thread_Sleep()
|
||||
{
|
||||
@@ -102,6 +116,7 @@ public class ScriptTrustValidatorTests
|
||||
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that all System.Net usage is rejected.</summary>
|
||||
[Fact]
|
||||
public void Rejects_All_SystemNet()
|
||||
{
|
||||
@@ -109,6 +124,7 @@ public class ScriptTrustValidatorTests
|
||||
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that reflection-gateway members are rejected on an allowed Task type.</summary>
|
||||
[Fact]
|
||||
public void Rejects_ReflectionGateway_OffAllowedTaskType()
|
||||
{
|
||||
@@ -119,6 +135,7 @@ public class ScriptTrustValidatorTests
|
||||
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that forbidden IO nested inside an allowed <c>Task.Run</c> lambda is rejected.</summary>
|
||||
[Fact]
|
||||
public void Rejects_ForbiddenIo_NestedInAllowedTaskRunLambda()
|
||||
{
|
||||
@@ -129,6 +146,7 @@ public class ScriptTrustValidatorTests
|
||||
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a forbidden mutex used as a generic argument under an allowed Tasks prefix is rejected.</summary>
|
||||
[Fact]
|
||||
public void Rejects_ForbiddenMutex_AsGenericArg_UnderAllowedTasksPrefix()
|
||||
{
|
||||
@@ -139,6 +157,7 @@ public class ScriptTrustValidatorTests
|
||||
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a direct threading mutex is rejected independently of <c>Thread.Sleep</c>.</summary>
|
||||
[Fact]
|
||||
public void Rejects_DirectThreadingMutex_NotThreadSleep()
|
||||
{
|
||||
@@ -148,6 +167,7 @@ public class ScriptTrustValidatorTests
|
||||
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that <c>FileInfo</c> used as a generic argument is rejected.</summary>
|
||||
[Fact]
|
||||
public void Rejects_ForbiddenFileInfo_AsGenericArg()
|
||||
{
|
||||
@@ -157,6 +177,7 @@ public class ScriptTrustValidatorTests
|
||||
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that <c>nameof</c> applied to a forbidden type is rejected.</summary>
|
||||
[Fact]
|
||||
public void Rejects_NameOf_ForbiddenType()
|
||||
{
|
||||
@@ -166,6 +187,7 @@ public class ScriptTrustValidatorTests
|
||||
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a qualified <c>Process</c> type reference is rejected.</summary>
|
||||
[Fact]
|
||||
public void Rejects_Process_QualifiedType()
|
||||
{
|
||||
@@ -173,6 +195,7 @@ public class ScriptTrustValidatorTests
|
||||
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a bare <c>Process</c> identifier introduced via a <c>using</c> is rejected.</summary>
|
||||
[Fact]
|
||||
public void Rejects_Process_BareIdentifier_ViaUsing()
|
||||
{
|
||||
@@ -185,6 +208,7 @@ public class ScriptTrustValidatorTests
|
||||
|
||||
// ---- Clean (empty violations) -------------------------------------------
|
||||
|
||||
/// <summary>Verifies that <c>Task.Delay</c> is allowed.</summary>
|
||||
[Fact]
|
||||
public void Allows_TasksDelay()
|
||||
{
|
||||
@@ -192,6 +216,7 @@ public class ScriptTrustValidatorTests
|
||||
Assert.Empty(ScriptTrustValidator.FindViolations(code));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that <c>Stopwatch</c> from System.Diagnostics is allowed while <c>Process</c> is not.</summary>
|
||||
[Fact]
|
||||
public void Allows_DiagnosticsStopwatch_NotProcess()
|
||||
{
|
||||
@@ -199,6 +224,7 @@ public class ScriptTrustValidatorTests
|
||||
Assert.Empty(ScriptTrustValidator.FindViolations(code));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that <c>CancellationTokenSource</c> is allowed.</summary>
|
||||
[Fact]
|
||||
public void Allows_CancellationTokenSource()
|
||||
{
|
||||
@@ -206,6 +232,7 @@ public class ScriptTrustValidatorTests
|
||||
Assert.Empty(ScriptTrustValidator.FindViolations(code));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that LINQ and <c>Math</c> usage is allowed.</summary>
|
||||
[Fact]
|
||||
public void Allows_LinqAndMath()
|
||||
{
|
||||
@@ -213,6 +240,7 @@ public class ScriptTrustValidatorTests
|
||||
Assert.Empty(ScriptTrustValidator.FindViolations(code));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that <c>Alarms.CurrentAsync</c> and the <c>ScriptAlarm</c> type are allowed.</summary>
|
||||
[Fact]
|
||||
public void Allows_AlarmsCurrentAsync_AndTheScriptAlarmType()
|
||||
{
|
||||
@@ -231,6 +259,7 @@ public class ScriptTrustValidatorTests
|
||||
Assert.Empty(ScriptTrustValidator.FindViolations(code));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the <c>Log</c> global is allowed.</summary>
|
||||
[Fact]
|
||||
public void Allows_TheLogGlobal()
|
||||
{
|
||||
@@ -249,6 +278,7 @@ public class ScriptTrustValidatorTests
|
||||
Assert.Empty(ScriptTrustValidator.FindViolations(code));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the forbidden scopes do not cover the Commons script surface.</summary>
|
||||
[Fact]
|
||||
public void ForbiddenScopes_DoNotCover_TheCommonsScriptSurface()
|
||||
{
|
||||
@@ -261,9 +291,9 @@ public class ScriptTrustValidatorTests
|
||||
.Namespace!.StartsWith(scope, StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
// ---- ScriptAnalysis-003: adversarial bypass-vector coverage --------------
|
||||
// ---- Adversarial bypass-vector coverage --------------
|
||||
|
||||
// (a) TPA-FALLBACK DEGRADATION (the SA-001 hole). Forces Pass 1 onto the
|
||||
// (a) TPA-FALLBACK DEGRADATION (the original bypass hole). Forces Pass 1 onto the
|
||||
// minimal fallback reference set (DefaultAssemblies + ForbiddenAnchorAssemblies)
|
||||
// — the set used on a single-file/AOT/trimmed host with no TPA list — and
|
||||
// proves a BARE forbidden type inside an ALLOWED namespace is STILL caught.
|
||||
@@ -272,18 +302,20 @@ public class ScriptTrustValidatorTests
|
||||
// bare identifier — so `Process.Start` slipped the validator entirely. The
|
||||
// anchor assemblies folded into the fallback close that hole.
|
||||
|
||||
/// <summary>Verifies that the minimal fallback reference set still rejects a bare <c>Process</c> introduced via a <c>using</c>.</summary>
|
||||
[Fact]
|
||||
public void TpaFallback_StillRejects_BareProcess_ViaUsing()
|
||||
{
|
||||
// The documented forbidden-type-in-allowed-namespace case: System.Diagnostics
|
||||
// is allowed (Stopwatch/Debug), the `using` is not flagged, and `Process`
|
||||
// is a BARE identifier. Against the minimal fallback set this must still
|
||||
// be rejected — otherwise the SA-001 fallback hole is open.
|
||||
// be rejected — otherwise the fallback hole is open.
|
||||
var minimal = ScriptTrustPolicy.BuildMinimalFallbackReferences();
|
||||
var code = "using System.Diagnostics; var p = Process.Start(\"x\");";
|
||||
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code, minimal));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the minimal fallback reference set still rejects a bare <c>Socket</c> introduced via a <c>using</c>.</summary>
|
||||
[Fact]
|
||||
public void TpaFallback_StillRejects_BareSocket_ViaUsing()
|
||||
{
|
||||
@@ -295,6 +327,7 @@ public class ScriptTrustValidatorTests
|
||||
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code, minimal));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the minimal fallback reference set still allows <c>Stopwatch</c>.</summary>
|
||||
[Fact]
|
||||
public void TpaFallback_StillAllows_DiagnosticsStopwatch()
|
||||
{
|
||||
@@ -305,6 +338,7 @@ public class ScriptTrustValidatorTests
|
||||
Assert.Empty(ScriptTrustValidator.FindViolations(code, minimal));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the minimal fallback references resolve <c>Process</c> as a forbidden type.</summary>
|
||||
[Fact]
|
||||
public void MinimalFallbackReferences_Resolve_Process_AsForbidden()
|
||||
{
|
||||
@@ -323,6 +357,7 @@ public class ScriptTrustValidatorTests
|
||||
// (a forbidden namespace) even though it is invoked in receiver position — the
|
||||
// semantic pass resolves the reduced extension method's containing type, so the
|
||||
// forbidden namespace is caught through the invocation itself.
|
||||
/// <summary>Verifies that an extension method from a forbidden namespace is rejected.</summary>
|
||||
[Fact]
|
||||
public void Rejects_ExtensionMethod_InForbiddenNamespace()
|
||||
{
|
||||
@@ -337,6 +372,7 @@ public class ScriptTrustValidatorTests
|
||||
// VisitIdentifierName compares Identifier.ValueText, which decodes both the
|
||||
// verbatim '@' prefix and \uXXXX escapes — so neither spelling evades the
|
||||
// ForbiddenIdentifiers deny-list.
|
||||
/// <summary>Verifies that a verbatim-identifier <c>Activator</c> reference is rejected.</summary>
|
||||
[Fact]
|
||||
public void Rejects_VerbatimIdentifier_Activator()
|
||||
{
|
||||
@@ -344,6 +380,7 @@ public class ScriptTrustValidatorTests
|
||||
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a Unicode-escaped <c>Activator</c> identifier is rejected.</summary>
|
||||
[Fact]
|
||||
public void Rejects_UnicodeEscapedIdentifier_Activator()
|
||||
{
|
||||
@@ -356,6 +393,7 @@ public class ScriptTrustValidatorTests
|
||||
// language-feature gate: a benign `unsafe` block reaches no forbidden API, so
|
||||
// it must NOT be a false positive — while a forbidden API used INSIDE an unsafe
|
||||
// block is still caught (the walker descends into the block).
|
||||
/// <summary>Verifies that an unsafe block without forbidden APIs is allowed.</summary>
|
||||
[Fact]
|
||||
public void Allows_BenignUnsafeBlock_NoForbiddenApi()
|
||||
{
|
||||
@@ -363,6 +401,7 @@ public class ScriptTrustValidatorTests
|
||||
Assert.Empty(ScriptTrustValidator.FindViolations(code));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a forbidden API inside an unsafe block is rejected.</summary>
|
||||
[Fact]
|
||||
public void Rejects_ForbiddenApi_InsideUnsafeBlock()
|
||||
{
|
||||
@@ -375,6 +414,7 @@ public class ScriptTrustValidatorTests
|
||||
// must stay clean (the walker inspects name/member nodes, never trivia or
|
||||
// literal text). Reconstructing a forbidden API from runtime strings is outside
|
||||
// the static validator's remit (documented sandbox caveat).
|
||||
/// <summary>Verifies that a forbidden namespace mentioned only in a comment is allowed.</summary>
|
||||
[Fact]
|
||||
public void Allows_ForbiddenNamespace_InCommentOnly()
|
||||
{
|
||||
@@ -382,6 +422,7 @@ public class ScriptTrustValidatorTests
|
||||
Assert.Empty(ScriptTrustValidator.FindViolations(code));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a forbidden namespace mentioned only in a string literal is allowed.</summary>
|
||||
[Fact]
|
||||
public void Allows_ForbiddenNamespace_InStringLiteralOnly()
|
||||
{
|
||||
@@ -389,10 +430,12 @@ public class ScriptTrustValidatorTests
|
||||
Assert.Empty(ScriptTrustValidator.FindViolations(code));
|
||||
}
|
||||
|
||||
// ---- PLAN-05 Task 21: hardened deny-list additions -----------------------
|
||||
// ---- Hardened deny-list additions -----------------------
|
||||
|
||||
// Environment (whole type), GC, and the concrete ADO.NET provider namespaces
|
||||
// (Microsoft.Data / System.Data.SqlClient / .Odbc / .OleDb) are now forbidden.
|
||||
/// <summary>Verifies that scripts using hardened deny-list APIs are flagged as violations.</summary>
|
||||
/// <param name="code">The script source expected to be flagged.</param>
|
||||
[Theory]
|
||||
[InlineData("Environment.Exit(0);")]
|
||||
[InlineData("Environment.FailFast(\"x\");")]
|
||||
@@ -402,6 +445,8 @@ public class ScriptTrustValidatorTests
|
||||
|
||||
// Syntactic gateway closure (matters in TPA-degraded fallback mode): these
|
||||
// reflection-gateway members are rejected regardless of receiver.
|
||||
/// <summary>Verifies that the syntactic pass flags reflection-gateway members regardless of receiver.</summary>
|
||||
/// <param name="code">The script source expected to be flagged.</param>
|
||||
[Theory]
|
||||
[InlineData("typeof(string).Assembly.GetTypes()")]
|
||||
[InlineData("asm.EntryPoint")]
|
||||
@@ -409,6 +454,8 @@ public class ScriptTrustValidatorTests
|
||||
|
||||
// Must NOT regress legitimate scripts: Task.Delay stays allowed and
|
||||
// delegate.Invoke() is deliberately NOT on the reflection-gateway list.
|
||||
/// <summary>Verifies that legitimate scripts are not flagged as violations.</summary>
|
||||
/// <param name="code">The script source expected to be allowed.</param>
|
||||
[Theory]
|
||||
[InlineData("await Task.Delay(1);")]
|
||||
[InlineData("Func<int,int> f = x => x; f.Invoke(3);")]
|
||||
|
||||
Reference in New Issue
Block a user