133 lines
4.9 KiB
C#
133 lines
4.9 KiB
C#
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using Assembly = System.Reflection.Assembly;
|
|
using Debug = UnityEngine.Debug;
|
|
|
|
namespace io.github.hatayama.uLoopMCP
|
|
{
|
|
internal static class CompiledAssemblyLoader
|
|
{
|
|
public static CompiledAssemblyLoadResult Load(
|
|
DynamicCodeSecurityLevel securityLevel,
|
|
byte[] assemblyBytes)
|
|
{
|
|
Debug.Assert(assemblyBytes != null, "assemblyBytes must not be null");
|
|
|
|
Stopwatch stopwatch = Stopwatch.StartNew();
|
|
|
|
if (securityLevel == DynamicCodeSecurityLevel.Restricted)
|
|
{
|
|
SecurityValidationResult preloadValidationResult = ValidateBeforeAssemblyLoad(assemblyBytes);
|
|
if (!preloadValidationResult.IsValid)
|
|
{
|
|
stopwatch.Stop();
|
|
return new CompiledAssemblyLoadResult(
|
|
false,
|
|
null,
|
|
preloadValidationResult.Violations,
|
|
stopwatch.Elapsed.TotalMilliseconds);
|
|
}
|
|
}
|
|
|
|
Assembly compiledAssembly = Assembly.Load(assemblyBytes);
|
|
if (securityLevel == DynamicCodeSecurityLevel.Restricted)
|
|
{
|
|
IlSecurityValidator postLoadValidator = new IlSecurityValidator();
|
|
SecurityValidationResult postLoadValidationResult = postLoadValidator.Validate(compiledAssembly);
|
|
if (!postLoadValidationResult.IsValid)
|
|
{
|
|
stopwatch.Stop();
|
|
return new CompiledAssemblyLoadResult(
|
|
false,
|
|
null,
|
|
postLoadValidationResult.Violations,
|
|
stopwatch.Elapsed.TotalMilliseconds);
|
|
}
|
|
}
|
|
|
|
stopwatch.Stop();
|
|
return new CompiledAssemblyLoadResult(
|
|
true,
|
|
compiledAssembly,
|
|
new List<SecurityViolation>(),
|
|
stopwatch.Elapsed.TotalMilliseconds);
|
|
}
|
|
|
|
private static SecurityValidationResult ValidateBeforeAssemblyLoad(byte[] assemblyBytes)
|
|
{
|
|
SecurityValidationResult aggregatedResult = new SecurityValidationResult
|
|
{
|
|
IsValid = true,
|
|
Violations = new List<SecurityViolation>(),
|
|
CompilationErrors = new List<string>()
|
|
};
|
|
bool shouldRunMetadataFallback = true;
|
|
|
|
IPreloadAssemblySecurityValidator registeredValidator;
|
|
if (PreloadAssemblySecurityValidatorRegistry.TryGetValidator(out registeredValidator))
|
|
{
|
|
MergeValidationResult(aggregatedResult, registeredValidator.Validate(assemblyBytes));
|
|
shouldRunMetadataFallback = registeredValidator is not IOverrideDefaultPreloadAssemblySecurityValidation;
|
|
}
|
|
|
|
if (shouldRunMetadataFallback)
|
|
{
|
|
SystemReflectionMetadataPreloadValidator fallbackValidator = new SystemReflectionMetadataPreloadValidator();
|
|
MergeValidationResult(aggregatedResult, fallbackValidator.Validate(assemblyBytes));
|
|
}
|
|
|
|
PreloadIlSecurityValidator ilValidator = new PreloadIlSecurityValidator();
|
|
MergeValidationResult(aggregatedResult, ilValidator.Validate(assemblyBytes));
|
|
|
|
aggregatedResult.IsValid = aggregatedResult.Violations.Count == 0 &&
|
|
aggregatedResult.CompilationErrors.Count == 0;
|
|
return aggregatedResult;
|
|
}
|
|
|
|
private static void MergeValidationResult(
|
|
SecurityValidationResult target,
|
|
SecurityValidationResult source)
|
|
{
|
|
if (source == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
HashSet<string> seenViolations = new HashSet<string>(System.StringComparer.Ordinal);
|
|
foreach (SecurityViolation violation in target.Violations)
|
|
{
|
|
seenViolations.Add(CreateViolationKey(violation));
|
|
}
|
|
|
|
if (source.Violations != null)
|
|
{
|
|
foreach (SecurityViolation violation in source.Violations)
|
|
{
|
|
if (seenViolations.Add(CreateViolationKey(violation)))
|
|
{
|
|
target.Violations.Add(violation);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (source.CompilationErrors == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
foreach (string error in source.CompilationErrors)
|
|
{
|
|
if (!target.CompilationErrors.Contains(error))
|
|
{
|
|
target.CompilationErrors.Add(error);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static string CreateViolationKey(SecurityViolation violation)
|
|
{
|
|
return $"{violation.Location}|{violation.ApiName}|{violation.Message}";
|
|
}
|
|
}
|
|
}
|