Skip to content

Commit ccf8cfd

Browse files
authored
StorageSync: Update character validation for InvokeCompatabilityCheckCmdlet (#23298)
2 parents 7eb2abb + ef55796 commit ccf8cfd

File tree

7 files changed

+47
-189
lines changed

7 files changed

+47
-189
lines changed

src/StorageSync/StorageSync.Test/UnitTests/FilenamesCharactersValidationTest.cs

Lines changed: 8 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ public void ItReturnsErrorOnDirectoryWithInvalidCodePoint()
4545
0x7A
4646
};
4747
configurationMockFactory.Setup(configuration => configuration.BlacklistOfCodePoints()).Returns(codePointBlacklist);
48-
configurationMockFactory.Setup(configuration => configuration.WhitelistOfCodePointRanges()).Returns(this._configuration.WhitelistOfCodePointRanges);
4948
FilenamesCharactersValidation validation = new FilenamesCharactersValidation(configurationMockFactory.Object);
5049

5150
var directoryInfoMockFactory = new Moq.Mock<IDirectoryInfo>();
@@ -69,7 +68,6 @@ public void ItReturnsErrorOnFileWithInvalidCodePoint()
6968
0x7A
7069
};
7170
configurationMockFactory.Setup(configuration => configuration.BlacklistOfCodePoints()).Returns(codePointBlacklist);
72-
configurationMockFactory.Setup(configuration => configuration.WhitelistOfCodePointRanges()).Returns(this._configuration.WhitelistOfCodePointRanges);
7371
FilenamesCharactersValidation validation = new FilenamesCharactersValidation(configurationMockFactory.Object);
7472

7573
var fileInfoMockFactory = new Moq.Mock<IFileInfo>();
@@ -80,69 +78,6 @@ public void ItReturnsErrorOnFileWithInvalidCodePoint()
8078
Assert.StrictEqual<Result>(Result.Fail, validationResult.Result);
8179
}
8280

83-
/// <summary>
84-
/// Defines the test method ItReturnsErrorWhenCodePointIsInBoundsOfRange.
85-
/// </summary>
86-
[Fact]
87-
[Trait(Category.AcceptanceType, Category.CheckIn)]
88-
public void ItReturnsErrorWhenCodePointIsInBoundsOfRange()
89-
{
90-
var configurationMockFactory = new Moq.Mock<IConfiguration>();
91-
List<Configuration.CodePointRange> whitelist = new List<Configuration.CodePointRange> {
92-
new Configuration.CodePointRange {
93-
Start = 0x00,
94-
End = 0x77 // x - 1
95-
},
96-
new Configuration.CodePointRange {
97-
Start = 0x7B, // z + 1
98-
End = 0x10FFFF
99-
},
100-
};
101-
configurationMockFactory.Setup(configuration => configuration.WhitelistOfCodePointRanges()).Returns(whitelist);
102-
configurationMockFactory.Setup(configuration => configuration.BlacklistOfCodePoints()).Returns(new List<int>());
103-
FilenamesCharactersValidation validation = new FilenamesCharactersValidation(configurationMockFactory.Object);
104-
105-
var fileInfoMockFactory = new Moq.Mock<IFileInfo>();
106-
fileInfoMockFactory.SetupGet(fileInfo => fileInfo.Name).Returns("AAAxAAAzAAA");
107-
IValidationResult validationResult = validation.Validate(fileInfoMockFactory.Object);
108-
109-
Assert.StrictEqual<Result>(Result.Fail, validationResult.Result);
110-
Assert.True(validationResult.Positions.Count == 2, $"Unexpected number of error positions");
111-
Assert.True(validationResult.Positions[0] == 4, $"Unexpected position of first error");
112-
Assert.True(validationResult.Positions[1] == 8, $"Unexpected position of second error");
113-
}
114-
115-
/// <summary>
116-
/// Defines the test method ItReturnsErrorWhenCodePointIsInMiddleOfRange.
117-
/// </summary>
118-
[Fact]
119-
[Trait(Category.AcceptanceType, Category.CheckIn)]
120-
public void ItReturnsErrorWhenCodePointIsInMiddleOfRange()
121-
{
122-
var configurationMockFactory = new Moq.Mock<IConfiguration>();
123-
List<Configuration.CodePointRange> whitelist = new List<Configuration.CodePointRange> {
124-
new Configuration.CodePointRange {
125-
Start = 0x00,
126-
End = 0x77 // x - 1
127-
},
128-
new Configuration.CodePointRange {
129-
Start = 0x7B, // z + 1
130-
End = 0x10FFFF
131-
},
132-
};
133-
134-
configurationMockFactory.Setup(configuration => configuration.WhitelistOfCodePointRanges()).Returns(whitelist);
135-
configurationMockFactory.Setup(configuration => configuration.BlacklistOfCodePoints()).Returns(new List<int>());
136-
FilenamesCharactersValidation validation = new FilenamesCharactersValidation(configurationMockFactory.Object);
137-
138-
var fileInfoMockFactory = new Moq.Mock<IFileInfo>();
139-
fileInfoMockFactory.SetupGet(fileInfo => fileInfo.Name).Returns("AAAyAAA");
140-
IValidationResult validationResult = validation.Validate(fileInfoMockFactory.Object);
141-
142-
Assert.StrictEqual<Result>(Result.Fail, validationResult.Result);
143-
Assert.True(validationResult.Positions.Count == 1, $"Unexpected number of error positions");
144-
Assert.True(validationResult.Positions[0] == 4, $"Unexpected position of first error");
145-
}
14681

