diff --git a/release_notes.md b/release_notes.md index 6c98c7eb73..93ed0a8aaa 100644 --- a/release_notes.md +++ b/release_notes.md @@ -33,3 +33,4 @@ - Update PowerShell 7.4 worker to [4.0.4021](https://github.com/Azure/azure-functions-powershell-worker/releases/tag/v4.0.4021) - Trim FunctionsNetHost artifacts (#10364) - Resolved thread safety issue in the `GrpcWorkerChannel.LoadResponse` method. (#10352) +- Worker termination path updated with sanitized logging (#10397) diff --git a/src/WebJobs.Script/Sanitizer.cs b/src/WebJobs.Script/Sanitizer.cs index fe798e2a15..0161eaada3 100644 --- a/src/WebJobs.Script/Sanitizer.cs +++ b/src/WebJobs.Script/Sanitizer.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. See License.txt in the project root for license information. using System; +using System.Text.RegularExpressions; using Newtonsoft.Json.Linq; namespace Microsoft.Azure.WebJobs.Logging @@ -20,6 +21,21 @@ internal static class Sanitizer internal static readonly string[] CredentialTokens = new string[] { "Token=", "DefaultEndpointsProtocol=http", "AccountKey=", "Data Source=", "Server=", "Password=", "pwd=", "&sig=", "&sig=", "?sig=", "SharedAccessKey=" }; private static readonly string[] CredentialNameFragments = new[] { "password", "pwd", "key", "secret", "token", "sas" }; + // Pattern of format : "://:@
:" + private static readonly string Pattern = @" + \b([a-zA-Z]+) # Capture protocol + :\/\/ # '://' + ([^:/\s]+) # Capture username + : # ':' + ([^@/\s]+) # Capture password + @ # '@' + ([^:/\s]+) # Capture address + : # ':' + ([0-9]+)\b # Capture port number + "; + + private static readonly Regex Regex = new Regex(Pattern, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); + /// /// Removes well-known credential strings from strings. /// @@ -73,6 +89,12 @@ internal static string Sanitize(string input) } } + // This check avoids unnecessary regex evaluation if the input does not contain any url + if (input.Contains(":")) + { + t = Regex.Replace(t, SecretReplacement); + } + return t; } @@ -153,6 +175,6 @@ static JToken Sanitize(JToken token) /// Checks if a string even *possibly* contains one of our . /// Useful for short-circuiting more expensive checks and replacements if it's known we wouldn't do anything. /// - internal static bool MayContainCredentials(string input) => input.Contains("="); + internal static bool MayContainCredentials(string input) => input.Contains("=") || input.Contains(":"); } } \ No newline at end of file diff --git a/src/WebJobs.Script/Workers/ProcessManagement/WorkerProcess.cs b/src/WebJobs.Script/Workers/ProcessManagement/WorkerProcess.cs index 79d048a824..1bee107af1 100644 --- a/src/WebJobs.Script/Workers/ProcessManagement/WorkerProcess.cs +++ b/src/WebJobs.Script/Workers/ProcessManagement/WorkerProcess.cs @@ -173,7 +173,8 @@ private void OnProcessExited(object sender, EventArgs e) else { string exceptionMessage = string.Join(",", _processStdErrDataQueue.Where(s => !string.IsNullOrEmpty(s))); - var processExitEx = new WorkerProcessExitException($"{Process.StartInfo.FileName} exited with code {Process.ExitCode} (0x{Process.ExitCode.ToString("X")})", new Exception(exceptionMessage)); + string sanitizedExceptionMessage = Sanitizer.Sanitize(exceptionMessage); + var processExitEx = new WorkerProcessExitException($"{Process.StartInfo.FileName} exited with code {Process.ExitCode} (0x{Process.ExitCode.ToString("X")})", new Exception(sanitizedExceptionMessage)); processExitEx.ExitCode = Process.ExitCode; processExitEx.Pid = Process.Id; HandleWorkerProcessExitError(processExitEx); diff --git a/test/WebJobs.Script.Tests/SanitizerTests.cs b/test/WebJobs.Script.Tests/SanitizerTests.cs index 57b7bb2d32..1e48da00b9 100644 --- a/test/WebJobs.Script.Tests/SanitizerTests.cs +++ b/test/WebJobs.Script.Tests/SanitizerTests.cs @@ -34,6 +34,10 @@ public class SanitizerTests [InlineData("SharedAccessKey=foo", "[Hidden Credential]")] [InlineData(@"Hey=AS1$@%#$%W-k2j"";SharedAccessKey=foo,Data Source=barzons,Server=bathouse'testing", @"Hey=AS1$@%#$%W-k2j"";[Hidden Credential]'testing")] [InlineData("test?sig=", "test[Hidden Credential]")] + [InlineData("aaa://aaa:aaaaaa1111aa@aaa.aaa.io:1111", "[Hidden Credential]")] + [InlineData("test,aaa://aaa:aaaaaa1111aa@aaa.aaa.io:1111,test", "test,[Hidden Credential],test")] + [InlineData(@"some text abc://abc:aaaaaa1111aa@aaa.abc.io:1111 some text abc://abc:aaaaaa1111aa@aaa.abc.io:1111 text", @"some text [Hidden Credential] some text [Hidden Credential] text")] + [InlineData(@"some text abc://abc:aaaaaa1111aa@aaa.abc.io:1111 some text AccountKey=heyyyyyyy text", @"some text [Hidden Credential] some text [Hidden Credential]")] public void SanitizeString(string input, string expectedOutput) { var sanitized = Sanitizer.Sanitize(input);