Skip to content

Commit

Permalink
handle value escaping
Browse files Browse the repository at this point in the history
  • Loading branch information
thboop committed Sep 7, 2022
1 parent 1ceed23 commit b6b210e
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 20 deletions.
22 changes: 19 additions & 3 deletions src/Runner.Worker/Container/DockerUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public static string CreateEscapedOption(string flag, string key)
{
return "";
}
return $"{flag} \"{EscapeString(key)}\"";
return $"{flag} {EscapeString(key)}";
}

public static string CreateEscapedOption(string flag, string key, string value)
Expand All @@ -77,12 +77,28 @@ public static string CreateEscapedOption(string flag, string key, string value)
{
return "";
}
return $"{flag} \"{EscapeString(key)}={value.Replace("\"", "\\\"")}\"";
var escapedString = EscapeString($"{key}={value}");
return $"{flag} {escapedString}";
}

private static string EscapeString(string value)
{
return value.Replace("\\", "\\\\").Replace("\"", "\\\"");
if (String.IsNullOrEmpty(value))
{
return "";
}
// Dotnet escaping rules are weird here, we can only escape \ if it precedes a "
// 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.
// 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.
// https://docs.microsoft.com/en-us/dotnet/api/system.environment.getcommandlineargs?redirectedfrom=MSDN&view=net-6.0#remarks

// First, find any \ followed by a " and double the number of \ + 1.
value = Regex.Replace(value, @"(\\*)" + "\"", @"$1$1\" + "\"");
// 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
// Luckily, we can just use the $ character with detects the end of string in regex
value = Regex.Replace(value, @"(\\+)$", @"$1$1");
// Finally, wrap it in quotes
return $"\"{value}\"";
}
}
}
29 changes: 12 additions & 17 deletions src/Test/L0/Container/DockerUtilL0.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,13 +149,12 @@ public void ParseRegistryHostnameFromImageName(string input, string expected)
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
[InlineData("", "")]
[InlineData("HOME alpine:3.8 sh -c id #", "HOME alpine:3.8 sh -c id #")]
[InlineData("HOME \"alpine:3.8 sh -c id #", "HOME \\\"alpine:3.8 sh -c id #")]
[InlineData("HOME \\\"alpine:3.8 sh -c id #", "HOME \\\\\\\"alpine:3.8 sh -c id #")]
[InlineData("HOME \\\\\"alpine:3.8 sh -c id #", "HOME \\\\\\\\\\\"alpine:3.8 sh -c id #")]
[InlineData("HOME \"\"alpine:3.8 sh -c id #", "HOME \\\"\\\"alpine:3.8 sh -c id #")]
[InlineData("HOME \\\"\"alpine:3.8 sh -c id #", "HOME \\\\\\\"\\\"alpine:3.8 sh -c id #")]
[InlineData("HOME \"\\\"alpine:3.8 sh -c id #", "HOME \\\"\\\\\\\"alpine:3.8 sh -c id #")]
[InlineData("foo", "foo")]
[InlineData("foo \\ bar", "foo \\ bar")]
[InlineData("foo \\", "foo \\\\")]
[InlineData("foo \\\\", "foo \\\\\\\\")]
[InlineData("foo \\\" bar", "foo \\\\\" bar")]
[InlineData("foo \\\\\" bar", "foo \\\\\\\\\" bar")]
public void CreateEscapedOption_keyOnly(string input, string escaped)
{
var flag = "--example";
Expand All @@ -175,15 +174,11 @@ public void CreateEscapedOption_keyOnly(string input, string escaped)
[Theory]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
[InlineData("HOME", "", "HOME", "")]
[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 #")]
[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 #")]
[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 #")]
[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 #")]
[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 #")]
[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 #")]
[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 #")]
public void CreateEscapedOption_keyValue(string keyInput, string valueInput, string escapedKey, string escapedValue)
[InlineData("foo", "bar", "foo=bar")]
[InlineData("foo\\", "bar", "foo\\=bar")]
[InlineData("foo\\", "bar\\", "foo\\=bar\\\\")]
[InlineData("foo \\","bar \\", "foo \\\\=bar \\\\")]
public void CreateEscapedOption_keyValue(string keyInput, string valueInput, string escapedString)
{
var flag = "--example";
var actual = DockerUtil.CreateEscapedOption(flag, keyInput, valueInput);
Expand All @@ -194,7 +189,7 @@ public void CreateEscapedOption_keyValue(string keyInput, string valueInput, str
}
else
{
expected = $"{flag} \"{escapedKey}={escapedValue}\"";
expected = $"{flag} \"{escapedString}\"";
}
Assert.Equal(expected, actual);
}
Expand Down

0 comments on commit b6b210e

Please sign in to comment.