From 5284534fa2c851672b0a8a61e3fd256ab036d4e9 Mon Sep 17 00:00:00 2001 From: Gregorius Soedharmo Date: Tue, 17 Aug 2021 02:04:30 +0700 Subject: [PATCH] Cleanup type blacklist PR (#254) * Move disallow unsafe type flag to settings * Code cleanup, port missing codes for netstandard 1.6 Co-authored-by: Aaron Stannard --- .../CoreApiSpec.ApproveApi.approved.txt | 3 + src/Hyperion/Extensions/TypeEx.cs | 99 +++++++++++-------- src/Hyperion/SerializerOptions.cs | 30 +++++- .../ValueSerializers/TypeSerializer.cs | 2 +- 4 files changed, 85 insertions(+), 49 deletions(-) diff --git a/src/Hyperion.API.Tests/CoreApiSpec.ApproveApi.approved.txt b/src/Hyperion.API.Tests/CoreApiSpec.ApproveApi.approved.txt index 7204f3de..a55d7c8f 100644 --- a/src/Hyperion.API.Tests/CoreApiSpec.ApproveApi.approved.txt +++ b/src/Hyperion.API.Tests/CoreApiSpec.ApproveApi.approved.txt @@ -91,7 +91,10 @@ namespace Hyperion public static readonly Hyperion.SerializerOptions Default; [System.Obsolete] public SerializerOptions(bool versionTolerance = false, bool preserveObjectReferences = false, System.Collections.Generic.IEnumerable surrogates = null, System.Collections.Generic.IEnumerable serializerFactories = null, System.Collections.Generic.IEnumerable knownTypes = null, bool ignoreISerializable = false) { } + [System.Obsolete] public SerializerOptions(bool versionTolerance, bool preserveObjectReferences, System.Collections.Generic.IEnumerable surrogates, System.Collections.Generic.IEnumerable serializerFactories, System.Collections.Generic.IEnumerable knownTypes, bool ignoreISerializable, System.Collections.Generic.IEnumerable> packageNameOverrides) { } + public SerializerOptions(bool versionTolerance, bool preserveObjectReferences, System.Collections.Generic.IEnumerable surrogates, System.Collections.Generic.IEnumerable serializerFactories, System.Collections.Generic.IEnumerable knownTypes, bool ignoreISerializable, System.Collections.Generic.IEnumerable> packageNameOverrides, bool disallowUnsafeTypes) { } + public Hyperion.SerializerOptions WithDisallowUnsafeType(bool disallowUnsafeType) { } public Hyperion.SerializerOptions WithIgnoreSerializable(bool ignoreISerializable) { } public Hyperion.SerializerOptions WithKnownTypes(System.Collections.Generic.IEnumerable knownTypes) { } public Hyperion.SerializerOptions WithPackageNameOverrides(System.Collections.Generic.IEnumerable> packageNameOverrides) { } diff --git a/src/Hyperion/Extensions/TypeEx.cs b/src/Hyperion/Extensions/TypeEx.cs index 49ab44a5..491b4851 100644 --- a/src/Hyperion/Extensions/TypeEx.cs +++ b/src/Hyperion/Extensions/TypeEx.cs @@ -46,6 +46,31 @@ internal static class TypeEx public static readonly Type TypeType = typeof(Type); public static readonly Type RuntimeType = Type.GetType("System.RuntimeType"); + private static readonly ReadOnlyCollection UnsafeTypesDenySet = + new ReadOnlyCollection(new[] + { + "System.Security.Claims.ClaimsIdentity", + "System.Windows.Forms.AxHost.State", + "System.Windows.Data.ObjectDataProvider", + "System.Management.Automation.PSObject", + "System.Web.Security.RolePrincipal", + "System.IdentityModel.Tokens.SessionSecurityToken", + "SessionViewStateHistoryItem", + "TextFormattingRunProperties", + "ToolboxItemContainer", + "System.Security.Principal.WindowsClaimsIdentity", + "System.Security.Principal.WindowsIdentity", + "System.Security.Principal.WindowsPrincipal", + "System.CodeDom.Compiler.TempFileCollection", + "System.IO.FileSystemInfo", + "System.Activities.Presentation.WorkflowDesigner", + "System.Windows.ResourceDictionary", + "System.Windows.Forms.BindingSource", + "Microsoft.Exchange.Management.SystemManager.WinForms.ExchangeSettingsProvider", + "System.Diagnostics.Process", + "System.Management.IWbemClassObjectFreeThreaded" + }); + public static bool IsHyperionPrimitive(this Type type) { return type == Int32Type || @@ -69,8 +94,15 @@ public static bool IsHyperionPrimitive(this Type type) } #if NETSTANDARD16 - //HACK: the GetUnitializedObject actually exists in .NET Core, its just not public - private static readonly Func getUninitializedObjectDelegate = (Func) + //HACK: IsValueType does not exist for netstandard1.6 + private static bool IsValueType(this Type type) + => type.IsSubclassOf(typeof(ValueType)); + + private static bool IsSubclassOf(this Type p, Type c) + => c.IsAssignableFrom(p); + + //HACK: the GetUnitializedObject actually exists in .NET Core, its just not public + private static readonly Func GetUninitializedObjectDelegate = (Func) typeof(string) .GetTypeInfo() .Assembly @@ -81,7 +113,7 @@ public static bool IsHyperionPrimitive(this Type type) public static object GetEmptyObject(this Type type) { - return getUninitializedObjectDelegate(type); + return GetUninitializedObjectDelegate(type); } #else public static object GetEmptyObject(this Type type) => System.Runtime.Serialization.FormatterServices.GetUninitializedObject(type); @@ -130,57 +162,39 @@ private static Type GetTypeFromManifestName(Stream stream, DeserializerSession s break; } - return LoadTypeByName(shortName); + return LoadTypeByName(shortName, session.Serializer.Options.DisallowUnsafeTypes); }); } - public static bool disallowUnsafeTypes = true; - - private static ReadOnlyCollection unsafeTypesDenySet = - new ReadOnlyCollection(new[] - { - "System.Security.Claims.ClaimsIdentity", - "System.Windows.Forms.AxHost.State", - "System.Windows.Data.ObjectDataProvider", - "System.Management.Automation.PSObject", - "System.Web.Security.RolePrincipal", - "System.IdentityModel.Tokens.SessionSecurityToken", - "SessionViewStateHistoryItem", - "TextFormattingRunProperties", - "ToolboxItemContainer", - "System.Security.Principal.WindowsClaimsIdentity", - "System.Security.Principal.WindowsIdentity", - "System.Security.Principal.WindowsPrincipal", - "System.CodeDom.Compiler.TempFileCollection", - "System.IO.FileSystemInfo", - "System.Activities.Presentation.WorkflowDesigner", - "System.Windows.ResourceDictionary", - "System.Windows.Forms.BindingSource", - "Microsoft.Exchange.Management.SystemManager.WinForms.ExchangeSettingsProvider", - "System.Diagnostics.Process", - "System.Management.IWbemClassObjectFreeThreaded" - }); - -#if !NETSTANDARD1_6 - public static bool UnsafeInheritanceCheck(Type type) + private static bool UnsafeInheritanceCheck(Type type) { +#if NETSTANDARD1_6 + if (type.IsValueType()) + return false; + var currentBase = type.DeclaringType; +#else if (type.IsValueType) return false; var currentBase = type.BaseType; +#endif + while (currentBase != null) { - if (unsafeTypesDenySet.Any(r => currentBase.FullName?.Contains(r) ?? false)) + if (UnsafeTypesDenySet.Any(r => currentBase.FullName?.Contains(r) ?? false)) return true; +#if NETSTANDARD1_6 + currentBase = currentBase.DeclaringType; +#else currentBase = currentBase.BaseType; +#endif } return false; } -#endif - public static Type LoadTypeByName(string name) + public static Type LoadTypeByName(string name, bool disallowUnsafeTypes) { - if (disallowUnsafeTypes && unsafeTypesDenySet.Any(r => name.Contains(r))) + if (disallowUnsafeTypes && UnsafeTypesDenySet.Any(name.Contains)) { throw new EvilDeserializationException( "Unsafe Type Deserialization Detected!", name); @@ -191,24 +205,18 @@ public static Type LoadTypeByName(string name) // i.e. if there are different version available in GAC and locally var typename = ToQualifiedAssemblyName(name, ignoreAssemblyVersion: false); var type = Type.GetType(typename, true); -#if NETSTANDARD1_6 - #else if (UnsafeInheritanceCheck(type)) throw new EvilDeserializationException( "Unsafe Type Deserialization Detected!", name); -#endif return type; } catch (FileLoadException) { var typename = ToQualifiedAssemblyName(name, ignoreAssemblyVersion: true); var type = Type.GetType(typename, true); -#if NETSTANDARD1_6 -#else if (UnsafeInheritanceCheck(type)) throw new EvilDeserializationException( "Unsafe Type Deserialization Detected!", name); -#endif return type; } } @@ -398,6 +406,11 @@ public class T /// private static bool IsSimilarType(this Type thisType, Type type) { + if (thisType == null && type == null) + return true; + if (thisType == null || type == null) + return false; + // Ignore any 'ref' types if (thisType.IsByRef) thisType = thisType.GetElementType(); diff --git a/src/Hyperion/SerializerOptions.cs b/src/Hyperion/SerializerOptions.cs index 13532b63..59d24754 100644 --- a/src/Hyperion/SerializerOptions.cs +++ b/src/Hyperion/SerializerOptions.cs @@ -23,7 +23,8 @@ public class SerializerOptions serializerFactories: null, knownTypes: null, ignoreISerializable: false, - packageNameOverrides: null); + packageNameOverrides: null, + disallowUnsafeTypes: true); internal static List> DefaultPackageNameOverrides() { @@ -77,8 +78,8 @@ internal static List> DefaultPackageNameOverrides() internal readonly bool VersionTolerance; internal readonly Type[] KnownTypes; internal readonly Dictionary KnownTypesDict = new Dictionary(); - internal readonly List> CrossFrameworkPackageNameOverrides = - DefaultPackageNameOverrides(); + internal readonly List> CrossFrameworkPackageNameOverrides = DefaultPackageNameOverrides(); + internal readonly bool DisallowUnsafeTypes; [Obsolete] public SerializerOptions( @@ -91,6 +92,7 @@ public SerializerOptions( : this(versionTolerance, preserveObjectReferences, surrogates, serializerFactories, knownTypes, ignoreISerializable, null) { } + [Obsolete] public SerializerOptions( bool versionTolerance, bool preserveObjectReferences, @@ -99,6 +101,18 @@ public SerializerOptions( IEnumerable knownTypes, bool ignoreISerializable, IEnumerable> packageNameOverrides) + : this(versionTolerance, preserveObjectReferences, surrogates, serializerFactories, knownTypes, ignoreISerializable, null, true) + { } + + public SerializerOptions( + bool versionTolerance, + bool preserveObjectReferences, + IEnumerable surrogates, + IEnumerable serializerFactories, + IEnumerable knownTypes, + bool ignoreISerializable, + IEnumerable> packageNameOverrides, + bool disallowUnsafeTypes) { VersionTolerance = versionTolerance; Surrogates = surrogates?.ToArray() ?? EmptySurrogates; @@ -119,6 +133,8 @@ public SerializerOptions( if(packageNameOverrides != null) CrossFrameworkPackageNameOverrides.AddRange(packageNameOverrides); + + DisallowUnsafeTypes = disallowUnsafeTypes; } public SerializerOptions WithVersionTolerance(bool versionTolerance) @@ -135,6 +151,8 @@ public SerializerOptions WithIgnoreSerializable(bool ignoreISerializable) => Copy(ignoreISerializable: ignoreISerializable); public SerializerOptions WithPackageNameOverrides(IEnumerable> packageNameOverrides) => Copy(packageNameOverrides: packageNameOverrides); + public SerializerOptions WithDisallowUnsafeType(bool disallowUnsafeType) + => Copy(disallowUnsafeType: disallowUnsafeType); private SerializerOptions Copy( bool? versionTolerance = null, @@ -143,7 +161,8 @@ private SerializerOptions Copy( IEnumerable serializerFactories = null, IEnumerable knownTypes = null, bool? ignoreISerializable = null, - IEnumerable> packageNameOverrides = null) + IEnumerable> packageNameOverrides = null, + bool? disallowUnsafeType = null) => new SerializerOptions( versionTolerance ?? VersionTolerance, preserveObjectReferences ?? PreserveObjectReferences, @@ -151,7 +170,8 @@ private SerializerOptions Copy( serializerFactories ?? ValueSerializerFactories, knownTypes ?? KnownTypes, ignoreISerializable ?? IgnoreISerializable, - packageNameOverrides ?? CrossFrameworkPackageNameOverrides + packageNameOverrides ?? CrossFrameworkPackageNameOverrides, + disallowUnsafeType ?? DisallowUnsafeTypes ); } } \ No newline at end of file diff --git a/src/Hyperion/ValueSerializers/TypeSerializer.cs b/src/Hyperion/ValueSerializers/TypeSerializer.cs index 33a442c0..633d2cc7 100644 --- a/src/Hyperion/ValueSerializers/TypeSerializer.cs +++ b/src/Hyperion/ValueSerializers/TypeSerializer.cs @@ -70,7 +70,7 @@ public override object ReadValue(Stream stream, DeserializerSession session) return null; var type = TypeNameLookup.GetOrAdd(shortname, - name => TypeEx.LoadTypeByName(shortname)); + name => TypeEx.LoadTypeByName(shortname, session.Serializer.Options.DisallowUnsafeTypes)); //add the deserialized type to lookup if (session.Serializer.Options.PreserveObjectReferences)