chore(unity): 固定Unity CLI Loop本地包并移除UnitySkills依赖

This commit is contained in:
Liubing\LB
2026-09-28 18:14:59 +08:00
parent 0eb43b2b40
commit c4ae02def9
952 changed files with 70137 additions and 11 deletions

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1650e75bb20ee4fffb2b799dec3cdcf8
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,34 @@
fileFormatVersion: 2
guid: 89f7926a488ff4a9eb02f251783176f3
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints:
- '!UNITY_6000_5_OR_NEWER'
isPreloaded: 0
isOverridable: 1
isExplicitlyReferenced: 1
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
DefaultValueInitialized: true
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,34 @@
fileFormatVersion: 2
guid: e56d8b8cca2dd44eebcabe60371720bd
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints:
- '!UNITY_6000_5_OR_NEWER'
isPreloaded: 0
isOverridable: 1
isExplicitlyReferenced: 1
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
DefaultValueInitialized: true
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,34 @@
fileFormatVersion: 2
guid: ea300f2dc17a34ffda4eb2b51807f532
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints:
- '!UNITY_6000_5_OR_NEWER'
isPreloaded: 0
isOverridable: 1
isExplicitlyReferenced: 1
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
DefaultValueInitialized: true
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,564 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Reflection.Emit;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Reflection.PortableExecutable;
namespace io.github.hatayama.uLoopMCP
{
/// <summary>
/// Reads IL directly from PE metadata before Assembly.Load so restricted-mode validation
/// never needs to materialize the assembly just to inspect method bodies.
/// </summary>
public sealed class PreloadIlSecurityValidator : IPreloadAssemblySecurityValidator
{
private const char DecodedTypeNameSeparator = '|';
private static readonly OpCode[] SingleByteOpCodes = BuildOpCodeTable(singleByte: true);
private static readonly OpCode[] MultiByteOpCodes = BuildOpCodeTable(singleByte: false);
public SecurityValidationResult Validate(byte[] assemblyBytes)
{
SecurityValidationResult result = new SecurityValidationResult
{
IsValid = true,
Violations = new List<SecurityViolation>()
};
HashSet<string> seenViolations = new HashSet<string>(System.StringComparer.Ordinal);
using MemoryStream stream = new MemoryStream(assemblyBytes, writable: false);
using PEReader peReader = new PEReader(stream);
MetadataReader reader = peReader.GetMetadataReader();
foreach (MethodDefinitionHandle methodHandle in reader.MethodDefinitions)
{
this.ValidateMethod(peReader, reader, methodHandle, result, seenViolations);
}
result.IsValid = result.Violations.Count == 0;
return result;
}
private void ValidateMethod(
PEReader peReader,
MetadataReader reader,
MethodDefinitionHandle methodHandle,
SecurityValidationResult result,
HashSet<string> seenViolations)
{
MethodDefinition methodDefinition = reader.GetMethodDefinition(methodHandle);
string declaringTypeName = this.GetTypeDefinitionFullName(reader, methodDefinition.GetDeclaringType());
string methodName = reader.GetString(methodDefinition.Name);
this.ValidateParameterTypes(reader, methodDefinition, declaringTypeName, methodName, result, seenViolations);
this.ValidateMethodBody(peReader, reader, methodDefinition, declaringTypeName, methodName, result, seenViolations);
}
private void ValidateParameterTypes(
MetadataReader reader,
MethodDefinition methodDefinition,
string declaringTypeName,
string methodName,
SecurityValidationResult result,
HashSet<string> seenViolations)
{
MetadataTypeNameDecoder decoder = new MetadataTypeNameDecoder(reader, this);
MethodSignature<string> signature = methodDefinition.DecodeSignature(decoder, genericContext: null);
foreach (string parameterTypeName in signature.ParameterTypes)
{
this.AddDecodedTypeViolations(
result,
seenViolations,
parameterTypeName,
dangerousTypeName => $"Dangerous parameter type detected before assembly load: {dangerousTypeName}",
dangerousTypeName => $"Parameter type '{dangerousTypeName}' exists in {declaringTypeName}.{methodName}");
}
this.AddDecodedTypeViolations(
result,
seenViolations,
signature.ReturnType,
dangerousTypeName => $"Dangerous return type detected before assembly load: {dangerousTypeName}",
dangerousTypeName => $"Return type '{dangerousTypeName}' exists in {declaringTypeName}.{methodName}");
}
private void ValidateMethodBody(
PEReader peReader,
MetadataReader reader,
MethodDefinition methodDefinition,
string declaringTypeName,
string methodName,
SecurityValidationResult result,
HashSet<string> seenViolations)
{
if (methodDefinition.RelativeVirtualAddress == 0)
{
return;
}
MethodBodyBlock methodBody = peReader.GetMethodBody(methodDefinition.RelativeVirtualAddress);
this.ValidateLocalVariableTypes(reader, methodBody, declaringTypeName, methodName, result, seenViolations);
this.ValidateIlInstructions(reader, methodBody, declaringTypeName, methodName, result, seenViolations);
}
private void ValidateLocalVariableTypes(
MetadataReader reader,
MethodBodyBlock methodBody,
string declaringTypeName,
string methodName,
SecurityValidationResult result,
HashSet<string> seenViolations)
{
if (methodBody.LocalSignature.IsNil)
{
return;
}
StandaloneSignature standaloneSignature = reader.GetStandaloneSignature(methodBody.LocalSignature);
MetadataTypeNameDecoder decoder = new MetadataTypeNameDecoder(reader, this);
ImmutableArray<string> localTypes = standaloneSignature.DecodeLocalSignature(decoder, genericContext: null);
foreach (string localTypeName in localTypes)
{
this.AddDecodedTypeViolations(
result,
seenViolations,
localTypeName,
dangerousTypeName => $"Dangerous local variable type detected before assembly load: {dangerousTypeName}",
dangerousTypeName => $"Local variable type '{dangerousTypeName}' exists in {declaringTypeName}.{methodName}");
}
}
private void ValidateIlInstructions(
MetadataReader reader,
MethodBodyBlock methodBody,
string declaringTypeName,
string methodName,
SecurityValidationResult result,
HashSet<string> seenViolations)
{
byte[] ilBytes = methodBody.GetILBytes();
int offset = 0;
while (offset < ilBytes.Length)
{
OpCode opCode = ReadOpCode(ilBytes, ref offset);
if (opCode.OperandType == OperandType.InlineMethod)
{
int token = ReadInt32(ilBytes, offset);
EntityHandle methodHandle = MetadataTokens.EntityHandle(token);
if (this.TryGetMethodReference(methodHandle, reader, out string targetTypeName, out string targetMethodName) &&
DangerousApiCatalog.IsDangerousApi(targetTypeName, targetMethodName))
{
string apiName = $"{targetTypeName}.{targetMethodName}";
this.AddViolation(
result,
seenViolations,
apiName,
$"Dangerous IL method reference detected before assembly load: {apiName}",
$"IL {opCode.Name} references '{apiName}' in {declaringTypeName}.{methodName}");
}
}
if (opCode == OpCodes.Ldtoken && opCode.OperandType == OperandType.InlineTok)
{
int token = ReadInt32(ilBytes, offset);
string referencedTypeName = this.TryGetTypeReference(MetadataTokens.EntityHandle(token), reader);
this.AddDecodedTypeViolations(
result,
seenViolations,
referencedTypeName,
dangerousTypeName => $"Dangerous IL type token detected before assembly load: {dangerousTypeName}",
dangerousTypeName => $"IL ldtoken references '{dangerousTypeName}' in {declaringTypeName}.{methodName}");
}
offset += GetOperandSize(opCode.OperandType, ilBytes, offset);
}
}
private bool TryGetMethodReference(
EntityHandle handle,
MetadataReader reader,
out string typeName,
out string methodName)
{
typeName = string.Empty;
methodName = string.Empty;
if (handle.Kind == HandleKind.MemberReference)
{
MemberReference memberReference = reader.GetMemberReference((MemberReferenceHandle)handle);
typeName = this.GetMemberReferenceParentTypeName(reader, memberReference.Parent);
methodName = reader.GetString(memberReference.Name);
return !string.IsNullOrEmpty(typeName) && !string.IsNullOrEmpty(methodName);
}
if (handle.Kind == HandleKind.MethodDefinition)
{
MethodDefinition methodDefinition = reader.GetMethodDefinition((MethodDefinitionHandle)handle);
typeName = this.GetTypeDefinitionFullName(reader, methodDefinition.GetDeclaringType());
methodName = reader.GetString(methodDefinition.Name);
return !string.IsNullOrEmpty(typeName) && !string.IsNullOrEmpty(methodName);
}
if (handle.Kind == HandleKind.MethodSpecification)
{
MethodSpecification methodSpecification = reader.GetMethodSpecification((MethodSpecificationHandle)handle);
return this.TryGetMethodReference(methodSpecification.Method, reader, out typeName, out methodName);
}
return false;
}
private string TryGetTypeReference(EntityHandle handle, MetadataReader reader)
{
if (handle.Kind == HandleKind.TypeReference ||
handle.Kind == HandleKind.TypeDefinition ||
handle.Kind == HandleKind.TypeSpecification)
{
return this.GetEntityTypeName(reader, handle);
}
return string.Empty;
}
private void AddDecodedTypeViolations(
SecurityValidationResult result,
HashSet<string> seenViolations,
string decodedTypeName,
System.Func<string, string> createMessage,
System.Func<string, string> createDescription)
{
foreach (string typeName in ExpandDecodedTypeNames(decodedTypeName))
{
if (!DangerousApiCatalog.IsDangerousType(typeName))
{
continue;
}
this.AddViolation(
result,
seenViolations,
typeName,
createMessage(typeName),
createDescription(typeName));
}
}
private static IEnumerable<string> ExpandDecodedTypeNames(string decodedTypeName)
{
if (string.IsNullOrEmpty(decodedTypeName))
{
yield break;
}
string[] parts = decodedTypeName.Split(DecodedTypeNameSeparator);
foreach (string part in parts)
{
if (!string.IsNullOrEmpty(part))
{
yield return part;
}
}
}
private void AddViolation(
SecurityValidationResult result,
HashSet<string> seenViolations,
string apiName,
string message,
string description)
{
if (!seenViolations.Add($"{apiName}|{message}"))
{
return;
}
result.Violations.Add(new SecurityViolation
{
Type = SecurityViolationType.DangerousApiCall,
ApiName = apiName,
Message = message,
Description = description,
Location = "il"
});
}
private string GetMemberReferenceParentTypeName(MetadataReader reader, EntityHandle parentHandle)
{
if (parentHandle.Kind == HandleKind.TypeReference ||
parentHandle.Kind == HandleKind.TypeDefinition ||
parentHandle.Kind == HandleKind.TypeSpecification)
{
return this.GetEntityTypeName(reader, parentHandle);
}
return string.Empty;
}
private string GetEntityTypeName(MetadataReader reader, EntityHandle handle)
{
if (handle.Kind == HandleKind.TypeReference)
{
return this.GetTypeReferenceFullName(reader, (TypeReferenceHandle)handle);
}
if (handle.Kind == HandleKind.TypeDefinition)
{
return this.GetTypeDefinitionFullName(reader, (TypeDefinitionHandle)handle);
}
if (handle.Kind == HandleKind.TypeSpecification)
{
return this.DecodeTypeSpecification(reader, (TypeSpecificationHandle)handle);
}
return string.Empty;
}
private string GetTypeReferenceFullName(MetadataReader reader, TypeReferenceHandle handle)
{
TypeReference typeReference = reader.GetTypeReference(handle);
string typeName = reader.GetString(typeReference.Name);
string namespaceName = reader.GetString(typeReference.Namespace);
if (typeReference.ResolutionScope.Kind == HandleKind.TypeReference)
{
string parentTypeName = this.GetTypeReferenceFullName(reader, (TypeReferenceHandle)typeReference.ResolutionScope);
return $"{parentTypeName}.{typeName}";
}
if (string.IsNullOrEmpty(namespaceName))
{
return typeName;
}
return $"{namespaceName}.{typeName}";
}
private string GetTypeDefinitionFullName(MetadataReader reader, TypeDefinitionHandle handle)
{
TypeDefinition typeDefinition = reader.GetTypeDefinition(handle);
string typeName = reader.GetString(typeDefinition.Name);
TypeDefinitionHandle declaringTypeHandle = typeDefinition.GetDeclaringType();
if (!declaringTypeHandle.IsNil)
{
string parentTypeName = this.GetTypeDefinitionFullName(reader, declaringTypeHandle);
return $"{parentTypeName}.{typeName}";
}
string namespaceName = reader.GetString(typeDefinition.Namespace);
if (string.IsNullOrEmpty(namespaceName))
{
return typeName;
}
return $"{namespaceName}.{typeName}";
}
private string DecodeTypeSpecification(MetadataReader reader, TypeSpecificationHandle handle)
{
TypeSpecification typeSpecification = reader.GetTypeSpecification(handle);
MetadataTypeNameDecoder decoder = new MetadataTypeNameDecoder(reader, this);
return typeSpecification.DecodeSignature(decoder, genericContext: null);
}
private static OpCode ReadOpCode(byte[] ilBytes, ref int offset)
{
byte first = ilBytes[offset++];
if (first != 0xFE)
{
return SingleByteOpCodes[first];
}
byte second = ilBytes[offset++];
return MultiByteOpCodes[second];
}
private static int ReadInt32(byte[] ilBytes, int offset)
{
return ilBytes[offset] |
(ilBytes[offset + 1] << 8) |
(ilBytes[offset + 2] << 16) |
(ilBytes[offset + 3] << 24);
}
private static int GetOperandSize(OperandType operandType, byte[] ilBytes, int offset)
{
switch (operandType)
{
case OperandType.InlineNone:
return 0;
case OperandType.ShortInlineBrTarget:
case OperandType.ShortInlineI:
case OperandType.ShortInlineVar:
return 1;
case OperandType.InlineVar:
return 2;
case OperandType.InlineI:
case OperandType.InlineBrTarget:
case OperandType.InlineField:
case OperandType.InlineMethod:
case OperandType.InlineSig:
case OperandType.InlineString:
case OperandType.InlineTok:
case OperandType.InlineType:
case OperandType.ShortInlineR:
return 4;
case OperandType.InlineI8:
case OperandType.InlineR:
return 8;
case OperandType.InlineSwitch:
int count = ReadInt32(ilBytes, offset);
return 4 + (count * 4);
default:
return 0;
}
}
private static OpCode[] BuildOpCodeTable(bool singleByte)
{
OpCode[] table = new OpCode[256];
System.Reflection.FieldInfo[] fields = typeof(OpCodes).GetFields();
foreach (System.Reflection.FieldInfo field in fields)
{
if (field.FieldType != typeof(OpCode))
{
continue;
}
OpCode opcode = (OpCode)field.GetValue(null);
ushort value = unchecked((ushort)opcode.Value);
if (singleByte)
{
if (value <= 0xFF)
{
table[value] = opcode;
}
continue;
}
if ((value & 0xFF00) == 0xFE00)
{
table[value & 0xFF] = opcode;
}
}
return table;
}
private sealed class MetadataTypeNameDecoder : ISignatureTypeProvider<string, object>
{
private readonly MetadataReader _reader;
private readonly PreloadIlSecurityValidator _validator;
public MetadataTypeNameDecoder(MetadataReader reader, PreloadIlSecurityValidator validator)
{
_reader = reader;
_validator = validator;
}
public string GetArrayType(string elementType, ArrayShape shape)
{
return elementType;
}
public string GetByReferenceType(string elementType)
{
return elementType;
}
public string GetFunctionPointerType(MethodSignature<string> signature)
{
return string.Empty;
}
public string GetGenericInstantiation(string genericType, ImmutableArray<string> typeArguments)
{
List<string> flattenedTypeNames = new List<string>();
AddFlattenedTypeName(flattenedTypeNames, genericType);
foreach (string typeArgument in typeArguments)
{
AddFlattenedTypeName(flattenedTypeNames, typeArgument);
}
return string.Join(DecodedTypeNameSeparator.ToString(), flattenedTypeNames);
}
public string GetGenericMethodParameter(object genericContext, int index)
{
return $"!!{index}";
}
public string GetGenericTypeParameter(object genericContext, int index)
{
return $"!{index}";
}
public string GetModifiedType(string modifierType, string unmodifiedType, bool isRequired)
{
return unmodifiedType;
}
public string GetPinnedType(string elementType)
{
return elementType;
}
public string GetPointerType(string elementType)
{
return elementType;
}
public string GetPrimitiveType(PrimitiveTypeCode typeCode)
{
return typeCode.ToString();
}
public string GetSZArrayType(string elementType)
{
return elementType;
}
public string GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind)
{
return _validator.GetTypeDefinitionFullName(reader, handle);
}
public string GetTypeFromReference(MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind)
{
return _validator.GetTypeReferenceFullName(reader, handle);
}
public string GetTypeFromSpecification(MetadataReader reader, object genericContext, TypeSpecificationHandle handle, byte rawTypeKind)
{
return _validator.DecodeTypeSpecification(reader, handle);
}
private static void AddFlattenedTypeName(List<string> flattenedTypeNames, string decodedTypeName)
{
if (string.IsNullOrEmpty(decodedTypeName))
{
return;
}
string[] parts = decodedTypeName.Split(DecodedTypeNameSeparator);
foreach (string part in parts)
{
if (!string.IsNullOrEmpty(part))
{
flattenedTypeNames.Add(part);
}
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3b107ee1e73b34016853fee6838ce6fb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,355 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Reflection.PortableExecutable;
namespace io.github.hatayama.uLoopMCP
{
/// <summary>
/// Reads ECMA-335 metadata before Assembly.Load so load-time side effects never run before strict validation.
/// </summary>
public sealed class SystemReflectionMetadataPreloadValidator : IPreloadAssemblySecurityValidator
{
private const string ModuleInitializerAttributeName = "System.Runtime.CompilerServices.ModuleInitializerAttribute";
public SecurityValidationResult Validate(byte[] assemblyBytes)
{
SecurityValidationResult result = new SecurityValidationResult
{
IsValid = true,
Violations = new List<SecurityViolation>()
};
HashSet<string> seenViolations = new HashSet<string>(System.StringComparer.Ordinal);
using MemoryStream stream = new MemoryStream(assemblyBytes, writable: false);
using PEReader peReader = new PEReader(stream);
MetadataReader reader = peReader.GetMetadataReader();
this.ValidateTypeReferences(reader, result, seenViolations);
this.ValidateTypeDefinitions(reader, result, seenViolations);
this.ValidateMemberReferences(reader, result, seenViolations);
this.ValidateCustomAttributes(reader, result, seenViolations);
result.IsValid = result.Violations.Count == 0;
return result;
}
private void ValidateTypeReferences(
MetadataReader reader,
SecurityValidationResult result,
HashSet<string> seenViolations)
{
foreach (TypeReferenceHandle handle in reader.TypeReferences)
{
string typeName = this.GetTypeReferenceFullName(reader, handle);
if (!DangerousApiCatalog.IsDangerousType(typeName))
{
continue;
}
this.AddViolation(
result,
seenViolations,
typeName,
$"Dangerous metadata reference detected before assembly load: {typeName}",
$"Metadata TypeReference contains '{typeName}'");
}
}
private void ValidateTypeDefinitions(
MetadataReader reader,
SecurityValidationResult result,
HashSet<string> seenViolations)
{
foreach (TypeDefinitionHandle handle in reader.TypeDefinitions)
{
TypeDefinition typeDefinition = reader.GetTypeDefinition(handle);
string declaringTypeName = this.GetTypeDefinitionFullName(reader, handle);
if (!typeDefinition.BaseType.IsNil)
{
string baseTypeName = this.GetEntityTypeName(reader, typeDefinition.BaseType);
if (DangerousApiCatalog.IsDangerousType(baseTypeName))
{
this.AddViolation(
result,
seenViolations,
baseTypeName,
$"Dangerous metadata inheritance detected before assembly load: {baseTypeName}",
$"Metadata base type '{baseTypeName}' exists on '{declaringTypeName}'");
}
}
foreach (InterfaceImplementationHandle interfaceHandle in typeDefinition.GetInterfaceImplementations())
{
InterfaceImplementation interfaceImplementation = reader.GetInterfaceImplementation(interfaceHandle);
string interfaceTypeName = this.GetEntityTypeName(reader, interfaceImplementation.Interface);
if (!DangerousApiCatalog.IsDangerousType(interfaceTypeName))
{
continue;
}
this.AddViolation(
result,
seenViolations,
interfaceTypeName,
$"Dangerous metadata interface detected before assembly load: {interfaceTypeName}",
$"Metadata interface '{interfaceTypeName}' exists on '{declaringTypeName}'");
}
}
}
private void ValidateMemberReferences(
MetadataReader reader,
SecurityValidationResult result,
HashSet<string> seenViolations)
{
foreach (MemberReferenceHandle handle in reader.MemberReferences)
{
MemberReference memberReference = reader.GetMemberReference(handle);
string parentTypeName = this.GetMemberReferenceParentTypeName(reader, memberReference.Parent);
string memberName = reader.GetString(memberReference.Name);
if (!DangerousApiCatalog.IsDangerousApi(parentTypeName, memberName))
{
continue;
}
string apiName = $"{parentTypeName}.{memberName}";
this.AddViolation(
result,
seenViolations,
apiName,
$"Dangerous metadata member detected before assembly load: {apiName}",
$"Metadata MemberReference contains '{apiName}'");
}
}
private void ValidateCustomAttributes(
MetadataReader reader,
SecurityValidationResult result,
HashSet<string> seenViolations)
{
foreach (CustomAttributeHandle handle in reader.CustomAttributes)
{
CustomAttribute attribute = reader.GetCustomAttribute(handle);
string attributeTypeName = this.GetCustomAttributeTypeName(reader, attribute.Constructor);
if (attributeTypeName != ModuleInitializerAttributeName)
{
continue;
}
this.AddViolation(
result,
seenViolations,
attributeTypeName,
$"Dangerous metadata attribute detected before assembly load: {attributeTypeName}",
$"Metadata CustomAttribute contains '{attributeTypeName}'");
}
}
private string GetCustomAttributeTypeName(MetadataReader reader, EntityHandle constructorHandle)
{
if (constructorHandle.Kind == HandleKind.MemberReference)
{
MemberReference memberReference = reader.GetMemberReference((MemberReferenceHandle)constructorHandle);
return this.GetMemberReferenceParentTypeName(reader, memberReference.Parent);
}
if (constructorHandle.Kind == HandleKind.MethodDefinition)
{
MethodDefinition methodDefinition = reader.GetMethodDefinition((MethodDefinitionHandle)constructorHandle);
return this.GetTypeDefinitionFullName(reader, methodDefinition.GetDeclaringType());
}
return string.Empty;
}
private string GetMemberReferenceParentTypeName(MetadataReader reader, EntityHandle parentHandle)
{
if (parentHandle.Kind == HandleKind.TypeReference ||
parentHandle.Kind == HandleKind.TypeDefinition ||
parentHandle.Kind == HandleKind.TypeSpecification)
{
return this.GetEntityTypeName(reader, parentHandle);
}
return string.Empty;
}
private string GetEntityTypeName(MetadataReader reader, EntityHandle handle)
{
if (handle.Kind == HandleKind.TypeReference)
{
return this.GetTypeReferenceFullName(reader, (TypeReferenceHandle)handle);
}
if (handle.Kind == HandleKind.TypeDefinition)
{
return this.GetTypeDefinitionFullName(reader, (TypeDefinitionHandle)handle);
}
if (handle.Kind == HandleKind.TypeSpecification)
{
return this.DecodeTypeSpecification(reader, (TypeSpecificationHandle)handle);
}
return string.Empty;
}
private string GetTypeReferenceFullName(MetadataReader reader, TypeReferenceHandle handle)
{
TypeReference typeReference = reader.GetTypeReference(handle);
string typeName = reader.GetString(typeReference.Name);
string namespaceName = reader.GetString(typeReference.Namespace);
if (typeReference.ResolutionScope.Kind == HandleKind.TypeReference)
{
string parentTypeName = this.GetTypeReferenceFullName(reader, (TypeReferenceHandle)typeReference.ResolutionScope);
return $"{parentTypeName}.{typeName}";
}
if (string.IsNullOrEmpty(namespaceName))
{
return typeName;
}
return $"{namespaceName}.{typeName}";
}
private string GetTypeDefinitionFullName(MetadataReader reader, TypeDefinitionHandle handle)
{
TypeDefinition typeDefinition = reader.GetTypeDefinition(handle);
string typeName = reader.GetString(typeDefinition.Name);
TypeDefinitionHandle declaringTypeHandle = typeDefinition.GetDeclaringType();
if (!declaringTypeHandle.IsNil)
{
string parentTypeName = this.GetTypeDefinitionFullName(reader, declaringTypeHandle);
return $"{parentTypeName}.{typeName}";
}
string namespaceName = reader.GetString(typeDefinition.Namespace);
if (string.IsNullOrEmpty(namespaceName))
{
return typeName;
}
return $"{namespaceName}.{typeName}";
}
private string DecodeTypeSpecification(MetadataReader reader, TypeSpecificationHandle handle)
{
TypeSpecification typeSpecification = reader.GetTypeSpecification(handle);
MetadataTypeNameDecoder decoder = new MetadataTypeNameDecoder(reader, this);
return typeSpecification.DecodeSignature(decoder, genericContext: null);
}
private void AddViolation(
SecurityValidationResult result,
HashSet<string> seenViolations,
string apiName,
string message,
string description)
{
if (!seenViolations.Add($"{apiName}|{message}"))
{
return;
}
result.Violations.Add(new SecurityViolation
{
Type = SecurityViolationType.DangerousApiCall,
ApiName = apiName,
Message = message,
Description = description,
Location = "metadata"
});
}
private sealed class MetadataTypeNameDecoder : ISignatureTypeProvider<string, object>
{
private readonly MetadataReader _reader;
private readonly SystemReflectionMetadataPreloadValidator _validator;
public MetadataTypeNameDecoder(MetadataReader reader, SystemReflectionMetadataPreloadValidator validator)
{
_reader = reader;
_validator = validator;
}
public string GetArrayType(string elementType, ArrayShape shape)
{
return elementType;
}
public string GetByReferenceType(string elementType)
{
return elementType;
}
public string GetFunctionPointerType(MethodSignature<string> signature)
{
return string.Empty;
}
public string GetGenericInstantiation(string genericType, ImmutableArray<string> typeArguments)
{
return genericType;
}
public string GetGenericMethodParameter(object genericContext, int index)
{
return $"!!{index}";
}
public string GetGenericTypeParameter(object genericContext, int index)
{
return $"!{index}";
}
public string GetModifiedType(string modifierType, string unmodifiedType, bool isRequired)
{
return unmodifiedType;
}
public string GetPinnedType(string elementType)
{
return elementType;
}
public string GetPointerType(string elementType)
{
return elementType;
}
public string GetPrimitiveType(PrimitiveTypeCode typeCode)
{
return typeCode.ToString();
}
public string GetSZArrayType(string elementType)
{
return elementType;
}
public string GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind)
{
return _validator.GetTypeDefinitionFullName(reader, handle);
}
public string GetTypeFromReference(MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind)
{
return _validator.GetTypeReferenceFullName(reader, handle);
}
public string GetTypeFromSpecification(MetadataReader reader, object genericContext, TypeSpecificationHandle handle, byte rawTypeKind)
{
return _validator.DecodeTypeSpecification(reader, handle);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 29099a206e9664637b537f37b0687fa5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,13 @@
using UnityEditor;
namespace io.github.hatayama.uLoopMCP
{
public static class SystemReflectionMetadataPreloadValidatorRegistration
{
[InitializeOnLoadMethod]
private static void Register()
{
PreloadAssemblySecurityValidatorRegistry.RegisterValidator(new SystemReflectionMetadataPreloadValidator());
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2a84b92869d584c85b84b6e7b0b378d2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,22 @@
{
"name": "uLoopMCP.Editor.MetadataValidation",
"rootNamespace": "",
"references": [
"uLoopMCP.Editor.Shared"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": true,
"precompiledReferences": [
"uLoopMCP.System.Collections.Immutable.dll",
"uLoopMCP.System.Runtime.CompilerServices.Unsafe.dll",
"uLoopMCP.System.Reflection.Metadata.dll"
],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 616b0fafa8621452d8d1ae18a41bab94
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: