Skip to content

Commit b6b210e

Browse files
committed
handle value escaping
1 parent 1ceed23 commit b6b210e

File tree

2 files changed

+31
-20
lines changed

2 files changed

+31
-20
lines changed

src/Runner.Worker/Container/DockerUtil.cs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public static string CreateEscapedOption(string flag, string key)
6868
{
6969
return "";
7070
}
71-
return $"{flag} \"{EscapeString(key)}\"";
71+
return $"{flag} {EscapeString(key)}";
7272
}
7373

7474
public static string CreateEscapedOption(string flag, string key, string value)
@@ -77,12 +77,28 @@ public static string CreateEscapedOption(string flag, string key, string value)
7777
{
7878
return "";
7979
}
80-
return $"{flag} \"{EscapeString(key)}={value.Replace("\"", "\\\"")}\"";
80+
var escapedString = EscapeString($"{key}={value}");
81+
return $"{flag} {escapedString}";
8182
}
8283

8384
private static string EscapeString(string value)
8485
{
85-
return value.Replace("\\", "\\\\").Replace("\"", "\\\"");
86+
if (String.IsNullOrEmpty(value))
87+
{
88+
return "";
89+
}
90+
// Dotnet escaping rules are weird here, we can only escape \ if it precedes a "
91+
// If a double quotation mark follows two or an even number of backslashes, each proceeding backslash pair is replaced with one backslash and the double quotation mark is removed.
92+
// If a double quotation mark follows an odd number of backslashes, including just one, each preceding pair is replaced with one backslash and the remaining backslash is removed; however, in this case the double quotation mark is not removed.
93+
// https://docs.microsoft.com/en-us/dotnet/api/system.environment.getcommandlineargs?redirectedfrom=MSDN&view=net-6.0#remarks
94+
95+
// First, find any \ followed by a " and double the number of \ + 1.
96+
value = Regex.Replace(value, @"(\\*)" + "\"", @"$1$1\" + "\"");
97+
// Next, what if it ends in `\`, it would escape the end quote. So, we need to detect that at the end of the string and perform the same escape
98+
// Luckily, we can just use the $ character with detects the end of string in regex
99+
value = Regex.Replace(value, @"(\\+)$", @"$1$1");
100+
// Finally, wrap it in quotes
101+
return $"\"{value}\"";
86102
}
87103
}
88104
}

src/Test/L0/Container/DockerUtilL0.cs

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -149,13 +149,12 @@ public void ParseRegistryHostnameFromImageName(string input, string expected)
149149
[Trait("Level", "L0")]
150150
[Trait("Category", "Worker")]
151151
[InlineData("", "")]
152-
[InlineData("HOME alpine:3.8 sh -c id #", "HOME alpine:3.8 sh -c id #")]
153-
[InlineData("HOME \"alpine:3.8 sh -c id #", "HOME \\\"alpine:3.8 sh -c id #")]
154-
[InlineData("HOME \\\"alpine:3.8 sh -c id #", "HOME \\\\\\\"alpine:3.8 sh -c id #")]
155-
[InlineData("HOME \\\\\"alpine:3.8 sh -c id #", "HOME \\\\\\\\\\\"alpine:3.8 sh -c id #")]
156-
[InlineData("HOME \"\"alpine:3.8 sh -c id #", "HOME \\\"\\\"alpine:3.8 sh -c id #")]
157-
[InlineData("HOME \\\"\"alpine:3.8 sh -c id #", "HOME \\\\\\\"\\\"alpine:3.8 sh -c id #")]
158-
[InlineData("HOME \"\\\"alpine:3.8 sh -c id #", "HOME \\\"\\\\\\\"alpine:3.8 sh -c id #")]
152+
[InlineData("foo", "foo")]
153+
[InlineData("foo \\ bar", "foo \\ bar")]
154+
[InlineData("foo \\", "foo \\\\")]
155+
[InlineData("foo \\\\", "foo \\\\\\\\")]
156+
[InlineData("foo \\\" bar", "foo \\\\\" bar")]
157+
[InlineData("foo \\\\\" bar", "foo \\\\\\\\\" bar")]
159158
public void CreateEscapedOption_keyOnly(string input, string escaped)
160159
{
161160
var flag = "--example";
@@ -175,15 +174,11 @@ public void CreateEscapedOption_keyOnly(string input, string escaped)
175174
[Theory]
176175
[Trait("Level", "L0")]
177176
[Trait("Category", "Worker")]
178-
[InlineData("HOME", "", "HOME", "")]
179-
[InlineData("HOME alpine:3.8 sh -c id #", "HOME alpine:3.8 sh -c id #", "HOME alpine:3.8 sh -c id #", "HOME alpine:3.8 sh -c id #")]
180-
[InlineData("HOME \"alpine:3.8 sh -c id #", "HOME \"alpine:3.8 sh -c id #", "HOME \\\"alpine:3.8 sh -c id #", "HOME \\\"alpine:3.8 sh -c id #")]
181-
[InlineData("HOME \\\"alpine:3.8 sh -c id #", "HOME \\\"alpine:3.8 sh -c id #", "HOME \\\\\\\"alpine:3.8 sh -c id #", "HOME \\\\\"alpine:3.8 sh -c id #")]
182-
[InlineData("HOME \\\\\"alpine:3.8 sh -c id #", "HOME \\\\\"alpine:3.8 sh -c id #", "HOME \\\\\\\\\\\"alpine:3.8 sh -c id #", "HOME \\\\\\\"alpine:3.8 sh -c id #")]
183-
[InlineData("HOME \"\"alpine:3.8 sh -c id #", "HOME \"\"alpine:3.8 sh -c id #", "HOME \\\"\\\"alpine:3.8 sh -c id #", "HOME \\\"\\\"alpine:3.8 sh -c id #")]
184-
[InlineData("HOME \\\"\"alpine:3.8 sh -c id #", "HOME \\\"\"alpine:3.8 sh -c id #", "HOME \\\\\\\"\\\"alpine:3.8 sh -c id #", "HOME \\\\\"\\\"alpine:3.8 sh -c id #")]
185-
[InlineData("HOME \"\\\"alpine:3.8 sh -c id #", "HOME \"\\\"alpine:3.8 sh -c id #", "HOME \\\"\\\\\\\"alpine:3.8 sh -c id #", "HOME \\\"\\\\\"alpine:3.8 sh -c id #")]
186-
public void CreateEscapedOption_keyValue(string keyInput, string valueInput, string escapedKey, string escapedValue)
177+
[InlineData("foo", "bar", "foo=bar")]
178+
[InlineData("foo\\", "bar", "foo\\=bar")]
179+
[InlineData("foo\\", "bar\\", "foo\\=bar\\\\")]
180+
[InlineData("foo \\","bar \\", "foo \\\\=bar \\\\")]
181+
public void CreateEscapedOption_keyValue(string keyInput, string valueInput, string escapedString)
187182
{
188183
var flag = "--example";
189184
var actual = DockerUtil.CreateEscapedOption(flag, keyInput, valueInput);
@@ -194,7 +189,7 @@ public void CreateEscapedOption_keyValue(string keyInput, string valueInput, str
194189
}
195190
else
196191
{
197-
expected = $"{flag} \"{escapedKey}={escapedValue}\"";
192+
expected = $"{flag} \"{escapedString}\"";
198193
}
199194
Assert.Equal(expected, actual);
200195
}

0 commit comments

Comments
 (0)