14782
/// <summary>
14883
/// Defines the test method TestWithRealConfigReturnsSuccessForValidSurrogateCodePoint.
@@ -175,16 +110,20 @@ public void TestWithRealConfigReturnsErrorForInvalidCodePoint()
175110
fileInfoMockFactory.SetupGet(fileInfo => fileInfo.Name).Returns(new string(new char[] {
176111
'a',
177112
(char)0xD834, (char)0xDD1E, // valid surrogate pair
178-
(char)0xFFF0, // invalid codepoint
113+
(char)0xD834, 'a', // invalid surrogate pair,
114+
'a', (char)0xDD1E, // invalid surrogate pair,
115+
(char)0xD834, 'a', // invalid surrogate pair,
179116
'z',
180117
(char)0x007C // blacklisted codepoint
181118
}));
182119
IValidationResult validationResult = validation.Validate(fileInfoMockFactory.Object);
183120

184121
Assert.StrictEqual<Result>(Result.Fail, validationResult.Result);
185-
Assert.True(validationResult.Positions.Count == 2, $"Unexpected number of error positions");
186-
Assert.True(validationResult.Positions[0] == 3, $"Unexpected position of first error");
187-
Assert.True(validationResult.Positions[1] == 5, $"Unexpected position of second error");
122+
Assert.True(validationResult.Positions.Count == 4, $"Unexpected number of error positions");
123+
Assert.True(validationResult.Positions[0] == 4, $"Unexpected position of first error");
124+
Assert.True(validationResult.Positions[1] == 7, $"Unexpected position of second error");
125+
Assert.True(validationResult.Positions[2] == 8, $"Unexpected position of third error");
126+
Assert.True(validationResult.Positions[3] == 11, $"Unexpected position of fourth error");
188127
}
189128

190129
}

src/StorageSync/StorageSync/ChangeLog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
## Version 2.1.0
2323
* Fixed minor issues.
24+
* Updated supported character sets in `Invoke-AzStorageSyncCompatibilityCheck`.
2425

2526
## Version 2.0.0
2627
* Deprecated "RegisteredServer" alias for InputObject parameter for Set-AzStorageSyncServerEndpoint

src/StorageSync/StorageSync/Config.json

