fix(scripting): block ThreadPool/Timer/AssemblyLoadContext in sandbox
Core.Scripting-012 (High, Security) resolution.
The Core.Scripting-008 rewrite broadened the BCL references list from a
narrow allow-list to the full System.* + netstandard +
Microsoft.Win32.Registry set, delegating the security gate entirely to
ForbiddenTypeAnalyzer. Three categories of dangerous BCL types were
reachable from script source without a deny-list entry:
- System.Threading.ThreadPool — QueueUserWorkItem re-introduces the
background-fanout threat Core.Scripting-003 closed against
System.Threading.Tasks.
- System.Threading.Timer — schedules unbounded callback work that
outlives the per-evaluation timeout.
- System.Runtime.Loader.AssemblyLoadContext — loads arbitrary DLLs.
Defense-in-depth gap; invocation needs reflection (already denied)
but the load itself was reachable.
Fix:
- Added 'System.Runtime.Loader' to ForbiddenNamespacePrefixes
(preferred over type-granular per the recommendation so future BCL
additions to that namespace are denied by default).
- Added 'System.Threading.ThreadPool' and 'System.Threading.Timer'
to ForbiddenFullTypeNames — both live in System.Threading shared
with allowed primitives so they must be type-granular.
Regression tests added to ScriptSandboxTests:
Rejects_ThreadPool_QueueUserWorkItem_at_compile
Rejects_Timer_new_at_compile
Rejects_AssemblyLoadContext_at_compile
Docs:
docs/v2/implementation/phase-7-scripting-and-alarming.md decision #6
and the Sandbox-escape compliance-check row both updated to enumerate
the new entries per the Core.Scripting-009 doc-sync convention.
Two lower-impact suggestions from the finding's recommendation
(System.Console, CultureInfo.DefaultThreadCurrentCulture) were
intentionally not addressed and are recorded as accepted minor risks
in the resolution.
Verification: Core.Scripting.Tests 107/107 (was 104 + 3 new rejection
tests).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -446,4 +446,52 @@ public sealed class ScriptSandboxTests
|
||||
return 0;
|
||||
"""));
|
||||
}
|
||||
|
||||
// --- Core.Scripting-012: BCL refs broadened by -008/-016 re-exposed three vectors
|
||||
// the original deny-list missed. Each is denied type-granularly in
|
||||
// ForbiddenTypeAnalyzer.ForbiddenFullTypeNames; these tests pin the rejection.
|
||||
|
||||
[Fact]
|
||||
public void Rejects_ThreadPool_QueueUserWorkItem_at_compile()
|
||||
{
|
||||
// System.Threading.ThreadPool.QueueUserWorkItem re-introduces the background-
|
||||
// fanout threat Core.Scripting-003 closed against System.Threading.Tasks. The
|
||||
// ThreadPool type lives in System.Threading (shared with allowed sync primitives
|
||||
// like CancellationToken), so it must be denied type-granularly. (Core.Scripting-012.)
|
||||
Should.Throw<ScriptSandboxViolationException>(() =>
|
||||
ScriptEvaluator<FakeScriptContext, int>.Compile(
|
||||
"""
|
||||
System.Threading.ThreadPool.QueueUserWorkItem(_ => { });
|
||||
return 0;
|
||||
"""));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Rejects_Timer_new_at_compile()
|
||||
{
|
||||
// System.Threading.Timer schedules unbounded callback work that outlives the
|
||||
// per-evaluation timeout. Same namespace as CancellationToken so type-granular.
|
||||
// (Core.Scripting-012.)
|
||||
Should.Throw<ScriptSandboxViolationException>(() =>
|
||||
ScriptEvaluator<FakeScriptContext, int>.Compile(
|
||||
"""
|
||||
var t = new System.Threading.Timer(_ => { });
|
||||
return 0;
|
||||
"""));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Rejects_AssemblyLoadContext_at_compile()
|
||||
{
|
||||
// System.Runtime.Loader.AssemblyLoadContext loads arbitrary DLLs into the
|
||||
// process — defense-in-depth gap that the BCL-wide reference set re-opened.
|
||||
// System.Reflection already denies the typical invocation path, but the
|
||||
// sandbox boundary stays type-explicit. (Core.Scripting-012.)
|
||||
Should.Throw<ScriptSandboxViolationException>(() =>
|
||||
ScriptEvaluator<FakeScriptContext, int>.Compile(
|
||||
"""
|
||||
var alc = System.Runtime.Loader.AssemblyLoadContext.Default;
|
||||
return 0;
|
||||
"""));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user