fix(security): deny Environment/GC and ADO.NET provider namespaces; close reflection-gateway list (GetTypes/EntryPoint/Declared*/DynamicInvoke)
Claude-Session: https://claude.ai/code/session_01MtdgwpEeCUn6cUA5f1LMPj
This commit is contained in:
@@ -42,6 +42,23 @@ public static class ScriptTrustPolicy
|
||||
"System.Net",
|
||||
"System.Runtime.InteropServices",
|
||||
"Microsoft.Win32",
|
||||
// Whole type — no legitimate script use. Exposes process control
|
||||
// (Exit/FailFast), the host environment, and secrets via environment
|
||||
// variables (SCADABRIDGE_API_KEY). Callers needing a line break use "\n".
|
||||
"System.Environment",
|
||||
// Whole type — GC control (Collect, KeepAlive, memory pressure knobs) has
|
||||
// no legitimate script use.
|
||||
"System.GC",
|
||||
// Concrete ADO.NET provider namespaces. Scripts reach a DbConnection ONLY
|
||||
// via the Database helper, whose abstract System.Data.Common types stay
|
||||
// allowed; forbidding the concrete providers closes the arbitrary-host
|
||||
// `new SqlConnection("Server=attacker")` channel. Deliberately NOT
|
||||
// forbidding System.Data or System.Data.Common broadly — only these
|
||||
// concrete provider namespaces.
|
||||
"Microsoft.Data",
|
||||
"System.Data.SqlClient",
|
||||
"System.Data.Odbc",
|
||||
"System.Data.OleDb",
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
@@ -85,6 +102,18 @@ public static class ScriptTrustPolicy
|
||||
"GetRuntimeMethods",
|
||||
"MethodHandle",
|
||||
"TypeHandle",
|
||||
"GetTypes",
|
||||
"EntryPoint",
|
||||
"DeclaredMethods",
|
||||
"DeclaredMembers",
|
||||
"DeclaredConstructors",
|
||||
"DynamicInvoke",
|
||||
// DELIBERATELY EXCLUDED: "Invoke". The syntactic pass rejects a gateway
|
||||
// member regardless of receiver, and delegate.Invoke() is a legitimate,
|
||||
// common script pattern (Func<>/Action<> invocation). MethodInfo.Invoke
|
||||
// (the reflection late-bind) is already caught semantically via the
|
||||
// System.Reflection scope, so excluding "Invoke" here avoids a
|
||||
// false-positive blast radius without weakening the reflection deny-list.
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -115,11 +115,21 @@ public static class ScriptTrustValidator
|
||||
if (extraReferences != null)
|
||||
references.AddRange(extraReferences);
|
||||
|
||||
// Apply the SAME default namespace imports the site runtime compiles the
|
||||
// real script with (ScriptTrustPolicy.DefaultImports — "using System;"
|
||||
// etc.). Without them the semantic pass under-resolves BARE identifiers
|
||||
// that the running script sees fully bound — e.g. `Environment.Exit(0)` or
|
||||
// `GC.Collect()`, whose `Environment`/`GC` resolve to the forbidden
|
||||
// System.Environment/System.GC types only when `using System;` is in
|
||||
// scope. Matching the runtime's import set closes that blind spot; it can
|
||||
// only make MORE identifiers resolve to their true (forbidden) namespace,
|
||||
// never produce a false allow.
|
||||
var compilation = CSharpCompilation.CreateScriptCompilation(
|
||||
"TrustValidation",
|
||||
tree,
|
||||
references,
|
||||
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
|
||||
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
|
||||
.WithUsings(ScriptTrustPolicy.DefaultImports));
|
||||
|
||||
var model = compilation.GetSemanticModel(tree);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user