在沙箱AppDomain中运行运行时编译的C#脚本 [英] Running a runtime compiled C# script in a sandbox AppDomain

查看:289
本文介绍了在沙箱AppDomain中运行运行时编译的C#脚本的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的应用程序应该可由C#中的用户编写脚本,但用户的脚本应在受限制的AppDomain中运行,以防止脚本意外造成损坏,但是我无法真正使它正常工作,并且由于我对AppDomains的理解令人遗憾有限,我无法真正说出原因.

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.

我目前正在尝试的解决方案基于此答案 https://stackoverflow.com/a/5998886/276070.

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

这是我的情况的模型(除驻留在全名程序集中的Script.cs之外的所有内容).请原谅代码墙,我无法再进一步压缩这个问题.

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();
}

沙箱类:

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);            
    }
}

已加载的脚本:

using System;

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

SandBoxCreateInstance中,我总是在标记的行得到null.我尝试了多种形式的名称命名,包括使用反射从results.CompiledAssembly读取类型名称(或完全限定名称). 我在这里做什么错了?

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?

推荐答案

我要检查的第一件事是是否存在编译错误(由该问题引起的几次头痛)

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

第二个想法是关于程序集的解析.我总是将AppDomain.CurrentDomain.AssemblyResolve的事件处理程序添加为安全检查,在其中寻找丢失的Assembly的已知路径.当未找到的程序集是我刚刚编译的程序集时,我为其添加静态引用并返回它.

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.

我通常这样做的是:

  • 使用编译器在文件系统上创建新的程序集
  • 使用File.ReadAllBytes
  • 加载其内容
  • 使用我将在其中使用对象的AppDomain中的Assembly.Load加载dll.
  • 添加AppDomain.CurrentDomain.AssemblyResolve事件

以防万一(因为我经常使用),我创建了一个小型库来实现这种事情

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

代码和文档在这里: Kendar Expression Builder 当nuget包在这里时: Nuget Sharp Template

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

这篇关于在沙箱AppDomain中运行运行时编译的C#脚本的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