Script sandbox: System.AppContext (SetSwitch/SetData/GetData/BaseDirectory) is reachable from scripts #792
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found while building the lmxopcua#780 P2 shadow-mode tests.
What.
System.AppContextis not inForbiddenTypeAnalyzer.ForbiddenFullTypeNames(which does listSystem.Environment,System.AppDomain,System.GC,System.Activator). A script can therefore call:System.AppContext.BaseDirectory— the host's install path (minor information disclosure; the shadow-mode tests use exactly this to tell the worker from the host:System.AppContext.BaseDirectory.Contains("script-worker")compiles and runs);System.AppContext.GetData(name)— e.g.TRUSTED_PLATFORM_ASSEMBLIES,APP_CONTEXT_DEPS_FILES;System.AppContext.SetSwitch(name, bool)/SetData(name, object)— process-wide runtime switches. In process (the defaultScripting:Runner=InProcess) a Designer-authored script can flip switches that change the host's own behaviour (serialization / networking / globalization compatibility switches read lazily by the BCL).Why it matters. Same class as
System.Environment/System.AppDomain, which are already denied for process-wide control. Script authoring is gated byConfigEditor(Administrator or Designer), so this is p3. Out of process (#780) the blast radius is the worker only.Suggested fix. Add
System.AppContexttoForbiddenFullTypeNames(or denySetSwitch/SetData/GetDataas forbidden members and keepBaseDirectoryif there is a legitimate use — there is not one I can see), plus aScriptSandboxHardeningTestscase per member. Note the #780 shadow tests (ShadowModeTests,InWorkerconstant) rely onAppContext.BaseDirectoryas a test-only divergence; they would need another way to tell the worker apart (e.g. a test-only input) when this lands.Fixed in
0e96dd08(master).Change.
ForbiddenTypeAnalyzer(shared by the in-process compile and the ScriptWorker compile) now denies, as types:System.AppContext(whole type incl.BaseDirectory/GetData/TryGetSwitch),System.Console,System.Runtime.GCSettings,System.Runtime.ProfileOptimization,System.Text.EncodingProvider/CodePagesEncodingProvider,System.ComponentModel.TypeDescriptor,System.Security.Cryptography.CryptoConfig(AddAlgorithm/AddOID mutate a process-wide map, CreateFromName is an Activator equivalent). As members:CultureInfo.DefaultThreadCurrentCulture/DefaultThreadCurrentUICulture/CurrentCulture/CurrentUICultureandTimeZoneInfo.ClearCachedData. The member deny-list previously only matched invoked methods; it now also matches properties and plain member accesses.Audit (already denied, now covered by tests):
AppDomain.SetData,Environment.SetEnvironmentVariable/Exit/FailFast,GC.*,Thread.CurrentThread.CurrentCulture,ThreadPool.SetMinThreads(System.Threading prefix). Not blocked, noted:Delegate.DynamicInvoke/CreateDelegate,System.Linq.ExpressionsCompile,WindowsIdentity.RunImpersonated(thread-scoped, no process state change).Shadow tests. Divergence is now a culture-sensitive
string.Compare("a","B")(worker isInvariantGlobalization=> ordinal; host linguistic), with a premise test against the real worker. No product-side seam.Evidence. New
Rejects_process_wide_mutators(24 cases) + diagnostic-name tests + a legit-usage control (InvariantCulture, GetCultureInfo, TimeZoneInfo.Utc compile fine). Mutation: with the analyzer reverted, 21 of the new tests fail; restored => green. Release build 0 warnings; AKKA_TEST_TIMEFACTOR=3: Core.Scripting 283/1 skip, ScriptWorker 161/4 skip, ScriptedAlarms 109, VirtualTags 16, Calculation 84, Host 275, Runtime 1002/3 skip, OpcUaServer.IntegrationTests 133, all green. Repo seeds/fixtures/docs examples grep clean for the newly blocked APIs.Before upgrading DEV/QA: check the ConfigDbs' script sources (VirtualTag, Calculation, ScriptedAlarm scripts) for
AppContext,Console.,CultureInfo.Current*/DefaultThread*,GCSettings,CryptoConfig,TypeDescriptor,EncodingProvider,TimeZoneInfo.ClearCachedData— any script using them will now fail to compile at load.