fix(review): full code-review remediation — 5 High + Medium/Low across 16 modules
Remediation from the full per-module code review at 4307c381 (findings recorded
separately in code-reviews/).
Highs fixed:
- DeploymentManager-025/SiteRuntime-031: stop broadcasting notification lists + SMTP
configs (incl. credentials) to sites; site purges already-persisted rows on apply
(enforces the central-only delivery design; clears plaintext SMTP creds at rest).
- DataConnectionLayer-023: guard the native-alarm subscribe path against the
mid-flight-unsubscribe adapter-feed leak (mirrors the DCL-021 tag-path fix).
- SiteEventLogging-024: normalize From/To query bounds to UTC (the -016 fix the
audit trail claimed but never committed).
- KpiHistory-001: add an in-flight guard to the recorder sample tick.
- ScriptAnalysis-001: harden the trust analyzer's TPA-absent fallback (resolve
forbidden anchors in the minimal reference set; warn on degraded mode) — anchors
added to validation references only, never the compile gate.
(InboundAPI-026 left to the feat/ipsen-movein effort per owner decision.)
Medium/Low: DM-026 deterministic deploy-status tiebreaker; SR-027/028/029/030
native-alarm leak/phantom-active/delete-during-redeploy fixes; AL-013/014/016;
TE-024 (folder-mutation audit rows now persisted)/025; SF-025 gauge-provider
clear-on-stop; ESG-025/026; SEC-023/024/025; SCA-007/008/009; plus doc/test
accuracy COM-023/024, HOST-025/026, HM-024/025, NS-027/028.
Full-solution build 0 warnings; ~3560 tests across 18 touched suites green.
This commit is contained in:
@@ -193,4 +193,132 @@ public class ScriptTrustValidatorTests
|
||||
var code = "var n = System.Linq.Enumerable.Range(0,3).Sum(); var m = System.Math.Max(1,2);";
|
||||
Assert.Empty(ScriptTrustValidator.FindViolations(code));
|
||||
}
|
||||
|
||||
// ---- ScriptAnalysis-003: adversarial bypass-vector coverage --------------
|
||||
|
||||
// (a) TPA-FALLBACK DEGRADATION (the SA-001 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.
|
||||
// Before the fix, `Process` resolved to nothing against the minimal set, the
|
||||
// syntactic fallback ignored the dotless identifier, and Pass 2 never flags a
|
||||
// bare identifier — so `Process.Start` slipped the validator entirely. The
|
||||
// anchor assemblies folded into the fallback close that hole.
|
||||
|
||||
[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.
|
||||
var minimal = ScriptTrustPolicy.BuildMinimalFallbackReferences();
|
||||
var code = "using System.Diagnostics; var p = Process.Start(\"x\");";
|
||||
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code, minimal));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TpaFallback_StillRejects_BareSocket_ViaUsing()
|
||||
{
|
||||
// System.Net.Sockets.Socket lives in its own assembly (not CoreLib); the
|
||||
// anchor set must include it so the minimal fallback still resolves a bare
|
||||
// `Socket` reference.
|
||||
var minimal = ScriptTrustPolicy.BuildMinimalFallbackReferences();
|
||||
var code = "using System.Net.Sockets; Socket s = null;";
|
||||
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code, minimal));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TpaFallback_StillAllows_DiagnosticsStopwatch()
|
||||
{
|
||||
// Control: the fallback must not over-block — Stopwatch (System.Diagnostics,
|
||||
// allowed) stays clean even against the minimal anchor-enriched set.
|
||||
var minimal = ScriptTrustPolicy.BuildMinimalFallbackReferences();
|
||||
var code = "var sw = System.Diagnostics.Stopwatch.StartNew();";
|
||||
Assert.Empty(ScriptTrustValidator.FindViolations(code, minimal));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MinimalFallbackReferences_Resolve_Process_AsForbidden()
|
||||
{
|
||||
// Pins the resolution mechanism directly: against the minimal fallback set,
|
||||
// bare `Process` resolves to its true namespace and is reported by the
|
||||
// semantic pass (the message names the forbidden scope), not merely caught
|
||||
// by some incidental syntactic rule.
|
||||
var minimal = ScriptTrustPolicy.BuildMinimalFallbackReferences();
|
||||
var violations = ScriptTrustValidator.FindViolations(
|
||||
"using System.Diagnostics; var p = Process.Start(\"x\");", minimal);
|
||||
Assert.Contains(violations, v => v.Contains("System.Diagnostics.Process", StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
// (b) EXTENSION-METHOD invocation of a forbidden API. `asm.GetCustomAttribute<T>()`
|
||||
// resolves to the extension method on System.Reflection.CustomAttributeExtensions
|
||||
// (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.
|
||||
[Fact]
|
||||
public void Rejects_ExtensionMethod_InForbiddenNamespace()
|
||||
{
|
||||
var code =
|
||||
"using System.Reflection; " +
|
||||
"Assembly asm = typeof(string).Assembly; " +
|
||||
"var a = asm.GetCustomAttribute<System.ObsoleteAttribute>();";
|
||||
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code));
|
||||
}
|
||||
|
||||
// (c) VERBATIM (@) and UNICODE-ESCAPE spellings of a forbidden identifier.
|
||||
// VisitIdentifierName compares Identifier.ValueText, which decodes both the
|
||||
// verbatim '@' prefix and \uXXXX escapes — so neither spelling evades the
|
||||
// ForbiddenIdentifiers deny-list.
|
||||
[Fact]
|
||||
public void Rejects_VerbatimIdentifier_Activator()
|
||||
{
|
||||
var code = "var o = @Activator.CreateInstance(typeof(string));";
|
||||
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Rejects_UnicodeEscapedIdentifier_Activator()
|
||||
{
|
||||
// 'A' is 'A' — the token spells "Activator" but ValueText is "Activator".
|
||||
var code = "var o = \\u0041ctivator.CreateInstance(typeof(string));";
|
||||
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code));
|
||||
}
|
||||
|
||||
// (d) UNSAFE block. The validator is a forbidden-API deny-list, not a
|
||||
// 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).
|
||||
[Fact]
|
||||
public void Allows_BenignUnsafeBlock_NoForbiddenApi()
|
||||
{
|
||||
var code = "unsafe { int x = 1; int* p = &x; var y = *p; }";
|
||||
Assert.Empty(ScriptTrustValidator.FindViolations(code));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Rejects_ForbiddenApi_InsideUnsafeBlock()
|
||||
{
|
||||
var code = "unsafe { var t = typeof(string).Assembly; }";
|
||||
Assert.NotEmpty(ScriptTrustValidator.FindViolations(code));
|
||||
}
|
||||
|
||||
// (e) COMMENT / STRING-LITERAL must NOT cause a false positive. A forbidden
|
||||
// namespace mentioned only in trivia or a string literal reaches no API and
|
||||
// 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).
|
||||
[Fact]
|
||||
public void Allows_ForbiddenNamespace_InCommentOnly()
|
||||
{
|
||||
var code = "// using System.IO; File.ReadAllText(\"x\")\nvar y = 1;";
|
||||
Assert.Empty(ScriptTrustValidator.FindViolations(code));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Allows_ForbiddenNamespace_InStringLiteralOnly()
|
||||
{
|
||||
var code = "var s = \"System.IO.File.ReadAllText\";";
|
||||
Assert.Empty(ScriptTrustValidator.FindViolations(code));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user