Add masks for multiline secrets from ::add-mask:: (#1521)

* Add mask for multiline secrets.

* .
This commit is contained in:
Tingluo Huang
2021-12-01 09:53:13 -05:00
committed by GitHub
parent 801a02ec89
commit b1ecffd707
3 changed files with 43 additions and 9 deletions

View File

@@ -381,6 +381,13 @@ namespace GitHub.Runner.Worker
HostContext.SecretMasker.AddValue(command.Data); HostContext.SecretMasker.AddValue(command.Data);
Trace.Info($"Add new secret mask with length of {command.Data.Length}"); Trace.Info($"Add new secret mask with length of {command.Data.Length}");
// Also add each individual line. Typically individual lines are processed from STDOUT of child processes.
var split = command.Data.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
foreach (var item in split)
{
HostContext.SecretMasker.AddValue(item);
}
} }
} }
} }

View File

@@ -22,12 +22,13 @@ namespace GitHub.Runner.Worker
{ {
private readonly TimeSpan _workerStartTimeout = TimeSpan.FromSeconds(30); private readonly TimeSpan _workerStartTimeout = TimeSpan.FromSeconds(30);
private ManualResetEvent _completedCommand = new ManualResetEvent(false); private ManualResetEvent _completedCommand = new ManualResetEvent(false);
// Do not mask the values of these secrets // Do not mask the values of these secrets
private static HashSet<String> SecretVariableMaskWhitelist = new HashSet<String>(StringComparer.OrdinalIgnoreCase){ private static HashSet<String> SecretVariableMaskWhitelist = new HashSet<String>(StringComparer.OrdinalIgnoreCase)
{
Constants.Variables.Actions.StepDebug, Constants.Variables.Actions.StepDebug,
Constants.Variables.Actions.RunnerDebug Constants.Variables.Actions.RunnerDebug
}; };
public async Task<int> RunAsync(string pipeIn, string pipeOut) public async Task<int> RunAsync(string pipeIn, string pipeOut)
{ {
@@ -138,10 +139,10 @@ namespace GitHub.Runner.Worker
HostContext.SecretMasker.AddValue(value); HostContext.SecretMasker.AddValue(value);
// Also add each individual line. Typically individual lines are processed from STDOUT of child processes. // Also add each individual line. Typically individual lines are processed from STDOUT of child processes.
var split = value.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); var split = value.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
foreach (var item in split) foreach (var item in split)
{ {
HostContext.SecretMasker.AddValue(item.Trim()); HostContext.SecretMasker.AddValue(item);
} }
} }
} }

View File

@@ -122,14 +122,14 @@ namespace GitHub.Runner.Common.Tests.Worker
{ {
_ec.Object.Global.EnvironmentVariables = new Dictionary<string, string>(); _ec.Object.Global.EnvironmentVariables = new Dictionary<string, string>();
var expressionValues = new DictionaryContextData var expressionValues = new DictionaryContextData
{ {
["env"] = ["env"] =
#if OS_WINDOWS #if OS_WINDOWS
new DictionaryContextData{ { Constants.Variables.Actions.AllowUnsupportedStopCommandTokens, new StringContextData(allowUnsupportedStopCommandTokens) }} new DictionaryContextData{ { Constants.Variables.Actions.AllowUnsupportedStopCommandTokens, new StringContextData(allowUnsupportedStopCommandTokens) }}
#else #else
new CaseSensitiveDictionaryContextData{ { Constants.Variables.Actions.AllowUnsupportedStopCommandTokens, new StringContextData(allowUnsupportedStopCommandTokens) }} new CaseSensitiveDictionaryContextData { { Constants.Variables.Actions.AllowUnsupportedStopCommandTokens, new StringContextData(allowUnsupportedStopCommandTokens) } }
#endif #endif
}; };
_ec.Setup(x => x.ExpressionValues).Returns(expressionValues); _ec.Setup(x => x.ExpressionValues).Returns(expressionValues);
_ec.Setup(x => x.JobTelemetry).Returns(new List<JobTelemetry>()); _ec.Setup(x => x.JobTelemetry).Returns(new List<JobTelemetry>());
@@ -418,6 +418,31 @@ namespace GitHub.Runner.Common.Tests.Worker
} }
} }
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void AddMaskWithMultilineValue()
{
using (TestHostContext hc = CreateTestContext())
{
// Act
_commandManager.TryProcessCommand(_ec.Object, $"::add-mask::abc%0Ddef%0Aghi%0D%0Ajkl", null);
_commandManager.TryProcessCommand(_ec.Object, $"::add-mask:: %0D %0A %0D%0A %0D", null);
// Assert
Assert.Equal("***", hc.SecretMasker.MaskSecrets("abc"));
Assert.Equal("***", hc.SecretMasker.MaskSecrets("def"));
Assert.Equal("***", hc.SecretMasker.MaskSecrets("ghi"));
Assert.Equal("***", hc.SecretMasker.MaskSecrets("jkl"));
Assert.Equal("***", hc.SecretMasker.MaskSecrets("abc\rdef\nghi\r\njkl"));
Assert.Equal("", hc.SecretMasker.MaskSecrets(""));
Assert.Equal(" ", hc.SecretMasker.MaskSecrets(" "));
Assert.Equal(" ", hc.SecretMasker.MaskSecrets(" "));
Assert.Equal(" ", hc.SecretMasker.MaskSecrets(" "));
Assert.Equal(" ", hc.SecretMasker.MaskSecrets(" "));
}
}
private TestHostContext CreateTestContext([CallerMemberName] string testName = "") private TestHostContext CreateTestContext([CallerMemberName] string testName = "")
{ {
var hostContext = new TestHostContext(this, testName); var hostContext = new TestHostContext(this, testName);
@@ -431,6 +456,7 @@ namespace GitHub.Runner.Common.Tests.Worker
new InternalPluginSetRepoPathCommandExtension(), new InternalPluginSetRepoPathCommandExtension(),
new SetEnvCommandExtension(), new SetEnvCommandExtension(),
new WarningCommandExtension(), new WarningCommandExtension(),
new AddMaskCommandExtension(),
}; };
foreach (var command in commands) foreach (var command in commands)
{ {