From 576f119367938cfbce339ac677a4a7d9c5df37e2 Mon Sep 17 00:00:00 2001 From: Bod9001 Date: Fri, 24 Nov 2023 17:59:21 +0000 Subject: [PATCH] security system (#212) * Initial security * White list jusst for a backup * Most of the easy stuff * Tweaking * Dialogue box * better dialogue * try ccatch * The first passsinng ssecuurity * First working scan * cleaning up * To do is * Moved some stuff into Interfaces * File service * ooops * zero warnings * Verssion bump * Naming * no Newtonsoft.Json * codacy channges * Passing Fix * No eerrors * version bump * formattting? * Formmattingg? * ssplit * more splitting and fix * formatting * changges * weird code * fix tthe errors * spacing * no gotos * spacing * Improvemennts * Comment * A puush * debug * win * win to max * remove * config * Works for windows * mac? And Linux funcctionality * Automatic run of pipe * Cleaning up * Requested changes * spacing * Assembly name * some requested changes * fixes * Coda see changes * cleaaning up * moree cleaaninng * cleaning cleaning ccleaaninng * http's * docs * Whiteespace? * whitespace changes * logs * whyyyy * fixes * logErroors * Whitespacee changes * Linux fix * log * logs * mac fix * Moree / shennanigans * Evenn more \ * yay mac is stupid * .app * AnyURL * move to Installation path * format * warnings * flatpack fix * format * try catch * Cleanup of security scanning services (#221) --------- Co-authored-by: Nickolas Gupton --- .github/workflows/dotnetcore.yml | 4 +- .../UnitystationLauncher.Tests.csproj | 2 +- UnitystationLauncher/CodeScanList.json | 3471 +++++++++++++++++ .../AssemblyTypeCheckerHelpers.cs | 447 +++ .../ContentScanning/FileInfoComparer.cs | 19 + .../ContentScanning/Parsers.cs | 174 + .../ContentScanning/Resolver.cs | 94 + .../ContentScanning/SandboxError.cs | 17 + .../ContentScanning/TypeProvider.cs | 83 + .../UnsupportedMetadataException.cs | 19 + .../PipeHubBuildCommunication.cs | 127 + .../Infrastructure/TypeExtensions.cs | 87 + UnitystationLauncher/Models/Api/Server.cs | 6 +- .../Models/ConfigFile/HubClientConfig.cs | 40 +- .../Models/ConfigFile/Preferences.cs | 43 +- .../Models/ConfigFile/SandboxConfig.cs | 18 + .../Models/ContentScanning/MType.cs | 24 + .../ScanningTypes/MMemberRef.cs | 13 + .../ScanningTypes/MMemberRefField.cs | 16 + .../ScanningTypes/MMemberRefMethod.cs | 23 + .../ScanningTypes/MResScope.cs | 5 + .../ScanningTypes/MResScopeAssembly.cs | 9 + .../ScanningTypes/MResScopeType.cs | 10 + .../ScanningTypes/MTypeByRef.cs | 19 + .../ScanningTypes/MTypeDefined.cs | 21 + .../ScanningTypes/MTypeGeneric.cs | 68 + .../MTypeGenericMethodPlaceHolder.cs | 14 + .../MTypeGenericTypePlaceHolder.cs | 14 + .../ScanningTypes/MTypeModified.cs | 21 + .../ScanningTypes/MTypeParsed.cs | 38 + .../ScanningTypes/MTypePointer.cs | 19 + .../ScanningTypes/MTypePrimitive.cs | 67 + .../ScanningTypes/MTypeReferenced.cs | 37 + .../ScanningTypes/MTypeSZArray.cs | 20 + .../ScanningTypes/MTypeWackyArray.cs | 34 + .../Models/ContentScanning/TypeConfig.cs | 20 + .../ContentScanning/WhitelistFieldDefine.cs | 13 + .../ContentScanning/WhitelistMethodDefine.cs | 23 + UnitystationLauncher/Models/Download.cs | 5 +- .../Models/Enums/ClientRequest.cs | 8 + .../Models/Enums/DumpFlags.cs | 14 + .../Models/Enums/InheritMode.cs | 11 + .../Services/AssemblyTypeCheckerService.cs | 623 +++ .../Services/CodeScanConfigService.cs | 291 ++ .../Services/CodeScanService.cs | 363 ++ .../Services/GameCommunicationPipeService.cs | 16 + .../Services/InstallationService.cs | 106 +- .../Interface/IAssemblyTypeCheckerService.cs | 10 + .../Interface/ICodeScanConfigService.cs | 15 + .../Services/Interface/ICodeScanService.cs | 10 + .../IGameCommunicationPipeService.cs | 4 + .../Interface/IInstallationService.cs | 2 +- UnitystationLauncher/StandardModule.cs | 4 + .../UnitystationLauncher.csproj | 18 +- .../ViewModels/LauncherViewModel.cs | 8 +- .../ViewModels/ServersPanelViewModel.cs | 19 +- UnitystationLauncher/Views/MainWindow.xaml.cs | 1 + UnitystationLauncher/Views/PopUpDialogue.cs | 39 + UnitystationLauncher/Views/PopUpDialogue.xaml | 17 + docs/how-code-scan.md | 20 + 60 files changed, 6691 insertions(+), 92 deletions(-) create mode 100644 UnitystationLauncher/CodeScanList.json create mode 100644 UnitystationLauncher/ContentScanning/AssemblyTypeCheckerHelpers.cs create mode 100644 UnitystationLauncher/ContentScanning/FileInfoComparer.cs create mode 100644 UnitystationLauncher/ContentScanning/Parsers.cs create mode 100644 UnitystationLauncher/ContentScanning/Resolver.cs create mode 100644 UnitystationLauncher/ContentScanning/SandboxError.cs create mode 100644 UnitystationLauncher/ContentScanning/TypeProvider.cs create mode 100644 UnitystationLauncher/Exceptions/UnsupportedMetadataException.cs create mode 100644 UnitystationLauncher/GameCommunicationPipe/PipeHubBuildCommunication.cs create mode 100644 UnitystationLauncher/Infrastructure/TypeExtensions.cs create mode 100644 UnitystationLauncher/Models/ConfigFile/SandboxConfig.cs create mode 100644 UnitystationLauncher/Models/ContentScanning/MType.cs create mode 100644 UnitystationLauncher/Models/ContentScanning/ScanningTypes/MMemberRef.cs create mode 100644 UnitystationLauncher/Models/ContentScanning/ScanningTypes/MMemberRefField.cs create mode 100644 UnitystationLauncher/Models/ContentScanning/ScanningTypes/MMemberRefMethod.cs create mode 100644 UnitystationLauncher/Models/ContentScanning/ScanningTypes/MResScope.cs create mode 100644 UnitystationLauncher/Models/ContentScanning/ScanningTypes/MResScopeAssembly.cs create mode 100644 UnitystationLauncher/Models/ContentScanning/ScanningTypes/MResScopeType.cs create mode 100644 UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypeByRef.cs create mode 100644 UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypeDefined.cs create mode 100644 UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypeGeneric.cs create mode 100644 UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypeGenericMethodPlaceHolder.cs create mode 100644 UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypeGenericTypePlaceHolder.cs create mode 100644 UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypeModified.cs create mode 100644 UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypeParsed.cs create mode 100644 UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypePointer.cs create mode 100644 UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypePrimitive.cs create mode 100644 UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypeReferenced.cs create mode 100644 UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypeSZArray.cs create mode 100644 UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypeWackyArray.cs create mode 100644 UnitystationLauncher/Models/ContentScanning/TypeConfig.cs create mode 100644 UnitystationLauncher/Models/ContentScanning/WhitelistFieldDefine.cs create mode 100644 UnitystationLauncher/Models/ContentScanning/WhitelistMethodDefine.cs create mode 100644 UnitystationLauncher/Models/Enums/ClientRequest.cs create mode 100644 UnitystationLauncher/Models/Enums/DumpFlags.cs create mode 100644 UnitystationLauncher/Models/Enums/InheritMode.cs create mode 100644 UnitystationLauncher/Services/AssemblyTypeCheckerService.cs create mode 100644 UnitystationLauncher/Services/CodeScanConfigService.cs create mode 100644 UnitystationLauncher/Services/CodeScanService.cs create mode 100644 UnitystationLauncher/Services/GameCommunicationPipeService.cs create mode 100644 UnitystationLauncher/Services/Interface/IAssemblyTypeCheckerService.cs create mode 100644 UnitystationLauncher/Services/Interface/ICodeScanConfigService.cs create mode 100644 UnitystationLauncher/Services/Interface/ICodeScanService.cs create mode 100644 UnitystationLauncher/Services/Interface/IGameCommunicationPipeService.cs create mode 100644 UnitystationLauncher/Views/PopUpDialogue.cs create mode 100644 UnitystationLauncher/Views/PopUpDialogue.xaml create mode 100644 docs/how-code-scan.md diff --git a/.github/workflows/dotnetcore.yml b/.github/workflows/dotnetcore.yml index a58b899f..ee63068c 100644 --- a/.github/workflows/dotnetcore.yml +++ b/.github/workflows/dotnetcore.yml @@ -9,7 +9,7 @@ on: - "*" env: - DOTNET_VERSION: '6.0.x' + DOTNET_VERSION: '7.0.x' jobs: test: @@ -115,4 +115,4 @@ jobs: # Build - name: Test Flatpak Build run: | - dotnet publish --framework net6.0 --runtime linux-x64 --configuration Release --self-contained true /p:DefineConstants="FLATPAK" /p:DisableBeauty=true + dotnet publish --framework net7.0 --runtime linux-x64 --configuration Release --self-contained true /p:DefineConstants="FLATPAK" /p:DisableBeauty=true diff --git a/UnitystationLauncher.Tests/UnitystationLauncher.Tests.csproj b/UnitystationLauncher.Tests/UnitystationLauncher.Tests.csproj index 00d4c5eb..60016471 100644 --- a/UnitystationLauncher.Tests/UnitystationLauncher.Tests.csproj +++ b/UnitystationLauncher.Tests/UnitystationLauncher.Tests.csproj @@ -1,7 +1,7 @@ - net6.0 + net7.0 enable false diff --git a/UnitystationLauncher/CodeScanList.json b/UnitystationLauncher/CodeScanList.json new file mode 100644 index 00000000..82174db9 --- /dev/null +++ b/UnitystationLauncher/CodeScanList.json @@ -0,0 +1,3471 @@ +{ + "AllowedVerifierErrors": [ + "InitOnly", + "InterfaceMethodNotImplemented" + ], + "WhitelistedNamespaces": [ + ], + "Types": { + "WebSocketSharp":{ + "MessageEventArgs":{ + "All": true + }, + "Ext" :{ + "All": true + }, + "CloseEventArgs" :{ + "All": true + }, + "WebSocketState" :{ + "All": true + }, + "WebSocket":{ + "All": true + } + }, + "WebSocketSharp.Server":{ + "HttpServer":{ + "All": true + }, + "WebSocketBehavior" :{ + "All": true + }, + "WebSocketSessionManager": { + "All": true + }, + "IWebSocketSession" : { + "All": true + }, + "WebSocketServiceManager" :{ + "All": true + }, + "WebSocketServiceHost" :{ + "All": true, + }, + }, + "WebSocketSharp.Net.WebSockets" :{ + "WebSocketContext":{ + "All": true + } + }, + "WebSocketSharp.Net" :{ + "NetworkCredential":{ + "All": true + }, + "AuthenticationSchemes" :{ + "All": true + } + }, + "System.Collections.Specialized" : { + "NotifyCollectionChangedEventHandler" : { + "All": true + }, + "NotifyCollectionChangedEventArgs" : { + "All": true + }, + }, + "System.Runtime.Serialization" : { + "SerializationInfo" : { + + }, + "ISerializable" : { + "All": true + }, + "IDeserializationCallback" : { + "All": true + }, + "StreamingContext" : { + + } + }, + "System.Security.Principal" :{ + "IIdentity": { + "All": true + }, + "IPrincipal": { + "All": true + }, + }, + "System.Drawing": { + "Color": { + "All": true + } + }, + "System.Buffers": { + "SpanAction`2": { + "All": true + }, + + }, + "System.Buffers.Binary": { + "BinaryPrimitives": { + "All": true + } + }, + "System.Collections.Generic": { + "CollectionExtensions": { + "All": true + }, + "Comparer`1": { + "All": true + }, + "Dictionary`2": { + "All": true + }, + "EqualityComparer`1": { + "All": true + }, + "HashSet`1": { + "All": true + }, + "IAsyncEnumerable`1": { + "All": true + }, + "ICollection`1": { + "All": true + }, + "IComparer`1": { + "All": true + }, + "IDictionary`2": { + "All": true + }, + "IEnumerable`1": { + "All": true + }, + "IEnumerator`1": { + "All": true + }, + "IEqualityComparer`1": { + "All": true + }, + "IList`1": { + "All": true + }, + "IReadOnlyCollection`1": { + "All": true + }, + "IReadOnlyDictionary`2": { + "All": true + }, + "IReadOnlyList`1": { + "All": true + }, + "IReadOnlySet`1": { + "All": true + }, + "ISet`1": { + "All": true + }, + "KeyNotFoundException": { + "All": true + }, + "KeyValuePair": { + "All": true + }, + "KeyValuePair`2": { + "All": true + }, + "LinkedList`1": { + "All": true + }, + "LinkedListNode`1": { + "All": true + }, + "List`1": { + "All": true + }, + "Queue`1": { + "All": true + }, + "ReferenceEqualityComparer": { + "All": true + }, + "SortedDictionary`2": { + "All": true + }, + "SortedList`2": { + "All": true + }, + "SortedSet`1": { + "All": true + }, + "Stack`1": { + "All": true + } + }, + "System.Collections.Immutable": { + "IImmutableDictionary`2": { + "All": true + }, + "IImmutableList`1": { + "All": true + }, + "IImmutableQueue`1": { + "All": true + }, + "IImmutableSet`1": { + "All": true + }, + "IImmutableStack`1": { + "All": true + }, + "ImmutableArray": { + "All": true + }, + "ImmutableArray`1": { + "All": true + }, + "ImmutableDictionary": { + "All": true + }, + "ImmutableDictionary`2": { + "All": true + }, + "ImmutableHashSet": { + "All": true + }, + "ImmutableHashSet`1": { + "All": true + }, + "ImmutableInterlocked": { + "All": true + }, + "ImmutableList": { + "All": true + }, + "ImmutableList`1": { + "All": true + }, + "ImmutableQueue": { + "All": true + }, + "ImmutableQueue`1": { + "All": true + }, + "ImmutableSortedDictionary": { + "All": true + }, + "ImmutableSortedDictionary`2": { + "All": true + }, + "ImmutableSortedSet": { + "All": true + }, + "ImmutableSortedSet`1": { + "All": true + }, + "ImmutableSortedStack": { + "All": true + }, + "ImmutableSortedStack`1": { + "All": true + } + }, + "System.Collections.Specialized": { + "NameValueCollection": { + "All": true + }, + "NotifyCollectionChangedEventHandler" : { + "All": true + }, + "NotifyCollectionChangedEventArgs" : { + "All": true + } + }, + "System.Collections.ObjectModel" : { + "ObservableCollection`1" : { + "All": true + }, + "ReadOnlyCollection`1" : { + "All": true + }, + "Collection`1" : { + "All": true + }, + }, + "System.Collections.Concurrent": { + "ConcurrentBag`1": { + "All": true + }, + "ConcurrentDictionary`2": { + "All": true + }, + "ConcurrentQueue`1": { + "All": true + }, + "ConcurrentStack`1": { + "All": true + } + }, + "System.Collections": { + "IEnumerable": { + "All": true + }, + "IEnumerator": { + "All": true + }, + "IReadOnlyList`1": { + "All": true + }, + "Collection": { + "All": true + }, + "Collection`1": { + "All": true + }, + "IList" : { + "All": true + }, + "IDictionary" :{ + "All": true + }, + "IDictionaryEnumerator" : { + "All": true + }, + "IDeserializationCallback" : { + "All": true + }, + "ICollection" : { + "All": true + } + }, + "System.ComponentModel": { + "CancelEventArgs": { + "All": true + }, + "PropertyDescriptor": {}, + "ISite": { + "All": true + }, + "IComponent": { + "All": true + }, + "IContainer": { + "All": true + }, + "ITypeDescriptorContext": { + "All": true + }, + "TypeDescriptor": { + "Methods": [ + "string GetClassName(object)" + ] + }, + "DescriptionAttribute" : { + "All": true + }, + "EditorBrowsableAttribute":{ + "All": true + }, + "EditorBrowsableState" :{ + "All": true + } + }, + "System.Diagnostics.CodeAnalysis": { + "AllowNullAttribute": { + "All": true + }, + "DisallowNullAttribute": { + "All": true + }, + "DoesNotReturnAttribute": { + "All": true + }, + "DoesNotReturnIfAttribute": { + "All": true + }, + "ExcludeFromCodeCoverageAttribute": { + "All": true + }, + "MaybeNullAttribute": { + "All": true + }, + "MaybeNullWhenAttribute": { + "All": true + }, + "MemberNotNullAttribute": { + "All": true + }, + "MemberNotNullWhenAttribute": { + "All": true + }, + "NotNullAttribute": { + "All": true + }, + "NotNullIfNotNullAttribute": { + "All": true + }, + "NotNullWhenAttribute": { + "All": true + }, + "SuppressMessageAttribute": { + "All": true + } + }, + "System.Diagnostics": { + "DebuggableAttribute": { + "All": true + }, + "DebuggerBrowsableAttribute": { + "All": true + }, + "DebuggerBrowsableState": {}, + "DebuggerDisplayAttribute": { + "All": true + }, + "DebuggerHiddenAttribute": { + "All": true + }, + "DebuggerNonUserCodeAttribute": { + "All": true + }, + "DebuggerStepperBoundaryAttribute": { + "All": true + }, + "DebuggerStepThroughAttribute": { + "All": true + }, + "DebuggerTypeProxyAttribute": { + "All": true + }, + "DebuggerVisualizerAttribute": { + "All": true + }, + "Stopwatch": { + "All": true + }, + "Process": { + "Methods": [ + "void Kill()", + "System.Diagnostics.Process GetCurrentProcess()" + ] + + }, + }, + "System.CodeDom.Compiler" : { + "GeneratedCodeAttribute": { + "All": true + } + }, + "System.Globalization": { + "CompareOptions": {}, + "CultureInfo": { + "All": true + }, + "DateTimeStyles": { + "All": true + }, + "NumberFormatInfo": { + "All": true + }, + "NumberStyles": {}, + "TextInfo": { + "Methods": [ + "bool get_IsRightToLeft()", + "char ToLower(char)", + "char ToUpper(char)", + "string get_ListSeparator()", + "string ToLower(string)", + "string ToTitleCase(string)", + "string ToTitleCase(string)", + "string ToUpper(string)" + ] + } + }, + "System.IO.Compression": { + "CompressionMode": {}, + "CompressionLevel": {}, + "DeflateStream": { + "All": true + }, + "ZipArchive": { + "All": true + }, + "ZipArchiveEntry": { + "All": true + }, + "ZipArchiveMode": {} + }, + "System.IO": { + "BinaryReader": { + "All": true + }, + "FileAccess": {}, + "FileMode": {}, + "FileShare": {}, + "FileNotFoundException": { + "All": true + }, + "InvalidDataException": { + "All": true + }, + "IOException": { + "All": true + }, + "MemoryStream": { + "All": true + }, + "SeekOrigin": {}, + "Stream": { + "All": true + }, + "StreamReader": { + "Fields": [ + "System.IO.StreamReader Null" + ], + "Methods": [ + "System.IO.Stream get_BaseStream()", + "void .ctor(System.IO.Stream)", + "void .ctor(System.IO.Stream, bool)", + "void .ctor(System.IO.Stream, System.Text.Encoding)", + "void .ctor(System.IO.Stream, System.Text.Encoding, bool)", + "void .ctor(System.IO.Stream, System.Text.Encoding, bool, int)", + "void .ctor(System.IO.Stream, System.Text.Encoding, bool, int, bool)" + ] + }, + "StreamWriter": { + "Fields": [ + "System.IO.StreamWriter Null" + ], + "Methods": [ + "bool get_AutoFlush()", + "System.IO.Stream get_BaseStream()", + "void .ctor(System.IO.Stream)", + "void .ctor(System.IO.Stream, System.Text.Encoding)", + "void .ctor(System.IO.Stream, System.Text.Encoding, int)", + "void .ctor(System.IO.Stream, System.Text.Encoding, int, bool)", + "void DiscardBufferedData()" + ] + }, + "TextReader": { + "All": true + }, + "TextWriter": { + "All": true + }, + "Path" : { + "Methods": [ + "string Combine(string, string)" + ] + }, + "StringReader" : { + "All": true + }, + }, + "System.Linq.Expressions": { + "ConstantExpression": {}, + "Expression": { + "Methods": [ + "System.Linq.Expressions.Expression`1 Lambda<>(System.Linq.Expressions.Expression, System.Linq.Expressions.ParameterExpression[])", + ] + }, + "Expression`1": {}, + "MemberExpression": {}, + "ParameterExpression": {} + }, + "System.Linq": { + "Enumerable": { + "All": true + }, + "IGrouping`2": { + "All": true + }, + "IOrderedEnumerable`1": { + "All": true + } + }, + "System.Net": { + "DnsEndPoint": {}, + "IPAddress": { + "All": true + }, + "HttpStatusCode": { + "All": true + }, + "HttpStatusCode": { + "All": true + }, + }, + "System.Net.Http" : { + "StringContent" : { + "All": true + }, + "HttpResponseMessage" : { + "All": true + }, + "HttpRequestMessage" : { + "All": true + }, + "HttpRequestException" : { + "All": true + }, + "HttpMethod" : { + "All": true + }, + "HttpContent" : { + "All": true + }, + }, + "System.Net.Http.Headers" : { + "MediaTypeWithQualityHeaderValue" : { + "All": true + }, + "HttpResponseHeaders" : { + "All": true + }, + "HttpHeaderValueCollection`1" : { + "All": true + }, + "HttpHeaders" : { + "All": true + }, + "HttpRequestHeaders" :{ + "All": true + } + }, + "System.Net.Sockets": { + "AddressFamily": {} + }, + "System.Net.NetworkInformation" : { + "PhysicalAddress" : { + "All": true + }, + "NetworkInterface" : { + "All": true + } + }, + "System.Numerics": { + "BitOperations": { + "All": true + }, + "Vector2": { + "All": true + } + }, + "System.Reflection": { + "Assembly": { + "Methods": [ + "string CreateQualifiedName(string, string)", + "string get_FullName()", + "System.Collections.Generic.IEnumerable`1 get_DefinedTypes()", + "System.IO.Stream GetManifestResourceStream(string)", + "System.IO.Stream GetManifestResourceStream(System.Type, string)", + "System.Reflection.Assembly GetAssembly(System.Type)", + "System.Reflection.Assembly GetExecutingAssembly()", + "System.Type[] GetTypes()", + "System.Reflection.AssemblyName GetName()" + ] + }, + "AssemblyCompanyAttribute": { + "All": true + }, + "AssemblyConfigurationAttribute": { + "All": true + }, + "AssemblyFileVersionAttribute": { + "All": true + }, + "AssemblyInformationalVersionAttribute": { + "All": true + }, + "AssemblyName": { + "Methods": [ + "string get_FullName()", + "System.Version get_Version()", + "string get_Name()" + ] + }, + "AssemblyProductAttribute": { + "All": true + }, + "AssemblyTitleAttribute": { + "All": true + }, + "DefaultMemberAttribute": { + "All": true + }, + "GenericParameterAttributes": {}, + "MemberFilter": {}, + "MemberInfo": { + "Methods": [ + "string get_Name()", + "System.Reflection.MemberTypes get_MemberType()", + "System.Type get_DeclaringType()", + "System.Type get_ReturnType()", + "System.Type get_ReflectedType()", + ] + }, + "MemberTypes": {}, + "MethodBase": { + "Methods": [ + "System.Reflection.ParameterInfo[] GetParameters()", + "bool get_IsStatic()", + ] + }, + "MethodInfo": { + "Methods": [ + "System.Type get_ReturnType()" + ] + }, + "TypeAttributes": {}, + "TypeInfo": {}, + "PropertyInfo": { + "Methods": [ + "bool op_Inequality(System.Reflection.PropertyInfo, System.Reflection.PropertyInfo)", + ] + }, + "FieldInfo" : {}, + "ParameterInfo" : { + "Methods": [ + "System.Type get_ParameterType()", + "bool op_Inequality(System.Reflection.PropertyInfo, System.Reflection.PropertyInfo)", + ] + }, + "CustomAttributeExtensions": { + "Methods": [ + "!!0 GetCustomAttribute(System.Reflection.MemberInfo, bool)" + ] + } + }, + "System.Runtime.CompilerServices": { + "AsyncStateMachineAttribute": { + "All": true + }, + "AsyncTaskMethodBuilder": { + "All": true + }, + "AsyncTaskMethodBuilder`1": { + "All": true + }, + "AsyncValueTaskMethodBuilder": { + "All": true + }, + "AsyncValueTaskMethodBuilder`1": { + "All": true + }, + "AsyncVoidMethodBuilder": { + "All": true + }, + "CallerArgumentExpressionAttribute": { + "All": true + }, + "CompilationRelaxationsAttribute": { + "All": true + }, + "CompilerFeatureRequiredAttribute": { + "All": true + }, + "CompilerGeneratedAttribute": { + "All": true + }, + "DefaultInterpolatedStringHandler": { + "All": true + }, + "ExtensionAttribute": { + "All": true + }, + "IAsyncStateMachine": { + "All": true + }, + "InternalsVisibleToAttribute": { + "All": true + }, + "InterpolatedStringHandlerAttribute": { + "All": true + }, + "IsByRefLikeAttribute": { + "All": true + }, + "IsExternalInit": { + "All": true + }, + "IsReadOnlyAttribute": { + "All": true + }, + "IteratorStateMachineAttribute": { + "All": true + }, + "PreserveBaseOverridesAttribute": { + "All": true + }, + "RuntimeCompatibilityAttribute": { + "All": true + }, + "RuntimeHelpers": { + "All": true + }, + "TaskAwaiter": { + "All": true + }, + "TaskAwaiter`1": { + "All": true + }, + "TupleElementNamesAttribute": { + "All": true + }, + "ValueTaskAwaiter": { + "All": true + }, + "ValueTaskAwaiter`1": { + "All": true + }, + "DecimalConstantAttribute": { + "All": true + }, + "ConfiguredTaskAwaitable" : { + "All": true + }, + "CallerMemberNameAttribute" : { + "All": true + }, + "ConfiguredTaskAwaitable/ConfiguredTaskAwaiter" : { + "All": true + }, + }, + "System.Runtime.ExceptionServices": { + "ExceptionDispatchInfo": { + "All": true + } + }, + "System.Runtime.InteropServices": { + "CollectionsMarshal": { + "Methods": [ + "System.Span`1 AsSpan<>(System.Collections.Generic.List`1)" + ] + }, + "InAttribute": { + "All": true + }, + "UnmanagedType": {} + }, + "System.Runtime.Versioning": { + "TargetFrameworkAttribute": { + "All": true + } + }, + "System.Text.Json.Serialization": { + "JsonIgnoreAttribute": { + "All": true + }, + "JsonPropertyNameAttribute": { + "All": true + } + }, + "System.Text.RegularExpressions": { + "Capture": { + "All": true + }, + "CaptureCollection": { + "All": true + }, + "Group": { + "All": true + }, + "GroupCollection": { + "All": true + }, + "Match": { + "All": true + }, + "MatchCollection": { + "All": true + }, + "MatchEvaluator": { + "All": true + }, + "Regex": { + "Methods": [ + "bool get_RightToLeft()", + "bool IsMatch(string)", + "bool IsMatch(string, int)", + "bool IsMatch(string, string)", + "bool IsMatch(string, string, System.Text.RegularExpressions.RegexOptions)", + "bool IsMatch(string, string, System.Text.RegularExpressions.RegexOptions, System.TimeSpan)", + "int GroupNumberFromName(string)", + "int[] GetGroupNumbers()", + "string Escape()", + "string GroupNameFromNumber(int)", + "string Replace(string, string)", + "string Replace(string, string, int)", + "string Replace(string, string, int, int)", + "string Replace(string, string, string)", + "string Replace(string, string, string, System.Text.RegularExpressions.RegexOptions)", + "string Replace(string, string, string, System.Text.RegularExpressions.RegexOptions, System.TimeSpan)", + "string Replace(string, string, System.Text.RegularExpressions.MatchEvaluator)", + "string Replace(string, string, System.Text.RegularExpressions.MatchEvaluator, System.Text.RegularExpressions.RegexOptions)", + "string Replace(string, string, System.Text.RegularExpressions.MatchEvaluator, System.Text.RegularExpressions.RegexOptions, System.TimeSpan)", + "string Replace(string, System.Text.RegularExpressions.MatchEvaluator)", + "string Replace(string, System.Text.RegularExpressions.MatchEvaluator, int)", + "string Replace(string, System.Text.RegularExpressions.MatchEvaluator, int, int)", + "string ToString()", + "string Unescape(string)", + "string[] GetGroupNames()", + "string[] Split(string)", + "string[] Split(string, int)", + "string[] Split(string, int, int)", + "string[] Split(string, string)", + "string[] Split(string, string, System.Text.RegularExpressions.RegexOptions)", + "string[] Split(string, string, System.Text.RegularExpressions.RegexOptions, System.TimeSpan)", + "System.Text.RegularExpressions.Match Match(string)", + "System.Text.RegularExpressions.Match Match(string, int)", + "System.Text.RegularExpressions.Match Match(string, int, int)", + "System.Text.RegularExpressions.Match Match(string, string)", + "System.Text.RegularExpressions.Match Match(string, string, System.Text.RegularExpressions.RegexOptions)", + "System.Text.RegularExpressions.Match Match(string, string, System.Text.RegularExpressions.RegexOptions, System.TimeSpan)", + "System.Text.RegularExpressions.Match[] Matches(string)", + "System.Text.RegularExpressions.Match[] Matches(string, int)", + "System.Text.RegularExpressions.Match[] Matches(string, string)", + "System.Text.RegularExpressions.Match[] Matches(string, string, System.Text.RegularExpressions.RegexOptions)", + "System.Text.RegularExpressions.Match[] Matches(string, string, System.Text.RegularExpressions.RegexOptions, System.TimeSpan)", + "System.Text.RegularExpressions.RegexOptions get_Options()", + "System.TimeSpan get_MatchTimeout()", + "void .ctor()", + "void .ctor(string)", + "void .ctor(string, System.Text.RegularExpressions.RegexOptions)", + "void .ctor(string, System.Text.RegularExpressions.RegexOptions, System.TimeSpan)", + "System.Text.RegularExpressions.MatchCollection Matches(string, string)" + ] + }, + "RegexMatchTimeoutException": { + "All": true + }, + "RegexOptions": {}, + "RegexParseError": {}, + "RegexParseException": { + "All": true + } + }, + "System.Text": { + "Encoding": { + "Methods": [ + "bool IsAlwaysNormalized()", + "bool IsAlwaysNormalized(System.Text.NormalizationForm)", + "byte[] Convert(System.Text.Encoding, System.Text.Encoding, byte[])", + "byte[] Convert(System.Text.Encoding, System.Text.Encoding, byte[], int, int)", + "byte[] GetBytes(char[])", + "byte[] GetBytes(char[], int, int)", + "byte[] GetBytes(string)", + "byte[] GetBytes(string, int, int)", + "byte[] GetPreamble()", + "char[] GetChars(byte[])", + "char[] GetChars(byte[], int, int)", + "int GetByteCount(char[])", + "int GetByteCount(char[], int, int)", + "int GetByteCount(string)", + "int GetByteCount(string, int, int)", + "int GetByteCount(System.ReadOnlySpan`1)", + "int GetBytes(char[], int, int, byte[], int)", + "int GetBytes(string, int, int, byte[], int)", + "int GetBytes(System.ReadOnlySpan`1, System.Span`1)", + "int GetCharCount(byte[])", + "int GetCharCount(byte[], int, int)", + "int GetCharCount(System.ReadOnlySpan`1)", + "int GetChars(byte[], int, int, char[], int)", + "int GetChars(System.ReadOnlySpan`1, System.Span`1)", + "int GetMaxByteCount(int)", + "int GetMaxCharCount(int)", + "string GetString(byte[])", + "string GetString(byte[], int, int)", + "string GetString(System.ReadOnlySpan`1)", + "System.Text.Decoder GetDecoder()", + "System.Text.Encoder GetEncoder()", + "System.Text.Encoding get_ASCII()", + "System.Text.Encoding get_BigEndianUnicode()", + "System.Text.Encoder GetEncoder()", + "System.Text.Encoding get_ASCII()", + "System.Text.Encoding get_BigEndianUnicode()", + "System.Text.Encoding get_Unicode()", + "System.Text.Encoding get_UTF7()", + "System.Text.Encoding get_UTF8()", + "System.Text.Encoding get_UTF32()", + "System.Text.Encoding GetEncoding(string)" + ] + }, + "NormalizationForm": {}, + "Rune": { + "All": true + }, + "StringBuilder": { + "Methods": [ + "bool Equals(System.ReadOnlySpan`1)", + "bool Equals(System.Text.StringBuilder)", + "char get_Chars(int)", + "int EnsureCapacity(int)", + "int get_Capacity()", + "int get_Length()", + "int get_MaxCapacity()", + "string ToString()", + "string ToString(int, int)", + "System.Text.StringBuilder Append(bool)", + "System.Text.StringBuilder Append(byte)", + "System.Text.StringBuilder Append(char)", + "System.Text.StringBuilder Append(char, int)", + "System.Text.StringBuilder Append(char[])", + "System.Text.StringBuilder Append(char[], int, int)", + "System.Text.StringBuilder Append(double)", + "System.Text.StringBuilder Append(int)", + "System.Text.StringBuilder Append(long)", + "System.Text.StringBuilder Append(object)", + "System.Text.StringBuilder Append(sbyte)", + "System.Text.StringBuilder Append(short)", + "System.Text.StringBuilder Append(single)", + "System.Text.StringBuilder Append(string)", + "System.Text.StringBuilder Append(string, int, int)", + "System.Text.StringBuilder Append(System.Decimal)", + "System.Text.StringBuilder Append(System.ReadOnlyMemory`2)", + "System.Text.StringBuilder Append(System.ReadOnlySpan`2)", + "System.Text.StringBuilder Append(ref System.Text.StringBuilder/AppendInterpolatedStringHandler)", + "System.Text.StringBuilder Append(System.Text.StringBuilder)", + "System.Text.StringBuilder Append(System.Text.StringBuilder, int, int)", + "System.Text.StringBuilder Append(uint)", + "System.Text.StringBuilder Append(ulong)", + "System.Text.StringBuilder Append(ushort)", + "System.Text.StringBuilder AppendFormat(string, object)", + "System.Text.StringBuilder AppendFormat(string, object, object)", + "System.Text.StringBuilder AppendFormat(string, object, object, object)", + "System.Text.StringBuilder AppendFormat(string, object[])", + "System.Text.StringBuilder AppendFormat(System.IFormatProvider, string, object)", + "System.Text.StringBuilder AppendFormat(System.IFormatProvider, string, object, object)", + "System.Text.StringBuilder AppendFormat(System.IFormatProvider, string, object, object, object)", + "System.Text.StringBuilder AppendFormat(System.IFormatProvider, string, object[])", + "System.Text.StringBuilder AppendJoin(char, object[])", + "System.Text.StringBuilder AppendJoin(char, string[])", + "System.Text.StringBuilder AppendJoin(string, object[])", + "System.Text.StringBuilder AppendJoin(string, string[])", + "System.Text.StringBuilder AppendJoin<>(char, System.Collections.Generic.IEnumerable`1)", + "System.Text.StringBuilder AppendJoin<>(string, System.Collections.Generic.IEnumerable`1)", + "System.Text.StringBuilder AppendLine()", + "System.Text.StringBuilder AppendLine(string)", + "System.Text.StringBuilder Clear()", + "System.Text.StringBuilder Insert(int, bool)", + "System.Text.StringBuilder Insert(int, byte)", + "System.Text.StringBuilder Insert(int, char)", + "System.Text.StringBuilder Insert(int, char[])", + "System.Text.StringBuilder Insert(int, char[], int, int)", + "System.Text.StringBuilder Insert(int, double)", + "System.Text.StringBuilder Insert(int, float)", + "System.Text.StringBuilder Insert(int, int)", + "System.Text.StringBuilder Insert(int, long)", + "System.Text.StringBuilder Insert(int, object)", + "System.Text.StringBuilder Insert(int, sbyte)", + "System.Text.StringBuilder Insert(int, short)", + "System.Text.StringBuilder Insert(int, string, int)", + "System.Text.StringBuilder Insert(int, System.Decimal)", + "System.Text.StringBuilder Insert(int, System.ReadOnlySpan`1)", + "System.Text.StringBuilder Insert(int, uint)", + "System.Text.StringBuilder Insert(int, ulong)", + "System.Text.StringBuilder Insert(int, ushort)", + "System.Text.StringBuilder Remove(int, int)", + "System.Text.StringBuilder Replace(char, char)", + "System.Text.StringBuilder Replace(char, char, int, int)", + "System.Text.StringBuilder Replace(string, string)", + "System.Text.StringBuilder Replace(string, string, int, int)", + "System.Text.StringBuilder/ChunkEnumerator GetChunks()", + "void .ctor()", + "void .ctor(int)", + "void .ctor(int, int)", + "void .ctor(string)", + "void .ctor(string, int)", + "void .ctor(string, int, int, int)", + "void CopyTo(int, char[], int, int)", + "void CopyTo(int, System.Span`1, int)", + "System.Text.StringBuilder Insert(int, string)", + "System.Text.StringBuilder Append(float)", + "void set_Length(int)" + ], + "NestedTypes": { + "ChunkEnumerator": { + "All": true + }, + "AppendInterpolatedStringHandler": { + "All": true + } + } + }, + "StringRuneEnumerator": { + "All": true + } + }, + "System.Threading.Tasks": { + "Task": { + "All": true + }, + "Task`1": { + "All": true + }, + "TaskCompletionSource": { + "All": true + }, + "TaskCompletionSource`1": { + "All": true + }, + "TaskCanceledException": { + "All": true + }, + "ValueTask": { + "All": true + }, + "ValueTask`1": { + "All": true + }, + "TaskScheduler" : { + "Methods" :[ + "System.Threading.Tasks.TaskScheduler FromCurrentSynchronizationContext()" + ] + } + }, + "System.Threading": { + "CancellationToken": { + "All": true + }, + "CancellationTokenSource": { + "All": true + }, + "Interlocked": { + "All": true + }, + "Monitor": { + "All": true + }, + "Thread": { + "Methods" :[ + "void Start()", + "void Sleep(int)", + "System.Threading.Thread get_CurrentThread()", + "System.Globalization.CultureInfo CurrentCulture()", + "void Abort()", + "System.Globalization.CultureInfo get_CurrentCulture()", + "void .ctor(System.Threading.ThreadStart)" + ] + }, + "ThreadPool" : { + "Methods" :[ + "bool QueueUserWorkItem(System.Threading.WaitCallback)" + ] + }, + "ThreadStart" : { + "All": true + }, + "WaitCallback" : { + "All": true + }, + "WaitHandle" : { + "All": true + }, + "Mutex" : { + "All": true + }, + }, + "System.Web": { + "HttpUtility": { + "Methods": [ + "System.Collections.Specialized.NameValueCollection ParseQueryString(string, System.Text.Encoding)", + "System.Collections.Specialized.NameValueCollection ParseQueryString(string)", + "string JavaScriptStringEncode(string)", + "string UrlDecode(string, System.Text.Encoding)", + "string UrlDecode(string)", + "string UrlEncode(string, System.Text.Encoding)", + "string UrlEncode(string)" + ] + } + }, + "System": { + "Decimal" : { + "All": true + }, + "IServiceProvider": { + "All": true + }, + "Action": { + "All": true + }, + "Action`1": { + "All": true + }, + "Action`2": { + "All": true + }, + "Action`3": { + "All": true + }, + "Action`4": { + "All": true + }, + "Action`5": { + "All": true + }, + "Action`6": { + "All": true + }, + "Action`7": { + "All": true + }, + "Action`8": { + "All": true + }, + "Action`9": { + "All": true + }, + "Action`10": { + "All": true + }, + "Action`11": { + "All": true + }, + "Action`12": { + "All": true + }, + "Action`13": { + "All": true + }, + "Action`14": { + "All": true + }, + "Action`15": { + "All": true + }, + "Action`16": { + "All": true + }, + "ArgumentException": { + "All": true + }, + "ArgumentOutOfRangeException": { + "All": true + }, + "Array": { + "Methods": [ + "!!0 Find<>(!!0[], System.Predicate`1)", + "!!0 Resize<>(!!0[], int)", + "!!1 ConvertAll<,>(!!0[], System.Converter`2)", + "!!0[] Empty<>()", + "!!0[] FindAll<>(!!0[], System.Predicate`1)", + "bool Exists<>(!!0[], System.Predicate`1)", + "bool get_IsFixedSize()", + "bool get_IsReadOnly()", + "bool get_IsSynchronized()", + "bool TrueForAll<>(!!0[], System.Predicate`1)", + "int BinarySearch(System.Array, int, int, object)", + "int BinarySearch(System.Array, int, int, object, System.Collections.IComparer)", + "int BinarySearch(System.Array, object)", + "int BinarySearch(System.Array, object, System.Collections.IComparer)", + "int BinarySearch<>(!!0[], !!0[])", + "int BinarySearch<>(!!0[], !!0[], System.Collections.IComparer)", + "int BinarySearch<>(!!0[], int, int, !!0[])", + "int BinarySearch<>(!!0[], int, int, !!0[], System.Collections.IComparer)", + "int FindIndex<>(!!0[], int, int, System.Predicate`1)", + "int FindIndex<>(!!0[], int, System.Predicate`1)", + "int FindIndex<>(!!0[], System.Predicate`1)", + "int FindLastIndex<>(!!0[], int, int, System.Predicate`1)", + "int FindLastIndex<>(!!0[], int, System.Predicate`1)", + "int FindLastIndex<>(!!0[], System.Predicate`1)", + "int get_Length()", + "int get_Rank()", + "int GetLength(int)", + "int GetLowerBound(int)", + "int GetUpperBound(int)", + "int IndexOf(System.Array, object)", + "int IndexOf(System.Array, object, int)", + "int IndexOf(System.Array, object, int, int)", + "int IndexOf<>(!!0[], !!0)", + "int IndexOf<>(!!0[], !!0, int)", + "int IndexOf<>(!!0[], !!0, int, int)", + "int LastIndexOf(System.Array, object)", + "int LastIndexOf(System.Array, object, int)", + "int LastIndexOf(System.Array, object, int, int)", + "int LastIndexOf<>(!!0[], !!0)", + "int LastIndexOf<>(!!0[], !!0, int)", + "int LastIndexOf<>(!!0[], !!0, int, int)", + "long get_LongLength()", + "long GetLongLength(int)", + "object Clone()", + "object get_SyncRoot()", + "object GetValue(int)", + "object GetValue(int, int)", + "object GetValue(int, int, int)", + "object GetValue(int[])", + "object GetValue(long)", + "object GetValue(long, long)", + "object GetValue(long, long, long)", + "object GetValue(long[])", + "System.Collections.IEnumerator GetEnumerator()", + "System.Collections.ObjectModel.ReadOnlyCollection`1 AsReadOnly<>(!!0[])", + "void Clear(System.Array, int, int)", + "void ConstrainedCopy(System.Array, int, System.Array, int, int)", + "void Copy(System.Array, int, System.Array, int, int)", + "void Copy(System.Array, long, System.Array, long, long)", + "void Copy(System.Array, System.Array, int)", + "void Copy(System.Array, System.Array, long)", + "void CopyTo(System.Array, int)", + "void CopyTo(System.Array, long)", + "void Fill<>(!!0[], !!0)", + "void Fill<>(!!0[], !!0, int, int)", + "void ForEach<>(!!0[], System.Action`1)", + "void Reverse(System.Array)", + "void Reverse(System.Array, int, int)", + "void Reverse<>(!!0[])", + "void Reverse<>(!!0[], int, int)", + "void SetValue(object, int)", + "void SetValue(object, int, int)", + "void SetValue(object, int, int, int)", + "void SetValue(object, int[])", + "void SetValue(object, long)", + "void SetValue(object, long, long)", + "void SetValue(object, long, long, long)", + "void SetValue(object, long[])", + "void Sort(System.Array)", + "void Sort(System.Array, int, int)", + "void Sort(System.Array, int, int, System.Collections.IComparer)", + "void Sort(System.Array, System.Array)", + "void Sort(System.Array, System.Array, int, int)", + "void Sort(System.Array, System.Array, int, int, System.Collections.IComparer)", + "void Sort(System.Array, System.Array, System.Collections.IComparer)", + "void Sort(System.Array, System.Collections.IComparer)", + "void Sort<,>(!!0[], !!1[])", + "void Sort<,>(!!0[], !!1[], int, int)", + "void Sort<,>(!!0[], !!1[], int, int, System.Collections.Generic.IComparer`1)", + "void Sort<,>(!!0[], !!1[], System.Collections.Generic.IComparer`1)", + "void Sort<>(!!0[])", + "void Sort<>(!!0[], int, int)", + "void Sort<>(!!0[], int, int, System.Collections.Generic.IComparer`1)", + "void Sort<>(!!0[], System.Collections.Generic.IComparer`1)", + "void Sort<>(!!0[], System.Collections.Generic.IComparer`1)", + "void Sort<>(!!0[], System.Comparison`1)", + "void Resize<>(ref !!0[], int)" + ] + }, + "ArraySegment`1": { + "All": true + }, + "AsyncCallback": {}, + "Attribute": { + "All": true + }, + "AttributeTargets": {}, + "AttributeUsageAttribute": { + "All": true + }, + "BitConverter": { + "All": true + }, + "Base64FormattingOptions": {}, + "Boolean": { + "All": true + }, + "Byte": { + "All": true + }, + "Char": { + "All": true + }, + "CharEnumerator": { + "All": true + }, + "Comparison`1": { + "All": true + }, + "Convert": { + "Fields": [ + "object DBNull" + ], + "Methods": [ + "System.TypeCode GetTypeCode(object)", + "bool IsDBNull(object)", + "string ToBase64String(byte[])", + "string ToBase64String(byte[], System.Base64FormattingOptions)", + "string ToBase64String(byte[], int, int)", + "string ToBase64String(byte[], int, int, System.Base64FormattingOptions)", + "string ToBase64String(System.ReadOnlySpan`1, System.Base64FormattingOptions)", + "int ToBase64CharArray(byte[], int, int, char[], int)", + "int ToBase64CharArray(byte[], int, int, char[], int, System.Base64FormattingOptions)", + "bool TryToBase64Chars(System.ReadOnlySpan`1, System.Span`1, ref int, System.Base64FormattingOptions)", + "byte[] FromBase64String(string)", + "bool TryFromBase64String(string, System.Span`1, ref int)", + "bool TryFromBase64Chars(System.ReadOnlySpan`1, System.Span`1, ref int)", + "byte[] FromBase64CharArray(char[], int, int)", + "byte[] FromHexString(string)", + "byte[] FromHexString(System.ReadOnlySpan`1)", + "string ToHexString(byte[])", + "string ToHexString(byte[], int, int)", + "string ToHexString(System.ReadOnlySpan`1)", + "int ToInt32(string)", + "int ToInt32(Double)", + "int ToInt32(float)", + "int ToInt32(System.Decimal)", + "float ToSingle(Double)", + "ushort ToUInt16(int)", + "ushort ToUInt16(float)", + "float ToSingle(double)", + "int ToInt32(int)", + "int ToInt32(double)", + "short ToInt16(double)", + "double ToDouble(string)", + "System.Decimal ToDecimal(int)", + "char ToChar(int)", + "bool ToBoolean(string)", + "short ToInt16(float)", + ] + }, + "Converter`2": { + "All": true + }, + "DateTime": { + "All": true + }, + "DateTimeKind": {}, + "DateTimeOffset": { + "All": true + }, + "Delegate": { + "Methods": [ + "System.Delegate Combine(System.Delegate, System.Delegate)", + "System.Delegate Remove(System.Delegate, System.Delegate)", + "System.Delegate[] GetInvocationList()", + "object get_Target()", + "System.Reflection.MethodInfo get_Method()", + "bool op_Equality(System.Delegate, System.Delegate)" + ] + }, + "DivideByZeroException": { + "All": true + }, + "Double": { + "All": true + }, + "Enum": { + "All": true + }, + "Environment": { + "Methods": [ + "int get_CurrentManagedThreadId()", + "int get_ProcessorCount()", + "string get_NewLine()", + "string get_StackTrace()", + "string[] GetCommandLineArgs() " + ] + }, + "EventArgs": { + "All": true + }, + "EventHandler": { + "All": true + }, + "EventHandler`1": { + "All": true + }, + "Exception": { + "All": true + }, + "FlagsAttribute": { + "All": true + }, + "Func`1": { + "All": true + }, + "Func`2": { + "All": true + }, + "Func`3": { + "All": true + }, + "Func`4": { + "All": true + }, + "Func`5": { + "All": true + }, + "Func`6": { + "All": true + }, + "Func`7": { + "All": true + }, + "Func`8": { + "All": true + }, + "Func`9": { + "All": true + }, + "Func`10": { + "All": true + }, + "Func`11": { + "All": true + }, + "Func`12": { + "All": true + }, + "Func`13": { + "All": true + }, + "Func`14": { + "All": true + }, + "Func`15": { + "All": true + }, + "Func`16": { + "All": true + }, + "Func`17": { + "All": true + }, + "Guid": { + "All": true + }, + "HashCode": { + "All": true + }, + "IAsyncDisposable": { + "All": true + }, + "IAsyncResult": {}, + "ICloneable": { + "All": true + }, + "IComparable": { + "All": true + }, + "IComparable`1": { + "All": true + }, + "IDisposable": { + "All": true + }, + "IEquatable`1": {}, + "IFormatProvider": { + "All": true + }, + "IFormattable": { + "All": true + }, + "Index": { + "All": true + }, + "IndexOutOfRangeException": { + "All": true + }, + "Int16": { + "All": true + }, + "Int32": { + "All": true + }, + "Int64": { + "All": true + }, + "IntPtr": { + "All": true + }, + "InvalidOperationException": { + "All": true + }, + "Math": { + "All": true + }, + "MathF": { + "All": true + }, + "Memory`1": { + "Methods": [ + "!0[] ToArray()", + "bool Equals(object)", + "bool Equals(System.Memory`1)", + "bool get_IsEmpty()", + "bool TryCopyTo(System.Memory`1)", + "int get_Length()", + "string ToString()", + "System.Memory`1 get_Empty()", + "System.Memory`1 op_Implicit(!0[])", + "System.Memory`1 op_Implicit(System.ArraySegment`1)", + "System.Memory`1 Slice(int)", + "System.Memory`1 Slice(int, int)", + "System.ReadOnlyMemory`1 op_Implicit(System.Memory`1)", + "System.Span`1 get_Span()", + "void .ctor(!0[])", + "void .ctor(!0[], int, int)", + "void CopyTo(System.Memory`1)" + ] + }, + "MemoryExtensions": { + "All": true + }, + "MidpointRounding": {}, + "MulticastDelegate": { + "Inherit": "Allow" + }, + "NotImplementedException": { + "All": true + }, + "NotSupportedException": { + "All": true + }, + "Nullable": { + "All": true + }, + "Nullable`1": { + "All": true + }, + "NullReferenceException": { + "All": true + }, + "Object": { + "All": true + }, + "ObsoleteAttribute": { + "All": true + }, + "OperationCanceledException": { + "All": true + }, + "ParamArrayAttribute": { + "All": true + }, + "Predicate`1": { + "All": true + }, + "Random": { + "All": true + }, + "Range": { + "All": true + }, + "ReadOnlyMemory`1": { + "Methods": [ + "!0[] ToArray()", + "bool Equals(object)", + "bool Equals(System.ReadOnlyMemory`1)", + "bool get_IsEmpty()", + "bool TryCopyTo(System.Memory`1)", + "int get_Length()", + "string ToString()", + "System.ReadOnlyMemory`1 get_Empty()", + "System.ReadOnlyMemory`1 op_Implicit(!0[])", + "System.ReadOnlyMemory`1 op_Implicit(System.ArraySegment`1)", + "System.ReadOnlyMemory`1 Slice(int)", + "System.ReadOnlyMemory`1 Slice(int, int)", + "System.ReadOnlySpan`1 get_Span()", + "void .ctor(!0[])", + "void .ctor(!0[], int, int)", + "void CopyTo(System.Memory`1)" + ] + }, + "ReadOnlySpan`1": { + "Methods": [ + "!0[] ToArray()", + "bool get_IsEmpty()", + "bool op_Equality(ReadOnlySystem.Span`1, ReadOnlySystem.Span`1)", + "bool op_Inequality(System.ReadOnlySpan`1, System.ReadOnlySpan`1)", + "bool TryCopyTo(System.Span`1)", + "int get_Length()", + "ref !0 get_Item(int)", + "string ToString()", + "System.ReadOnlySpan`1/Enumerator GetEnumerator()", + "System.ReadOnlySpan`1 get_Empty()", + "System.ReadOnlySpan`1 op_Implicit(!0[])", + "System.ReadOnlySpan`1 op_Implicit(System.ArraySegment`1)", + "System.ReadOnlySpan`1 Slice(int)", + "System.ReadOnlySpan`1 Slice(int, int)", + "void .ctor(!0[])", + "void .ctor(!0[], int, int)", + "void Clear()", + "void CopyTo(System.Span`1)", + "void Fill(!0)" + ], + "NestedTypes": { + "Enumerator": { + "All": true + } + } + }, + "Rune": { + "All": true + }, + "RuntimeFieldHandle": {}, + "RuntimeMethodHandle": {}, + "RuntimeTypeHandle": {}, + "SByte": { + "All": true + }, + "Single": { + "All": true + }, + "Span`1": { + "Methods": [ + "!0[] ToArray()", + "bool get_IsEmpty()", + "bool op_Equality(System.Span`1, System.Span`1)", + "bool op_Inequality(System.Span`1, System.Span`1)", + "bool TryCopyTo(System.Span`1)", + "int get_Length()", + "ref !0 get_Item(int)", + "string ToString()", + "System.ReadOnlySpan`1 op_Implicit(System.Span`1)", + "System.Span`1/Enumerator GetEnumerator()", + "System.Span`1 get_Empty()", + "System.Span`1 op_Implicit(!0[])", + "System.Span`1 op_Implicit(System.ArraySegment`1)", + "System.Span`1 Slice(int)", + "System.Span`1 Slice(int, int)", + "void .ctor(!0[])", + "void .ctor(!0[], int, int)", + "void Clear()", + "void CopyTo(System.Span`1)", + "void Fill(!0)" + ], + "NestedTypes": { + "Enumerator": { + "All": true + } + } + }, + "String": { + "Fields": [ + "string Empty" + ], + "Methods": [ + "bool Contains(char)", + "bool Contains(char, System.StringComparison)", + "bool Contains(string)", + "bool Contains(string, System.StringComparison)", + "bool EndsWith(char)", + "bool EndsWith(string)", + "bool EndsWith(string, bool, System.Globalization.CultureInfo)", + "bool EndsWith(string, System.StringComparison)", + "bool Equals(object)", + "bool Equals(string)", + "bool Equals(string, string)", + "bool Equals(string, string, System.StringComparison)", + "bool Equals(string, System.StringComparison)", + "bool Equals(string, System.StringComparison)", + "bool IsNormalized()", + "bool IsNormalized(System.Text.NormalizationForm)", + "bool IsNullOrEmpty(string)", + "bool IsNullOrWhiteSpace(string)", + "bool op_Equality(string, string)", + "bool op_Inequality(string, string)", + "bool StartsWith(char)", + "bool StartsWith(string)", + "bool StartsWith(string, bool, System.Globalization.CultureInfo)", + "bool StartsWith(string, System.StringComparison)", + "char get_Chars(int)", + "char[] ToCharArray()", + "char[] ToCharArray(int, int)", + "int Compare(string, int, string, int, int)", + "int Compare(string, int, string, int, int, bool)", + "int Compare(string, int, string, int, int, bool, System.Globalization.CultureInfo)", + "int Compare(string, int, string, int, int, bool, System.Globalization.CultureInfo, System.Globalization.CompareOptions)", + "int Compare(string, int, string, int, int, System.StringComparison)", + "int Compare(string, string)", + "int Compare(string, string, bool)", + "int Compare(string, string, bool, System.Globalization.CultureInfo)", + "int Compare(string, string, bool, System.Globalization.CultureInfo, System.Globalization.CompareOptions)", + "int Compare(string, string, System.StringComparison)", + "int CompareOrdinal(string, int, string, int, int)", + "int CompareOrdinal(string, string)", + "int CompareTo(object)", + "int CompareTo(string)", + "int get_Length()", + "int GetHashCode()", + "int GetHashCode(System.ReadOnlySpan`1)", + "int GetHashCode(System.ReadOnlySpan`1, System.StringComparison)", + "int GetHashCode(System.StringComparison)", + "int GetHashCode(System.StringComparison)", + "int IndexOf(char)", + "int IndexOf(char, int)", + "int IndexOf(char, int, int)", + "int IndexOf(char, System.StringComparison)", + "int IndexOf(string)", + "int IndexOf(string, int)", + "int IndexOf(string, int, int)", + "int IndexOf(string, int, int, System.StringComparison)", + "int IndexOf(string, int, System.StringComparison)", + "int IndexOf(string, System.StringComparison)", + "int IndexOfAny(char[])", + "int IndexOfAny(char[], int)", + "int LastIndexOf(char)", + "int LastIndexOf(char, int)", + "int LastIndexOf(char, int, int)", + "int LastIndexOf(string)", + "int LastIndexOf(string, int)", + "int LastIndexOf(string, int, int)", + "int LastIndexOf(string, int, int, System.StringComparison)", + "int LastIndexOf(string, int, System.StringComparison)", + "int LastIndexOf(string, System.StringComparison)", + "int LastIndexOfAny(char[])", + "int LastIndexOfAny(char[], int)", + "int LastIndexOfAny(char[], int, int)", + "object Clone()", + "string Concat(object)", + "string Concat(object, object)", + "string Concat(object, object, object)", + "string Concat(object[])", + "string Concat(string, string)", + "string Concat(string, string, string)", + "string Concat(string, string, string, string)", + "string Concat(string[])", + "string Concat<>(System.Collections.Generic.IEnumerable`1)", + "string Concat(System.Collections.Generic.IEnumerable`1)", + "string Concat(System.ReadOnlySpan`1, System.ReadOnlySpan`1)", + "string Concat(System.ReadOnlySpan`1, System.ReadOnlySpan`1, System.ReadOnlySpan`1)", + "string Concat(System.ReadOnlySpan`1, System.ReadOnlySpan`1, System.ReadOnlySpan`1, System.ReadOnlySpan`1)", + "string Create<>(int, !!0, System.Buffers.SpanAction`2)", + "string Format(string, object)", + "string Format(string, object, object)", + "string Format(string, object, object, object)", + "string Format(string, object[])", + "string Format(System.IFormatProvider, string, object)", + "string Format(System.IFormatProvider, string, object, object)", + "string Format(System.IFormatProvider, string, object, object, object)", + "string Format(System.IFormatProvider, string, object[])", + "string Insert(int, string)", + "string Intern(string)", + "string IsInterned(string)", + "string Join(char, object[])", + "string Join(char, string[])", + "string Join(char, string[], int, int)", + "string Join(string, object[])", + "string Join(string, string[])", + "string Join(string, string[], int, int)", + "string Join(string, System.Collections.Generic.IEnumerable`1)", + "string Join<>(char, System.Collections.Generic.IEnumerable`1)", + "string Join<>(string, System.Collections.Generic.IEnumerable`1)", + "string Normalize()", + "string Normalize(System.Text.NormalizationForm)", + "string PadLeft(int)", + "string PadLeft(int, char)", + "string PadRight(int)", + "string PadRight(int, char)", + "string Remove(int)", + "string Remove(int, int)", + "string Replace(char, char)", + "string Replace(string, string)", + "string Replace(string, string, bool, System.Globalization.CultureInfo)", + "string Replace(string, string, System.StringComparison)", + "string Substring(int)", + "string Substring(int, int)", + "string ToLower()", + "string ToLower(System.Globalization.CultureInfo)", + "string ToLowerInvariant()", + "string ToString()", + "string ToString(System.IFormatProvider)", + "string ToUpper()", + "string ToUpper(System.Globalization.CultureInfo)", + "string ToUpperInvariant()", + "string Trim()", + "string Trim(char)", + "string Trim(char[])", + "string TrimEnd()", + "string TrimEnd(char)", + "string TrimEnd(char[])", + "string TrimStart()", + "string TrimStart(char)", + "string TrimStart(char[])", + "string[] Split(char, int, System.StringSplitOptions)", + "string[] Split(char, System.StringSplitOptions)", + "string[] Split(char[])", + "string[] Split(char[], int)", + "string[] Split(char[], int, System.StringSplitOptions)", + "string[] Split(char[], System.StringSplitOptions)", + "string[] Split(string, int, System.StringSplitOptions)", + "string[] Split(string, System.StringSplitOptions)", + "string[] Split(string[], int, System.StringSplitOptions)", + "string[] Split(string[], System.StringSplitOptions)", + "System.Text.StringRuneEnumerator EnumerateRunes()", + "System.CharEnumerator GetEnumerator()", + "System.ReadOnlySpan`1 op_Implicit(string)", + "System.TypeCode GetTypeCode()", + "void .ctor(char, int)", + "void .ctor(char[])", + "void .ctor(char[], int, int)", + "void .ctor(System.ReadOnlySpan`1)", + "void CopyTo(int, char[], int, int)", + "string Copy(string)" + ] + }, + "StringComparison": {}, + "StringSplitOptions": {}, + "TimeSpan": { + "All": true + }, + "Type": { + "Methods": [ + "bool Equals(object)", + "bool Equals(System.Type)", + "bool get_ContainsGenericParameters()", + "bool get_HasElementType()", + "bool get_IsAbstract()", + "bool get_IsArray()", + "bool get_IsByRef()", + "bool get_IsByRefLike()", + "bool get_IsClass()", + "bool get_IsConstructedGenericType()", + "bool get_IsEnum()", + "bool get_IsGenericMethodParameter()", + "bool get_IsGenericParameter()", + "bool get_IsGenericType()", + "bool get_IsGenericTypeDefinition()", + "bool get_IsGenericTypeParameter()", + "bool get_IsInterface()", + "bool get_IsNested()", + "bool get_IsNestedAssembly()", + "bool get_IsNestedFamANDAssem()", + "bool get_IsNestedFamily()", + "bool get_IsNestedFamORAssem()", + "bool get_IsNestedPrivate()", + "bool get_IsNestedPublic()", + "bool get_IsNotPublic()", + "bool get_IsPointer()", + "bool get_IsPrimitive()", + "bool get_IsPublic()", + "bool get_IsSealed()", + "bool get_IsSerializable()", + "bool get_IsSignatureType()", + "bool get_IsSpecialName()", + "bool get_IsSZArray()", + "bool get_IsTypeDefinition()", + "bool get_IsValueType()", + "bool IsAssignableFrom(System.Type)", + "bool IsAssignableTo(System.Type)", + "bool IsInstanceOfType(object)", + "bool IsSubclassOf(System.Type)", + "bool op_Equality(System.Type, System.Type)", + "bool op_Inequality(System.Type, System.Type)", + "int get_GenericParameterPosition()", + "int GetArrayRank()", + "string get_AssemblyQualifiedName()", + "string get_FullName()", + "string get_Namespace()", + "string GetEnumName(object)", + "System.Array GetEnumValues()", + "System.Guid get_GUID()", + "System.Reflection.Assembly get_Assembly()", + "System.Reflection.ConstructorInfo GetConstructor(System.Type[])", + "System.Reflection.ConstructorInfo[] GetConstructors()", + "System.Reflection.ConstructorInfo[] GetConstructors(System.Reflection.BindingFlags)", + "System.Reflection.EventInfo GetEvent(string)", + "System.Reflection.EventInfo GetEvent(string, System.Reflection.BindingFlags)", + "System.Reflection.EventInfo[] GetEvents()", + "System.Reflection.EventInfo[] GetEvents(System.Reflection.BindingFlags)", + "System.Reflection.FieldInfo GetField(string)", + "System.Reflection.FieldInfo GetField(string, System.Reflection.BindingFlags)", + "System.Reflection.FieldInfo[] GetFields()", + "System.Reflection.FieldInfo[] GetFields(System.Reflection.BindingFlags)", + "System.Reflection.GenericParameterAttributes get_GenericParameterAttributes()", + "System.Reflection.MemberInfo GetMember(string)", + "System.Reflection.MemberInfo GetMember(string, System.Reflection.BindingFlags)", + "System.Reflection.MemberInfo GetMember(string, System.Reflection.MemberTypes, System.Reflection.BindingFlags)", + "System.Reflection.MemberInfo[] FindMembers(System.Reflection.MemberTypes, System.Reflection.BindingFlags, System.Reflection.MemberFilter, object)", + "System.Reflection.MemberInfo[] GetMembers()", + "System.Reflection.MemberInfo[] GetMembers(System.Reflection.BindingFlags)", + "System.Reflection.MemberTypes get_MemberType()", + "System.Reflection.PropertyInfo GetProperty(string)", + "System.Reflection.PropertyInfo GetProperty(string, System.Reflection.BindingFlags)", + "System.Reflection.PropertyInfo GetProperty(string, System.Type)", + "System.Reflection.PropertyInfo GetProperty(string, System.Type, System.Type[])", + "System.Reflection.PropertyInfo GetProperty(string, System.Type[])", + "System.Reflection.PropertyInfo[] GetProperties()", + "System.Reflection.PropertyInfo[] GetProperties(System.Reflection.BindingFlags)", + "System.Reflection.TypeAttributes get_Attributes()", + "System.Type get_BaseType()", + "System.Type get_DeclaringType()", + "System.Type get_ReflectedType()", + "System.Type GetElementType()", + "System.Type GetEnumUnderlyingType()", + "System.Type GetGenericTypeDefinition()", + "System.Type GetInterface(string)", + "System.Type GetInterface(string, bool)", + "System.Type GetNestedType(string)", + "System.Type GetNestedType(string, System.Reflection.BindingFlags)", + "System.Type GetType(string)", + "System.Type GetType(string, bool)", + "System.Type GetType(string, bool, bool)", + "System.Type GetTypeFromHandle(System.RuntimeTypeHandle)", + "System.Type MakeArrayType()", + "System.Type MakeArrayType(int)", + "System.Type MakeByRefType()", + "System.Type MakeGenericMethodParameter(int)", + "System.Type MakeGenericType(System.Type[])", + "System.Type[] FindInterfaces(System.Reflection.TypeFilter, object)", + "System.Type[] get_GenericTypeArguments()", + "System.Type[] GetGenericParameterConstraints()", + "System.Type[] GetInterfaces()", + "System.Type[] GetNestedTypes()", + "System.Type[] GetNestedTypes(System.Reflection.BindingFlags)" + ], + "Fields": [ + "System.Reflection.MemberFilter FilterAttribute", + "System.Reflection.MemberFilter FilterName", + "System.Reflection.MemberFilter FilterNameIgnoreCase", + "object Missing", + "char Delimiter", + "System.Type[] EmptyTypes" + ] + }, + "TypeCode": {}, + "UInt16": { + "All": true + }, + "UInt32": { + "All": true + }, + "UInt64": { + "All": true + }, + "UIntPtr": { + "All": true + }, + "Uri": { + "All": true + }, + "ValueTuple": { + "All": true + }, + "ValueTuple`1": { + "All": true + }, + "ValueTuple`2": { + "All": true + }, + "ValueTuple`3": { + "All": true + }, + "ValueTuple`4": { + "All": true + }, + "ValueTuple`5": { + "All": true + }, + "ValueTuple`6": { + "All": true + }, + "ValueTuple`7": { + "All": true + }, + "ValueTuple`8": { + "All": true + }, + "ValueType": { + "All": true + }, + "Version": { + "All": true + }, + "Void": { + "All": true + }, + "Tuple": { + "All": true + }, + "Tuple`1": { + "All": true + }, + "Tuple`2": { + "All": true + }, + "Tuple`3": { + "All": true + }, + "TupleExtensions": { + "All": true + }, + "IConvertible": { + "All": true + }, + "WeakReference" : { + "All": true + }, + "WeakReference`1" : { + "All": true + }, + "ArgumentNullException" : { + "All": true + }, + "UriHostNameType" : { + "All": true + }, + "Buffer" : { + "Methods" : [ + "void BlockCopy (System.Array, int, System.Array, int, int)", + ] + }, + "AppDomain" : { + + }, + "AggregateException" :{ + "All": true + }, + "Activator" : { + + } + + }, + "UnityEngine": { + "MonoBehaviour": { + "All": true, + "Inherit": "Allow" + }, + "ScriptableObject": { + "All": true, + "Inherit": "Allow" + }, + "TooltipAttribute":{ + "All": true + }, + "SerializeField":{ + "All": true + }, + "GameObject":{ + "All": true + }, + "Component":{ + "All": true + }, + "Vector4":{ + "All": true + }, + "Vector3":{ + "All": true + }, + "Vector3Int":{ + "All": true + }, + "Vector2":{ + "All": true + }, + "Vector2Int":{ + "All": true + }, + "Coroutine":{ + }, + "Object":{ + "All": true + }, + "Debug":{ + "All": true + }, + "WaitForEndOfFrame":{ + "All": true + }, + "WaitForSeconds":{ + "All": true + }, + "CreateAssetMenuAttribute":{ + "All": true + }, + "Collider2D":{ + "All": true + }, + "LayerMask":{ + "All": true + }, + "Physics2D":{ + "All": true + }, + "Mathf":{ + "All": true + }, + "RangeAttribute":{ + "All": true + }, + "Quaternion":{ + "All": true + }, + "Transform":{ + "All": true + }, + "Random":{ + "All": true + }, + "Matrix4x4":{ + "All": true + }, + "Color":{ + "All": true + }, + "Color32":{ + "All": true + }, + "HideInInspector":{ + "All": true + }, + "Time":{ + "All": true + }, + "Behaviour":{ + "All": true + }, + "RuntimeInitializeOnLoadMethodAttribute":{ + "All": true + }, + "RuntimeInitializeLoadType":{ + "All": true + }, + "Rect":{ + "All": true + }, + "Plane":{ + "All": true + }, + "Ray":{ + "All": true + }, + "Texture2D":{ + "All": true + }, + "Sprite":{ + "All": true + }, + "SystemInfo":{ + "All": true + }, + "CanvasGroup":{ + "All": true + }, + "RaycastHit2D":{ + "All": true + }, + "CanvasGroup":{ + "All": true + }, + "RenderTexture":{ + "All": true + }, + "ColorUtility" :{ + "All": true + }, + "LineRenderer" :{ + "All": true + }, + "Bounds":{ + "All": true + }, + "BoundsInt":{ + "All": true + }, + "ColorSpace" :{ + "All": true + }, + "Canvas":{ + "All": true + }, + "PropertyAttribute":{ + "All": true + }, + "RectTransformUtility":{ + "All": true + }, + "ICanvasRaycastFilter":{ + "All": true + }, + "CanvasRenderer":{ + "All": true + }, + "AudioSource":{ + "All": true + }, + "TextGenerator" : { + "All": true + }, + "HorizontalWrapMode" : { + "All": true + }, + "Physics" :{ + "All": true + }, + "SimulationMode2D" :{ + "All": true + }, + "BoxCollider2D" : { + "All": true + }, + "ParticleSystem":{ + + }, + "Touch":{ + "All": true + }, + "Input" : { + "All": true + }, + "GUIUtility" : { + "Methods" : [ + "void set_systemCopyBuffer(string)", + "void RotateAroundPivot(float, UnityEngine.Vector2)" + ] + }, + "GUI" : { + "All": true + }, + "Event" : { + "All": true + }, + "ImageConversion":{ + "Methods" : [ + "byte[] EncodeToPNG(UnityEngine.Texture2D)", + ] + + }, + "GridLayout":{ + "All": true + }, + "WaitForSecondsRealtime" : { + "All": true + }, + "WaitForFixedUpdate" : { + "All": true + }, + "TextureWrapMode" : { + "All": true + }, + "TextureFormat" : { + "All": true + }, + "Texture" : { + "All": true + }, + "TextAsset" : { + "All": true + }, + "TextAreaAttribute":{ + "All": true + }, + "SpriteRenderer":{ + "All": true + }, + "SpritePackingMode":{ + "All": true + }, + "SpriteMeshType":{ + "All": true + }, + "SpaceAttribute":{ + "All": true + }, + "SortingLayer":{ + "All": true + }, + "Skybox":{ + "All": true + }, + "Shader":{ + "All": true + }, + "Space":{ + "All": true + }, + "SerializeReference":{ + "All": true + }, + "Material":{ + "All": true + }, + "Camera" : { + "All": true + }, + "AudioClip" : { + "All": true + }, + "Screen" : { + "All": true + }, + "RuntimePlatform" : { + "All": true + }, + "Resolution" : { + "All": true + }, + "RequireComponent" : { + "All": true + }, + "RenderTextureFormat" : { + "All": true + }, + "Renderer" : { + "All": true + }, + "RectTransform" : { + "All": true + }, + "RectOffset" : { + "All": true + }, + "RectInt" : { + "All": true + }, + "QualitySettings" : { + "All": true + }, + "PlayerPrefs" : { + "All": true + }, + "MinAttribute" : { + "All": true + }, + "MeshRenderer" : { + "All": true + }, + "MaterialPropertyBlock" : { + "All": true + }, + "LogType" : { + "All": true + }, + "Keyframe" : { + "All": true + }, + "Keyframe" : { + "All": true + }, + "KeyCode" : { + "All": true + }, + "HideFlags" : { + "All": true + }, + "HeaderAttribute" : { + "All": true + }, + "Graphics" : { + "All": true + }, + "GradientAlphaKey" : { + "All": true + }, + "Gradient" : { + "All": true + }, + "Gizmos" : { + "All": true + }, + "FullScreenMode" : { + "All": true + }, + "ExecuteInEditMode" : { + "All": true + }, + "DrivenTransformProperties" : { + "All": true + }, + "DrivenRectTransformTracker" : { + "All": true + }, + "DisallowMultipleComponent" : { + "All": true + }, + "CursorMode" : { + "All": true + }, + "CursorLockMode" : { + "All": true + }, + "Cursor" : { + "All": true + }, + "ContextMenu" : { + "All": true + }, + "ComputeShader" : { + "All": true + }, + "CameraClearFlags" : { + "All": true + }, + "AsyncOperation" : { + "All": true + }, + "Application" : { + "Methods" : [ + "bool get_isPlaying()", + "bool get_isMobilePlatform()", + "bool get_isFocused()", + "bool get_isEditor()", + "bool get_isBatchMode()", + "string get_version()", + "string get_temporaryCachePath()", + "string get_streamingAssetsPath()", + "string get_productName()", + "string get_persistentDataPath()", + "string get_dataPath()", + "void set_targetFrameRate(int)", + "int get_targetFrameRate()", + "void set_runInBackground(bool)", + "void Quit()", + "void remove_logMessageReceived(UnityEngine.Application/LogCallback)", + "void add_logMessageReceived(UnityEngine.Application/LogCallback)", + ], + "NestedTypes" :{ + "LogCallback" : { + "All": true + } + } + }, + "AnimationCurve" : { + "All": true + }, + "AddComponentMenu" : { + "All": true + }, + "CanBeNullAttribute" : { + "All": true + }, + "AudioSourceCurveType" : { + "All": true + }, + "AudioRolloffMode" : { + "All": true + }, + "AudioListener" : { + "All": true + }, + "Animator" : { + "All": true + }, + "SendMessageOptions" : { + "All": true + }, + "SelectionBaseAttribute" : { + "All": true + }, + "ScreenOrientation" : { + "All": true + }, + "FilterMode" : { + "All": true + }, + "Resources" : { + "All": true + }, + "AnimatorControllerParameterType" : { + "All": true + }, + "AnimatorControllerParameterType" : { + "All": true + }, + "ParticleSystem" : { + "All": true + }, + "RenderMode":{ + "All": true + }, + "ParticleSystemRenderer":{ + "All": true + }, + "GUIStyle":{ + "All": true + }, + "Mesh" : { + "All": true + }, + "GUIStyleState" : { + "All": true + }, + "GUISkin" : { + "All": true + }, + "MeshFilter" : { + "All": true + }, + "GL" : { + "All": true + }, + "TextAnchor" : { + "All": true + }, + "MultilineAttribute" :{ + "All": true + }, + "DeviceType" : { + "All": true + }, + "FFTWindow" : { + "All": true + }, + "TextEditor" : { + "All": true + }, + "ISerializationCallbackReceiver" : { + "All": true + } + }, + "UnityEngine.Events" :{ + "UnityEvent":{ + "All": true + }, + "UnityEvent`1":{ + "All": true + }, + "UnityEvent`2":{ + "All": true + }, + "UnityEvent`3":{ + "All": true + }, + "UnityEvent`4":{ + "All": true + }, + "UnityEvent`5":{ + "All": true + }, + "UnityEvent`6":{ + "All": true + }, + "UnityAction":{ + "All": true + }, + "UnityAction`1":{ + "All": true + }, + "UnityAction`2":{ + "All": true + }, + "UnityAction`3":{ + "All": true + }, + "UnityAction`4":{ + "All": true + }, + "UnityEventCallState":{ + "All": true + }, + "UnityEventBase":{ + "All": true + }, + + }, + "UnityEngine.UI":{ + "Dropdown":{ + "All": true + }, + "InputField":{ + "All": true + }, + "Scrollbar":{ + "All": true + }, + "CanvasScaler":{ + "All": true + }, + "ColorBlock":{ + "All": true + }, + "Graphic":{ + "All": true + }, + "Button":{ + "All": true + }, + "LayoutGroup":{ + "All": true + }, + "VerticalLayoutGroup":{ + "All": true + }, + "ToggleGroup":{ + "All": true + }, + "Toggle":{ + "All": true + }, + "Text":{ + "All": true + }, + "Slider":{ + "All": true + }, + "Shadow":{ + "All": true + }, + "Selectable":{ + "All": true + }, + "ScrollRect":{ + "All": true + }, + "RawImage":{ + "All": true + }, + "LayoutRebuilder":{ + "All": true + }, + "Image":{ + "All": true + }, + "ICanvasElement":{ + "All": true + }, + "HorizontalOrVerticalLayoutGroup":{ + "All": true + }, + "GridLayoutGroup":{ + "All": true + }, + "GraphicRaycaster":{ + "All": true + }, + "ContentSizeFitter":{ + "All": true + }, + "CanvasUpdate":{ + "All": true + }, + "LayoutElement":{ + "All": true + }, + "MaskableGraphic" : { + "All": true + }, + }, + "UnityEngine.EventSystems":{ + "EventSystem":{ + "All": true + }, + "IPointerExitHandler":{ + "All": true + }, + "IDragHandler":{ + "All": true + }, + "IDragHandler":{ + "All": true + }, + "IPointerDownHandler":{ + "All": true + }, + "IEventSystemHandler":{ + "All": true + }, + "IPointerExitHandler":{ + "All": true + }, + "IPointerEnterHandler":{ + "All": true + }, + "IPointerUpHandler":{ + "All": true + }, + "IPointerClickHandler":{ + "All": true + }, + "IBeginDragHandler":{ + "All": true + }, + "IEndDragHandler":{ + "All": true + }, + "PointerEventData":{ + "All": true + }, + "IScrollHandler":{ + "All": true + }, + "UIBehaviour":{ + "All": true + }, + "RaycastResult":{ + "All": true + }, + "IDropHandler":{ + "All": true + }, + "ExecuteEvents":{ + "All": true + }, + "EventTriggerType":{ + "All": true + }, + "EventTrigger":{ + "All": true + }, + "IInitializePotentialDragHandler":{ + "All": true + }, + "BaseRaycaster":{ + "All": true + }, + "BaseEventData":{ + "All": true + }, + + + }, + "UnityEngine.Tilemaps":{ + "TileData":{ + "All": true + }, + "TileBase":{ + "All": true + }, + "Tilemap":{ + "All": true + }, + "Tilemap":{ + "All": true + }, + "ITilemap":{ + "All": true + }, + "TileFlags":{ + "All": true + }, + "TilemapRenderer":{ + "All": true + }, + "TileAnimationData" : { + "All": true + }, + "Tile" : { + "All": true + } + }, + "UnityEngine.Video":{ + "VideoPlayer":{ + "All": true + }, + "VideoClip":{ + "All": true + }, + + }, + "UnityEngine.SceneManagement":{ + "SceneUtility":{ + "All": true + }, + "SceneManager":{ + "All": true + }, + "Scene":{ + "All": true + }, + "LoadSceneMode":{ + "All": true + }, + + }, + "UnityEngine.Rendering":{ + "SortingGroup":{ + "All": true + }, + "GraphicsDeviceType":{ + "All": true + }, + "AsyncGPUReadbackRequest" : { + "All": true + }, + "AsyncGPUReadback" : { + "All": true + } + }, + "UnityEngine.Profiling" :{ + + "Profiler":{ + "Methods" : [ + "long GetTotalAllocatedMemoryLong()", + "void EndThreadProfiling()", + "void EndThreadProfiling()", + "long GetTotalReservedMemoryLong()", + "long GetMonoUsedSizeLong()" + + ] + }, + "CustomSampler":{ + "All": true + }, + }, + "UnityEngine.Pool" : { + "PooledObject`1":{ + "All": true + }, + "CollectionPool`1":{ + "All": true + }, + "CollectionPool`2":{ + "All": true + }, + }, + "UnityEngine.Audio" : { + "AudioMixerGroup":{ + "All": true + }, + "AudioMixer":{ + "All": true + } + }, + "UnityEngine.Animations" : { + "ParentConstraint":{ + "All": true + }, + "ConstraintSource":{ + "All": true + }, + }, + "UnityEngine.Serialization" : { + "FormerlySerializedAsAttribute":{ + "All": true + }, + }, + "UnityEngine.ResourceManagement.ResourceLocations" : { + "IResourceLocation":{ + "All": true + }, + }, + "UnityEngine.ResourceManagement.AsyncOperations" : { + "AsyncOperationStatus":{ + "All": true + }, + "AsyncOperationHandle":{ + "All": true + }, + "AsyncOperationHandle`1":{ + "All": true + }, + + }, + "UnityEngine.AddressableAssets" :{ + "ResourceLocators" :{ + "All": true + }, + "AssetReference" :{ + "All": true + }, + "Addressables" : { + "All": true + } + }, + "UnityEngine.AddressableAssets.ResourceLocators" :{ + "IResourceLocator" :{ + "All": true + }, + "ResourceLocationMap" : { + "All": true + } + }, + "UnityEngine.U2D" : { + "PixelPerfectCamera" : { + "All": true + } + }, + "Unity.Collections" : { + "NativeArray`1" : { + "Methods" : [ + "int get_Length()", + "!0 get_Item(int)", + "void Dispose()", + "void .ctor(Unity.Collections.NativeArray`1, Unity.Collections.Allocator)" + ] + }, + "Allocator" : { + "All": true + } + }, + "UnityEngine.Experimental.Rendering" : { + "GraphicsFormat" : { + "All": true + } + }, + "JetBrains.Annotations" : { + "CanBeNullAttribute":{ + "All": true + }, + }, + "Mirror": { + "Reader`1":{ + "Fields":[ + "System.Func`2 read" + ] + }, + "Writer`1":{ + "Fields":[ + "System.Action`2 write" + ] + }, + "NetworkBehaviourSyncVar": { + }, + "NetworkPongMessage":{ + "All": true + }, + "NetworkPingMessage":{ + "All": true + }, + "EntityStateMessage":{ + "All": true + }, + "ObjectHideMessage":{ + "All": true + }, + "ObjectDestroyMessage":{ + "All": true + }, + "ObjectSpawnFinishedMessage":{ + "All": true + }, + "ObjectSpawnStartedMessage":{ + "All": true + }, + "ChangeOwnerMessage":{ + "All": true + }, + "SpawnMessage":{ + "All": true + }, + "RpcBufferMessage":{ + "All": true + }, + "RpcMessage":{ + "All": true + }, + "RpcMessage":{ + "All": true + }, + "CommandMessage":{ + "All": true + }, + "SceneOperation":{ + "All": true + }, + "SceneMessage":{ + "All": true + }, + "AddPlayerMessage":{ + "All": true + }, + "NotReadyMessage":{ + "All": true + }, + "ReadyMessage":{ + "All": true + }, + "TimeSnapshotMessage":{ + "All": true + }, + "ServerAttribute":{ + "All": true + }, + "CommandAttribute":{ + "All": true + }, + "SyncVarAttribute":{ + "All": true + }, + "TargetRpcAttribute":{ + "All": true + }, + "NetworkServer":{ + "All": true + }, + "NetworkClient":{ + "All": true + }, + "NetworkConnectionToClient":{ + "All": true + }, + "NetworkWriter":{ + "Methods" : [ + "void WriteByte(byte)", + "void Write<>(!!0)", + ] + }, + "NetworkWriterExtensions":{ + "All": true + }, + "NetworkReader":{ + "Methods" : [ + "!!0 Read<>()", + "byte ReadByte()}" + ] + }, + "NetworkReaderExtensions":{ + "All": true + }, + "NetworkWriterPooled":{ + "All": true + }, + "NetworkWriterPool":{ + "All": true + }, + "NetworkConnection":{ + "All": true + }, + "NetworkIdentity" :{ + "All": true + }, + "NetworkBehaviour": { + "Inherit": "Allow", + "All": true + }, + "NetworkMessage": { + "All": true + }, + "SyncList`1": { + "All": true + }, + "NetworkManager":{ + "All": true + }, + "TelepathyTransport":{ + "Fields":[ + "ushort port" + ] + }, + "SyncObject" : { + "All": true + }, + "Transport" :{ + "Fields":[ + "Mirror.Transport active" + ], + "Methods" : [ + "void Shutdown()", + "int GetMaxPacketSize(int)" + ], + + }, + "ServerCallbackAttribute" :{ + "All": true + }, + "NetworkTime" :{ + "All": true + }, + "ClientRpcAttribute" : { + "All": true + }, + "ClientAttribute" : { + "All": true + }, + "Extensions" : { + "All": true + }, + "InterestManagement" : { + "All": true + }, + "LocalConnectionToClient" : { + "All": true + }, + "NetworkAuthenticator" : { + "All": true + }, + "NetworkStartPosition" : { + "All": true + }, + }, + "IgnoranceTransport":{ + "Ignorance" :{ + "Fields" : [ + "int port", + "bool serverBindsAll", + "string serverBindAddress" + ] + } + }, + "Mirror.RemoteCalls" : { + "RemoteCallDelegate":{ + "Methods" : [ + "void .ctor(object, nint)", + ] + }, + "RemoteProcedureCalls":{ + "Methods" : [ + "void RemoteCallDelegate(Mirror.NetworkBehaviour, Mirror.NetworkReader , Mirror.NetworkConnectionToClient)", + "void RegisterCommand(System.Type, string, Mirror.RemoteCalls.RemoteCallDelegate, bool)", + "void RegisterRpc(System.Type, string, Mirror.RemoteCalls.RemoteCallDelegate)" + ], + "Fields" : [ + "bool mirrorProcessingCMD", + "Mirror.RemoteCalls.Invoker mirrorLastInvoker" + ] + }, + "Invoker" : { + "All": true + } + }, + "Logs" :{ + "Loggy" :{ + "All": true + }, + "LogOverridePref" :{ + "All": true + }, + "LoggerPreferences" :{ + "All": true + }, + "Category" :{ + "All": true + }, + "LogLevel" :{ + "All": true + }, + "ThreadLoggy" :{ + "All": true + } + }, + "TMPro" : { + "TMP_VertexDataUpdateFlags" : { + "All": true + }, + "TMP_TextUtilities" : { + "All": true + }, + "TMP_TextInfo" : { + "All": true + }, + "TMP_Text" : { + "All": true + }, + "TMP_SpriteAsset" : { + "All": true + }, + "TMP_MeshInfo" :{ + "All": true + }, + "TMP_LinkInfo" :{ + "All": true + }, + "TMP_InputField" :{ + "All": true + }, + "TMP_FontAsset" :{ + "All": true + }, + "TMP_Dropdown" :{ + "All": true + }, + "TMP_CharacterInfo" :{ + "All": true + }, + "TextMeshProUGUI" :{ + "All": true + }, + "FontStyles" :{ + "All": true + } + }, + "SecureStuff" : { + "SafeURL" : { + "All": true + }, + "SafeProfileManager" : { + "All": true + }, + "SafeHttpRequest" : { + "All": true + }, + "SafeHttpRequest" : { + "All": true + }, + "AccessFile" : { + "All": true + }, + "FolderType" : { + "All": true + }, + "VVNote" : { + "All": true + }, + "VVNote" : { + "All": true + }, + "VVHighlight":{ + "All": true + }, + "Librarian" :{ + "All": true + }, + "ICustomSerialisationSystem" : { + "All": true + }, + "BaseAttribute" : { + "All": true + }, + "AllowedReflection" : { + "All": true + }, + "MethodsAndAttributee`1" : { + "All": true + }, + "SecureMapsSaver" : { + "All": true + }, + "SceneObjectReference" : { + "All": true + }, + "IPopulateIDRelation" : { + "All": true + }, + "IAllowedReflection" : { + "All": true + }, + "FieldData" : { + "All": true + }, + "AllowedEnvironmentVariables" : { + "All": true + }, + "SpriteMetadata" : { + "Methods" : [ + "float get_Scale()", + "UnityEngine.Vector2 get_Offset()", + "SecureStuff.SpriteMetadata Create(UnityEngine.Texture2D, ref UnityEngine.Rect)" + ], + "Fields" : [ + "SecureStuff.SpriteMetadata Default()" + ] + } + }, + "Newtonsoft.Json" : { + "ReferenceLoopHandling" : { + "All": true + }, + "PreserveReferencesHandling" : { + "All": true + }, + "NullValueHandling" : { + "All": true + }, + "JsonWriter" : { + "All": true + }, + "JsonSerializerSettings" : { + "All": true + }, + "JsonSerializer" : { + "All": true + }, + "JsonReader" : { + "All": true + }, + "JsonPropertyAttribute" : { + "All": true + }, + "JsonIgnoreAttribute" : { + "All": true + }, + "JsonConvert" : { + "All": true + }, + "Formatting" : { + "All": true + } + + }, + "Newtonsoft.Json.Bson" : { + "BsonWriter" : { + "All": true + }, + "BsonReader" : { + "All": true + }, + }, + "Tomlyn" : { + "Toml" : { + "Methods" : [ + "bool TryToModel<>(string, ref !!0, ref Tomlyn.Syntax.DiagnosticsBag, string, Tomlyn.TomlModelOptions)" + ] + }, + "TomlModelOptions" : { + "All": true + } + }, + "Tomlyn.Syntax" : { + "DiagnosticsBag" : { + "All": true + }, + "DiagnosticMessage" : { + "All": true + } + }, + "Tomlyn.Model" : { + "TomlPropertiesMetadata" : { + "All": true + }, + "ITomlMetadataProvider" : { + "All": true + } + }, + "YamlDotNet.Serialization" :{ + "IDeserializer" : { + "All": true + }, + "DeserializerBuilder" : { + "All": true + } + }, + "YamlDotNet.RepresentationModel" :{ + "YamlStream" : { + "All": true + }, + "YamlScalarNode" : { + "All": true + }, + "YamlNode" : { + "All": true + }, + "YamlMappingNode" : { + "All": true + }, + "YamlDocument" : { + "All": true + } + }, + "YamlDotNet.Helpers" :{ + "IOrderedDictionary`2" : { + "All": true + } + }, + "Firebase.Auth" : { + "UserProfile" :{ + "All": true, + "TEMP" : true + }, + "FirebaseUser" :{ + "All": true, + "TEMP" : true + }, + "FirebaseAuth" :{ + "All": true, + "TEMP" : true + }, + "FirebaseException" :{ + "All": true, + "TEMP" : true + }, + "FirebaseApp" :{ + "All": true, + "TEMP" : true + } + }, + "Firebase" : { + "FirebaseAuth" :{ + "All": true, + "TEMP" : true + }, + "FirebaseException" :{ + "All": true, + "TEMP" : true + }, + "FirebaseApp" :{ + "All": true, + "TEMP" : true + } + }, + "Firebase.Extensions" : { + "TaskExtension" :{ + "All": true, + "TEMP" : true + }, + }, + "C5" :{ + "IntervalHeap`1" : { + "All": true, + }, + "CollectionValueBase`1" : { + "All": true, + } + }, + "SunVox" : { + "SunVox" : { + "All": true + } + } + } +} diff --git a/UnitystationLauncher/ContentScanning/AssemblyTypeCheckerHelpers.cs b/UnitystationLauncher/ContentScanning/AssemblyTypeCheckerHelpers.cs new file mode 100644 index 00000000..c7689f77 --- /dev/null +++ b/UnitystationLauncher/ContentScanning/AssemblyTypeCheckerHelpers.cs @@ -0,0 +1,447 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Reflection.Metadata; +using UnitystationLauncher.Exceptions; +using UnitystationLauncher.Models.ContentScanning; +using UnitystationLauncher.Models.ContentScanning.ScanningTypes; + +// psst +// You know ECMA-335 right? The specification for the CLI that .NET runs on? +// Yeah, you need it to understand a lot of this code. So get a copy. +// You know the cool thing? +// ISO has a version that has correct PDF metadata so there's an actual table of contents. +// Right here: https://standards.iso.org/ittf/PubliclyAvailableStandards/c058046_ISO_IEC_23271_2012(E).zip + +namespace UnitystationLauncher.ContentScanning; + +/// +/// Manages the type white/black list of types and namespaces, and verifies assemblies against them. +/// +internal static class AssemblyTypeCheckerHelpers +{ + // Used to be in Sandbox.yml, moved out of there to facilitate faster loading. + internal const string SystemAssemblyName = "mscorlib"; //TODO check security + //UnityEngine.dll + //mscorlib + //System.Runtime + + internal static Resolver CreateResolver(DirectoryInfo ManagedPath) + { + return new Resolver(ManagedPath); + } + + internal static string FormatMethodName(MetadataReader reader, MethodDefinition method) + { + MethodSignature methodSig = method.DecodeSignature(new TypeProvider(), 0); + MTypeDefined type = GetTypeFromDefinition(reader, method.GetDeclaringType()); + + return + $"{type}.{reader.GetString(method.Name)}({string.Join(", ", methodSig.ParameterTypes)}) Returns {methodSig.ReturnType} "; + } + + internal static void CheckNoUnmanagedMethodDefs(MetadataReader reader, ConcurrentBag errors) + { + foreach (MethodDefinitionHandle methodDefHandle in reader.MethodDefinitions) + { + MethodDefinition methodDef = reader.GetMethodDefinition(methodDefHandle); + MethodImplAttributes implAttr = methodDef.ImplAttributes; + MethodAttributes attr = methodDef.Attributes; + + if ((implAttr & MethodImplAttributes.Unmanaged) != 0 || + (implAttr & MethodImplAttributes.CodeTypeMask) is not (MethodImplAttributes.IL + or MethodImplAttributes.Runtime)) + { + string err = $"Method has illegal MethodImplAttributes: {FormatMethodName(reader, methodDef)}"; + errors.Add(new SandboxError(err)); + } + + if ((attr & (MethodAttributes.PinvokeImpl | MethodAttributes.UnmanagedExport)) != 0) + { + string err = $"Method has illegal MethodAttributes: {FormatMethodName(reader, methodDef)}"; + errors.Add(new SandboxError(err)); + } + } + } + + internal static void CheckNoTypeAbuse(MetadataReader reader, ConcurrentBag errors) + { + foreach (TypeDefinitionHandle typeDefHandle in reader.TypeDefinitions) + { + TypeDefinition typeDef = reader.GetTypeDefinition(typeDefHandle); + if ((typeDef.Attributes & TypeAttributes.ExplicitLayout) != 0) + { + // The C# compiler emits explicit layout types for some array init logic. These have no fields. + // Only ban explicit layout if it has fields. + + MTypeDefined type = GetTypeFromDefinition(reader, typeDefHandle); + + if (typeDef.GetFields().Count > 0) + { + string err = $"Explicit layout type {type} may not have fields."; + errors.Add(new SandboxError(err)); + } + } + } + } + + internal static List GetReferencedTypes(MetadataReader reader, ConcurrentBag errors) + { + return reader.TypeReferences.Select(typeRefHandle => + { + try + { + return ParseTypeReference(reader, typeRefHandle); + } + catch (UnsupportedMetadataException e) + { + errors.Add(new SandboxError(e)); + return null; + } + }) + .Where(p => p != null) + .ToList()!; + } + + internal static List GetReferencedMembers(MetadataReader reader, ConcurrentBag errors) + { + bool Parallel = true; + if (Parallel) + { + return reader.MemberReferences.AsParallel() + .Select(memRefHandle => + { + MemberReference memRef = reader.GetMemberReference(memRefHandle); + string memName = reader.GetString(memRef.Name); + MType parent; + switch (memRef.Parent.Kind) + { + // See II.22.25 in ECMA-335. + case HandleKind.TypeReference: + { + // Regular type reference. + try + { + parent = ParseTypeReference(reader, (TypeReferenceHandle)memRef.Parent); + } + catch (UnsupportedMetadataException u) + { + errors.Add(new SandboxError(u)); + return null; + } + + break; + } + case HandleKind.TypeDefinition: + { + try + { + parent = GetTypeFromDefinition(reader, (TypeDefinitionHandle)memRef.Parent); + } + catch (UnsupportedMetadataException u) + { + errors.Add(new SandboxError(u)); + return null; + } + + break; + } + case HandleKind.TypeSpecification: + { + TypeSpecification typeSpec = reader.GetTypeSpecification((TypeSpecificationHandle)memRef.Parent); + // Generic type reference. + TypeProvider provider = new TypeProvider(); + parent = typeSpec.DecodeSignature(provider, 0); + + if (parent.IsCoreTypeDefined()) + { + // Ensure this isn't a self-defined type. + // This can happen due to generics since MethodSpec needs to point to MemberRef. + return null; + } + + break; + } + case HandleKind.ModuleReference: + { + errors.Add(new SandboxError( + $"Module global variables and methods are unsupported. Name: {memName}")); + return null; + } + case HandleKind.MethodDefinition: + { + errors.Add(new SandboxError($"Vararg calls are unsupported. Name: {memName}")); + return null; + } + default: + { + errors.Add(new SandboxError( + $"Unsupported member ref parent type: {memRef.Parent.Kind}. Name: {memName}")); + return null; + } + } + + MMemberRef memberRef; + + switch (memRef.GetKind()) + { + case MemberReferenceKind.Method: + { + MethodSignature sig = memRef.DecodeMethodSignature(new TypeProvider(), 0); + + memberRef = new MMemberRefMethod( + parent, + memName, + sig.ReturnType, + sig.GenericParameterCount, + sig.ParameterTypes); + + break; + } + case MemberReferenceKind.Field: + { + MType fieldType = memRef.DecodeFieldSignature(new TypeProvider(), 0); + memberRef = new MMemberRefField(parent, memName, fieldType); + break; + } + default: + throw new ArgumentOutOfRangeException(); + } + + return memberRef; + }) + .Where(p => p != null) + .ToList()!; + } + else + { + return reader.MemberReferences.Select(memRefHandle => + { + MemberReference memRef = reader.GetMemberReference(memRefHandle); + string memName = reader.GetString(memRef.Name); + MType parent; + switch (memRef.Parent.Kind) + { + // See II.22.25 in ECMA-335. + case HandleKind.TypeReference: + { + // Regular type reference. + try + { + parent = ParseTypeReference(reader, (TypeReferenceHandle)memRef.Parent); + } + catch (UnsupportedMetadataException u) + { + errors.Add(new SandboxError(u)); + return null; + } + + break; + } + case HandleKind.TypeDefinition: + { + try + { + parent = GetTypeFromDefinition(reader, (TypeDefinitionHandle)memRef.Parent); + } + catch (UnsupportedMetadataException u) + { + errors.Add(new SandboxError(u)); + return null; + } + + break; + } + case HandleKind.TypeSpecification: + { + TypeSpecification typeSpec = reader.GetTypeSpecification((TypeSpecificationHandle)memRef.Parent); + // Generic type reference. + TypeProvider provider = new TypeProvider(); + parent = typeSpec.DecodeSignature(provider, 0); + + if (parent.IsCoreTypeDefined()) + { + // Ensure this isn't a self-defined type. + // This can happen due to generics since MethodSpec needs to point to MemberRef. + return null; + } + + break; + } + case HandleKind.ModuleReference: + { + errors.Add(new SandboxError( + $"Module global variables and methods are unsupported. Name: {memName}")); + return null; + } + case HandleKind.MethodDefinition: + { + errors.Add(new SandboxError($"Vararg calls are unsupported. Name: {memName}")); + return null; + } + default: + { + errors.Add(new SandboxError( + $"Unsupported member ref parent type: {memRef.Parent.Kind}. Name: {memName}")); + return null; + } + } + + MMemberRef memberRef; + + switch (memRef.GetKind()) + { + case MemberReferenceKind.Method: + { + MethodSignature sig = memRef.DecodeMethodSignature(new TypeProvider(), 0); + + memberRef = new MMemberRefMethod( + parent, + memName, + sig.ReturnType, + sig.GenericParameterCount, + sig.ParameterTypes); + + break; + } + case MemberReferenceKind.Field: + { + MType fieldType = memRef.DecodeFieldSignature(new TypeProvider(), 0); + memberRef = new MMemberRefField(parent, memName, fieldType); + break; + } + default: + throw new ArgumentOutOfRangeException(); + } + + return memberRef; + }) + .Where(p => p != null) + .ToList()!; + } + } + + internal static bool ParseInheritType(MType ownerType, EntityHandle handle, [NotNullWhen(true)] out MType? type, MetadataReader reader, ConcurrentBag errors) + { + type = default; + + switch (handle.Kind) + { + case HandleKind.TypeDefinition: + // Definition to type in same assembly, allowed without hassle. + return false; + + case HandleKind.TypeReference: + // Regular type reference. + try + { + type = ParseTypeReference(reader, (TypeReferenceHandle)handle); + return true; + } + catch (UnsupportedMetadataException u) + { + errors.Add(new SandboxError(u)); + return false; + } + + case HandleKind.TypeSpecification: + TypeSpecification typeSpec = reader.GetTypeSpecification((TypeSpecificationHandle)handle); + // Generic type reference. + TypeProvider provider = new TypeProvider(); + type = typeSpec.DecodeSignature(provider, 0); + + if (type.IsCoreTypeDefined()) + { + // Ensure this isn't a self-defined type. + // This can happen due to generics. + return false; + } + + break; + + default: + errors.Add(new SandboxError( + $"Unsupported BaseType of kind {handle.Kind} on type {ownerType}")); + return false; + } + + type = default!; + return false; + } + + /// + /// Thrown if the metadata does something funny we don't "support" like type forwarding. + /// + internal static MTypeReferenced ParseTypeReference(MetadataReader reader, TypeReferenceHandle handle) + { + TypeReference typeRef = reader.GetTypeReference(handle); + string name = reader.GetString(typeRef.Name); + string? nameSpace = NilNullString(reader, typeRef.Namespace); + MResScope resScope; + + // See II.22.38 in ECMA-335 + if (typeRef.ResolutionScope.IsNil) + { + throw new UnsupportedMetadataException( + $"Null resolution scope on type Name: {nameSpace}.{name}. This indicates exported/forwarded types"); + } + + switch (typeRef.ResolutionScope.Kind) + { + case HandleKind.AssemblyReference: + { + // Different assembly. + AssemblyReference assemblyRef = + reader.GetAssemblyReference((AssemblyReferenceHandle)typeRef.ResolutionScope); + string assemblyName = reader.GetString(assemblyRef.Name); + resScope = new MResScopeAssembly(assemblyName); + break; + } + case HandleKind.TypeReference: + { + // Nested type. + MTypeReferenced enclosingType = ParseTypeReference(reader, (TypeReferenceHandle)typeRef.ResolutionScope); + resScope = new MResScopeType(enclosingType); + break; + } + case HandleKind.ModuleReference: + { + // Same-assembly-different-module + throw new UnsupportedMetadataException( + $"Cross-module reference to type {nameSpace}.{name}. "); + } + default: + // Edge cases not handled: + // https://github.com/dotnet/runtime/blob/b2e5a89085fcd87e2fa9300b4bb00cd499c5845b/src/libraries/System.Reflection.Metadata/tests/Metadata/Decoding/DisassemblingTypeProvider.cs#L130-L132 + throw new UnsupportedMetadataException( + $"TypeRef to {typeRef.ResolutionScope.Kind} for type {nameSpace}.{name}"); + } + + return new MTypeReferenced(resScope, name, nameSpace); + } + + internal static string? NilNullString(MetadataReader reader, StringHandle handle) + { + return handle.IsNil ? null : reader.GetString(handle); + } + + + + internal static MTypeDefined GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle) + { + TypeDefinition typeDef = reader.GetTypeDefinition(handle); + string name = reader.GetString(typeDef.Name); + string? ns = NilNullString(reader, typeDef.Namespace); + MTypeDefined? enclosing = null; + if (typeDef.IsNested) + { + enclosing = GetTypeFromDefinition(reader, typeDef.GetDeclaringType()); + } + + return new MTypeDefined(name, ns, enclosing); + } +} \ No newline at end of file diff --git a/UnitystationLauncher/ContentScanning/FileInfoComparer.cs b/UnitystationLauncher/ContentScanning/FileInfoComparer.cs new file mode 100644 index 00000000..0b704464 --- /dev/null +++ b/UnitystationLauncher/ContentScanning/FileInfoComparer.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace UnitystationLauncher.ContentScanning; + +internal class FileInfoComparer : IEqualityComparer +{ + public bool Equals(FileInfo? x, FileInfo? y) + { + if (x == null || y == null) return false; + return x.Name.Equals(y.Name, StringComparison.OrdinalIgnoreCase); + } + + public int GetHashCode(FileInfo obj) + { + return obj.Name.GetHashCode(); + } +} \ No newline at end of file diff --git a/UnitystationLauncher/ContentScanning/Parsers.cs b/UnitystationLauncher/ContentScanning/Parsers.cs new file mode 100644 index 00000000..d68f7cf3 --- /dev/null +++ b/UnitystationLauncher/ContentScanning/Parsers.cs @@ -0,0 +1,174 @@ +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Globalization; +using System.Linq; +using System.Reflection.Metadata; +using Pidgin; +using UnitystationLauncher.Models.ContentScanning; +using UnitystationLauncher.Models.ContentScanning.ScanningTypes; +using static Pidgin.Parser; +using static Pidgin.Parser; + +namespace UnitystationLauncher.ContentScanning; + +public static class Parsers +{ + // Contains primary parsing code for method and field declarations in the sandbox whitelist. + + private static readonly Parser VoidTypeParser = + String("void").ThenReturn(PrimitiveTypeCode.Void); + + private static readonly Parser BooleanTypeParser = + String("bool").ThenReturn(PrimitiveTypeCode.Boolean); + + private static readonly Parser CharTypeParser = + String("char").ThenReturn(PrimitiveTypeCode.Char); + + private static readonly Parser SByteTypeParser = + String("sbyte").ThenReturn(PrimitiveTypeCode.SByte); + + private static readonly Parser ByteTypeParser = + String("byte").ThenReturn(PrimitiveTypeCode.Byte); + + private static readonly Parser Int16TypeParser = + String("short").ThenReturn(PrimitiveTypeCode.Int16); + + private static readonly Parser UInt16TypeParser = + String("ushort").ThenReturn(PrimitiveTypeCode.UInt16); + + private static readonly Parser Int32TypeParser = + String("int").ThenReturn(PrimitiveTypeCode.Int32); + + private static readonly Parser UInt32TypeParser = + String("uint").ThenReturn(PrimitiveTypeCode.UInt32); + + private static readonly Parser Int64TypeParser = + String("long").ThenReturn(PrimitiveTypeCode.Int64); + + private static readonly Parser UInt64TypeParser = + String("ulong").ThenReturn(PrimitiveTypeCode.UInt64); + + private static readonly Parser IntPtrTypeParser = + String("nint").ThenReturn(PrimitiveTypeCode.IntPtr); + + private static readonly Parser UIntPtrTypeParser = + String("nuint").ThenReturn(PrimitiveTypeCode.UIntPtr); + + private static readonly Parser SingleTypeParser = + String("float").ThenReturn(PrimitiveTypeCode.Single); + + private static readonly Parser DoubleTypeParser = + String("double").ThenReturn(PrimitiveTypeCode.Double); + + private static readonly Parser StringTypeParser = + String("string").ThenReturn(PrimitiveTypeCode.String); + + private static readonly Parser ObjectTypeParser = + String("object").ThenReturn(PrimitiveTypeCode.Object); + + private static readonly Parser TypedReferenceTypeParser = + String("typedref").ThenReturn(PrimitiveTypeCode.TypedReference); + + private static readonly Parser PrimitiveTypeParser = + OneOf( + Try(VoidTypeParser), + Try(BooleanTypeParser), + Try(CharTypeParser), + Try(SByteTypeParser), + Try(ByteTypeParser), + Try(Int16TypeParser), + Try(UInt16TypeParser), + Try(Int32TypeParser), + Try(UInt32TypeParser), + Try(Int64TypeParser), + Try(UInt64TypeParser), + Try(IntPtrTypeParser), + Try(UIntPtrTypeParser), + Try(SingleTypeParser), + Try(DoubleTypeParser), + Try(StringTypeParser), + Try(ObjectTypeParser), + TypedReferenceTypeParser) + .Select(code => (MType)new MTypePrimitive(code)).Labelled("Primitive type"); + + private static readonly Parser NamespacedIdentifier = + Token(c => char.IsLetterOrDigit(c) || c == '.' || c == '_' || c == '`') + .AtLeastOnceString() + .Labelled("valid identifier"); + + private static readonly Parser> GenericParametersParser = + Rec(() => MaybeArrayTypeParser!) + .Between(SkipWhitespaces) + .Separated(Char(',')) + .Between(Char('<'), Char('>')); + + private static readonly Parser GenericMethodPlaceholderParser = + String("!!") + .Then(Digit.AtLeastOnceString()) + .Select(p => (MType)new MTypeGenericMethodPlaceHolder(int.Parse(p, CultureInfo.InvariantCulture))); + + private static readonly Parser GenericTypePlaceholderParser = + String("!") + .Then(Digit.AtLeastOnceString()) + .Select(p => (MType)new MTypeGenericTypePlaceHolder(int.Parse(p, CultureInfo.InvariantCulture))); + + private static readonly Parser GenericPlaceholderParser = Try(GenericTypePlaceholderParser) + .Or(Try(GenericMethodPlaceholderParser)).Labelled("Generic placeholder"); + + private static readonly Parser TypeNameParser = + Parser.Map( + (a, b) => b.Aggregate(new MTypeParsed(a), (parsed, s) => new MTypeParsed(s, parsed)), + NamespacedIdentifier, + Char('/').Then(NamespacedIdentifier).Many()); + + private static readonly Parser ConstructedObjectTypeParser = + Parser.Map((arg1, arg2) => + { + MType type = arg1; + if (arg2.HasValue) + { + type = new MTypeGeneric(type, arg2.Value.ToImmutableArray()); + } + + return type; + }, + TypeNameParser, + GenericParametersParser.Optional()); + + private static readonly Parser MaybeArrayTypeParser = Parser.Map( + (a, b) => b.Aggregate(a, (type, _) => new MTypeSZArray(type)), + Try(GenericPlaceholderParser).Or(Try(PrimitiveTypeParser)).Or(ConstructedObjectTypeParser), + String("[]").Many()); + + private static readonly Parser ByRefTypeParser = + String("ref") + .Then(SkipWhitespaces) + .Then(MaybeArrayTypeParser) + .Select(t => (MType)new MTypeByRef(t)) + .Labelled("ByRef type"); + + private static readonly Parser TypeParser = Try(ByRefTypeParser).Or(MaybeArrayTypeParser); + + private static readonly Parser> MethodParamsParser = + TypeParser + .Between(SkipWhitespaces) + .Separated(Char(',')) + .Between(Char('('), Char(')')) + .Select(p => p.ToImmutableArray()); + + internal static readonly Parser MethodGenericParameterCountParser = + Try(Char(',').Many().Select(p => p.Count() + 1).Between(Char('<'), Char('>'))).Or(Return(0)); + + internal static readonly Parser MethodParser = + Parser.Map( + (a, b, d, c) => new WhitelistMethodDefine(b, a, c.ToList(), d), + SkipWhitespaces.Then(TypeParser), + SkipWhitespaces.Then(NamespacedIdentifier), + MethodGenericParameterCountParser, + SkipWhitespaces.Then(MethodParamsParser)); + + internal static readonly Parser FieldParser = Parser.Map( + (a, b) => new WhitelistFieldDefine(b, a), + MaybeArrayTypeParser.Between(SkipWhitespaces), + NamespacedIdentifier); +} \ No newline at end of file diff --git a/UnitystationLauncher/ContentScanning/Resolver.cs b/UnitystationLauncher/ContentScanning/Resolver.cs new file mode 100644 index 00000000..b41dfe4f --- /dev/null +++ b/UnitystationLauncher/ContentScanning/Resolver.cs @@ -0,0 +1,94 @@ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Reflection.PortableExecutable; +using ILVerify; + +namespace UnitystationLauncher.ContentScanning; + +public sealed class Resolver : IResolver +{ + private readonly DirectoryInfo _managedPath; + + + private readonly Dictionary _dictionaryLookup = new Dictionary(); + + public Resolver(DirectoryInfo inManagedPath) + { + _managedPath = inManagedPath; + } + + public void Dispose() + { + foreach (KeyValuePair Lookup in _dictionaryLookup) + { + Lookup.Value.Dispose(); + } + } + + PEReader IResolver.ResolveAssembly(AssemblyName assemblyName) + { + if (assemblyName.Name == null) + { + throw new FileNotFoundException("Unable to find " + assemblyName.FullName); + } + + if (_dictionaryLookup.TryGetValue(assemblyName.Name, out PEReader? assembly)) + { + return assembly; + } + + FileInfo[] files = _managedPath.GetFiles("*.dll"); // Change the file extension to match your DLLs + + foreach (FileInfo file in files) + { + string fileName = Path.GetFileNameWithoutExtension(file.Name); + if (string.Equals(fileName, assemblyName.Name, StringComparison.OrdinalIgnoreCase)) + { + Console.WriteLine($"Found DLL for assembly '{assemblyName.Name}': {file.FullName}"); + _dictionaryLookup[assemblyName.Name] = + new PEReader(file.Open(FileMode.Open, FileAccess.Read, FileShare.Read)); + return _dictionaryLookup[assemblyName.Name]; + } + } + + files = _managedPath.GetFiles("*.so"); // Change the file extension to match Linux stuff to + + foreach (FileInfo file in files) + { + string fileName = Path.GetFileNameWithoutExtension(file.Name); + if (string.Equals(fileName, assemblyName.Name, StringComparison.OrdinalIgnoreCase)) + { + Console.WriteLine($"Found DLL for assembly '{assemblyName.Name}': {file.FullName}"); + _dictionaryLookup[assemblyName.Name] = + new PEReader(file.Open(FileMode.Open, FileAccess.Read, FileShare.Read)); + return _dictionaryLookup[assemblyName.Name]; + } + } + + files = _managedPath.GetFiles("*.dylib"); // Change the file extension to match mac stuff to + + foreach (FileInfo file in files) + { + string fileName = Path.GetFileNameWithoutExtension(file.Name); + if (string.Equals(fileName, assemblyName.Name, StringComparison.OrdinalIgnoreCase)) + { + Console.WriteLine($"Found DLL for assembly '{assemblyName.Name}': {file.FullName}"); + _dictionaryLookup[assemblyName.Name] = + new PEReader(file.Open(FileMode.Open, FileAccess.Read, FileShare.Read)); + return _dictionaryLookup[assemblyName.Name]; + } + } + + throw new FileNotFoundException("Unable to find it " + assemblyName.FullName); + } + + PEReader IResolver.ResolveModule(AssemblyName referencingAssembly, string fileName) + { + //TODO idk This is never used anywhere + throw new NotImplementedException( + $"idk How IResolver.ResolveModule(AssemblyName {referencingAssembly}, string {fileName}) , And it's never been called so.. "); + } +} \ No newline at end of file diff --git a/UnitystationLauncher/ContentScanning/SandboxError.cs b/UnitystationLauncher/ContentScanning/SandboxError.cs new file mode 100644 index 00000000..b1fc12c6 --- /dev/null +++ b/UnitystationLauncher/ContentScanning/SandboxError.cs @@ -0,0 +1,17 @@ +using UnitystationLauncher.Exceptions; + +namespace UnitystationLauncher.ContentScanning; + +internal sealed class SandboxError +{ + public string Message; + + public SandboxError(string message) + { + Message = message; + } + + public SandboxError(UnsupportedMetadataException ume) : this($"Unsupported metadata: {ume.Message}") + { + } +} \ No newline at end of file diff --git a/UnitystationLauncher/ContentScanning/TypeProvider.cs b/UnitystationLauncher/ContentScanning/TypeProvider.cs new file mode 100644 index 00000000..5483dc6b --- /dev/null +++ b/UnitystationLauncher/ContentScanning/TypeProvider.cs @@ -0,0 +1,83 @@ + +using System; +using System.Collections.Immutable; +using System.Reflection.Metadata; +using UnitystationLauncher.Models.ContentScanning; +using UnitystationLauncher.Models.ContentScanning.ScanningTypes; + +namespace UnitystationLauncher.ContentScanning; + +internal sealed class TypeProvider : ISignatureTypeProvider +{ + public MType GetSZArrayType(MType elementType) + { + return new MTypeSZArray(elementType); + } + + public MType GetArrayType(MType elementType, ArrayShape shape) + { + return new MTypeWackyArray(elementType, shape); + } + + public MType GetByReferenceType(MType elementType) + { + return new MTypeByRef(elementType); + } + + public MType GetGenericInstantiation(MType genericType, ImmutableArray typeArguments) + { + return new MTypeGeneric(genericType, typeArguments); + } + + public MType GetPointerType(MType elementType) + { + return new MTypePointer(elementType); + } + + public MType GetPrimitiveType(PrimitiveTypeCode typeCode) + { + return new MTypePrimitive(typeCode); + } + + public MType GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind) + { + return AssemblyTypeCheckerHelpers.GetTypeFromDefinition(reader, handle); + } + + public MType GetTypeFromReference(MetadataReader inReader, TypeReferenceHandle inHandle, byte inRawTypeKind) + { + return AssemblyTypeCheckerHelpers.ParseTypeReference(inReader, inHandle); + } + + public MType GetFunctionPointerType(MethodSignature signature) + { + throw new NotImplementedException(); + } + + public MType GetGenericMethodParameter(int genericContext, int index) + { + return new MTypeGenericMethodPlaceHolder(index); + } + + public MType GetGenericTypeParameter(int genericContext, int index) + { + return new MTypeGenericTypePlaceHolder(index); + } + + public MType GetModifiedType(MType modifier, MType unmodifiedType, bool isRequired) + { + return new MTypeModified(unmodifiedType, modifier, isRequired); + } + + public MType GetPinnedType(MType elementType) + { + throw new NotImplementedException(); + } + + public MType GetTypeFromSpecification(MetadataReader reader, int genericContext, + TypeSpecificationHandle handle, + byte rawTypeKind) + { + return reader.GetTypeSpecification(handle).DecodeSignature(this, 0); + } +} \ No newline at end of file diff --git a/UnitystationLauncher/Exceptions/UnsupportedMetadataException.cs b/UnitystationLauncher/Exceptions/UnsupportedMetadataException.cs new file mode 100644 index 00000000..bfe77379 --- /dev/null +++ b/UnitystationLauncher/Exceptions/UnsupportedMetadataException.cs @@ -0,0 +1,19 @@ + +using System; + +namespace UnitystationLauncher.Exceptions; + +public sealed class UnsupportedMetadataException : Exception +{ + public UnsupportedMetadataException() + { + } + + public UnsupportedMetadataException(string message) : base(message) + { + } + + public UnsupportedMetadataException(string message, Exception inner) : base(message, inner) + { + } +} \ No newline at end of file diff --git a/UnitystationLauncher/GameCommunicationPipe/PipeHubBuildCommunication.cs b/UnitystationLauncher/GameCommunicationPipe/PipeHubBuildCommunication.cs new file mode 100644 index 00000000..71c040fd --- /dev/null +++ b/UnitystationLauncher/GameCommunicationPipe/PipeHubBuildCommunication.cs @@ -0,0 +1,127 @@ +using System; +using System.IO; +using System.IO.Pipes; +using System.Reactive.Concurrency; +using System.Threading.Tasks; +using MessageBox.Avalonia.BaseWindows.Base; +using ReactiveUI; +using Serilog; +using UnitystationLauncher.Infrastructure; +using UnitystationLauncher.Models.Enums; + +namespace UnitystationLauncher.GameCommunicationPipe; + +public class PipeHubBuildCommunication : IDisposable +{ + private NamedPipeServerStream _serverPipe; + private StreamReader? _reader; + private StreamWriter? _writer; + + public PipeHubBuildCommunication() + { + _serverPipe = new NamedPipeServerStream("Unitystation_Hub_Build_Communication", PipeDirection.InOut, 1, + PipeTransmissionMode.Byte, PipeOptions.Asynchronous); + } + + public async Task StartServerPipe() + { + await _serverPipe.WaitForConnectionAsync(); + _reader = new StreamReader(_serverPipe); + _writer = new StreamWriter(_serverPipe); + + while (true) + { + string? request = await _reader.ReadLineAsync(); + if (request == null) + { + try + { + await _serverPipe.WaitForConnectionAsync(); + } + catch (IOException e) + { + Log.Error(e.ToString()); + _serverPipe.Close(); + _serverPipe = new NamedPipeServerStream("Unitystation_Hub_Build_Communication", PipeDirection.InOut, + 1, + PipeTransmissionMode.Byte, PipeOptions.Asynchronous); + await _serverPipe.WaitForConnectionAsync(); + } + + _reader = new StreamReader(_serverPipe); + _writer = new StreamWriter(_serverPipe); + continue; + } + + string[] requests = request.Split(","); + Log.Information($"Server: Received request: {request}"); + + if (ClientRequest.URL.ToString() == requests[0]) + { + RxApp.MainThreadScheduler.ScheduleAsync(async (_, _) => + { + IMsBoxWindow msgBox = MessageBoxBuilder.CreateMessageBox( + MessageBoxButtons.YesNo, + string.Empty, + $"would you like to add this Domain to The allowed domains to be opened In your browser, {requests[1]} " + + @" +Justification given by the Fork : " + requests[2]); + + string response = await msgBox.Show(); + Log.Information($"response {response}"); + await _writer.WriteLineAsync(response == "No" ? false.ToString() : true.ToString()); + await _writer.FlushAsync(); + return Task.CompletedTask; + }); + } + else if (ClientRequest.API_URL.ToString() == requests[0]) + { + RxApp.MainThreadScheduler.ScheduleAsync(async (_, _) => + { + IMsBoxWindow msgBox = MessageBoxBuilder.CreateMessageBox( + MessageBoxButtons.YesNo, + string.Empty, + $"The build would like to send an API request to, {requests[1]} " + @" +do you allow this fork to now on access this domain +Justification given by the Fork : " + requests[2]); + + + string response = await msgBox.Show(); + Log.Information($"response {response}"); + await _writer.WriteLineAsync(response == "No" ? false.ToString() : true.ToString()); + await _writer.FlushAsync(); + return Task.CompletedTask; + }); + } + else if (ClientRequest.Host_Trust_Mode.ToString() == requests[0]) + { + RxApp.MainThreadScheduler.ScheduleAsync(async (_, _) => + { + IMsBoxWindow msgBox = MessageBoxBuilder.CreateMessageBox( + MessageBoxButtons.YesNo, + string.Empty, + @" Trusted mode automatically allows every API and open URL action to happen without prompt, this also enables the +Variable viewer ( Application that can modify the games Data ) that Could potentially be used to Perform malicious actions on your PC, + The main purpose of this Prompt is to allow the Variable viewer (Variable editing), +What follows is given by the build, we do not control what is written in the Following text So treat with caution and use your brain + Justification : " + requests[1]); //TODO Add text + + string response = await msgBox.Show(); + Log.Information($"response {response}"); + await _writer.WriteLineAsync(response == "No" ? false.ToString() : true.ToString()); + await _writer.FlushAsync(); + return Task.CompletedTask; + }); + } + } + } + + public void Dispose() + { + _serverPipe.Dispose(); + _reader?.Dispose(); + _writer?.Dispose(); + + GC.SuppressFinalize(this); + } +} \ No newline at end of file diff --git a/UnitystationLauncher/Infrastructure/TypeExtensions.cs b/UnitystationLauncher/Infrastructure/TypeExtensions.cs new file mode 100644 index 00000000..b6b29725 --- /dev/null +++ b/UnitystationLauncher/Infrastructure/TypeExtensions.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Reflection.Metadata; +using System.Reflection.PortableExecutable; +using Internal.TypeSystem.Ecma; +using UnitystationLauncher.ContentScanning; +using UnitystationLauncher.Models.ContentScanning; + +namespace UnitystationLauncher.Infrastructure; + +public static class TypeExtensions +{ + public static IEnumerable DumpMetaMembers(this Type type) + { + string assemblyLoc = type.Assembly.Location; + + // Load assembly with System.Reflection.Metadata. + using FileStream fs = File.OpenRead(assemblyLoc); + using PEReader peReader = new PEReader(fs); + + MetadataReader metaReader = peReader.GetMetadataReader(); + + // Find type definition in raw assembly metadata. + // Is there a better way to do this than iterating?? + TypeDefinition typeDef = default; + bool found = false; + foreach (TypeDefinitionHandle typeDefHandle in metaReader.TypeDefinitions) + { + TypeDefinition tempTypeDef = metaReader.GetTypeDefinition(typeDefHandle); + string name = metaReader.GetString(tempTypeDef.Name); + string? @namespace = AssemblyTypeCheckerHelpers.NilNullString(metaReader, tempTypeDef.Namespace); + if (name == type.Name && @namespace == type.Namespace) + { + typeDef = tempTypeDef; + found = true; + break; + } + } + + if (!found) + { + throw new InvalidOperationException("Type didn't exist??"); + } + + // Dump the list. + TypeProvider provider = new(); + + foreach (FieldDefinitionHandle fieldHandle in typeDef.GetFields()) + { + FieldDefinition fieldDef = metaReader.GetFieldDefinition(fieldHandle); + + if ((fieldDef.Attributes & FieldAttributes.FieldAccessMask) != FieldAttributes.Public) + { + continue; + } + + string fieldName = metaReader.GetString(fieldDef.Name); + MType fieldType = fieldDef.DecodeSignature(provider, 0); + + yield return $"{fieldType.WhitelistToString()} {fieldName}"; + } + + foreach (MethodDefinitionHandle methodHandle in typeDef.GetMethods()) + { + MethodDefinition methodDef = metaReader.GetMethodDefinition(methodHandle); + + if (!methodDef.Attributes.IsPublic()) + { + continue; + } + + string methodName = metaReader.GetString(methodDef.Name); + MethodSignature methodSig = methodDef.DecodeSignature(provider, 0); + + string paramString = string.Join(", ", methodSig.ParameterTypes.Select(t => t.WhitelistToString())); + int genericCount = methodSig.GenericParameterCount; + string typeParamString = genericCount == 0 + ? "" + : $"<{new string(',', genericCount - 1)}>"; + + yield return $"{methodSig.ReturnType.WhitelistToString()} {methodName}{typeParamString}({paramString})"; + } + } +} \ No newline at end of file diff --git a/UnitystationLauncher/Models/Api/Server.cs b/UnitystationLauncher/Models/Api/Server.cs index 650dff08..6a3be60b 100644 --- a/UnitystationLauncher/Models/Api/Server.cs +++ b/UnitystationLauncher/Models/Api/Server.cs @@ -30,6 +30,9 @@ public Server(string forkName, int buildVersion, string serverIp, int serverPort public string? WinDownload { get; set; } public string? OsxDownload { get; set; } public string? LinuxDownload { get; set; } + + public string ServerGoodFileVersion { get; set; } = string.Empty; + public (string, int) ForkAndVersion => (ForkName, BuildVersion); public string? GetDownloadUrl(IEnvironmentService environmentService) @@ -48,7 +51,6 @@ public bool HasTrustedUrlSource { get { - const string trustedHost = "unitystationfile.b-cdn.net"; string?[] urls = { WinDownload, OsxDownload, LinuxDownload }; foreach (string? url in urls) { @@ -58,7 +60,7 @@ public bool HasTrustedUrlSource } Uri uri = new(url); - if (uri.Scheme != "https" || uri.Host != trustedHost) + if (uri.Scheme != "https") { return false; } diff --git a/UnitystationLauncher/Models/ConfigFile/HubClientConfig.cs b/UnitystationLauncher/Models/ConfigFile/HubClientConfig.cs index c3c84f15..afc9a280 100644 --- a/UnitystationLauncher/Models/ConfigFile/HubClientConfig.cs +++ b/UnitystationLauncher/Models/ConfigFile/HubClientConfig.cs @@ -1,29 +1,27 @@ using System; -using System.Runtime.InteropServices; using UnitystationLauncher.Models.Enums; using UnitystationLauncher.Services.Interface; -namespace UnitystationLauncher.Models.ConfigFile +namespace UnitystationLauncher.Models.ConfigFile; + +[Serializable] +public class HubClientConfig { - [Serializable] - public class HubClientConfig - { - public int? BuildNumber { get; set; } - public string? WinUrl { get; set; } - public string? OsxUrl { get; set; } - public string? LinuxUrl { get; set; } - public string? DailyMessage { get; set; } + public int? BuildNumber { get; set; } + public string? WinUrl { get; set; } + public string? OsxUrl { get; set; } + public string? LinuxUrl { get; set; } + public string? DailyMessage { get; set; } - public string? GetDownloadUrl(IEnvironmentService environmentService) + public string? GetDownloadUrl(IEnvironmentService environmentService) + { + return environmentService.GetCurrentEnvironment() switch { - return environmentService.GetCurrentEnvironment() switch - { - CurrentEnvironment.WindowsStandalone => WinUrl, - CurrentEnvironment.MacOsStandalone => OsxUrl, - CurrentEnvironment.LinuxStandalone - or CurrentEnvironment.LinuxFlatpak => LinuxUrl, - _ => null - }; - } + CurrentEnvironment.WindowsStandalone => WinUrl, + CurrentEnvironment.MacOsStandalone => OsxUrl, + CurrentEnvironment.LinuxStandalone + or CurrentEnvironment.LinuxFlatpak => LinuxUrl, + _ => null + }; } -} \ No newline at end of file +} diff --git a/UnitystationLauncher/Models/ConfigFile/Preferences.cs b/UnitystationLauncher/Models/ConfigFile/Preferences.cs index 67479846..7f9b340f 100644 --- a/UnitystationLauncher/Models/ConfigFile/Preferences.cs +++ b/UnitystationLauncher/Models/ConfigFile/Preferences.cs @@ -1,29 +1,28 @@ using ReactiveUI; -namespace UnitystationLauncher.Models.ConfigFile +namespace UnitystationLauncher.Models.ConfigFile; + +public class Preferences : ReactiveObject { - public class Preferences : ReactiveObject - { - private bool _autoRemove = true; - private int _ignoreVersionUpdate; - private string _installationPath = string.Empty; + private bool _autoRemove = true; + private int _ignoreVersionUpdate; + private string _installationPath = string.Empty; - public bool AutoRemove - { - get => _autoRemove; - set => this.RaiseAndSetIfChanged(ref _autoRemove, value); - } + public bool AutoRemove + { + get => _autoRemove; + set => this.RaiseAndSetIfChanged(ref _autoRemove, value); + } - public int IgnoreVersionUpdate - { - get => _ignoreVersionUpdate; - set => this.RaiseAndSetIfChanged(ref _ignoreVersionUpdate, value); - } + public int IgnoreVersionUpdate + { + get => _ignoreVersionUpdate; + set => this.RaiseAndSetIfChanged(ref _ignoreVersionUpdate, value); + } - public string InstallationPath - { - get => _installationPath; - set => this.RaiseAndSetIfChanged(ref _installationPath, value); - } + public string InstallationPath + { + get => _installationPath; + set => this.RaiseAndSetIfChanged(ref _installationPath, value); } -} \ No newline at end of file +} diff --git a/UnitystationLauncher/Models/ConfigFile/SandboxConfig.cs b/UnitystationLauncher/Models/ConfigFile/SandboxConfig.cs new file mode 100644 index 00000000..47182577 --- /dev/null +++ b/UnitystationLauncher/Models/ConfigFile/SandboxConfig.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using ILVerify; +using UnitystationLauncher.Models.ContentScanning; + +namespace UnitystationLauncher.Models.ConfigFile; + +[Serializable] +public sealed class SandboxConfig +{ + public string? SystemAssemblyName { get; set; } + public List AllowedVerifierErrors { get; set; } = new List(); + public List WhitelistedNamespaces { get; set; } = new List(); + public List MultiAssemblyOtherReferences { get; set; } = new List(); + + public Dictionary> Types { get; set; } = + new Dictionary>(); +} \ No newline at end of file diff --git a/UnitystationLauncher/Models/ContentScanning/MType.cs b/UnitystationLauncher/Models/ContentScanning/MType.cs new file mode 100644 index 00000000..b56b42fe --- /dev/null +++ b/UnitystationLauncher/Models/ContentScanning/MType.cs @@ -0,0 +1,24 @@ +namespace UnitystationLauncher.Models.ContentScanning; + + +public record MType +{ + public virtual bool WhitelistEquals(MType other) + { + return false; + } + + public virtual bool IsCoreTypeDefined() + { + return false; + } + + /// + /// Outputs this type in a format re-parseable for the sandbox config whitelist. + /// + public virtual string? WhitelistToString() + { + return ToString(); + } +} + diff --git a/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MMemberRef.cs b/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MMemberRef.cs new file mode 100644 index 00000000..268e00df --- /dev/null +++ b/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MMemberRef.cs @@ -0,0 +1,13 @@ +namespace UnitystationLauncher.Models.ContentScanning.ScanningTypes; + +public class MMemberRef +{ + public readonly MType ParentType; + public readonly string Name; + + protected MMemberRef(MType parentType, string name) + { + ParentType = parentType; + Name = name; + } +} \ No newline at end of file diff --git a/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MMemberRefField.cs b/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MMemberRefField.cs new file mode 100644 index 00000000..aa335591 --- /dev/null +++ b/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MMemberRefField.cs @@ -0,0 +1,16 @@ +namespace UnitystationLauncher.Models.ContentScanning.ScanningTypes; + +internal sealed class MMemberRefField : MMemberRef +{ + internal readonly MType FieldType; + + public MMemberRefField(MType parentType, string name, MType fieldType) : base(parentType, name) + { + FieldType = fieldType; + } + + public override string ToString() + { + return $"{ParentType}.{Name} Returns {FieldType}"; + } +} \ No newline at end of file diff --git a/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MMemberRefMethod.cs b/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MMemberRefMethod.cs new file mode 100644 index 00000000..7e728bcc --- /dev/null +++ b/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MMemberRefMethod.cs @@ -0,0 +1,23 @@ +using System.Collections.Immutable; + +namespace UnitystationLauncher.Models.ContentScanning.ScanningTypes; + +internal sealed class MMemberRefMethod : MMemberRef +{ + public readonly MType ReturnType; + public readonly int GenericParameterCount; + public readonly ImmutableArray ParameterTypes; + + public MMemberRefMethod(MType parentType, string name, MType returnType, + int genericParameterCount, ImmutableArray parameterTypes) : base(parentType, name) + { + ReturnType = returnType; + GenericParameterCount = genericParameterCount; + ParameterTypes = parameterTypes; + } + + public override string ToString() + { + return $"{ParentType}.{Name}({string.Join(", ", ParameterTypes)}) Returns {ReturnType}"; + } +} \ No newline at end of file diff --git a/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MResScope.cs b/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MResScope.cs new file mode 100644 index 00000000..4f57f210 --- /dev/null +++ b/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MResScope.cs @@ -0,0 +1,5 @@ +namespace UnitystationLauncher.Models.ContentScanning.ScanningTypes; + +public record MResScope +{ +} \ No newline at end of file diff --git a/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MResScopeAssembly.cs b/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MResScopeAssembly.cs new file mode 100644 index 00000000..ccacbf7b --- /dev/null +++ b/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MResScopeAssembly.cs @@ -0,0 +1,9 @@ +namespace UnitystationLauncher.Models.ContentScanning.ScanningTypes; + +internal sealed record MResScopeAssembly(string Name) : MResScope +{ + public override string ToString() + { + return $"[{Name}]"; + } +} \ No newline at end of file diff --git a/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MResScopeType.cs b/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MResScopeType.cs new file mode 100644 index 00000000..b906240e --- /dev/null +++ b/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MResScopeType.cs @@ -0,0 +1,10 @@ +namespace UnitystationLauncher.Models.ContentScanning.ScanningTypes; + + +internal sealed record MResScopeType(MType Type) : MResScope +{ + public override string ToString() + { + return $"{Type}/"; + } +} \ No newline at end of file diff --git a/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypeByRef.cs b/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypeByRef.cs new file mode 100644 index 00000000..5091e3d6 --- /dev/null +++ b/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypeByRef.cs @@ -0,0 +1,19 @@ +namespace UnitystationLauncher.Models.ContentScanning.ScanningTypes; + +internal sealed record MTypeByRef(MType ElementType) : MType +{ + public override string ToString() + { + return $"{ElementType}&"; + } + + public override string WhitelistToString() + { + return $"ref {ElementType.WhitelistToString()}"; + } + + public override bool WhitelistEquals(MType other) + { + return other is MTypeByRef byRef && ElementType.WhitelistEquals(byRef.ElementType); + } +} \ No newline at end of file diff --git a/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypeDefined.cs b/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypeDefined.cs new file mode 100644 index 00000000..47819126 --- /dev/null +++ b/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypeDefined.cs @@ -0,0 +1,21 @@ +namespace UnitystationLauncher.Models.ContentScanning.ScanningTypes; + +internal sealed record MTypeDefined(string Name, string? Namespace, MTypeDefined? Enclosing) : MType +{ + public override string ToString() + { + string name = Namespace != null ? $"{Namespace}.{Name}" : Name; + + if (Enclosing != null) + { + return $"{Enclosing}/{name}"; + } + + return name; + } + + public override bool IsCoreTypeDefined() + { + return true; + } +} \ No newline at end of file diff --git a/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypeGeneric.cs b/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypeGeneric.cs new file mode 100644 index 00000000..6bc8a848 --- /dev/null +++ b/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypeGeneric.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Immutable; +using System.Linq; + +namespace UnitystationLauncher.Models.ContentScanning.ScanningTypes; + +internal sealed record MTypeGeneric(MType GenericType, ImmutableArray TypeArguments) : MType +{ + public override string ToString() + { + return $"{GenericType}<{string.Join(", ", TypeArguments)}>"; + } + + public override string WhitelistToString() + { + return + $"{GenericType.WhitelistToString()}<{string.Join(", ", TypeArguments.Select(t => t.WhitelistToString()))}>"; + } + + public override bool WhitelistEquals(MType other) + { + if (!(other is MTypeGeneric generic)) + { + return false; + } + + if (TypeArguments.Length != generic.TypeArguments.Length) + { + return false; + } + + for (int i = 0; i < TypeArguments.Length; i++) + { + MType argA = TypeArguments[i]; + MType argB = generic.TypeArguments[i]; + + if (!argA.WhitelistEquals(argB)) + { + return false; + } + } + + return GenericType.WhitelistEquals(generic.GenericType); + } + + public bool Equals(MTypeGeneric? otherGeneric) + { + return otherGeneric != null && GenericType.Equals(otherGeneric.GenericType) && + TypeArguments.SequenceEqual(otherGeneric.TypeArguments); + } + + public override int GetHashCode() + { + HashCode hc = new HashCode(); + hc.Add(GenericType); + foreach (MType typeArg in TypeArguments) + { + hc.Add(typeArg); + } + + return hc.ToHashCode(); + } + + public override bool IsCoreTypeDefined() + { + return GenericType.IsCoreTypeDefined(); + } +} \ No newline at end of file diff --git a/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypeGenericMethodPlaceHolder.cs b/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypeGenericMethodPlaceHolder.cs new file mode 100644 index 00000000..17d78835 --- /dev/null +++ b/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypeGenericMethodPlaceHolder.cs @@ -0,0 +1,14 @@ +namespace UnitystationLauncher.Models.ContentScanning.ScanningTypes; + +internal sealed record MTypeGenericMethodPlaceHolder(int Index) : MType +{ + public override string ToString() + { + return $"!!{Index}"; + } + + public override bool WhitelistEquals(MType other) + { + return Equals(other); + } +} \ No newline at end of file diff --git a/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypeGenericTypePlaceHolder.cs b/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypeGenericTypePlaceHolder.cs new file mode 100644 index 00000000..0da30605 --- /dev/null +++ b/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypeGenericTypePlaceHolder.cs @@ -0,0 +1,14 @@ +namespace UnitystationLauncher.Models.ContentScanning.ScanningTypes; + +internal sealed record MTypeGenericTypePlaceHolder(int Index) : MType +{ + public override string ToString() + { + return $"!{Index}"; + } + + public override bool WhitelistEquals(MType other) + { + return Equals(other); + } +} \ No newline at end of file diff --git a/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypeModified.cs b/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypeModified.cs new file mode 100644 index 00000000..cd55f07c --- /dev/null +++ b/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypeModified.cs @@ -0,0 +1,21 @@ +namespace UnitystationLauncher.Models.ContentScanning.ScanningTypes; + +internal sealed record MTypeModified(MType UnmodifiedType, MType ModifierType, bool Required) : MType +{ + public override string ToString() + { + string modName = Required ? "modreq" : "modopt"; + return $"{UnmodifiedType} {modName}({ModifierType})"; + } + + public override string? WhitelistToString() + { + return UnmodifiedType.WhitelistToString(); + } + + public override bool WhitelistEquals(MType other) + { + // TODO: This is asymmetric shit. + return UnmodifiedType.WhitelistEquals(other); + } +} \ No newline at end of file diff --git a/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypeParsed.cs b/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypeParsed.cs new file mode 100644 index 00000000..e51b8591 --- /dev/null +++ b/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypeParsed.cs @@ -0,0 +1,38 @@ +namespace UnitystationLauncher.Models.ContentScanning.ScanningTypes; + +internal sealed record MTypeParsed(string FullName, MTypeParsed? NestedParent = null) : MType +{ + public override string ToString() + { + return NestedParent != null ? $"{NestedParent}/{FullName}" : FullName; + } + + public override bool WhitelistEquals(MType other) + { + switch (other) + { + case MTypeParsed parsed: + if (NestedParent != null + && (parsed.NestedParent == null || NestedParent.WhitelistEquals(parsed.NestedParent) == false)) + { + return false; + } + + return parsed.FullName == FullName; + case MTypeReferenced referenced: + if (NestedParent != null + && (referenced.ResolutionScope is not MResScopeType parentRes || + NestedParent.WhitelistEquals(parentRes.Type) == false)) + { + return false; + } + + string refFullName = referenced.Namespace == null + ? referenced.Name + : $"{referenced.Namespace}.{referenced.Name}"; + return FullName == refFullName; + default: + return false; + } + } +} \ No newline at end of file diff --git a/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypePointer.cs b/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypePointer.cs new file mode 100644 index 00000000..3711d5ae --- /dev/null +++ b/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypePointer.cs @@ -0,0 +1,19 @@ +namespace UnitystationLauncher.Models.ContentScanning.ScanningTypes; + +internal sealed record MTypePointer(MType ElementType) : MType +{ + public override string ToString() + { + return $"{ElementType}*"; + } + + public override string WhitelistToString() + { + return $"{ElementType.WhitelistToString()}*"; + } + + public override bool WhitelistEquals(MType other) + { + return other is MTypePointer ptr && ElementType.WhitelistEquals(ptr.ElementType); + } +} \ No newline at end of file diff --git a/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypePrimitive.cs b/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypePrimitive.cs new file mode 100644 index 00000000..f994ddab --- /dev/null +++ b/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypePrimitive.cs @@ -0,0 +1,67 @@ +using System.Reflection.Metadata; + +namespace UnitystationLauncher.Models.ContentScanning.ScanningTypes; + +internal sealed record MTypePrimitive(PrimitiveTypeCode TypeCode) : MType +{ + public override string ToString() + { + return TypeCode switch + { + PrimitiveTypeCode.Void => "void", + PrimitiveTypeCode.Boolean => "bool", + PrimitiveTypeCode.Char => "char", + PrimitiveTypeCode.SByte => "int8", + PrimitiveTypeCode.Byte => "unsigned int8", + PrimitiveTypeCode.Int16 => "int16", + PrimitiveTypeCode.UInt16 => "unsigned int16", + PrimitiveTypeCode.Int32 => "int32", + PrimitiveTypeCode.UInt32 => "unsigned int32", + PrimitiveTypeCode.Int64 => "int64", + PrimitiveTypeCode.UInt64 => "unsigned int64", + PrimitiveTypeCode.Single => "float32", + PrimitiveTypeCode.Double => "float64", + PrimitiveTypeCode.String => "string", + // ReSharper disable once StringLiteralTypo + PrimitiveTypeCode.TypedReference => "typedref", + PrimitiveTypeCode.IntPtr => "native int", + PrimitiveTypeCode.UIntPtr => "unsigned native int", + PrimitiveTypeCode.Object => "object", + _ => "???" + }; + } + + public override string WhitelistToString() + { + return TypeCode switch + { + PrimitiveTypeCode.Void => "void", + PrimitiveTypeCode.Boolean => "bool", + PrimitiveTypeCode.Char => "char", + PrimitiveTypeCode.SByte => "sbyte", + PrimitiveTypeCode.Byte => "byte", + PrimitiveTypeCode.Int16 => "short", + PrimitiveTypeCode.UInt16 => "ushort", + PrimitiveTypeCode.Int32 => "int", + PrimitiveTypeCode.UInt32 => "uint", + PrimitiveTypeCode.Int64 => "long", + PrimitiveTypeCode.UInt64 => "ulong", + PrimitiveTypeCode.Single => "float", + PrimitiveTypeCode.Double => "double", + PrimitiveTypeCode.String => "string", + // ReSharper disable once StringLiteralTypo + PrimitiveTypeCode.TypedReference => "typedref", + // ReSharper disable once StringLiteralTypo + PrimitiveTypeCode.IntPtr => "nint", + // ReSharper disable once StringLiteralTypo + PrimitiveTypeCode.UIntPtr => "unint", + PrimitiveTypeCode.Object => "object", + _ => "???" + }; + } + + public override bool WhitelistEquals(MType other) + { + return Equals(other); + } +} \ No newline at end of file diff --git a/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypeReferenced.cs b/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypeReferenced.cs new file mode 100644 index 00000000..d244824f --- /dev/null +++ b/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypeReferenced.cs @@ -0,0 +1,37 @@ +namespace UnitystationLauncher.Models.ContentScanning.ScanningTypes; + +internal sealed record MTypeReferenced(MResScope ResolutionScope, string Name, string? Namespace) : MType +{ + public override string ToString() + { + if (Namespace == null) + { + return $"{ResolutionScope}{Name}"; + } + + return $"{ResolutionScope}{Namespace}.{Name}"; + } + + public override string WhitelistToString() + { + if (Namespace == null) + { + return Name; + } + + return $"{Namespace}.{Name}"; + } + + public override bool WhitelistEquals(MType other) + { + return other switch + { + MTypeParsed p => p.WhitelistEquals(this), + // TODO: ResolutionScope doesn't actually implement equals + // This is fine since we're not comparing these anywhere + MTypeReferenced r => r.Namespace == Namespace && r.Name == Name && + r.ResolutionScope.Equals(ResolutionScope), + _ => false + }; + } +} \ No newline at end of file diff --git a/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypeSZArray.cs b/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypeSZArray.cs new file mode 100644 index 00000000..1b2a4d3e --- /dev/null +++ b/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypeSZArray.cs @@ -0,0 +1,20 @@ +namespace UnitystationLauncher.Models.ContentScanning.ScanningTypes; + +// Normal single dimensional array with zero lower bound. +internal sealed record MTypeSZArray(MType ElementType) : MType +{ + public override string ToString() + { + return $"{ElementType}[]"; + } + + public override string WhitelistToString() + { + return $"{ElementType.WhitelistToString()}[]"; + } + + public override bool WhitelistEquals(MType other) + { + return other is MTypeSZArray arr && ElementType.WhitelistEquals(arr.ElementType); + } +} \ No newline at end of file diff --git a/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypeWackyArray.cs b/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypeWackyArray.cs new file mode 100644 index 00000000..5aa08cbd --- /dev/null +++ b/UnitystationLauncher/Models/ContentScanning/ScanningTypes/MTypeWackyArray.cs @@ -0,0 +1,34 @@ +using System; +using System.Linq; +using System.Reflection.Metadata; + +namespace UnitystationLauncher.Models.ContentScanning.ScanningTypes; + +// Multi-dimension arrays with funny lower and upper bounds. +internal sealed record MTypeWackyArray(MType ElementType, ArrayShape Shape) : MType +{ + public override string ToString() + { + return $"{ElementType}[TODO]"; + } + + public override string WhitelistToString() + { + return $"{ElementType.WhitelistToString()}[TODO]"; + } + + public override bool WhitelistEquals(MType other) + { + return other is MTypeWackyArray arr && ShapesEqual(Shape, arr.Shape) && ElementType.WhitelistEquals(arr); + } + + private static bool ShapesEqual(in ArrayShape a, in ArrayShape b) + { + return a.Rank == b.Rank && a.LowerBounds.SequenceEqual(b.LowerBounds) && a.Sizes.SequenceEqual(b.Sizes); + } + + public override bool IsCoreTypeDefined() + { + return ElementType.IsCoreTypeDefined(); + } +} \ No newline at end of file diff --git a/UnitystationLauncher/Models/ContentScanning/TypeConfig.cs b/UnitystationLauncher/Models/ContentScanning/TypeConfig.cs new file mode 100644 index 00000000..3c8e2b5f --- /dev/null +++ b/UnitystationLauncher/Models/ContentScanning/TypeConfig.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using UnitystationLauncher.Models.Enums; + +namespace UnitystationLauncher.Models.ContentScanning; + +public sealed class TypeConfig +{ + // Used for type configs where the type config doesn't exist due to a bigger-scoped All whitelisting. + // e.g. nested types or namespace whitelist. + public static readonly TypeConfig DefaultAll = new TypeConfig { All = true }; + + public bool All { get; set; } + public InheritMode Inherit { get; set; } = InheritMode.Default; + public string[]? Methods { get; set; } + [NonSerialized] public WhitelistMethodDefine[] MethodsParsed = Array.Empty(); + public string[]? Fields { get; set; } + [NonSerialized] public WhitelistFieldDefine[] FieldsParsed = Array.Empty(); + public Dictionary? NestedTypes { get; set; } +} \ No newline at end of file diff --git a/UnitystationLauncher/Models/ContentScanning/WhitelistFieldDefine.cs b/UnitystationLauncher/Models/ContentScanning/WhitelistFieldDefine.cs new file mode 100644 index 00000000..ba14d4b6 --- /dev/null +++ b/UnitystationLauncher/Models/ContentScanning/WhitelistFieldDefine.cs @@ -0,0 +1,13 @@ +namespace UnitystationLauncher.Models.ContentScanning; + +public sealed class WhitelistFieldDefine +{ + public string Name { get; } + public MType FieldType { get; } + + public WhitelistFieldDefine(string name, MType fieldType) + { + Name = name; + FieldType = fieldType; + } +} \ No newline at end of file diff --git a/UnitystationLauncher/Models/ContentScanning/WhitelistMethodDefine.cs b/UnitystationLauncher/Models/ContentScanning/WhitelistMethodDefine.cs new file mode 100644 index 00000000..4853efe7 --- /dev/null +++ b/UnitystationLauncher/Models/ContentScanning/WhitelistMethodDefine.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; + +namespace UnitystationLauncher.Models.ContentScanning; + +public sealed class WhitelistMethodDefine +{ + public string Name { get; } + public MType ReturnType { get; } + public List ParameterTypes { get; } + public int GenericParameterCount { get; } + + public WhitelistMethodDefine( + string name, + MType returnType, + List parameterTypes, + int genericParameterCount) + { + Name = name; + ReturnType = returnType; + ParameterTypes = parameterTypes; + GenericParameterCount = genericParameterCount; + } +} diff --git a/UnitystationLauncher/Models/Download.cs b/UnitystationLauncher/Models/Download.cs index 656d2e27..1d63b2bc 100644 --- a/UnitystationLauncher/Models/Download.cs +++ b/UnitystationLauncher/Models/Download.cs @@ -10,6 +10,8 @@ public class Download : ReactiveObject public string ForkName { get; } public int BuildVersion { get; } + public string GoodFileVersion { get; } + private long _size; public long Size { @@ -41,12 +43,13 @@ public long Downloaded public int Progress => (int)(Downloaded * 100 / Math.Max(1, Size)); - public Download(string url, string installationPath, string forkName, int buildVersion) + public Download(string url, string installationPath, string forkName, int buildVersion, string inGoodFileVersion) { DownloadUrl = url; InstallPath = installationPath; ForkName = forkName; BuildVersion = buildVersion; + GoodFileVersion = inGoodFileVersion; } } } \ No newline at end of file diff --git a/UnitystationLauncher/Models/Enums/ClientRequest.cs b/UnitystationLauncher/Models/Enums/ClientRequest.cs new file mode 100644 index 00000000..fb7f1d72 --- /dev/null +++ b/UnitystationLauncher/Models/Enums/ClientRequest.cs @@ -0,0 +1,8 @@ +namespace UnitystationLauncher.Models.Enums; + +internal enum ClientRequest +{ + URL = 1, + API_URL = 2, + Host_Trust_Mode = 3, +} \ No newline at end of file diff --git a/UnitystationLauncher/Models/Enums/DumpFlags.cs b/UnitystationLauncher/Models/Enums/DumpFlags.cs new file mode 100644 index 00000000..395ace56 --- /dev/null +++ b/UnitystationLauncher/Models/Enums/DumpFlags.cs @@ -0,0 +1,14 @@ +using System; + +namespace UnitystationLauncher.Models.Enums; + +[Flags] +public enum DumpFlags : byte +{ + None = 0, + Types = 1, + Members = 2, + Inheritance = 4, + + All = Types | Members | Inheritance +} \ No newline at end of file diff --git a/UnitystationLauncher/Models/Enums/InheritMode.cs b/UnitystationLauncher/Models/Enums/InheritMode.cs new file mode 100644 index 00000000..42468529 --- /dev/null +++ b/UnitystationLauncher/Models/Enums/InheritMode.cs @@ -0,0 +1,11 @@ +namespace UnitystationLauncher.Models.Enums; + +public enum InheritMode : byte +{ + // Allow if All is set, block otherwise + Default, + Allow, + + // Block even is All is set + Block +} \ No newline at end of file diff --git a/UnitystationLauncher/Services/AssemblyTypeCheckerService.cs b/UnitystationLauncher/Services/AssemblyTypeCheckerService.cs new file mode 100644 index 00000000..49fa87e2 --- /dev/null +++ b/UnitystationLauncher/Services/AssemblyTypeCheckerService.cs @@ -0,0 +1,623 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Reflection; +using System.Reflection.Metadata; +using System.Reflection.PortableExecutable; +using System.Threading.Tasks; +using ILVerify; +using MoreLinq; +using UnitystationLauncher.ContentScanning; +using UnitystationLauncher.Models.ConfigFile; +using UnitystationLauncher.Models.ContentScanning; +using UnitystationLauncher.Models.ContentScanning.ScanningTypes; +using UnitystationLauncher.Models.Enums; +using UnitystationLauncher.Services.Interface; + +// psst +// You know ECMA-335 right? The specification for the CLI that .NET runs on? +// Yeah, you need it to understand a lot of this code. So get a copy. +// You know the cool thing? +// ISO has a version that has correct PDF metadata so there's an actual table of contents. +// Right here: https://standards.iso.org/ittf/PubliclyAvailableStandards/c058046_ISO_IEC_23271_2012(E).zip + +namespace UnitystationLauncher.Services; + +/// +/// Manages the type white/black list of types and namespaces, and verifies assemblies against them. +/// +public sealed partial class AssemblyTypeCheckerService : IAssemblyTypeCheckerService +{ + /// + /// Completely disables type checking, allowing everything. + /// + public bool DisableTypeCheck { get; init; } + + public DumpFlags Dump { get; init; } = DumpFlags.None; + public bool VerifyIl { get; init; } + + private readonly Task _config; + + private readonly IEnvironmentService _environmentService; + + + private readonly ICodeScanConfigService _codeScanConfigService; + + private readonly HttpClient _httpClient; + + public AssemblyTypeCheckerService(IEnvironmentService environmentService, HttpClient httpClient, ICodeScanConfigService codeScanConfigService) + { + _environmentService = environmentService; + VerifyIl = true; + DisableTypeCheck = false; + _httpClient = httpClient; + _codeScanConfigService = codeScanConfigService; + _config = Task.Run(_codeScanConfigService.LoadConfigAsync); + } + + /// + /// Check the assembly for any illegal types. Any types not on the white list + /// will cause the assembly to be rejected. + /// + /// + /// + /// + /// + /// + /// Assembly to load. + /// + public bool CheckAssembly(FileInfo diskPath, DirectoryInfo managedPath, List otherAssemblies, + Action info, Action Errors) + { + using FileStream assembly = diskPath.OpenRead(); + Stopwatch fullStopwatch = Stopwatch.StartNew(); + + Resolver resolver = AssemblyTypeCheckerHelpers.CreateResolver(managedPath); + using PEReader peReader = new PEReader(assembly, PEStreamOptions.LeaveOpen); + MetadataReader reader = peReader.GetMetadataReader(); + + string asmName = reader.GetString(reader.GetAssemblyDefinition().Name); + + if (peReader.PEHeaders.CorHeader?.ManagedNativeHeaderDirectory is { Size: not 0 }) + { + Errors.Invoke($"Assembly {asmName} contains native code."); + return false; + } + + if (VerifyIl) + { + if (DoVerifyIL(asmName, resolver, peReader, reader, info, Errors) == false) + { + Errors.Invoke($"Assembly {asmName} Has invalid IL code"); + return false; + } + } + + + ConcurrentBag errors = new ConcurrentBag(); + + List types = AssemblyTypeCheckerHelpers.GetReferencedTypes(reader, errors); + List members = AssemblyTypeCheckerHelpers.GetReferencedMembers(reader, errors); + List<(MType type, MType parent, ArraySegment interfaceImpls)> inherited = GetExternalInheritedTypes(reader, errors); + info.Invoke($"References loaded... {fullStopwatch.ElapsedMilliseconds}ms"); + + if (DisableTypeCheck) + { + resolver.Dispose(); + peReader.Dispose(); + return true; + } + + + SandboxConfig loadedConfig = _config.Result; + + loadedConfig.MultiAssemblyOtherReferences.Clear(); + loadedConfig.MultiAssemblyOtherReferences.AddRange(otherAssemblies); + + // We still do explicit type reference scanning, even though the actual whitelists work with raw members. + // This is so that we can simplify handling of generic type specifications during member checking: + // we won't have to check that any types in their type arguments are whitelisted. + foreach (MTypeReferenced type in types) + { + if (IsTypeAccessAllowed(loadedConfig, type, out _) == false) + { + errors.Add(new SandboxError($"Access to type not allowed: {type} asmName {asmName}")); + } + } + + info.Invoke($"Types... {fullStopwatch.ElapsedMilliseconds}ms"); + + CheckInheritance(loadedConfig, inherited, errors); + + info.Invoke($"Inheritance... {fullStopwatch.ElapsedMilliseconds}ms"); + + AssemblyTypeCheckerHelpers.CheckNoUnmanagedMethodDefs(reader, errors); + + info.Invoke($"Unmanaged methods... {fullStopwatch.ElapsedMilliseconds}ms"); + + AssemblyTypeCheckerHelpers.CheckNoTypeAbuse(reader, errors); + + info.Invoke($"Type abuse... {fullStopwatch.ElapsedMilliseconds}ms"); + + CheckMemberReferences(loadedConfig, members, errors); + + errors = new ConcurrentBag(errors.OrderBy(x => x.Message)); + + foreach (SandboxError error in errors) + { + Errors.Invoke($"Sandbox violation: {error.Message}"); + } + + info.Invoke($"Checked assembly in {fullStopwatch.ElapsedMilliseconds}ms"); + resolver.Dispose(); + peReader.Dispose(); + return errors.IsEmpty; + } + + private bool DoVerifyIL( + string name, + IResolver resolver, + PEReader peReader, + MetadataReader reader, + Action info, + Action logErrors) + { + info.Invoke($"{name}: Verifying IL..."); + Stopwatch sw = Stopwatch.StartNew(); + ConcurrentBag bag = new ConcurrentBag(); + + + bool UesParallel = false; + + if (UesParallel) + { + OrderablePartitioner partitioner = Partitioner.Create(reader.TypeDefinitions); + Parallel.ForEach(partitioner.GetPartitions(Environment.ProcessorCount), handle => + { + Verifier ver = new Verifier(resolver); + ver.SetSystemModuleName(new AssemblyName(AssemblyTypeCheckerHelpers.SystemAssemblyName)); + while (handle.MoveNext()) + { + foreach (VerificationResult? result in ver.Verify(peReader, handle.Current, verifyMethods: true)) + { + bag.Add(result); + } + } + }); + } + else + { + Verifier ver = new Verifier(resolver); + //mscorlib + ver.SetSystemModuleName(new AssemblyName(AssemblyTypeCheckerHelpers.SystemAssemblyName)); + foreach (TypeDefinitionHandle Definition in reader.TypeDefinitions) + { + IEnumerable Errors = ver.Verify(peReader, Definition, verifyMethods: true); + foreach (VerificationResult? Error in Errors) + { + bag.Add(Error); + } + } + } + + SandboxConfig loadedCfg = _config.Result; + + bool verifyErrors = false; + foreach (VerificationResult res in bag) + { + if (loadedCfg.AllowedVerifierErrors.Contains(res.Code)) + { + continue; + } + + string formatted = res.Args == null ? res.Message : string.Format(res.Message, res.Args); + string msg = $"{name}: ILVerify: {formatted}"; + + if (!res.Method.IsNil) + { + MethodDefinition method = reader.GetMethodDefinition(res.Method); + string methodName = AssemblyTypeCheckerHelpers.FormatMethodName(reader, method); + + msg = $"{msg}, method: {methodName}"; + } + + if (!res.Type.IsNil) + { + MTypeDefined type = AssemblyTypeCheckerHelpers.GetTypeFromDefinition(reader, res.Type); + msg = $"{msg}, type: {type}"; + } + + + verifyErrors = true; + logErrors.Invoke(msg); + } + + info.Invoke($"{name}: Verified IL in {sw.Elapsed.TotalMilliseconds}ms"); + + if (verifyErrors) + { + return false; + } + + return true; + } + + private void CheckMemberReferences( + SandboxConfig sandboxConfig, + List members, + ConcurrentBag errors) + { + bool IsParallel = true; + + if (IsParallel) + { + Parallel.ForEach(members, memberRef => + { + MType baseType = memberRef.ParentType; + while (!(baseType is MTypeReferenced)) + { + switch (baseType) + { + case MTypeGeneric generic: + { + baseType = generic.GenericType; + + break; + } + case MTypeWackyArray: + { + // Members on arrays (not to be confused with vectors) are all fine. + // See II.14.2 in ECMA-335. + return; + } + case MTypeDefined: + { + // Valid for this to show up, safe to ignore. + return; + } + default: + { + throw new ArgumentOutOfRangeException(); + } + } + } + + MTypeReferenced baseTypeReferenced = (MTypeReferenced)baseType; + + if (IsTypeAccessAllowed(sandboxConfig, baseTypeReferenced, out TypeConfig? typeCfg) == false) + { + // Technically this error isn't necessary since we have an earlier pass + // checking all referenced types. That should have caught this + // We still need the typeCfg so that's why we're checking. Might as well. + errors.Add(new SandboxError($"Access to type not allowed: {baseTypeReferenced}")); + return; + } + + if (typeCfg.All) + { + // Fully whitelisted for the type, we good. + return; + } + + switch (memberRef) + { + case MMemberRefField mMemberRefField: + { + foreach (WhitelistFieldDefine field in typeCfg.FieldsParsed) + { + if (field.Name == mMemberRefField.Name && + mMemberRefField.FieldType.WhitelistEquals(field.FieldType)) + { + return; // Found + } + } + + errors.Add(new SandboxError($"Access to field not allowed: {mMemberRefField}")); + break; + } + case MMemberRefMethod mMemberRefMethod: + foreach (WhitelistMethodDefine parsed in typeCfg.MethodsParsed) + { + bool notParamMismatch = true; + + if (parsed.Name == mMemberRefMethod.Name && + mMemberRefMethod.ReturnType.WhitelistEquals(parsed.ReturnType) && + mMemberRefMethod.ParameterTypes.Length == parsed.ParameterTypes.Count && + mMemberRefMethod.GenericParameterCount == parsed.GenericParameterCount) + { + for (int i = 0; i < mMemberRefMethod.ParameterTypes.Length; i++) + { + MType a = mMemberRefMethod.ParameterTypes[i]; + MType b = parsed.ParameterTypes[i]; + + if (a.WhitelistEquals(b) == false) + { + notParamMismatch = false; + break; + } + } + + if (notParamMismatch) + { + return; // Found + } + } + } + + errors.Add(new SandboxError($"Access to method not allowed: {mMemberRefMethod}")); + break; + default: + throw new ArgumentOutOfRangeException(nameof(memberRef)); + } + }); + } + else + { + foreach (MMemberRef memberRef in members) + { + MType baseType = memberRef.ParentType; + while (!(baseType is MTypeReferenced)) + { + switch (baseType) + { + case MTypeGeneric generic: + { + baseType = generic.GenericType; + + break; + } + case MTypeWackyArray: + { + // Members on arrays (not to be confused with vectors) are all fine. + // See II.14.2 in ECMA-335. + continue; + } + case MTypeDefined: + { + // Valid for this to show up, safe to ignore. + continue; + } + default: + { + throw new ArgumentOutOfRangeException(); + } + } + } + + MTypeReferenced baseTypeReferenced = (MTypeReferenced)baseType; + + if (IsTypeAccessAllowed(sandboxConfig, baseTypeReferenced, out TypeConfig? typeCfg) == false) + { + // Technically this error isn't necessary since we have an earlier pass + // checking all referenced types. That should have caught this + // We still need the typeCfg so that's why we're checking. Might as well. + errors.Add(new SandboxError($"Access to type not allowed: {baseTypeReferenced}")); + continue; + } + + if (typeCfg.All) + { + // Fully whitelisted for the type, we good. + continue; + } + + switch (memberRef) + { + case MMemberRefField mMemberRefField: + { + foreach (WhitelistFieldDefine field in typeCfg.FieldsParsed) + { + if (field.Name == mMemberRefField.Name && + mMemberRefField.FieldType.WhitelistEquals(field.FieldType)) + { + continue; // Found + } + } + + errors.Add(new SandboxError($"Access to field not allowed: {mMemberRefField}")); + break; + } + case MMemberRefMethod mMemberRefMethod: + bool notParamMismatch = true; + foreach (WhitelistMethodDefine parsed in typeCfg.MethodsParsed) + { + if (parsed.Name == mMemberRefMethod.Name && + mMemberRefMethod.ReturnType.WhitelistEquals(parsed.ReturnType) && + mMemberRefMethod.ParameterTypes.Length == parsed.ParameterTypes.Count && + mMemberRefMethod.GenericParameterCount == parsed.GenericParameterCount) + { + for (int i = 0; i < mMemberRefMethod.ParameterTypes.Length; i++) + { + MType a = mMemberRefMethod.ParameterTypes[i]; + MType b = parsed.ParameterTypes[i]; + + if (!a.WhitelistEquals(b)) + { + notParamMismatch = false; + break; + + } + } + + if (notParamMismatch) + { + break; // Found + } + break; + } + } + + if (notParamMismatch == false) + { + continue; + } + + errors.Add(new SandboxError($"Access to method not allowed: {mMemberRefMethod}")); + break; + default: + throw new ArgumentOutOfRangeException(nameof(memberRef)); + } + } + } + } + + private void CheckInheritance( + SandboxConfig sandboxConfig, + List<(MType type, MType parent, ArraySegment interfaceImpls)> inherited, + ConcurrentBag errors) + { + // This inheritance whitelisting primarily serves to avoid content doing funny stuff + // by e.g. inheriting Type. + foreach ((MType _, MType baseType, ArraySegment interfaces) in inherited) + { + if (CanInherit(baseType) == false) + { + errors.Add(new SandboxError($"Inheriting of type not allowed: {baseType}")); + } + + foreach (MType @interface in interfaces) + { + if (CanInherit(@interface) == false) + { + errors.Add(new SandboxError($"Implementing of interface not allowed: {@interface}")); + } + } + + bool CanInherit(MType inheritType) + { + MTypeReferenced realBaseType = inheritType switch + { + MTypeGeneric generic => (MTypeReferenced)generic.GenericType, + MTypeReferenced referenced => referenced, + _ => throw new InvalidOperationException() // Can't happen. + }; + + if (IsTypeAccessAllowed(sandboxConfig, realBaseType, out TypeConfig? cfg) == false) + { + return false; + } + + return cfg.Inherit != InheritMode.Block && (cfg.Inherit == InheritMode.Allow || cfg.All); + } + } + } + + private bool IsTypeAccessAllowed(SandboxConfig sandboxConfig, MTypeReferenced type, + [NotNullWhen(true)] out TypeConfig? cfg) + { + if (type.Namespace == null) + { + if (type.ResolutionScope is MResScopeType parentType) + { + if (IsTypeAccessAllowed(sandboxConfig, (MTypeReferenced)parentType.Type, out TypeConfig? parentCfg) == false) + { + cfg = null; + return false; + } + + if (parentCfg.All) + { + // Enclosing type is namespace-whitelisted so we don't have to check anything else. + cfg = TypeConfig.DefaultAll; + return true; + } + + // Found enclosing type, checking if we are allowed to access this nested type. + // Also pass it up in case of multiple nested types. + if (parentCfg.NestedTypes != null && parentCfg.NestedTypes.TryGetValue(type.Name, out cfg)) + { + return true; + } + + cfg = null; + return false; + } + + if (type.ResolutionScope is MResScopeAssembly mResScopeAssembly && + sandboxConfig.MultiAssemblyOtherReferences.Contains(mResScopeAssembly.Name)) + { + cfg = TypeConfig.DefaultAll; + return true; + } + + // Types without namespaces or nesting parent are not allowed at all. + cfg = null; + return false; + } + + // Check if in whitelisted namespaces. + foreach (string whNamespace in sandboxConfig.WhitelistedNamespaces) + { + if (type.Namespace.StartsWith(whNamespace)) + { + cfg = TypeConfig.DefaultAll; + return true; + } + } + + if (type.ResolutionScope is MResScopeAssembly resScopeAssembly && + sandboxConfig.MultiAssemblyOtherReferences.Contains(resScopeAssembly.Name)) + { + cfg = TypeConfig.DefaultAll; + return true; + } + + + if (sandboxConfig.Types.TryGetValue(type.Namespace, out Dictionary? nsDict) == false) + { + cfg = null; + return false; + } + + return nsDict.TryGetValue(type.Name, out cfg); + } + + private List<(MType type, MType parent, ArraySegment interfaceImpls)> GetExternalInheritedTypes( + MetadataReader reader, + ConcurrentBag errors) + { + List<(MType, MType, ArraySegment)> list = new List<(MType, MType, ArraySegment)>(); + foreach (TypeDefinitionHandle typeDefHandle in reader.TypeDefinitions) + { + TypeDefinition typeDef = reader.GetTypeDefinition(typeDefHandle); + ArraySegment interfaceImpls; + MTypeDefined type = AssemblyTypeCheckerHelpers.GetTypeFromDefinition(reader, typeDefHandle); + + if (!AssemblyTypeCheckerHelpers.ParseInheritType(type, typeDef.BaseType, out MType? parent, reader, errors)) + { + continue; + } + + InterfaceImplementationHandleCollection interfaceImplsCollection = typeDef.GetInterfaceImplementations(); + if (interfaceImplsCollection.Count == 0) + { + interfaceImpls = Array.Empty(); + } + else + { + interfaceImpls = new MType[interfaceImplsCollection.Count]; + int i = 0; + foreach (InterfaceImplementationHandle implHandle in interfaceImplsCollection) + { + InterfaceImplementation interfaceImpl = reader.GetInterfaceImplementation(implHandle); + + if (AssemblyTypeCheckerHelpers.ParseInheritType(type, interfaceImpl.Interface, out MType? implemented, reader, errors)) + { + interfaceImpls[i++] = implemented; + } + } + + interfaceImpls = interfaceImpls[..i]; + } + + list.Add((type, parent, interfaceImpls)); + } + + return list; + } +} \ No newline at end of file diff --git a/UnitystationLauncher/Services/CodeScanConfigService.cs b/UnitystationLauncher/Services/CodeScanConfigService.cs new file mode 100644 index 00000000..190168af --- /dev/null +++ b/UnitystationLauncher/Services/CodeScanConfigService.cs @@ -0,0 +1,291 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.IO; +using System.IO.Compression; +using System.Net.Http; +using System.Reflection; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using Pidgin; +using Serilog; +using UnitystationLauncher.ContentScanning; +using UnitystationLauncher.Models.ConfigFile; +using UnitystationLauncher.Models.ContentScanning; +using UnitystationLauncher.Models.Enums; +using UnitystationLauncher.Services.Interface; + +namespace UnitystationLauncher.Services; + +public class CodeScanConfigService : ICodeScanConfigService +{ + private static string NameConfig = @"CodeScanList.json"; + + private readonly HttpClient _httpClient; + + private readonly IPreferencesService _preferencesService; + private readonly IEnvironmentService _environmentService; + + private const string GoodFileURL = "Https://unitystationfile.b-cdn.net/GoodFiles/"; + + public CodeScanConfigService(HttpClient httpClient, IPreferencesService preferencesService, IEnvironmentService environmentService) + { + _httpClient = httpClient; + _preferencesService = preferencesService; + _environmentService = environmentService; + + } + + public async Task<(string, bool)> GetGoodFileVersion(string version) + { + if (await ValidGoodFilesVersion(version) == false) + { + return ("", false); + } + + string pathBase = _preferencesService.GetPreferences().InstallationPath; + string folderName = GetFolderName(version); + string versionPath = Path.Combine(pathBase, version, folderName); + + if (Directory.Exists(versionPath) == false) + { + string ZIPExtractPath = Path.Combine(pathBase, version); + HttpResponseMessage request = await _httpClient.GetAsync(GoodFileURL + version + "/" + folderName + ".zip", HttpCompletionOption.ResponseHeadersRead); + await using Stream responseStream = await request.Content.ReadAsStreamAsync(); + ZipArchive archive = new(responseStream); + archive.ExtractToDirectory(ZIPExtractPath, true); + + string ZIPDirectory = Path.Combine(ZIPExtractPath, GetZipFolderName()); + Directory.Move(ZIPDirectory, versionPath); + } + + return (versionPath, true); + } + + + private string GetZipFolderName() + { + CurrentEnvironment OS = _environmentService.GetCurrentEnvironment(); + switch (OS) + { + case CurrentEnvironment.WindowsStandalone: + return "Windows"; + case CurrentEnvironment.LinuxFlatpak: + case CurrentEnvironment.LinuxStandalone: + return "Linux"; + case CurrentEnvironment.MacOsStandalone: + return "Mac"; + default: + throw new Exception($"Unable to determine OS Version {OS}"); + } + } + + private string GetFolderName(string version) + { + CurrentEnvironment OS = _environmentService.GetCurrentEnvironment(); + switch (OS) + { + case CurrentEnvironment.WindowsStandalone: + return version + "_Windows"; + case CurrentEnvironment.LinuxFlatpak: + case CurrentEnvironment.LinuxStandalone: + return version + "_Linux"; + case CurrentEnvironment.MacOsStandalone: + return version + "_Mac"; + default: + throw new Exception($"Unable to determine OS Version {OS}"); + } + } + + public async Task ValidGoodFilesVersion(string goodFileVersion) + { + string jsonData = ""; + try + { + HttpResponseMessage response = await _httpClient.GetAsync("https://unitystationfile.b-cdn.net/GoodFiles/AllowGoodFiles.json"); + if (!response.IsSuccessStatusCode) + { + Log.Error("Unable to download config" + response); + return false; + } + + jsonData = await response.Content.ReadAsStringAsync(); + } + catch (Exception e) + { + Log.Error("Unable to download ValidGoodFilesVersion config" + e); + return false; + } + + + HashSet? allowedList = JsonSerializer.Deserialize>(jsonData, options: new() + { + IgnoreReadOnlyProperties = true, + PropertyNameCaseInsensitive = true + }); + + if (allowedList == null) + { + return false; + } + + return allowedList.Contains(goodFileVersion); + } + + public string SanitiseStringPath(string inString) + { + return inString.Replace(@"\", "").Replace("/", "").Replace(".", "_"); + } + + private static bool TryDownloadVersion() + { + return false; + } + + public async Task LoadConfigAsync() + { + string configPath = Path.Combine(_environmentService.GetUserdataDirectory(), NameConfig); + try + { + HttpResponseMessage response = await _httpClient.GetAsync("https://raw.githubusercontent.com/unitystation/unitystation/develop/CodeScanList.json"); + if (response.IsSuccessStatusCode) + { + string jsonData = await response.Content.ReadAsStringAsync(); + File.Delete(configPath); + await File.WriteAllTextAsync(configPath, jsonData); + Console.WriteLine("JSON file saved successfully."); + } + else + { + Log.Error("Unable to download config" + response.ToString()); + } + } + catch (Exception e) + { + Log.Error("Unable to download config" + e.ToString()); + } + + + if (Exists(configPath) == false) + { + Assembly assembly = Assembly.GetExecutingAssembly(); + string resourceName = "UnitystationLauncher.CodeScanList.json"; + using (Stream? stream = assembly.GetManifestResourceStream(resourceName)) + { + if (stream != null) + { + // Copy the contents of the resource to a file location + using (FileStream fileStream = File.Create(configPath)) + { + stream.Seek(0L, SeekOrigin.Begin); + await stream.CopyToAsync(fileStream); + } + } + } + Log.Error("had to use backup config"); + } + + using (StreamReader file = OpenText(configPath)) + { + try + { + SandboxConfig? data = JsonSerializer.Deserialize(file.ReadToEnd(), new JsonSerializerOptions + { + AllowTrailingCommas = true, + Converters = + { + new JsonStringEnumConverter(allowIntegerValues: false) + } + }); + + if (data == null) + { + Log.Error("unable to de-serialise config"); + throw new DataException("unable to de-serialise config"); + } + + foreach (KeyValuePair> @namespace in data.Types) + { + foreach (KeyValuePair @class in @namespace.Value) + { + ParseTypeConfig(@class.Value); + } + } + + return data; + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } + } + } + + private static void ParseTypeConfig(TypeConfig cfg) + { + if (cfg.Methods != null) + { + List list = new List(); + foreach (string m in cfg.Methods) + { + try + { + list.Add(Parsers.MethodParser.ParseOrThrow(m)); + } + catch (ParseException e) + { + Log.Error($"Parse exception for '{m}': {e}"); + } + } + + cfg.MethodsParsed = list.ToArray(); + } + else + { + cfg.MethodsParsed = Array.Empty(); + } + + if (cfg.Fields != null) + { + List list = new List(); + foreach (string f in cfg.Fields) + { + try + { + list.Add(Parsers.FieldParser.ParseOrThrow(f)); + } + catch (ParseException e) + { + Log.Error($"Parse exception for '{f}': {e}"); + throw; + } + } + + cfg.FieldsParsed = list.ToArray(); + } + else + { + cfg.FieldsParsed = Array.Empty(); + } + + if (cfg.NestedTypes != null) + { + foreach (TypeConfig nested in cfg.NestedTypes.Values) + { + ParseTypeConfig(nested); + } + } + } + + public StreamReader OpenText(string path) + { + return File.OpenText(path); + } + + public bool Exists(string path) + { + return File.Exists(path); + } +} \ No newline at end of file diff --git a/UnitystationLauncher/Services/CodeScanService.cs b/UnitystationLauncher/Services/CodeScanService.cs new file mode 100644 index 00000000..51370ef8 --- /dev/null +++ b/UnitystationLauncher/Services/CodeScanService.cs @@ -0,0 +1,363 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Threading.Tasks; +using Serilog; +using UnitystationLauncher.Models.Enums; +using UnitystationLauncher.Services.Interface; + +namespace UnitystationLauncher.Services; + +public class CodeScanService : ICodeScanService +{ + private readonly IAssemblyTypeCheckerService _IAssemblyChecker; + private readonly IEnvironmentService _environmentService; + private readonly ICodeScanConfigService _iGoodFileService; + private readonly IPreferencesService _preferencesService; + + private const string Managed = "Managed"; + private const string Plugins = "Plugins"; + private const string Unitystation_Data = "Unitystation_Data"; + + public CodeScanService(IAssemblyTypeCheckerService assemblyChecker, IEnvironmentService environmentService, ICodeScanConfigService iGoodFileService, + IPreferencesService ipreferencesService) + { + _IAssemblyChecker = assemblyChecker; + _environmentService = environmentService; + _iGoodFileService = iGoodFileService; + _preferencesService = ipreferencesService; + } + + + + + + public async Task OnScan(ZipArchive archive, string targetDirectory, string goodFileVersion, Action info, Action errors) + { + // TODO: Enable extraction cancelling + DirectoryInfo root = new DirectoryInfo(_preferencesService.GetPreferences().InstallationPath); + + DirectoryInfo stagingDirectory = root.CreateSubdirectory("UnsafeBuildZipDirectory"); + DirectoryInfo processingDirectory = root.CreateSubdirectory("UnsafeBuildProcessing"); + DirectoryInfo? dataPath = null; + archive.ExtractToDirectory(stagingDirectory.ToString(), true); + try + { + DeleteContentsOfDirectory(processingDirectory); + info.Invoke("Copying files"); + CopyFilesRecursively(stagingDirectory.ToString(), processingDirectory.ToString()); + + info.Invoke("Cleaning out Dlls and Executables"); + DeleteFilesWithExtension(processingDirectory.ToString(), ".exe"); + DeleteFilesWithExtension(processingDirectory.ToString(), ".dll"); + DeleteFilesWithExtension(processingDirectory.ToString(), ".so"); + DeleteFilesWithExtension(processingDirectory.ToString(), ".dylib"); + DeleteFilesWithExtension(processingDirectory.ToString(), "", false); + + if (_environmentService.GetCurrentEnvironment() == CurrentEnvironment.MacOsStandalone) + { + DeleteFilesWithExtension(processingDirectory.ToString(), ".bundle", exceptionDirectory: Path.Combine(processingDirectory.ToString(), @"Unitystation.app/Contents/Resources/Data/StreamingAssets")); + } + + + + DirectoryInfo? stagingManaged = null; + if (_environmentService.GetCurrentEnvironment() != CurrentEnvironment.MacOsStandalone) + { + // Get all files in the directory + DirectoryInfo[] directories = processingDirectory.GetDirectories(); + // Loop through each file + foreach (DirectoryInfo directorie in directories) + { + if (directorie.Name.Contains("_Data")) + { + if (dataPath != null) + { + errors.Invoke("oh God 2 Datapaths Exiting!!!"); + return false; + } + + dataPath = directorie; + } + } + + if (dataPath == null) + { + errors.Invoke("oh God NO Datapath Exiting!!!"); + return false; + } + stagingManaged = stagingDirectory.CreateSubdirectory(Path.Combine(dataPath.Name, Managed)); + } + else + { + //MAC + dataPath = new DirectoryInfo(Path.Combine(processingDirectory.ToString(), @"Unitystation.app/Contents/Resources/Data")); + stagingManaged = stagingDirectory.CreateSubdirectory(Path.Combine(@"Unitystation.app/Contents/Resources/Data", Managed)); + } + + + + DirectoryInfo dllDirectory = dataPath.CreateSubdirectory(Managed); + + CopyFilesRecursively(stagingManaged.ToString(), dllDirectory.ToString()); + + (string goodFilePath, bool booly) = await _iGoodFileService.GetGoodFileVersion(goodFileVersion); + + if (booly == false) + { + DeleteContentsOfDirectory(processingDirectory); + DeleteContentsOfDirectory(stagingDirectory); + return false; + } + + DirectoryInfo goodFileCopy = new DirectoryInfo(GetManagedOnOS(goodFilePath)); + + info.Invoke("Proceeding to scan folder"); + if (ScanFolder(dllDirectory, goodFileCopy, info, errors) == false) + { + try + { + DeleteContentsOfDirectory(processingDirectory); + DeleteContentsOfDirectory(stagingDirectory); + } + catch (Exception e) + { + Log.Error(e.ToString()); + } + return false; + } + + + + CopyFilesRecursively(goodFilePath, processingDirectory.ToString()); + if (dataPath.Name != Unitystation_Data && _environmentService.GetCurrentEnvironment() != CurrentEnvironment.MacOsStandalone) //I know Cases and to file systems but F + { + string oldPath = Path.Combine(processingDirectory.ToString(), Unitystation_Data); + CopyFilesRecursively(oldPath, dataPath.ToString()); + Directory.Delete(oldPath, true); + } + + + switch (_environmentService.GetCurrentEnvironment()) + { + case CurrentEnvironment.WindowsStandalone: + FileInfo? exeRename = processingDirectory.GetFiles() + .FirstOrDefault(x => x.Extension == ".exe" && x.Name != "UnityCrashHandler64.exe"); //TODO OS + + + if (exeRename == null || exeRename.Directory == null) + { + errors.Invoke("no Executable found "); + DeleteContentsOfDirectory(processingDirectory); + DeleteContentsOfDirectory(stagingDirectory); + return false; + } + info.Invoke($"Found exeRename {exeRename}"); + exeRename.MoveTo(Path.Combine(exeRename.Directory.ToString(), dataPath.Name.Replace("_Data", "") + ".exe")); + break; + case CurrentEnvironment.LinuxFlatpak: + case CurrentEnvironment.LinuxStandalone: + FileInfo? ExecutableRename = processingDirectory.GetFiles() + .FirstOrDefault(x => x.Extension == ""); + + if (ExecutableRename == null || ExecutableRename.Directory == null) + { + errors.Invoke("no Executable found "); + DeleteContentsOfDirectory(processingDirectory); + DeleteContentsOfDirectory(stagingDirectory); + return false; + } + info.Invoke($"Found ExecutableRename {ExecutableRename}"); + ExecutableRename.MoveTo(Path.Combine(ExecutableRename.Directory.ToString(), dataPath.Name.Replace("_Data", "") + "")); + break; + } + + + DirectoryInfo targetDirectoryinfo = new DirectoryInfo(targetDirectory); + if (targetDirectoryinfo.Exists) + { + DeleteContentsOfDirectory(targetDirectoryinfo); + } + + CopyFilesRecursively(processingDirectory.ToString(), targetDirectory.ToString()); + DeleteContentsOfDirectory(processingDirectory); + DeleteContentsOfDirectory(stagingDirectory); + } + catch (Exception e) + { + errors.Invoke(" an Error happened > " + e); + DeleteContentsOfDirectory(processingDirectory); + DeleteContentsOfDirectory(stagingDirectory); + return false; + } + + return true; + } + + public string GetManagedOnOS(string GoodFiles) + { + CurrentEnvironment OS = _environmentService.GetCurrentEnvironment(); + switch (OS) + { + case CurrentEnvironment.WindowsStandalone: + return Path.Combine(GoodFiles, Unitystation_Data, Managed); + case CurrentEnvironment.LinuxFlatpak: + case CurrentEnvironment.LinuxStandalone: + return Path.Combine(GoodFiles, Unitystation_Data, Managed); + case CurrentEnvironment.MacOsStandalone: + return Path.Combine(GoodFiles, @"Unitystation.app/Contents/Resources/Data", Managed); + default: + throw new Exception($"Unable to determine OS Version {OS}"); + } + } + + + + public bool ScanFolder(DirectoryInfo @unsafe, DirectoryInfo saveFiles, Action info, Action errors) + { + List goodFiles = saveFiles.GetFiles().Select(x => x.Name).ToList(); + + info.Invoke("Provided files " + string.Join(",", goodFiles)); + + CopyFilesRecursively(saveFiles.ToString(), @unsafe.ToString()); + + FileInfo[] files = @unsafe.GetFiles(); + + + List multiAssemblyReference = new List(); + + foreach (FileInfo file in files) + { + if (goodFiles.Contains(file.Name)) continue; + multiAssemblyReference.Add(Path.GetFileNameWithoutExtension(file.Name)); + } + + + foreach (FileInfo file in files) + { + if (goodFiles.Contains(file.Name)) continue; + + info.Invoke("Scanning " + file.Name); + + try + { + List listy = multiAssemblyReference.ToList(); + listy.Remove(Path.GetFileNameWithoutExtension(file.Name)); + if (_IAssemblyChecker.CheckAssembly(file, @unsafe, listy, info, errors) == false) + { + errors.Invoke($"{file.Name} Failed scanning Cancelling"); + return false; + } + } + catch (Exception e) + { + errors.Invoke(" Failed scan due to error of " + e); + return false; + } + } + + + return true; + } + + + #region Utilities + + public static void DeleteContentsOfDirectory(DirectoryInfo directory) + { + // Delete files within the folder + FileInfo[] files = directory.GetFiles(); + foreach (FileInfo file in files) + { + file.Delete(); + } + + // Delete subdirectories within the folder + DirectoryInfo[] subdirectories = directory.GetDirectories(); + foreach (DirectoryInfo subdirectory in subdirectories) + { + subdirectory.Delete(true); + } + } + + static void DeleteFilesWithExtension(string directoryPath, string fileExtension, bool recursive = true, string? exceptionDirectory = null) + { + DirectoryInfo directory = new DirectoryInfo(directoryPath); + + // Check if the directory exists + if (!directory.Exists) + { + Console.WriteLine("Directory not found: " + directoryPath); + return; + } + + // Process all the files in the directory + FileInfo[] files = directory.GetFiles("*" + fileExtension); + foreach (FileInfo file in files) + { + if (exceptionDirectory != null) + { + if (file.Directory?.ToString().Contains(exceptionDirectory) == true) + { + continue; + } + } + // Delete the file + file.Delete(); + Console.WriteLine("Deleted file: " + file.FullName); + } + + if (recursive) + { + // Recursively process subdirectories + DirectoryInfo[] subdirectories = directory.GetDirectories(); + foreach (DirectoryInfo subdirectory in subdirectories) + { + DeleteFilesWithExtension(subdirectory.FullName, fileExtension, exceptionDirectory: exceptionDirectory); + } + } + } + + static void CopyFilesRecursively(string sourceDirectory, string destinationDirectory) + { + try + { + // Create the destination directory if it doesn't exist + Directory.CreateDirectory(destinationDirectory); + + DirectoryInfo source = new DirectoryInfo(sourceDirectory); + + // Get the files from the source directory + FileInfo[] files = source.GetFiles(); + + foreach (FileInfo file in files) + { + // Get the destination file path by combining the destination directory with the file name + string destinationFile = Path.Combine(destinationDirectory, file.Name); + + // Copy the file to the destination + file.CopyTo(destinationFile, true); + } + + // Get the directories from the source directory + DirectoryInfo[] directories = source.GetDirectories(); + + foreach (DirectoryInfo directory in directories) + { + // Get the destination directory path by combining the destination directory with the directory name + string destinationSubdirectory = Path.Combine(destinationDirectory, directory.Name); + + // Copy the files from the subdirectory recursively + CopyFilesRecursively(directory.FullName, destinationSubdirectory); + } + } + catch (Exception e) + { + Log.Error(e.ToString()); + } + } + + #endregion +} \ No newline at end of file diff --git a/UnitystationLauncher/Services/GameCommunicationPipeService.cs b/UnitystationLauncher/Services/GameCommunicationPipeService.cs new file mode 100644 index 00000000..80695161 --- /dev/null +++ b/UnitystationLauncher/Services/GameCommunicationPipeService.cs @@ -0,0 +1,16 @@ +using UnitystationLauncher.GameCommunicationPipe; + +namespace UnitystationLauncher.Services; + +public class GameCommunicationPipeService : IGameCommunicationPipeService +{ + + private PipeHubBuildCommunication? _coolPipeHubBuildCommunication; + + public void Init() + { + PipeHubBuildCommunication data = new(); + _ = data.StartServerPipe(); + _coolPipeHubBuildCommunication = data; + } +} \ No newline at end of file diff --git a/UnitystationLauncher/Services/InstallationService.cs b/UnitystationLauncher/Services/InstallationService.cs index debe7dcf..1895ddd9 100644 --- a/UnitystationLauncher/Services/InstallationService.cs +++ b/UnitystationLauncher/Services/InstallationService.cs @@ -30,16 +30,24 @@ public class InstallationService : IInstallationService private readonly IPreferencesService _preferencesService; private readonly IServerService _serverService; + private readonly ICodeScanService _codeScanService; + private readonly ICodeScanConfigService _iGoodFileService; + private readonly List _downloads; private List _installations = new(); private readonly string _installationsJsonFilePath; - public InstallationService(HttpClient httpClient, IPreferencesService preferencesService, IEnvironmentService environmentService, IServerService serverService) + public InstallationService(HttpClient httpClient, IPreferencesService preferencesService, + IEnvironmentService environmentService, IServerService serverService, ICodeScanService codeScanService, + ICodeScanConfigService iGoodFileService) { _httpClient = httpClient; _preferencesService = preferencesService; _environmentService = environmentService; _serverService = serverService; + _codeScanService = codeScanService; + _iGoodFileService = iGoodFileService; + _downloads = new(); _installationsJsonFilePath = Path.Combine(_environmentService.GetUserdataDirectory(), "installations.json"); @@ -70,16 +78,28 @@ public List GetInstallations() && d.BuildVersion == buildVersion); } - public (Download?, string) DownloadInstallation(Server server) + public async Task<(Download?, string)> DownloadInstallation(Server server) { string? downloadUrl = server.GetDownloadUrl(_environmentService); if (string.IsNullOrWhiteSpace(downloadUrl)) { const string failureReason = "Empty or missing download url for server."; Log.Warning(failureReason + $" ServerName: {server.ServerName}"); - return (null, failureReason); + return (null!, failureReason); + } + + server.ServerGoodFileVersion = "1.0.0"; //TODO + + bool result = await _iGoodFileService.ValidGoodFilesVersion(server.ServerGoodFileVersion); + + if (result == false) + { + const string failureReason = "server does not have a valid ServerGoodFileVersion "; + Log.Warning(failureReason + $" ServerName: {server.ServerName} ServerGoodFileVersion : {server.ServerGoodFileVersion}"); + return (null!, failureReason); } + Download? download = GetInProgressDownload(server.ForkName, server.BuildVersion); if (download != null) @@ -90,11 +110,11 @@ public List GetInstallations() string installationBasePath = _preferencesService.GetPreferences().InstallationPath; // should be something like {basePath}/{forkName}/{version} - string installationPath = Path.Combine(Path.Combine(installationBasePath, server.ForkName), server.BuildVersion.ToString()); + string installationPath = Path.Combine(installationBasePath, _iGoodFileService.SanitiseStringPath(server.ForkName), _iGoodFileService.SanitiseStringPath(server.ServerGoodFileVersion), server.BuildVersion.ToString()); - download = new(downloadUrl, installationPath, server.ForkName, server.BuildVersion); + download = new(downloadUrl, installationPath, server.ForkName, server.BuildVersion, server.ServerGoodFileVersion); - (bool canStartDownload, string cantDownloadReason) = CanStartDownload(download); + (bool canStartDownload, string cantDownloadReason) = InstallationService.CanStartDownload(download); if (!canStartDownload) { @@ -114,6 +134,9 @@ public List GetInstallations() return (download, string.Empty); } + + + public (bool, string) StartInstallation(Guid installationId, string? server = null, short? port = null) { Installation? installation = GetInstallationById(installationId); @@ -134,7 +157,7 @@ public List GetInstallations() EnsureExecutableFlagOnUnixSystems(executable); - string arguments = GetArguments(server, port); + string arguments = InstallationService.GetArguments(server, port); ProcessStartInfo? startInfo = _environmentService.GetGameProcessStartInfo(executable, arguments); if (startInfo == null) @@ -246,7 +269,7 @@ public bool MoveInstallations(string newBasePath) continue; } - CreateParentDirectory(newPath); + InstallationService.CreateParentDirectory(newPath); if (Directory.Exists(newPath)) { @@ -344,7 +367,7 @@ private void WriteInstallations() Log.Debug("Installations JSON written"); } - private void CreateParentDirectory(string path) + private static void CreateParentDirectory(string path) { DirectoryInfo directoryInfo = new(path); string? parentDir = directoryInfo.Parent?.FullName; @@ -360,7 +383,7 @@ private void CreateParentDirectory(string path) return _installations.FirstOrDefault(i => i.InstallationId == installationId); } - private (bool, string) CanStartDownload(Download download) + private static (bool, string) CanStartDownload(Download download) { if (Directory.Exists(download.InstallPath)) { @@ -379,7 +402,7 @@ private void CreateParentDirectory(string path) return (true, string.Empty); } - private string GetArguments(string? server, long? port) + private static string GetArguments(string? server, long? port) { string arguments = string.Empty; @@ -396,12 +419,13 @@ private string GetArguments(string? server, long? port) return arguments; } + public static List InfoList = new List(); + public static List ErrorList = new List(); private async Task StartDownloadAsync(Download download) { Log.Information("Download requested, Installation Path '{Path}', Url '{Url}'", download.InstallPath, download.DownloadUrl); try { - Directory.CreateDirectory(download.InstallPath); Log.Information("Download started..."); HttpResponseMessage request = await _httpClient.GetAsync(download.DownloadUrl, HttpCompletionOption.ResponseHeadersRead); await using Stream responseStream = await request.Content.ReadAsStreamAsync(); @@ -410,7 +434,7 @@ private async Task StartDownloadAsync(Download download) download.Size = request.Content.Headers.ContentLength ?? throw new ContentLengthNullException(download.DownloadUrl); - using IDisposable logProgressDisposable = LogProgress(progressStream, download); + using IDisposable logProgressDisposable = InstallationService.LogProgress(progressStream, download); using IDisposable progressDisposable = progressStream.Progress .Subscribe(p => { download.Downloaded = p; }); @@ -422,26 +446,52 @@ await Task.Run(() => { ZipArchive archive = new(progressStream); - // TODO: Enable extraction cancelling - archive.ExtractToDirectory(download.InstallPath, true); - - Log.Information("Download completed"); - _installations.Add(new() + //TODO UI + Action info = new Action((string log) => { - BuildVersion = download.BuildVersion, - ForkName = download.ForkName, - InstallationId = Guid.NewGuid(), - InstallationPath = download.InstallPath, - LastPlayedDate = DateTime.Now + Console.WriteLine($"info {log}"); + InfoList.Add(log); }); + Action errors = new Action((string log) => + { + Console.WriteLine($"error {log}"); + ErrorList.Add(log); + }); + Task scanTask = _codeScanService.OnScan(archive, download.InstallPath, download.GoodFileVersion, + info, errors); + scanTask.Wait(); + if (scanTask.Result) + { + Log.Information("Download completed"); + + _installations.Add(new() + { + BuildVersion = download.BuildVersion, + ForkName = download.ForkName, + InstallationId = Guid.NewGuid(), + InstallationPath = download.InstallPath, + LastPlayedDate = DateTime.Now + }); + + WriteInstallations(); + EnsureExecutableFlagOnUnixSystems(download.InstallPath); + } + else + { + string jsonString = System.Text.Json.JsonSerializer.Serialize(ErrorList); + + string filePath = Path.Combine(_preferencesService.GetPreferences().InstallationPath, "CodeScanErrors.json"); - WriteInstallations(); - EnsureExecutableFlagOnUnixSystems(download.InstallPath); + File.WriteAllText(filePath, jsonString); + + //TODO UI + Log.Information($"Scan Failed saved to filePath {filePath}"); + } } - catch + catch (Exception e) { - Log.Information("Extracting stopped"); + Log.Information($"Extracting stopped with {e.ToString()}"); } }); } @@ -456,7 +506,7 @@ await Task.Run(() => } } - private IDisposable LogProgress(ProgressStream progressStream, Download download) + private static IDisposable LogProgress(ProgressStream progressStream, Download download) { long lastPosition = 0L; DateTime lastTime = DateTime.Now; diff --git a/UnitystationLauncher/Services/Interface/IAssemblyTypeCheckerService.cs b/UnitystationLauncher/Services/Interface/IAssemblyTypeCheckerService.cs new file mode 100644 index 00000000..51536a44 --- /dev/null +++ b/UnitystationLauncher/Services/Interface/IAssemblyTypeCheckerService.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace UnitystationLauncher.Services.Interface; + +public interface IAssemblyTypeCheckerService +{ + public bool CheckAssembly(FileInfo diskPath, DirectoryInfo managedPath, List otherAssemblies, Action info, Action errors); +} \ No newline at end of file diff --git a/UnitystationLauncher/Services/Interface/ICodeScanConfigService.cs b/UnitystationLauncher/Services/Interface/ICodeScanConfigService.cs new file mode 100644 index 00000000..7e4f24be --- /dev/null +++ b/UnitystationLauncher/Services/Interface/ICodeScanConfigService.cs @@ -0,0 +1,15 @@ +using System.Threading.Tasks; +using UnitystationLauncher.Models.ConfigFile; + +namespace UnitystationLauncher.Services.Interface; + +public interface ICodeScanConfigService +{ + public Task<(string, bool)> GetGoodFileVersion(string version); + + public Task ValidGoodFilesVersion(string goodFileVersion); + + public string SanitiseStringPath(string inString); + + public Task LoadConfigAsync(); +} \ No newline at end of file diff --git a/UnitystationLauncher/Services/Interface/ICodeScanService.cs b/UnitystationLauncher/Services/Interface/ICodeScanService.cs new file mode 100644 index 00000000..9ad44d80 --- /dev/null +++ b/UnitystationLauncher/Services/Interface/ICodeScanService.cs @@ -0,0 +1,10 @@ +using System; +using System.IO.Compression; +using System.Threading.Tasks; + +namespace UnitystationLauncher.Services.Interface; + +public interface ICodeScanService +{ + public Task OnScan(ZipArchive archive, string targetDirectory, string goodFileVersion, Action info, Action errors); +} \ No newline at end of file diff --git a/UnitystationLauncher/Services/Interface/IGameCommunicationPipeService.cs b/UnitystationLauncher/Services/Interface/IGameCommunicationPipeService.cs new file mode 100644 index 00000000..4f45e365 --- /dev/null +++ b/UnitystationLauncher/Services/Interface/IGameCommunicationPipeService.cs @@ -0,0 +1,4 @@ +public interface IGameCommunicationPipeService +{ + public void Init(); +} \ No newline at end of file diff --git a/UnitystationLauncher/Services/Interface/IInstallationService.cs b/UnitystationLauncher/Services/Interface/IInstallationService.cs index ccec79b1..d5d53c7d 100644 --- a/UnitystationLauncher/Services/Interface/IInstallationService.cs +++ b/UnitystationLauncher/Services/Interface/IInstallationService.cs @@ -38,7 +38,7 @@ public interface IInstallationService /// /// The server to get the download from /// Download will be `null` if it was unsuccessful in starting, and the string will have the reason - public (Download?, string) DownloadInstallation(Server server); + public Task<(Download?, string)> DownloadInstallation(Server server); /// /// Starts an installation so we can finally just play the game. diff --git a/UnitystationLauncher/StandardModule.cs b/UnitystationLauncher/StandardModule.cs index b8c4f91e..0d311d60 100644 --- a/UnitystationLauncher/StandardModule.cs +++ b/UnitystationLauncher/StandardModule.cs @@ -19,6 +19,10 @@ protected override void Load(ContainerBuilder builder) builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); // View Models builder.RegisterAssemblyTypes(ThisAssembly) diff --git a/UnitystationLauncher/UnitystationLauncher.csproj b/UnitystationLauncher/UnitystationLauncher.csproj index ba801f1c..8d142c2a 100644 --- a/UnitystationLauncher/UnitystationLauncher.csproj +++ b/UnitystationLauncher/UnitystationLauncher.csproj @@ -1,7 +1,7 @@ WinExe - net6.0 + net7.0 latest enable enable @@ -46,6 +46,12 @@ + + PopUpDialogue.xaml + + + PopUpDialogue.xaml + @@ -54,8 +60,10 @@ + + @@ -91,4 +99,12 @@ + + + + Always + InBuiltData + InBuiltData + + diff --git a/UnitystationLauncher/ViewModels/LauncherViewModel.cs b/UnitystationLauncher/ViewModels/LauncherViewModel.cs index f2d749be..bb0d9b52 100644 --- a/UnitystationLauncher/ViewModels/LauncherViewModel.cs +++ b/UnitystationLauncher/ViewModels/LauncherViewModel.cs @@ -48,25 +48,27 @@ public LauncherViewModel( PreferencesPanelViewModel preferencesPanel, IHubService hubService, IPreferencesService preferencesService, - IEnvironmentService environmentService) + IEnvironmentService environmentService, + IGameCommunicationPipeService gameCommunicationPipeService) { _hubUpdateVm = hubUpdateVm; _hubService = hubService; _preferencesService = preferencesService; _environmentService = environmentService; + gameCommunicationPipeService.Init(); OpenMainSite = ReactiveCommand.Create(() => OpenLink(LinkUrls.MainSiteUrl)); OpenPatreon = ReactiveCommand.Create(() => OpenLink(LinkUrls.PatreonUrl)); OpenDiscordInvite = ReactiveCommand.Create(() => OpenLink(LinkUrls.DiscordInviteUrl)); - _panels = GetEnabledPanels(newsPanel, serversPanel, installationsPanel, preferencesPanel); + _panels = LauncherViewModel.GetEnabledPanels(newsPanel, serversPanel, installationsPanel, preferencesPanel); ShowUpdateView = ReactiveCommand.Create(ShowUpdateImp); SelectedPanel = serversPanel; RxApp.MainThreadScheduler.ScheduleAsync((_, _) => ValidateClientVersionAsync()); } - private PanelBase[] GetEnabledPanels( + private static PanelBase[] GetEnabledPanels( NewsPanelViewModel newsPanel, ServersPanelViewModel serversPanel, InstallationsPanelViewModel installationsPanel, diff --git a/UnitystationLauncher/ViewModels/ServersPanelViewModel.cs b/UnitystationLauncher/ViewModels/ServersPanelViewModel.cs index 2e63403f..454dad0f 100644 --- a/UnitystationLauncher/ViewModels/ServersPanelViewModel.cs +++ b/UnitystationLauncher/ViewModels/ServersPanelViewModel.cs @@ -32,7 +32,8 @@ public class ServersPanelViewModel : PanelBase private readonly IPingService _pingService; private readonly IServerService _serverService; - public ServersPanelViewModel(IInstallationService installationService, IPingService pingService, IServerService serverService) + public ServersPanelViewModel(IInstallationService installationService, IPingService pingService, + IServerService serverService) { _installationService = installationService; _pingService = pingService; @@ -40,7 +41,7 @@ public ServersPanelViewModel(IInstallationService installationService, IPingServ DownloadCommand = ReactiveCommand.Create(server => { - DownloadServer(server.Server); + _ = DownloadServer(server.Server); return Unit.Default; }); @@ -55,10 +56,8 @@ private void InitializeServersList() Log.Information("Scheduling periodic refresh for servers list..."); // Why can you not just run async methods with this?? Instead we have to do this ugly thing - RxApp.TaskpoolScheduler.SchedulePeriodic(_refreshInterval, () => - { - RxApp.MainThreadScheduler.ScheduleAsync((_, _) => RefreshServersList()); - }); + RxApp.TaskpoolScheduler.SchedulePeriodic(_refreshInterval, + () => { RxApp.MainThreadScheduler.ScheduleAsync((_, _) => RefreshServersList()); }); } private async Task RefreshServersList() @@ -116,13 +115,13 @@ private void RemoveDeletedServers(List servers) } } - private void DownloadServer(Server server) + private async Task DownloadServer(Server server) { - (Download? download, string downloadFailReason) = _installationService.DownloadInstallation(server); + (Download? download, string downloadFailReason) = await _installationService.DownloadInstallation(server); if (download == null) { - MessageBoxBuilder.CreateMessageBox(MessageBoxButtons.Ok, "Problem downloading server", + _ = MessageBoxBuilder.CreateMessageBox(MessageBoxButtons.Ok, "Problem downloading server", downloadFailReason).Show(); return; } @@ -148,4 +147,4 @@ public override void Refresh() viewModel.Refresh(); } } -} +} \ No newline at end of file diff --git a/UnitystationLauncher/Views/MainWindow.xaml.cs b/UnitystationLauncher/Views/MainWindow.xaml.cs index e7dad331..6734ebb9 100644 --- a/UnitystationLauncher/Views/MainWindow.xaml.cs +++ b/UnitystationLauncher/Views/MainWindow.xaml.cs @@ -20,6 +20,7 @@ public class MainWindow : Window public MainWindow() { + InitializeComponent(); #if DEBUG this.AttachDevTools(); diff --git a/UnitystationLauncher/Views/PopUpDialogue.cs b/UnitystationLauncher/Views/PopUpDialogue.cs new file mode 100644 index 00000000..9dd0f245 --- /dev/null +++ b/UnitystationLauncher/Views/PopUpDialogue.cs @@ -0,0 +1,39 @@ +using System; +using System.Windows.Input; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.Markup.Xaml; +using MessageBox.Avalonia.ViewModels.Commands; + +namespace UnitystationLauncher.Views +{ + public class PopUpDialogue : Window + { + public event EventHandler? DialogResult; + + + public PopUpDialogue() + { + + InitializeComponent(); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + + public void AYesCommand(object? sender, RoutedEventArgs routedEventArgs) + { + DialogResult?.Invoke(this, true); + Close(); + } + + public void ANoCommand(object? sender, RoutedEventArgs routedEventArgs) + { + DialogResult?.Invoke(this, false); + Close(); + } + } +} diff --git a/UnitystationLauncher/Views/PopUpDialogue.xaml b/UnitystationLauncher/Views/PopUpDialogue.xaml new file mode 100644 index 00000000..001790ff --- /dev/null +++ b/UnitystationLauncher/Views/PopUpDialogue.xaml @@ -0,0 +1,17 @@ + + + + + +