Skip to content

Commit 33f2bd6

Browse files
saibulususaibulusu
andauthored
Obscuring secrets in Log Process Details (#472)
* Obscuring secrets in SysbenchClientExecutor * Obscuring full command. * Updating version. * obscuring process arguments in log process details. * Moving SensitiveData.ObscureSecrets to LogProcessDetails in telemetry. * Obscuring secrets in stack trace and error message. * Fixes. * Fixes. * Adding a unit test for error logging. * Fixing unit tests. * Adding more to unit test. --------- Co-authored-by: saibulusu <[email protected]>
1 parent 0e38773 commit 33f2bd6

File tree

5 files changed

+87
-6
lines changed

5 files changed

+87
-6
lines changed

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.16.39
1+
1.16.39

src/VirtualClient/VirtualClient.Common/Telemetry/EventContextExtensions.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public static EventContext AddError(this EventContext context, Exception error,
4242
errors.Add(new
4343
{
4444
errorType = currentError.GetType().FullName,
45-
errorMessage = currentError.Message
45+
errorMessage = SensitiveData.ObscureSecrets(currentError.Message)
4646
});
4747

4848
currentError = currentError.InnerException;
@@ -65,11 +65,11 @@ public static EventContext AddError(this EventContext context, Exception error,
6565
{
6666
if (error.StackTrace.Length > maxCallStackLength)
6767
{
68-
context.Properties[EventContextExtensions.ErrorCallstackProperty] = error.StackTrace.Substring(0, maxCallStackLength);
68+
context.Properties[EventContextExtensions.ErrorCallstackProperty] = SensitiveData.ObscureSecrets(error.StackTrace.Substring(0, maxCallStackLength));
6969
}
7070
else
7171
{
72-
context.Properties[EventContextExtensions.ErrorCallstackProperty] = error.StackTrace;
72+
context.Properties[EventContextExtensions.ErrorCallstackProperty] = SensitiveData.ObscureSecrets(error.StackTrace);
7373
}
7474
}
7575
}

src/VirtualClient/VirtualClient.Contracts.UnitTests/VirtualClientLoggingExtensionsTests.cs

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ namespace VirtualClient.Contracts
1717
using Newtonsoft.Json;
1818
using Newtonsoft.Json.Linq;
1919
using NUnit.Framework;
20+
using Polly;
21+
using VirtualClient.Common;
2022
using VirtualClient.Common.Contracts;
2123
using VirtualClient.Common.Extensions;
2224
using VirtualClient.Common.Telemetry;
@@ -991,7 +993,7 @@ public async Task LogProcessDetailsAsyncExtensionEmitsTheExpectedProcessInformat
991993
StandardOutput = expectedStandardOutput != null ? new Common.ConcurrentBuffer(new StringBuilder(expectedStandardOutput)) : null,
992994
StandardError = expectedStandardError != null ? new Common.ConcurrentBuffer(new StringBuilder(expectedStandardError)) : null
993995
};
994-
996+
995997
string expectedResults = "Any results output by the process.";
996998
bool expectedProcessDetailsCaptured = false;
997999
bool expectedProcessResultsCaptured = false;
@@ -1000,7 +1002,7 @@ public async Task LogProcessDetailsAsyncExtensionEmitsTheExpectedProcessInformat
10001002
{
10011003
Assert.AreEqual(LogLevel.Information, level, $"Log level not matched");
10021004
Assert.IsInstanceOf<EventContext>(state);
1003-
1005+
10041006
if (eventInfo.Name == $"{nameof(TestExecutor)}.ProcessDetails")
10051007
{
10061008
Assert.IsTrue((state as EventContext).Properties.TryGetValue("process", out object processContext));
@@ -1046,6 +1048,73 @@ public async Task LogProcessDetailsAsyncExtensionEmitsTheExpectedProcessInformat
10461048
Assert.IsTrue(expectedProcessResultsCaptured);
10471049
}
10481050

1051+
[Test]
1052+
[TestCase(0, "run password=secret123", null)]
1053+
[TestCase(1, "run password=secret123", "run password=secret123")]
1054+
public async Task LogProcessDetailsAsyncObscuresSecrets(int exitCode, string standardOutput, string standardError)
1055+
{
1056+
InMemoryProcess process = new InMemoryProcess
1057+
{
1058+
ExitCode = exitCode,
1059+
StartInfo = new ProcessStartInfo
1060+
{
1061+
FileName = "run",
1062+
Arguments = "password=secret123"
1063+
},
1064+
StandardOutput = new ConcurrentBuffer(new StringBuilder(standardOutput)),
1065+
StandardError = new ConcurrentBuffer(new StringBuilder(standardError))
1066+
};
1067+
1068+
this.mockFixture.Logger.OnLog = (level, eventInfo, state, error) =>
1069+
{
1070+
if (eventInfo.Name == $"{nameof(TestExecutor)}.ProcessDetails")
1071+
{
1072+
(state as EventContext).Properties.TryGetValue("process", out object processContext);
1073+
string actualProcessInfo = processContext.ToJson();
1074+
1075+
Assert.IsFalse(actualProcessInfo.ToString().Contains("secret123"));
1076+
}
1077+
};
1078+
1079+
TestExecutor component = new TestExecutor(this.mockFixture);
1080+
await component.LogProcessDetailsAsync(process, new EventContext(Guid.NewGuid()), results: new List<string> { }, logToTelemetry: true)
1081+
.ConfigureAwait(false);
1082+
}
1083+
1084+
[Test]
1085+
public void LogErrorMessageObscuresSecrets()
1086+
{
1087+
Exception expectedError = null;
1088+
try
1089+
{
1090+
// To ensure a call stack is included.
1091+
throw new Exception("An error occurred, password=secret123");
1092+
}
1093+
catch (Exception exc)
1094+
{
1095+
expectedError = exc;
1096+
}
1097+
1098+
this.mockLogger.Object.LogErrorMessage(expectedError, this.mockEventContext);
1099+
1100+
this.mockLogger
1101+
.Setup(logger => logger.Log(LogLevel.Error, It.IsAny<EventId>(), It.IsAny<EventContext>(), null, null))
1102+
.Callback<LogLevel, EventId, EventContext, Exception, Func<EventContext, Exception, string>>((level, eventId, state, exc, formatter) =>
1103+
{
1104+
Assert.IsNotNull(state);
1105+
Assert.IsTrue(state.Properties.ContainsKey("error"));
1106+
Assert.IsTrue(state.Properties.ContainsKey("errorCallstack"));
1107+
1108+
List<object> errorEntries = state.Properties["error"] as List<object>;
1109+
Assert.IsNotNull(errorEntries);
1110+
Assert.IsTrue(errorEntries.Count == 1);
1111+
1112+
Assert.IsFalse(JsonConvert.SerializeObject(errorEntries.First()).Contains("secret123"));
1113+
});
1114+
1115+
this.mockLogger.Object.LogErrorMessage(expectedError, this.mockEventContext);
1116+
}
1117+
10491118
[Test]
10501119
[TestCase(0, null, null, null, null, null)]
10511120
[TestCase(0, "", "", "", "", "")]

src/VirtualClient/VirtualClient.Contracts/VirtualClientLoggingExtensions.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,8 @@ public static void LogErrorMessage(this ILogger logger, string errorMessage, Exc
369369
eventContext.ThrowIfNull(nameof(eventContext));
370370
errorMessage.ThrowIfNullOrWhiteSpace(nameof(errorMessage));
371371

372+
errorMessage = SensitiveData.ObscureSecrets(errorMessage);
373+
372374
if (error != null)
373375
{
374376
EventContext errorContext = eventContext.Clone();

src/VirtualClient/VirtualClient.Core/VirtualClientLoggingExtensions.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,11 @@ internal static void LogProcessDetails(this ILogger logger, ProcessDetails proce
174174

175175
try
176176
{
177+
// Obscure sensitive data in the command line
178+
processDetails.CommandLine = SensitiveData.ObscureSecrets(processDetails.CommandLine);
179+
processDetails.StandardOutput = SensitiveData.ObscureSecrets(processDetails.StandardOutput);
180+
processDetails.StandardError = SensitiveData.ObscureSecrets(processDetails.StandardError);
181+
177182
// Examples:
178183
// --------------
179184
// GeekbenchExecutor.ProcessDetails
@@ -229,6 +234,11 @@ internal static void LogProcessDetails(this ILogger logger, IProcessProxy proces
229234

230235
try
231236
{
237+
// Obscure sensitive data in the command line
238+
process.ProcessDetails.CommandLine = SensitiveData.ObscureSecrets(process.ProcessDetails.CommandLine);
239+
process.ProcessDetails.StandardOutput = SensitiveData.ObscureSecrets(process.ProcessDetails.StandardOutput);
240+
process.ProcessDetails.StandardError = SensitiveData.ObscureSecrets(process.ProcessDetails.StandardError);
241+
232242
// Examples:
233243
// --------------
234244
// GeekbenchExecutor.ProcessDetails

0 commit comments

Comments
 (0)