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:
Joseph Doherty
2026-06-20 17:55:12 -04:00
parent 4307c38117
commit fd618cf1dc
52 changed files with 2239 additions and 313 deletions
@@ -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));
}
}