diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index ad72f0a4..6c2c1efc 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,3 +1,33 @@ +### 0.11.1 August 17 2021 #### +* Add [unsafe deserialization type blacklist](https://github.com/akkadotnet/Hyperion/pull/242) +* Bump [Akka version from 1.4.21 to 1.4.23](https://github.com/akkadotnet/Hyperion/pull/246) + +We've added a deserialization safety check to block dangerous types from being deserialized. +This is done to add a layer of security from possible code injection and code execution attack. +Currently it is an all or nothing feature that can be turned on and off by using the new `DisallowUnsafeTypes` flag inside `SerializerOptions` (defaults to true). + +The unsafe types that are currently blocked are: +- 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 + ### 0.11.0 July 8 2021 #### * [Fix array of user defined structs serialization failure](https://github.com/akkadotnet/Hyperion/pull/235) * [Remove dynamic keyword usage from array serializer](https://github.com/akkadotnet/Hyperion/pull/139) diff --git a/build-system/linux-pr-validation.yaml b/build-system/linux-pr-validation.yaml deleted file mode 100644 index 61f748e4..00000000 --- a/build-system/linux-pr-validation.yaml +++ /dev/null @@ -1,22 +0,0 @@ -# Pull request validation for Linux against the `dev` and `master` branches -# See https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema for reference -trigger: - branches: - include: - - dev - - master - -name: $(Year:yyyy).$(Month).$(DayOfMonth)$(Rev:.r) - -pr: - autoCancel: true # indicates whether additional pushes to a PR should cancel in-progress runs for the same PR. Defaults to true - branches: - include: [ dev, master ] # branch names which will trigger a build - -jobs: -- template: azure-pipeline.template.yaml - parameters: - name: Ubuntu - vmImage: 'ubuntu-16.04' - scriptFileName: ./build.sh - scriptArgs: all \ No newline at end of file diff --git a/build-system/windows-pr-validation.yaml b/build-system/windows-pr-validation.yaml index 0b50b03e..5f14abd4 100644 --- a/build-system/windows-pr-validation.yaml +++ b/build-system/windows-pr-validation.yaml @@ -19,4 +19,11 @@ jobs: name: Windows vmImage: 'windows-2019' scriptFileName: build.cmd - scriptArgs: all \ No newline at end of file + scriptArgs: all +- template: azure-pipeline.template.yaml + parameters: + name: 'linux_pr' + displayName: 'Linux PR Validation' + vmImage: 'ubuntu-18.04' + scriptFileName: ./build.sh + scriptArgs: all diff --git a/build.fsx b/build.fsx index c3e5b00d..2b65f763 100644 --- a/build.fsx +++ b/build.fsx @@ -25,7 +25,7 @@ let output = __SOURCE_DIRECTORY__ @@ "bin" let outputTests = __SOURCE_DIRECTORY__ @@ "TestResults" let outputPerfTests = __SOURCE_DIRECTORY__ @@ "PerfResults" let outputBinaries = output @@ "binaries" -let outputBinariesNet461 = outputBinaries @@ "net461" +let outputBinariesNet461 = outputBinaries @@ "net471" let outputBinariesNetStandard = outputBinaries @@ "netstandard2.0" let outputBinariesNet = outputBinaries @@ "net5.0" let outputNuGet = output @@ "nuget" @@ -52,7 +52,7 @@ let versionSuffix = | str -> str // Configuration values for tests -let testNetFrameworkVersion = "net461" +let testNetFrameworkVersion = "net471" let testNetCoreVersion = "netcoreapp3.1" let testNetVersion = "net5.0" @@ -115,7 +115,8 @@ Target "RunTests" (fun _ -> let projects = match (isWindows) with | true -> !! "./src/**/*.Tests.csproj" - | _ -> !! "./src/**/*.Tests.csproj" // if you need to filter specs for Linux vs. Windows, do it here + | _ -> !! "./src/**/*.Tests.csproj" + -- "./src/**/*.API.Tests.csproj" // if you need to filter specs for Linux vs. Windows, do it here let runSingleProject project = let arguments = diff --git a/src/Hyperion.API.Tests/CoreApiSpec.ApproveApi.approved.txt b/src/Hyperion.API.Tests/CoreApiSpec.ApproveApi.approved.txt index cf895557..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) { } @@ -350,6 +353,11 @@ namespace Hyperion.Internal public string Contract { get; } public bool ForceFullStates { get; } } + public class EvilDeserializationException : System.Security.SecurityException + { + public EvilDeserializationException(string message, string typeString) { } + public string BadTypeString { get; } + } [System.AttributeUsage(System.AttributeTargets.Property | System.AttributeTargets.Field | System.AttributeTargets.Parameter | System.AttributeTargets.All)] public sealed class HtmlAttributeValueAttribute : System.Attribute { diff --git a/src/Hyperion.API.Tests/Hyperion.API.Tests.csproj b/src/Hyperion.API.Tests/Hyperion.API.Tests.csproj index 260d64dd..1c1c0aee 100644 --- a/src/Hyperion.API.Tests/Hyperion.API.Tests.csproj +++ b/src/Hyperion.API.Tests/Hyperion.API.Tests.csproj @@ -1,14 +1,15 @@ + - net461;netcoreapp3.1;net5.0 + $(NetFrameworkTestVersion);$(NetTestVersion);$(NetCoreTestVersion) false - + @@ -16,7 +17,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/src/Hyperion.Akka.Integration.Tests/Hyperion.Akka.Integration.Tests.csproj b/src/Hyperion.Akka.Integration.Tests/Hyperion.Akka.Integration.Tests.csproj index 6528c020..a0b6417d 100644 --- a/src/Hyperion.Akka.Integration.Tests/Hyperion.Akka.Integration.Tests.csproj +++ b/src/Hyperion.Akka.Integration.Tests/Hyperion.Akka.Integration.Tests.csproj @@ -1,8 +1,9 @@ + - net461;netcoreapp3.1;net5.0 + $(NetFrameworkTestVersion);$(NetTestVersion);$(NetCoreTestVersion) false @@ -18,7 +19,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/src/Hyperion.Benchmarks/Hyperion.Benchmarks.csproj b/src/Hyperion.Benchmarks/Hyperion.Benchmarks.csproj index d95e2c60..59ec7d23 100644 --- a/src/Hyperion.Benchmarks/Hyperion.Benchmarks.csproj +++ b/src/Hyperion.Benchmarks/Hyperion.Benchmarks.csproj @@ -1,13 +1,13 @@ - + Exe - netcoreapp2.0 + $(NetFrameworkTestVersion);$(NetTestVersion);$(NetCoreTestVersion) false - + diff --git a/src/Hyperion.Tests/ExpressionTests.cs b/src/Hyperion.Tests/ExpressionTests.cs index cc693634..e42d5a49 100644 --- a/src/Hyperion.Tests/ExpressionTests.cs +++ b/src/Hyperion.Tests/ExpressionTests.cs @@ -9,6 +9,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Linq.Expressions; diff --git a/src/Hyperion.Tests/Hyperion.Tests.csproj b/src/Hyperion.Tests/Hyperion.Tests.csproj index 74a9b28e..6983f5f3 100644 --- a/src/Hyperion.Tests/Hyperion.Tests.csproj +++ b/src/Hyperion.Tests/Hyperion.Tests.csproj @@ -1,22 +1,30 @@  + Exe - net461;netcoreapp3.1;net5.0 + $(NetFrameworkTestVersion);$(NetTestVersion);$(NetCoreTestVersion) true latest Hyperion.Tests.Generator.Program - + $(DefineConstants);NETCOREAPP - + $(DefineConstants);NETFX + + + + true + + + @@ -30,9 +38,4 @@ - - - true - - diff --git a/src/Hyperion.Tests/UnsafeDeserializationExclusionTests.cs b/src/Hyperion.Tests/UnsafeDeserializationExclusionTests.cs new file mode 100644 index 00000000..0444f377 --- /dev/null +++ b/src/Hyperion.Tests/UnsafeDeserializationExclusionTests.cs @@ -0,0 +1,26 @@ +using System.IO; +using Hyperion.Extensions; +using Hyperion.Internal; +using Xunit; + +namespace Hyperion.Tests +{ + public class UnsafeDeserializationExclusionTests + { + [Fact] + public void CantDeserializeANaughtyType() + { + //System.Diagnostics.Process p = new Process(); + var serializer = new Hyperion.Serializer(); + var di =new System.IO.DirectoryInfo(@"c:\"); + + using (var stream = new MemoryStream()) + { + serializer.Serialize(di, stream); + stream.Position = 0; + Assert.Throws(() => + serializer.Deserialize(stream)); + } + } + } +} \ No newline at end of file diff --git a/src/Hyperion/Extensions/TypeEx.cs b/src/Hyperion/Extensions/TypeEx.cs index 1ad02766..491b4851 100644 --- a/src/Hyperion/Extensions/TypeEx.cs +++ b/src/Hyperion/Extensions/TypeEx.cs @@ -10,10 +10,12 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Reflection; using System.Text.RegularExpressions; +using Hyperion.Internal; namespace Hyperion.Extensions { @@ -44,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 || @@ -67,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 @@ -79,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); @@ -128,23 +162,62 @@ private static Type GetTypeFromManifestName(Stream stream, DeserializerSession s break; } - return LoadTypeByName(shortName); + return LoadTypeByName(shortName, session.Serializer.Options.DisallowUnsafeTypes); }); } - public static Type LoadTypeByName(string name) + 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)) + return true; +#if NETSTANDARD1_6 + currentBase = currentBase.DeclaringType; +#else + currentBase = currentBase.BaseType; +#endif + } + + return false; + } + + public static Type LoadTypeByName(string name, bool disallowUnsafeTypes) { + if (disallowUnsafeTypes && UnsafeTypesDenySet.Any(name.Contains)) + { + throw new EvilDeserializationException( + "Unsafe Type Deserialization Detected!", name); + } try { // Try to load type name using strict version to avoid possible conflicts // i.e. if there are different version available in GAC and locally var typename = ToQualifiedAssemblyName(name, ignoreAssemblyVersion: false); - return Type.GetType(typename, true); + var type = Type.GetType(typename, true); + if (UnsafeInheritanceCheck(type)) + throw new EvilDeserializationException( + "Unsafe Type Deserialization Detected!", name); + return type; } catch (FileLoadException) { var typename = ToQualifiedAssemblyName(name, ignoreAssemblyVersion: true); - return Type.GetType(typename, true); + var type = Type.GetType(typename, true); + if (UnsafeInheritanceCheck(type)) + throw new EvilDeserializationException( + "Unsafe Type Deserialization Detected!", name); + return type; } } @@ -333,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/Internal/Annotations.cs b/src/Hyperion/Internal/Annotations.cs index 9ee76793..b1c5c0f6 100644 --- a/src/Hyperion/Internal/Annotations.cs +++ b/src/Hyperion/Internal/Annotations.cs @@ -8,6 +8,7 @@ #endregion using System; +using System.Security; #pragma warning disable 1591 // ReSharper disable UnusedMember.Global @@ -19,6 +20,16 @@ namespace Hyperion.Internal { + public class EvilDeserializationException : SecurityException + { + public EvilDeserializationException(string message, + string typeString) : base(message) + { + BadTypeString = typeString; + } + + public string BadTypeString { get; } + } /// /// Indicates that the value of the marked element could be null sometimes, /// so the check for null is necessary before its usage. 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 bee8d1b9..633d2cc7 100644 --- a/src/Hyperion/ValueSerializers/TypeSerializer.cs +++ b/src/Hyperion/ValueSerializers/TypeSerializer.cs @@ -8,6 +8,7 @@ #endregion using System; +using System.Collections.Concurrent; using System.IO; using Hyperion.Extensions; @@ -60,13 +61,16 @@ public override void WriteValue(Stream stream, object value, SerializerSession s } } + private static readonly ConcurrentDictionary TypeNameLookup = + new ConcurrentDictionary(); public override object ReadValue(Stream stream, DeserializerSession session) { var shortname = stream.ReadString(session); if (shortname == null) return null; - var type = TypeEx.LoadTypeByName(shortname); + var type = TypeNameLookup.GetOrAdd(shortname, + name => TypeEx.LoadTypeByName(shortname, session.Serializer.Options.DisallowUnsafeTypes)); //add the deserialized type to lookup if (session.Serializer.Options.PreserveObjectReferences) diff --git a/src/common.props b/src/common.props index 9637364f..83c58bb9 100644 --- a/src/common.props +++ b/src/common.props @@ -1,29 +1,31 @@ - Copyright © 2016-2017 Akka.NET Team + Copyright © 2016-2021 Akka.NET Team Akka.NET Team - 0.9.17 - [Bump Microsoft.NET.Test.Sdk from 16.6.1 to 16.7.1](https://github.com/akkadotnet/Hyperion/pull/182) -[Fix unit test problem](https://github.com/akkadotnet/Hyperion/pull/191) -[Bump FSharp.Core from 4.7.2 to 5.0.0](https://github.com/akkadotnet/Hyperion/pull/189) -[Fix issue #40 regarding partial streams](https://github.com/akkadotnet/Hyperion/pull/185) -[Fix Hyperion not using known serializers when defined](https://github.com/akkadotnet/Hyperion/pull/184) -[Bump Microsoft.NET.Test.Sdk from 16.7.1 to 16.8.3](https://github.com/akkadotnet/Hyperion/pull/196) -[Bump System.Collections.Immutable from 1.7.1 to 5.0.0](https://github.com/akkadotnet/Hyperion/pull/195) -[Bump FSharp.Core from 5.0.0 to 5.0.1](https://github.com/akkadotnet/Hyperion/pull/202) -[Update the cross framework spec to include complex POCO object, Type serialization, and support for netcoreapp3.1 and net5.0](https://github.com/akkadotnet/Hyperion/pull/204) + 0.11.0 + [Fix array of user defined structs serialization failure](https://github.com/akkadotnet/Hyperion/pull/235) +[Remove dynamic keyword usage from array serializer](https://github.com/akkadotnet/Hyperion/pull/139) +[Change field ordering to ordinal](https://github.com/akkadotnet/Hyperion/pull/236) +Possible breaking changes** +The change to the object serializer field ordering might cause a deserialization failure of persisted objects +that are serialized using the Hyperion serializer. +Please report any serialization problem that occurs after an upgrade to this version at the +[issue tracker](https://github.com/akkadotnet/Hyperion/issues) http://getakka.net/images/akkalogo.png https://github.com/akkadotnet/Hyperion https://github.com/akkadotnet/Hyperion/blob/master/LICENSE $(NoWarn);CS1591 - 1.4.21 - - 5.10.3 + netcoreapp3.1 + net5.0 + net471 + netstandard2.0 + 1.4.23 + 6.0.0 2.4.1 2.4.3 - 16.10.0 + 16.11.0 1.2.2 \ No newline at end of file diff --git a/src/xunit.runner.json b/src/xunit.runner.json new file mode 100644 index 00000000..4a73b1e5 --- /dev/null +++ b/src/xunit.runner.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://xunit.github.io/schema/current/xunit.runner.schema.json", + "longRunningTestSeconds": 60, + "parallelizeAssembly": false, + "parallelizeTestCollections": false +} \ No newline at end of file diff --git a/src/xunitSettings.props b/src/xunitSettings.props new file mode 100644 index 00000000..099c40f4 --- /dev/null +++ b/src/xunitSettings.props @@ -0,0 +1,8 @@ + + + + + Always + + + \ No newline at end of file