Skip to content

Commit

Permalink
Cleanup type blacklist PR (#254)
Browse files Browse the repository at this point in the history
* Move disallow unsafe type flag to settings

* Code cleanup, port missing codes for netstandard 1.6

Co-authored-by: Aaron Stannard <aaron@petabridge.com>
  • Loading branch information
Arkatufus and Aaronontheweb authored Aug 16, 2021
1 parent 03d7826 commit 5284534
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 49 deletions.
3 changes: 3 additions & 0 deletions src/Hyperion.API.Tests/CoreApiSpec.ApproveApi.approved.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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<Hyperion.Surrogate> surrogates = null, System.Collections.Generic.IEnumerable<Hyperion.SerializerFactories.ValueSerializerFactory> serializerFactories = null, System.Collections.Generic.IEnumerable<System.Type> knownTypes = null, bool ignoreISerializable = false) { }
[System.Obsolete]
public SerializerOptions(bool versionTolerance, bool preserveObjectReferences, System.Collections.Generic.IEnumerable<Hyperion.Surrogate> surrogates, System.Collections.Generic.IEnumerable<Hyperion.SerializerFactories.ValueSerializerFactory> serializerFactories, System.Collections.Generic.IEnumerable<System.Type> knownTypes, bool ignoreISerializable, System.Collections.Generic.IEnumerable<System.Func<string, string>> packageNameOverrides) { }
public SerializerOptions(bool versionTolerance, bool preserveObjectReferences, System.Collections.Generic.IEnumerable<Hyperion.Surrogate> surrogates, System.Collections.Generic.IEnumerable<Hyperion.SerializerFactories.ValueSerializerFactory> serializerFactories, System.Collections.Generic.IEnumerable<System.Type> knownTypes, bool ignoreISerializable, System.Collections.Generic.IEnumerable<System.Func<string, string>> packageNameOverrides, bool disallowUnsafeTypes) { }
public Hyperion.SerializerOptions WithDisallowUnsafeType(bool disallowUnsafeType) { }
public Hyperion.SerializerOptions WithIgnoreSerializable(bool ignoreISerializable) { }
public Hyperion.SerializerOptions WithKnownTypes(System.Collections.Generic.IEnumerable<System.Type> knownTypes) { }
public Hyperion.SerializerOptions WithPackageNameOverrides(System.Collections.Generic.IEnumerable<System.Func<string, string>> packageNameOverrides) { }
Expand Down
99 changes: 56 additions & 43 deletions src/Hyperion/Extensions/TypeEx.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<string> UnsafeTypesDenySet =
new ReadOnlyCollection<string>(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 ||
Expand All @@ -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<Type, object> getUninitializedObjectDelegate = (Func<Type, object>)
//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<Type, object> GetUninitializedObjectDelegate = (Func<Type, object>)
typeof(string)
.GetTypeInfo()
.Assembly
Expand All @@ -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);
Expand Down Expand Up @@ -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<string> unsafeTypesDenySet =
new ReadOnlyCollection<string>(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);
Expand All @@ -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;
}
}
Expand Down Expand Up @@ -398,6 +406,11 @@ public class T
/// </summary>
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();
Expand Down
30 changes: 25 additions & 5 deletions src/Hyperion/SerializerOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ public class SerializerOptions
serializerFactories: null,
knownTypes: null,
ignoreISerializable: false,
packageNameOverrides: null);
packageNameOverrides: null,
disallowUnsafeTypes: true);

internal static List<Func<string, string>> DefaultPackageNameOverrides()
{
Expand Down Expand Up @@ -77,8 +78,8 @@ internal static List<Func<string, string>> DefaultPackageNameOverrides()
internal readonly bool VersionTolerance;
internal readonly Type[] KnownTypes;
internal readonly Dictionary<Type, ushort> KnownTypesDict = new Dictionary<Type, ushort>();
internal readonly List<Func<string, string>> CrossFrameworkPackageNameOverrides =
DefaultPackageNameOverrides();
internal readonly List<Func<string, string>> CrossFrameworkPackageNameOverrides = DefaultPackageNameOverrides();
internal readonly bool DisallowUnsafeTypes;

[Obsolete]
public SerializerOptions(
Expand All @@ -91,6 +92,7 @@ public SerializerOptions(
: this(versionTolerance, preserveObjectReferences, surrogates, serializerFactories, knownTypes, ignoreISerializable, null)
{ }

[Obsolete]
public SerializerOptions(
bool versionTolerance,
bool preserveObjectReferences,
Expand All @@ -99,6 +101,18 @@ public SerializerOptions(
IEnumerable<Type> knownTypes,
bool ignoreISerializable,
IEnumerable<Func<string, string>> packageNameOverrides)
: this(versionTolerance, preserveObjectReferences, surrogates, serializerFactories, knownTypes, ignoreISerializable, null, true)
{ }

public SerializerOptions(
bool versionTolerance,
bool preserveObjectReferences,
IEnumerable<Surrogate> surrogates,
IEnumerable<ValueSerializerFactory> serializerFactories,
IEnumerable<Type> knownTypes,
bool ignoreISerializable,
IEnumerable<Func<string, string>> packageNameOverrides,
bool disallowUnsafeTypes)
{
VersionTolerance = versionTolerance;
Surrogates = surrogates?.ToArray() ?? EmptySurrogates;
Expand All @@ -119,6 +133,8 @@ public SerializerOptions(

if(packageNameOverrides != null)
CrossFrameworkPackageNameOverrides.AddRange(packageNameOverrides);

DisallowUnsafeTypes = disallowUnsafeTypes;
}

public SerializerOptions WithVersionTolerance(bool versionTolerance)
Expand All @@ -135,6 +151,8 @@ public SerializerOptions WithIgnoreSerializable(bool ignoreISerializable)
=> Copy(ignoreISerializable: ignoreISerializable);
public SerializerOptions WithPackageNameOverrides(IEnumerable<Func<string, string>> packageNameOverrides)
=> Copy(packageNameOverrides: packageNameOverrides);
public SerializerOptions WithDisallowUnsafeType(bool disallowUnsafeType)
=> Copy(disallowUnsafeType: disallowUnsafeType);

private SerializerOptions Copy(
bool? versionTolerance = null,
Expand All @@ -143,15 +161,17 @@ private SerializerOptions Copy(
IEnumerable<ValueSerializerFactory> serializerFactories = null,
IEnumerable<Type> knownTypes = null,
bool? ignoreISerializable = null,
IEnumerable<Func<string, string>> packageNameOverrides = null)
IEnumerable<Func<string, string>> packageNameOverrides = null,
bool? disallowUnsafeType = null)
=> new SerializerOptions(
versionTolerance ?? VersionTolerance,
preserveObjectReferences ?? PreserveObjectReferences,
surrogates ?? Surrogates,
serializerFactories ?? ValueSerializerFactories,
knownTypes ?? KnownTypes,
ignoreISerializable ?? IgnoreISerializable,
packageNameOverrides ?? CrossFrameworkPackageNameOverrides
packageNameOverrides ?? CrossFrameworkPackageNameOverrides,
disallowUnsafeType ?? DisallowUnsafeTypes
);
}
}
2 changes: 1 addition & 1 deletion src/Hyperion/ValueSerializers/TypeSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit 5284534

Please sign in to comment.