Skip to content

Commit

Permalink
Port hotfix to main branch (actions#2108)
Browse files Browse the repository at this point in the history
* fix issue with env's overwriting environment

* add release notes

* handle value escaping

* compile regex for runtime perf improvements
  • Loading branch information
thboop authored and nikola-jokic committed May 12, 2023
1 parent a419d6c commit 43853e5
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 21 deletions.
2 changes: 1 addition & 1 deletion releaseNote.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
## Bugs
- Fixed an issue where job and service container envs were corrupted (#2091)
- Fixed an issue where self hosted environments had their docker env's overwritten (#2107)
## Misc

## Windows x64
Expand Down
13 changes: 3 additions & 10 deletions src/Runner.Worker/Container/DockerCommandManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@ public async Task<int> DockerBuild(IExecutionContext context, string workingDire
public async Task<string> DockerCreate(IExecutionContext context, ContainerInfo container)
{
IList<string> dockerOptions = new List<string>();
IDictionary<string, string> environment = new Dictionary<string, string>();
// OPTIONS
dockerOptions.Add($"--name {container.ContainerDisplayName}");
dockerOptions.Add($"--label {DockerInstanceLabel}");
Expand Down Expand Up @@ -136,8 +135,7 @@ public async Task<string> DockerCreate(IExecutionContext context, ContainerInfo
}
else
{
environment.Add(env.Key, env.Value);
dockerOptions.Add(DockerUtil.CreateEscapedOption("-e", env.Key));
dockerOptions.Add(DockerUtil.CreateEscapedOption("-e", env.Key, env.Value));
}
}

Expand Down Expand Up @@ -185,7 +183,7 @@ public async Task<string> DockerCreate(IExecutionContext context, ContainerInfo
dockerOptions.Add($"{container.ContainerEntryPointArgs}");

var optionsString = string.Join(" ", dockerOptions);
List<string> outputStrings = await ExecuteDockerCommandAsync(context, "create", optionsString, environment);
List<string> outputStrings = await ExecuteDockerCommandAsync(context, "create", optionsString);

return outputStrings.FirstOrDefault();
}
Expand Down Expand Up @@ -445,11 +443,6 @@ public Task<int> DockerLogin(IExecutionContext context, string configFileDirecto
}

private async Task<List<string>> ExecuteDockerCommandAsync(IExecutionContext context, string command, string options)
{
return await ExecuteDockerCommandAsync(context, command, options, null);
}

private async Task<List<string>> ExecuteDockerCommandAsync(IExecutionContext context, string command, string options, IDictionary<string, string> environment)
{
string arg = $"{command} {options}".Trim();
context.Command($"{DockerPath} {arg}");
Expand Down Expand Up @@ -477,7 +470,7 @@ await processInvoker.ExecuteAsync(
workingDirectory: context.GetGitHubContext("workspace"),
fileName: DockerPath,
arguments: arg,
environment: environment,
environment: null,
requireExitCodeZero: true,
outputEncoding: null,
cancellationToken: CancellationToken.None);
Expand Down
32 changes: 30 additions & 2 deletions src/Runner.Worker/Container/DockerUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ namespace GitHub.Runner.Worker.Container
{
public class DockerUtil
{
private static readonly Regex QuoteEscape = new Regex(@"(\\*)" + "\"", RegexOptions.Compiled);
private static readonly Regex EndOfStringEscape = new Regex(@"(\\+)$", RegexOptions.Compiled);

public static List<PortMapping> ParseDockerPort(IList<string> portMappingLines)
{
const string targetPort = "targetPort";
Expand Down Expand Up @@ -68,12 +71,37 @@ 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)
{
if (String.IsNullOrEmpty(key))
{
return "";
}
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 = QuoteEscape.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 = EndOfStringEscape.Replace(value, @"$1$1");
// Finally, wrap it in quotes
return $"\"{value}\"";
}
}
}
36 changes: 29 additions & 7 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 @@ -171,5 +170,28 @@ public void CreateEscapedOption_keyOnly(string input, string escaped)
}
Assert.Equal(expected, actual);
}

[Theory]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
[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);
string expected;
if (String.IsNullOrEmpty(keyInput))
{
expected = "";
}
else
{
expected = $"{flag} \"{escapedString}\"";
}
Assert.Equal(expected, actual);
}
}
}
2 changes: 1 addition & 1 deletion src/runnerversion
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.296.1
2.296.2

0 comments on commit 43853e5

Please sign in to comment.