Lines changed: 1 addition & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -49,97 +49,6 @@
4949
62,
5050
63,
5151
92,
52-
124,
53-
127
54-
],
55-
"WhitelistOfCodePointRanges": [
56-
{
57-
"Start": 0,
58-
"End": 127
59-
},
60-
{
61-
"Start": 160,
62-
"End": 55295
63-
},
64-
{
65-
"Start": 57344,
66-
"End": 63743
67-
},
68-
{
69-
"Start": 63744,
70-
"End": 64975
71-
},
72-
{
73-
"Start": 65008,
74-
"End": 65519
75-
},
76-
{
77-
"Start": 65536,
78-
"End": 131069
79-
},
80-
{
81-
"Start": 131072,
82-
"End": 196605
83-
},
84-
{
85-
"Start": 196608,
86-
"End": 262141
87-
},
88-
{
89-
"Start": 262144,
90-
"End": 327677
91-
},
92-
{
93-
"Start": 327680,
94-
"End": 393213
95-
},
96-
{
97-
"Start": 393216,
98-
"End": 458749
99-
},
100-
{
101-
"Start": 458752,
102-
"End": 524285
103-
},
104-
{
105-
"Start": 524288,
106-
"End": 589821
107-
},
108-
{
109-
"Start": 589824,
110-
"End": 655357
111-
},
112-
{
113-
"Start": 655360,
114-
"End": 720893
115-
},
116-
{
117-
"Start": 720896,
118-
"End": 786429
119-
},
120-
{
121-
"Start": 786432,
122-
"End": 851965
123-
},
124-
{
125-
"Start": 851968,
126-
"End": 917501
127-
},
128-
{
129-
"Start": 917504,
130-
"End": 921599
131-
},
132-
{
133-
"Start": 921600,
134-
"End": 983037
135-
},
136-
{
137-
"Start": 983040,
138-
"End": 1048573
139-
},
140-
{
141-
"Start": 1048576,
142-
"End": 1114109
143-
}
52+
124
14453
]
14554
}

src/StorageSync/StorageSync/Configuration.cs

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,6 @@ public class ValidationsConfiguration
5454
/// </summary>
5555
[DataMember] public List<string> InvalidFilenames;
5656
/// <summary>
57-
/// The whitelist of code point ranges
58-
/// </summary>
59-
[DataMember] public List<CodePointRange> WhitelistOfCodePointRanges;
60-
/// <summary>
6157
/// The blacklist of code points
6258
/// </summary>
6359
[DataMember] public List<int> BlacklistOfCodePoints;
@@ -198,15 +194,6 @@ public IEnumerable<string> InvalidFileNames()
198194
return _validationsConfiguration.InvalidFilenames;
199195
}
200196

201-
/// <summary>
202-
/// Whitelists the of code point ranges.
203-
/// </summary>
204-
/// <returns>IEnumerable&lt;Configuration.CodePointRange&gt;.</returns>
205-
public IEnumerable<CodePointRange> WhitelistOfCodePointRanges()
206-
{
207-
return _validationsConfiguration.WhitelistOfCodePointRanges;
208-
}
209-
210197
/// <summary>
211198
/// Blacklists the of code points.
212199
/// </summary>

src/StorageSync/StorageSync/Interfaces/IConfiguration.cs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,6 @@ public interface IConfiguration
3737
/// <returns>IEnumerable&lt;System.String&gt;.</returns>
3838
IEnumerable<string> ValidFilesystems();
3939
/// <summary>
40-
/// Whitelists the of code point ranges.
41-
/// </summary>
42-
/// <returns>IEnumerable&lt;Configuration.CodePointRange&gt;.</returns>
43-
IEnumerable<Configuration.CodePointRange> WhitelistOfCodePointRanges();
44-
/// <summary>
4540
/// Blacklists the of code points.
4641
/// </summary>
4742
/// <returns>IEnumerable&lt;System.Int32&gt;.</returns>

src/StorageSync/StorageSync/Validations/NamespaceValidations/FilenamesCharactersValidation.cs

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ namespace Microsoft.Azure.Commands.StorageSync.Evaluation.Validations.NamespaceV
1818
using System;
1919
using System.Collections;
2020
using System.Collections.Generic;
21+
using System.Diagnostics;
2122
using System.Linq;
2223

