在沙箱 AppDomain 中运行运行时编译的 C# 脚本 [英] Running a runtime compiled C# script in a sandbox AppDomain
问题描述
我的应用程序应该可以由用户在 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");
}
}
在SandBox
的CreateInstance
中,我总是在标记行得到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 添加一个事件处理程序作为安全检查,我在那里寻找丢失的程序集的已知路径.当未找到的程序集是我刚刚编译的程序集时,我向它添加一个静态引用并返回它.
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 模板
The code and documentation are here: Kendar Expression Builder While the nuget package is here: Nuget Sharp Template
这篇关于在沙箱 AppDomain 中运行运行时编译的 C# 脚本的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!