我的应用程序应该由C#中的用户编写脚本,但是用户的脚本应该在受限制的AppDomain中运行,以防止脚本意外造成损害,但我无法真正使用它,因为我对AppDomains的理解很可悲有限,我真的不知道为什么。
我目前正在尝试的解决方案基于此答案https://stackoverflow.com/a/5998886/276070。
这是我的情况的模型(除了强制命名的程序集中的Script.cs之外的所有内容)。请原谅代码墙,我无法进一步压缩问题。
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");
}
}
在CreateInstance
的{{1}}中,我总是在标记的行中获得SandBox
。我尝试了各种形式的名称,包括使用反射从null
读取类型名称(或fuly限定名称)。
我在这里做错了什么?
答案 0 :(得分:0)
我要检查的第一件事是,是否存在编译错误(由于此问题引起了几次头痛)
第二个想法是关于集会的解决方案。我总是为AppDomain.CurrentDomain.AssemblyResolve添加一个事件处理程序作为安全检查,我在我的已知路径上寻找缺少的程序集。当未找到的程序集是我刚刚编译的程序集时,我会添加一个静态引用并返回它。
我通常做的是:
以防万一(因为我经常使用这个)我创建了一个小型图书馆,以此类型的东西
代码和文档在这里:Kendar Expression Builder 虽然nuget包在这里:Nuget Sharp Template