Running a runtime compiled C# script in a sandbox AppDomain

旧城冷巷雨未停 提交于 2019-12-11 04:17:42

问题


My application should be scriptable by the users in C#, but the user's script should run in a restricted AppDomain to prevent scripts accidentally causing damage, but I can't really get it to work, and since my understanding of AppDomains is sadly limited, I can't really tell why.

The solution I am currently trying is based on this answer https://stackoverflow.com/a/5998886/276070.

This is a model of my situation (everything except Script.cs residing in a strongly named assembly). Please excuse the wall of code, I could not condense the problem any further.

class Program
{
    static void Main(string[] args)
    {
        // Compile the script
        CodeDomProvider codeProvider = CodeDomProvider.CreateProvider("CSharp");
        CompilerParameters parameters = new CompilerParameters()
        {
            GenerateExecutable = false,
            OutputAssembly = System.IO.Path.GetTempFileName() + ".dll",                         
        };
        parameters.ReferencedAssemblies.Add(Assembly.GetEntryAssembly().Location);

        CompilerResults results = codeProvider.CompileAssemblyFromFile(parameters, "Script.cs");

        // ... here error checks happen ....//                 

        var sandbox = Sandbox.Create();
        var script = (IExecutable)sandbox.CreateInstance(results.PathToAssembly, "Script");

        if(script != null)
            script.Execute();

    }        
}  

public interface IExecutable
{
    void Execute();
}

The Sandbox class:

public class Sandbox : MarshalByRefObject
{
    const string BaseDirectory = "Untrusted";
    const string DomainName = "Sandbox";        

    public static Sandbox Create()
    {
        var setup = new AppDomainSetup()
        {
            ApplicationBase = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, BaseDirectory),
            ApplicationName = DomainName,
            DisallowBindingRedirects = true,
            DisallowCodeDownload = true,
            DisallowPublisherPolicy = true
        };

        var permissions = new PermissionSet(PermissionState.None);
        permissions.AddPermission(new ReflectionPermission(ReflectionPermissionFlag.RestrictedMemberAccess));
        permissions.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));

        var domain = AppDomain.CreateDomain(DomainName, null, setup, permissions,
            typeof(Sandbox).Assembly.Evidence.GetHostEvidence<StrongName>());

        return (Sandbox)Activator.CreateInstanceFrom(domain, typeof(Sandbox).Assembly.ManifestModule.FullyQualifiedName, typeof(Sandbox).FullName).Unwrap();
    }

    public object CreateInstance(string assemblyPath, string typeName)
    {
        new FileIOPermission(FileIOPermissionAccess.Read | FileIOPermissionAccess.PathDiscovery, assemblyPath).Assert();
        var assembly = Assembly.LoadFile(assemblyPath);
        CodeAccessPermission.RevertAssert();

        Type type = assembly.GetType(typeName); // ****** I get null here
        if (type == null)
            return null;

        return Activator.CreateInstance(type);            
    }
}

The loaded Script:

using System;

public class Script : IExecutable
{
    public void Execute()
    {
        Console.WriteLine("Boo");
    }
}

In CreateInstance of SandBox, I always get null at the marked line. I tried various forms of giving the name, including reading the type name (or fuly qualified name) from results.CompiledAssembly using reflection. What am I doing wrong here?


回答1:


The first thing that i'll check is if there are compilation errors (i had several headache caused by this issues)

The second idea is about the resolution of assemblies. I always add as a security check an event handler for AppDomain.CurrentDomain.AssemblyResolve, where i seek on my known path for the missing Assemblies. When the not found assembly is the one i just compiled i add a static reference to it and return it.

What I usually do is this:

  • Create the new Assembly on file system with the compiler
  • Load its content with the File.ReadAllBytes
  • Load the dll with the Assembly.Load in the AppDomain in which i will be using the object
  • Add the AppDomain.CurrentDomain.AssemblyResolve event

Just in case (since i use this a lot) i created a small library to accomply this kind of things

The code and documentation are here: Kendar Expression Builder While the nuget package is here: Nuget Sharp Template



来源:https://stackoverflow.com/questions/18255390/running-a-runtime-compiled-c-sharp-script-in-a-sandbox-appdomain

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!