diff --git a/benchmarks/WebJobs.Script.Benchmarks/SanitizerBenchmarks.cs b/benchmarks/WebJobs.Script.Benchmarks/SanitizerBenchmarks.cs new file mode 100644 index 000000000..b8a04fede --- /dev/null +++ b/benchmarks/WebJobs.Script.Benchmarks/SanitizerBenchmarks.cs @@ -0,0 +1,17 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using BenchmarkDotNet.Attributes; +using Microsoft.Azure.WebJobs.Logging; + +namespace Microsoft.Azure.WebJobs.Script.Benchmarks +{ + public class SanitizerBenchmarks + { + [Benchmark] + public void Sanitize() + { + Sanitizer.Sanitize("testprotocol://name:password@address:1111"); + } + } +} diff --git a/release_notes.md b/release_notes.md index f3187da67..6e83f4280 100644 --- a/release_notes.md +++ b/release_notes.md @@ -15,3 +15,4 @@ - Updated dotnet-isolated worker to [1.0.11](https://github.com/Azure/azure-functions-dotnet-worker/pull/2653) (#10379) - Update Java Worker Version to [2.15.0](https://github.com/Azure/azure-functions-java-worker/releases/tag/2.15.0) - Update grpc-protobuf to 1.64.0 and application insights agent version to 3.5.2 +- Worker termination path updated with sanitized logging (#10367) \ No newline at end of file diff --git a/src/WebJobs.Script/Sanitizer.cs b/src/WebJobs.Script/Sanitizer.cs index 5166e0b6c..ffff40c9b 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 @@ namespace Microsoft.Azure.WebJobs.Logging internal static readonly string[] CredentialTokens = new string[] { "Token=", "DefaultEndpointsProtocol=http", "AccountKey=", "Data Source=", "Server=", "Password=", "pwd=", "&sig=", "&sig=", "?sig=", "SharedAccessKey=", "&code=", "&code=", "?code=" }; 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 @@ namespace Microsoft.Azure.WebJobs.Logging } } + // 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 @@ namespace Microsoft.Azure.WebJobs.Logging /// 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 79d048a82..1bee107af 100644 --- a/src/WebJobs.Script/Workers/ProcessManagement/WorkerProcess.cs +++ b/src/WebJobs.Script/Workers/ProcessManagement/WorkerProcess.cs @@ -173,7 +173,8 @@ namespace Microsoft.Azure.WebJobs.Script.Workers 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 f94f2f7bd..031a86dbb 100644 --- a/test/WebJobs.Script.Tests/SanitizerTests.cs +++ b/test/WebJobs.Script.Tests/SanitizerTests.cs @@ -37,6 +37,10 @@ namespace Microsoft.Azure.WebJobs.Script.Tests [InlineData("test?code=XPAAAAAAAAAAAAAT-ag==", "test[Hidden Credential]")] [InlineData("test?foo=bar&code=REAAAAAAAAAAAAAT-ag==", "test?foo=bar[Hidden Credential]")] [InlineData("test&code=MiAAAAAAAAAAAAAAAAT-ag==", "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);