@@ -17,6 +17,8 @@ namespace VirtualClient.Contracts
17
17
using Newtonsoft . Json ;
18
18
using Newtonsoft . Json . Linq ;
19
19
using NUnit . Framework ;
20
+ using Polly ;
21
+ using VirtualClient . Common ;
20
22
using VirtualClient . Common . Contracts ;
21
23
using VirtualClient . Common . Extensions ;
22
24
using VirtualClient . Common . Telemetry ;
@@ -991,7 +993,7 @@ public async Task LogProcessDetailsAsyncExtensionEmitsTheExpectedProcessInformat
991
993
StandardOutput = expectedStandardOutput != null ? new Common . ConcurrentBuffer ( new StringBuilder ( expectedStandardOutput ) ) : null ,
992
994
StandardError = expectedStandardError != null ? new Common . ConcurrentBuffer ( new StringBuilder ( expectedStandardError ) ) : null
993
995
} ;
994
-
996
+
995
997
string expectedResults = "Any results output by the process." ;
996
998
bool expectedProcessDetailsCaptured = false ;
997
999
bool expectedProcessResultsCaptured = false ;
@@ -1000,7 +1002,7 @@ public async Task LogProcessDetailsAsyncExtensionEmitsTheExpectedProcessInformat
1000
1002
{
1001
1003
Assert . AreEqual ( LogLevel . Information , level , $ "Log level not matched") ;
1002
1004
Assert . IsInstanceOf < EventContext > ( state ) ;
1003
-
1005
+
1004
1006
if ( eventInfo . Name == $ "{ nameof ( TestExecutor ) } .ProcessDetails")
1005
1007
{
1006
1008
Assert . IsTrue ( ( state as EventContext ) . Properties . TryGetValue ( "process" , out object processContext ) ) ;
@@ -1046,6 +1048,73 @@ public async Task LogProcessDetailsAsyncExtensionEmitsTheExpectedProcessInformat
1046
1048
Assert . IsTrue ( expectedProcessResultsCaptured ) ;
1047
1049
}
1048
1050
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
+
1049
1118
[ Test ]
1050
1119
[ TestCase ( 0 , null , null , null , null , null ) ]
1051
1120
[ TestCase ( 0 , "" , "" , "" , "" , "" ) ]
0 commit comments