2324
/// <summary>
@@ -49,14 +50,13 @@ public FilenamesCharactersValidation(
4950
"File/Directory names with unsupported characters",
5051
ValidationType.FilenameCharacters)
5152
{
52-
var whitelistOfCodePointRanges = configuration.WhitelistOfCodePointRanges().ToList();
5353
var blacklistOfCodePoints = new HashSet<int>(configuration.BlacklistOfCodePoints());
5454

5555
_codePointBlackList = new bool[NumberOfCodePoints];
5656

5757
for (int i = 0; i < NumberOfCodePoints; ++i)
5858
{
59-
_codePointBlackList[i] = blacklistOfCodePoints.Contains(i) || !whitelistOfCodePointRanges.Any(range => range.Includes(i));
59+
_codePointBlackList[i] = blacklistOfCodePoints.Contains(i);
6060
}
6161
}
6262
#endregion
@@ -98,16 +98,35 @@ protected override IValidationResult DoValidate(IDirectoryInfo directoryInfo)
9898
/// <param name="node">The node.</param>
9999
/// <param name="isDirectory">if set to <c>true</c> [is directory].</param>
100100
/// <returns>IValidationResult.</returns>
101-
private IValidationResult ValidateInternal (INamedObjectInfo node, bool isDirectory)
101+
private IValidationResult ValidateInternal(INamedObjectInfo node, bool isDirectory)
102102
{
103103
string name = node.Name;
104104
List<int> positions = new List<int>();
105+
string message = string.Empty;
106+
char prevChar = 'a';
105107

106108
for (int i = 0, codepointIndex = 0; i < name.Length; ++i, ++codepointIndex)
107109
{
108110
int codePoint;
111+
message = string.Empty;
109112

110-
if (char.IsSurrogatePair(name, i))
113+
if (char.IsHighSurrogate(name[i]) && name.Length == 1)
114+
{
115+
codePoint = name[i];
116+
message = $"Invalid surrogate char found in the file name at location {i + 1}";
117+
}
118+
else if (!char.IsHighSurrogate(prevChar) && char.IsLowSurrogate(name[i]))
119+
{
120+
codePoint = name[i];
121+
message = $"Found a low surrogate character without a preceding high surrogate character at location {i + 1}";
122+
}
123+
else if (char.IsHighSurrogate(prevChar) && !char.IsLowSurrogate(name[i]))
124+
{
125+
--codepointIndex;
126+
codePoint = name[i];
127+
message = $"Invalid hight surrogate char found in the file name at location {i + 1}";
128+
}
129+
else if (char.IsSurrogatePair(name, i))
111130
{
112131
codePoint = char.ConvertToUtf32(name, i);
113132
i += 1;
@@ -117,14 +136,24 @@ private IValidationResult ValidateInternal (INamedObjectInfo node, bool isDirect
117136
codePoint = name[i];
118137
}
119138

120-
if (codePoint < 0 ||
121-
codePoint >= NumberOfCodePoints ||
122-
_codePointBlackList[codePoint])
139+
prevChar = name[i];
140+
141+
if (codePoint < 0 ||
142+
codePoint >= NumberOfCodePoints ||
143+
_codePointBlackList[codePoint] ||
144+
!string.IsNullOrEmpty(message))
123145
{
124146
// adding +1 so that positions are 1-based
125147
// to make them more human friendly
126148
positions.Add(codepointIndex + 1);
127149
}
150+
151+
codepointIndex = i;
152+
153+
if (!string.IsNullOrEmpty(message))
154+
{
155+
Trace.TraceWarning(message);
156+
}
128157
}
129158

130159
if (positions.Count > 0)

src/StorageSync/StorageSync/debug.Netcore.ps1

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,12 @@ function Build-CharacterTable
3939
param ($Configuration)
4040

4141
$blacklistOfCodePoints = $Configuration.BlacklistOfCodePoints
42-
$whitelistOfCodePointRanges = $Configuration.WhitelistOfCodePointRanges
4342

4443
$arraySize = 0x10FFFF + 1
4544
$blacklistedCodePointsTable = new-object bool[] $arraySize
4645
for ($i = 0; $i -lt $arraySize; $i += 1)
4746
{
48-
$blacklistedCodePointsTable[$i] = $blacklistOfCodePoints.Contains($i) -or
49-
($whitelistOfCodePointRanges.Where({ ($_.Start -le $i) -and ($_.End -ge $i) }).Count -eq 0);
47+
$blacklistedCodePointsTable[$i] = $blacklistOfCodePoints.Contains($i);
5048
}
5149
return $blacklistedCodePointsTable
5250
}

0 commit comments

Comments
 (0)