diff --git a/Samples/PowerShell/ExecuteSampleSplitMerge.ps1 b/Samples/PowerShell/ExecuteSampleSplitMerge.ps1 index c115ee3..672ef99 100644 --- a/Samples/PowerShell/ExecuteSampleSplitMerge.ps1 +++ b/Samples/PowerShell/ExecuteSampleSplitMerge.ps1 @@ -16,7 +16,7 @@ .EXAMPLES .\ExecuteSampleSplitMerge.ps1 ` -UserName 'mysqluser' ` - -Password 'MySqlPassw0rd' ` + -Password '' ` -ShardMapManagerServerName 'abcdefghij.database.windows.net' ` -ShardKeyType 'Int32' ` -SplitMergeServiceEndpoint 'https://mysplitmergeservice.cloudapp.net' ` @@ -24,7 +24,7 @@ .\ExecuteSampleSplitMerge.ps1 ` -UserName 'mysqluser' ` - -Password 'MySqlPassw0rd' ` + -Password '' ` -ShardMapManagerServerName 'abcdefghij.database.windows.net' ` -ShardKeyType 'Int64' ` -SplitMergeServiceEndpoint 'https://mysplitmergeservice.cloudapp.net' ` @@ -32,7 +32,7 @@ .\ExecuteSampleSplitMerge.ps1 ` -UserName 'mysqluser' ` - -Password 'MySqlPassw0rd' ` + -Password '' ` -ShardMapManagerServerName 'abcdefghij.database.windows.net' ` -ShardKeyType 'Guid' ` -SplitRangeLow '00000000-0000-0000-0000-000000000000' ` @@ -43,7 +43,7 @@ .\ExecuteSampleSplitMerge.ps1 ` -UserName 'mysqluser' ` - -Password 'MySqlPassw0rd' ` + -Password '' ` -ShardMapManagerServerName 'abcdefghij.database.windows.net' ` -ShardKeyType 'Binary' ` -SplitRangeLow '0x00' ` @@ -54,7 +54,7 @@ .\ExecuteSampleSplitMerge.ps1 ` -UserName 'mysqluser' ` - -Password 'MySqlPassw0rd' ` + -Password '' ` -ShardMapManagerServerName 'abcdefghij.database.windows.net' ` -ShardKeyType 'Datetime' ` -SplitRangeLow '2010-3-21 12:00:00' ` diff --git a/Samples/PowerShell/GetMappings.ps1 b/Samples/PowerShell/GetMappings.ps1 index e0b2654..cbd00b1 100644 --- a/Samples/PowerShell/GetMappings.ps1 +++ b/Samples/PowerShell/GetMappings.ps1 @@ -16,7 +16,7 @@ .EXAMPLES .\GetMappings.ps1 ` -UserName 'mysqluser' ` - -Password 'MySqlPassw0rd' ` + -Password '' ` -ShardMapManagerServerName 'abcdefghij.database.windows.net' ` -ShardMapManagerDatabaseName 'MyShardMapManagerDB' -ShardMapName 'CustomerIdShardMap' diff --git a/Samples/PowerShell/GetShards.ps1 b/Samples/PowerShell/GetShards.ps1 index 3713d1f..ca232c7 100644 --- a/Samples/PowerShell/GetShards.ps1 +++ b/Samples/PowerShell/GetShards.ps1 @@ -15,7 +15,7 @@ .EXAMPLES .\GetShards.ps1 ` -UserName 'mysqluser' ` - -Password 'MySqlPassw0rd' ` + -Password '' ` -ShardMapManagerServerName 'abcdefghij.database.windows.net' ` -ShardMapManagerDatabaseName 'MyShardMapManagerDB' -ShardMapName 'CustomerIdShardMap' diff --git a/Samples/PowerShell/SetupSampleSplitMergeEnvironment.ps1 b/Samples/PowerShell/SetupSampleSplitMergeEnvironment.ps1 index 1f0534a..20b9101 100644 --- a/Samples/PowerShell/SetupSampleSplitMergeEnvironment.ps1 +++ b/Samples/PowerShell/SetupSampleSplitMergeEnvironment.ps1 @@ -16,19 +16,19 @@ .EXAMPLES .\SetupSampleSplitMergeEnvironment.ps1 ` -UserName 'mysqluser' ` - -Password 'MySqlPassw0rd' ` + -Password '' ` -ShardMapManagerServerName 'abcdefghij.database.windows.net' ` -ShardKeyType 'Int32' .\SetupSampleSplitMergeEnvironment.ps1 ` -UserName 'mysqluser' ` - -Password 'MySqlPassw0rd' ` + -Password '' ` -ShardMapManagerServerName 'abcdefghij.database.windows.net' ` -ShardKeyType 'Int64' .\SetupSampleSplitMergeEnvironment.ps1 ` -UserName 'mysqluser' ` - -Password 'MySqlPassw0rd' ` + -Password '' ` -ShardMapManagerServerName 'abcdefghij.database.windows.net' ` -ShardKeyType 'Guid' ` -SplitRangeLow '00000000-0000-0000-0000-000000000000' ` @@ -37,7 +37,7 @@ .\SetupSampleSplitMergeEnvironment.ps1 ` -UserName 'mysqluser' ` - -Password 'MySqlPassw0rd' ` + -Password '' ` -ShardMapManagerServerName 'abcdefghij.database.windows.net' ` -ShardKeyType 'Binary' ` -SplitRangeLow '0x00' ` @@ -46,7 +46,7 @@ .\SetupSampleSplitMergeEnvironment.ps1 ` -UserName 'mysqluser' ` - -Password 'MySqlPassw0rd' ` + -Password '' ` -ShardMapManagerServerName 'abcdefghij.database.windows.net' ` -ShardKeyType 'Datetime' ` -SplitRangeLow '2010-3-21 12:00:00' ` diff --git a/Test/ElasticScale.ClientTestCommon/CommonTestUtils.cs b/Test/ElasticScale.ClientTestCommon/CommonTestUtils.cs index b5785ea..c1092c9 100644 --- a/Test/ElasticScale.ClientTestCommon/CommonTestUtils.cs +++ b/Test/ElasticScale.ClientTestCommon/CommonTestUtils.cs @@ -1,5 +1,9 @@ using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; using System.IO; +using System.Linq; +using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; namespace Microsoft.Azure.SqlDatabase.ElasticScale.Test.Common @@ -10,7 +14,13 @@ public static T SerializeDeserialize(T originalException) where T : Exception { using (MemoryStream memStream = new MemoryStream()) { - BinaryFormatter formatter = new BinaryFormatter(); + BinaryFormatter formatter = new BinaryFormatter + { + // Restrict deserialization to the exception graph these tests round-trip. + // This satisfies CA2301 and prevents BinaryFormatter from instantiating + // arbitrary (potentially dangerous) types. + Binder = new ExceptionRoundTripBinder(), + }; formatter.Serialize(memStream, originalException); memStream.Seek(0, SeekOrigin.Begin); @@ -18,5 +28,59 @@ public static T SerializeDeserialize(T originalException) where T : Exception return (T)formatter.Deserialize(memStream); } } + + /// + /// A that only permits the types that make up the + /// serialized graph of the Elastic Scale exceptions exercised by the tests: the + /// Elastic Scale types themselves (exceptions, ShardLocation, error-code enums), + /// , and lists/collections/arrays of those types. Any other type + /// is rejected before it can be instantiated. + /// + private sealed class ExceptionRoundTripBinder : SerializationBinder + { + private const string ElasticScaleNamespacePrefix = "Microsoft.Azure.SqlDatabase.ElasticScale"; + + public override Type BindToType(string assemblyName, string typeName) + { + // Resolving a Type does not instantiate anything; the object is only created + // by the formatter after we return an allowed type. Reject disallowed types up front. + Type type = Type.GetType(string.Format("{0}, {1}", typeName, assemblyName), throwOnError: false); + + if (type == null || !IsAllowed(type)) + { + throw new SerializationException( + string.Format("Type '{0}' from assembly '{1}' is not allowed to be deserialized.", typeName, assemblyName)); + } + + return type; + } + + private static bool IsAllowed(Type type) + { + if (type.IsArray) + { + return IsAllowed(type.GetElementType()); + } + + if (type.IsGenericType) + { + Type definition = type.GetGenericTypeDefinition(); + if (definition != typeof(List<>) && definition != typeof(ReadOnlyCollection<>)) + { + return false; + } + + return type.GetGenericArguments().All(IsAllowed); + } + + return type.IsPrimitive + || type == typeof(string) + || type == typeof(Exception) + || IsElasticScaleType(type); + } + + private static bool IsElasticScaleType(Type type) => + type.Namespace != null && type.Namespace.StartsWith(ElasticScaleNamespacePrefix, StringComparison.Ordinal); + } } } diff --git a/Test/ElasticScale.Query.UnitTests/MultiShardTestUtils.cs b/Test/ElasticScale.Query.UnitTests/MultiShardTestUtils.cs index d217222..1233e75 100644 --- a/Test/ElasticScale.Query.UnitTests/MultiShardTestUtils.cs +++ b/Test/ElasticScale.Query.UnitTests/MultiShardTestUtils.cs @@ -30,11 +30,6 @@ internal static class MultiShardTestUtils /// private static string s_testUserId = "TestUser"; - /// - /// User password to use when connecting to shards during a fanout query. - /// - private static string s_testPassword = "J8X2ndQTZ8cvu1r"; - /// /// Table name for the sharded table we will issue fanout queries against. /// @@ -326,7 +321,7 @@ private static void AddDropAndReCreateTestUserCommandsToList(List output // Then re create it. // - output.Add(string.Format("CREATE LOGIN {0} WITH Password = '{1}';", s_testUserId, s_testPassword)); + output.Add(string.Format("CREATE LOGIN {0} WITH Password = '{1}';", s_testUserId, Guid.NewGuid().ToString())); // Then grant it lots of permissions. // diff --git a/Test/ElasticScale.ShardManagement.UnitTests/Globals.cs b/Test/ElasticScale.ShardManagement.UnitTests/Globals.cs index c66bb09..0791e89 100644 --- a/Test/ElasticScale.ShardManagement.UnitTests/Globals.cs +++ b/Test/ElasticScale.ShardManagement.UnitTests/Globals.cs @@ -83,9 +83,10 @@ internal static class Globals internal static string SqlLoginTestUser = "ElasticDatabaseToolsTestUser_" + System.Environment.CurrentManagedThreadId; /// - /// Password for test user. (with ' and ; replaced with _ to enable test code to work without T/SQL and connection string escaping) + /// Password for test user. Randomly generated per run so no credential is hard-coded in source. A GUID + /// contains no quote/semicolon and satisfies SQL Server password complexity (lowercase, digits, hyphens). /// - internal static readonly string SqlLoginTestPassword = "TestPa$$w0rd" + Guid.NewGuid().ToString("N"); + internal static readonly string SqlLoginTestPassword = Guid.NewGuid().ToString(); /// /// SMM connection string.