Skip to content

Commit 8594ebc

Browse files
authored
Validate UseTextForParameters as a file name (#1893)
The text is appended to the snapshot file name verbatim, with no FileNameCleaner pass, but it was validated with AgainstBadExtension, which only rejects a leading period and directory separators. The sibling naming APIs UseFileName, UseTypeName and UseMethodName all use the file name guard. So UseTextForParameters($"{time:HH:mm}") put a ':' in the path: on Windows the received write silently went to an NTFS alternate data stream, so no received file appeared at all, and on net4x it failed deep in IO rather than as a clear argument error. '*' and '?' failed at write time. '#' is rejected too, since it starts the indexed-target namespace and would make one case's files look like another case's targets.
1 parent 39f4900 commit 8594ebc

3 files changed

Lines changed: 47 additions & 1 deletion

File tree

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
public class UseTextForParametersTests
2+
{
3+
// The text is appended to the file name verbatim, so it has to be a valid file name.
4+
// A `:` used to pass validation and then, on Windows, divert the received file into an
5+
// NTFS alternate data stream, so no received file appeared at all.
6+
[Theory]
7+
[InlineData("ratio 16:9")]
8+
[InlineData("a*b")]
9+
[InlineData("a?b")]
10+
[InlineData("a|b")]
11+
// `#` is reserved for the indexed-target namespace
12+
[InlineData("a#1")]
13+
public void InvalidCharactersThrow(string text)
14+
{
15+
var settings = new VerifySettings();
16+
17+
var exception = Assert.Throws<ArgumentException>(() => settings.UseTextForParameters(text));
18+
Assert.Contains("Invalid character for file name", exception.Message);
19+
}
20+
21+
[Fact]
22+
public void ValidTextIsAccepted()
23+
{
24+
var settings = new VerifySettings();
25+
settings.UseTextForParameters("ratio 16-9");
26+
27+
Assert.Equal("ratio 16-9", settings.parametersText);
28+
}
29+
}

src/Verify/Guards.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,18 @@ public static void BadFileName(string name, [CallerArgumentExpression("name")] s
1616
}
1717
}
1818

19+
public static void BadParametersText(string value, [CallerArgumentExpression(nameof(value))] string argumentName = "")
20+
{
21+
BadFileName(value, argumentName);
22+
23+
// `#` starts the indexed-target namespace, so a value containing one would make
24+
// this case's files look like the targets of another case.
25+
if (value.Contains('#'))
26+
{
27+
throw new ArgumentException($"Invalid character for file name. Value: {value}. Char:#", argumentName);
28+
}
29+
}
30+
1931
static char[] invalidPathChars = Path
2032
.GetInvalidPathChars()
2133
.Concat(invalidFileChars.Except(['/', '\\', ':']))

src/Verify/VerifySettings.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,12 @@ public VerifySettings()
156156
/// </summary>
157157
public void UseTextForParameters(string parametersText)
158158
{
159-
Guards.AgainstBadExtension(parametersText);
159+
// The text goes into the file name verbatim, so it is validated as a file name,
160+
// the same as UseFileName, UseTypeName and UseMethodName. The extension guard
161+
// used to be applied here, which let `:`, `*`, `?`, `"`, `<`, `>` and `|` through
162+
// to fail at write time, or on Windows silently divert the received file into an
163+
// NTFS alternate data stream.
164+
Guards.BadParametersText(parametersText);
160165

161166
if (parameters is not null)
162167
{

0 commit comments

Comments
 (0)