-
Notifications
You must be signed in to change notification settings - Fork 0
/
ScriptInstance.cs
146 lines (115 loc) · 4.91 KB
/
ScriptInstance.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security;
using System.Security.Permissions;
using System.Security.Policy;
using AppDomainToolkit;
using CSharpSandbox.ClientSharedApi;
namespace CSharpSandbox.Host
{
public class ScriptInstance
{
private readonly string _scriptDirectoryPath;
private readonly ClientApi _clientApi;
private readonly string _scriptDataDirectoryPath;
public bool IsRunning { get; private set; }
public ScriptInstance(string scriptDirectoryPath, ClientApi clientApi)
{
_scriptDirectoryPath = scriptDirectoryPath;
_clientApi = clientApi;
_scriptDataDirectoryPath = Path.Combine(_scriptDirectoryPath, "Data");
Directory.CreateDirectory(_scriptDataDirectoryPath);
}
public void Start()
{
IsRunning = true;
var clientScriptAppDomain = CreateScriptAppDomain();
var remoteScript = Remote<Loader>.CreateProxy(clientScriptAppDomain.Domain);
try
{
remoteScript.RemoteObject.LoadClientScripts(_scriptDataDirectoryPath, _clientApi);
}
catch (Exception e)
{
Console.WriteLine($"Failed to initialize script {_scriptDirectoryPath}:\r\n" + e.Message);
}
}
private AppDomainContext<AssemblyTargetLoader, PathBasedAssemblyResolver> CreateScriptAppDomain()
{
var permissionSet = CreatePermissionSet();
var allowedStrongNames = GetLoadedAssembliesStrongNames(permissionSet);
var scriptDomainSetup = new AppDomainSetup
{
ApplicationTrust = new ApplicationTrust(permissionSet, allowedStrongNames),
};
var clientScriptAppDomain = AppDomainContext.Create(scriptDomainSetup);
clientScriptAppDomain.AssemblyImporter.AddProbePath(_scriptDirectoryPath);
clientScriptAppDomain.RemoteResolver.AddProbePath(_scriptDirectoryPath);
foreach (var dllFile in GetDllFiles())
clientScriptAppDomain.LoadAssembly(LoadMethod.LoadFrom, dllFile);
return clientScriptAppDomain;
}
private PermissionSet CreatePermissionSet()
{
var permissionSet = new PermissionSet(PermissionState.None);
permissionSet.AddPermission(new ReflectionPermission(ReflectionPermissionFlag.MemberAccess)); // Needed for AppDomainToolkit loader. AppDomainContext.cs, line 106, RemoteAction.Invoke(...)
permissionSet.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution)); // Needed to load and execute the assembly at all
permissionSet.AddPermission(new SecurityPermission(SecurityPermissionFlag.SerializationFormatter)); // Needed when accessing ClientApi on the provided IClientApi interface from the plugin script. Why? Not sure.
permissionSet.AddPermission(new FileIOPermission(FileIOPermissionAccess.Read | FileIOPermissionAccess.PathDiscovery, _scriptDirectoryPath));
permissionSet.AddPermission(new FileIOPermission(FileIOPermissionAccess.Read | FileIOPermissionAccess.Write | FileIOPermissionAccess.Append | FileIOPermissionAccess.PathDiscovery, _scriptDataDirectoryPath));
return permissionSet;
}
private static IEnumerable<StrongName> GetLoadedAssembliesStrongNames(PermissionSet permissionSet)
{
var allowedLibraries = new List<string>();
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
var path = GetAssemblyLocation(assembly);
if (!string.IsNullOrWhiteSpace(path))
{
allowedLibraries.Add(path);
}
var tmpPath = assembly.ManifestModule.FullyQualifiedName;
if (path != tmpPath && File.Exists(tmpPath))
allowedLibraries.Add(tmpPath);
if (assembly.GlobalAssemblyCache && assembly.FullName.StartsWith("System"))
{
allowedLibraries.Add(GetSystemPaths(assembly));
}
var strongName = assembly.Evidence.GetHostEvidence<StrongName>();
if (strongName != null)
yield return strongName;
}
permissionSet.AddPermission(new FileIOPermission(FileIOPermissionAccess.Read | FileIOPermissionAccess.PathDiscovery, allowedLibraries.ToArray()));
}
private IEnumerable<string> GetDllFiles()
{
var loadedAssembliesFullNames = AppDomain.CurrentDomain.GetAssemblies().Select(a => a.FullName).ToList();
foreach (var dllFile in Directory.GetFiles(_scriptDirectoryPath, "*.dll"))
{
var dllFileFullName = Assembly.ReflectionOnlyLoadFrom(dllFile)?.FullName;
if (string.IsNullOrWhiteSpace(dllFileFullName)) continue;
if (loadedAssembliesFullNames.Contains(dllFileFullName)) continue;
yield return dllFile;
}
}
private static string GetAssemblyLocation(Assembly assembly)
{
if (assembly.IsDynamic)
return null;
var codeBase = assembly.CodeBase;
var uri = new UriBuilder(codeBase);
var path = Uri.UnescapeDataString(uri.Path);
path = Path.Combine(Path.GetDirectoryName(path) ?? string.Empty, Path.GetFileName(assembly.Location) ?? string.Empty);
return path;
}
private static string GetSystemPaths(Assembly assembly)
{
var path = $@"C:\Windows\Microsoft.NET\Framework\{assembly.ImageRuntimeVersion}\{assembly.ManifestModule.Name}";
return path;
}
}
}