2323import java .io .IOException ;
2424import java .io .InputStream ;
2525import java .nio .charset .StandardCharsets ;
26+ import java .util .regex .Matcher ;
27+ import java .util .regex .Pattern ;
2628
2729import org .apache .commons .io .IOUtils ;
2830import org .apache .commons .lang3 .StringUtils ;
@@ -40,6 +42,23 @@ public class SshHelper {
4042 private static final int DEFAULT_CONNECT_TIMEOUT = 180000 ;
4143 private static final int DEFAULT_KEX_TIMEOUT = 60000 ;
4244 private static final int DEFAULT_WAIT_RESULT_TIMEOUT = 120000 ;
45+ private static final String MASKED_VALUE = "*****" ;
46+
47+ private static final Pattern [] SENSITIVE_COMMAND_PATTERNS = new Pattern [] {
48+ Pattern .compile ("(?i)(\\ s+-p\\ s+['\" ])([^'\" ]*)(['\" ])" ),
49+ Pattern .compile ("(?i)(\\ s+-p\\ s+)([^\\ s]+)" ),
50+ Pattern .compile ("(?i)(\\ s+-p=['\" ])([^'\" ]*)(['\" ])" ),
51+ Pattern .compile ("(?i)(\\ s+-p=)([^\\ s]+)" ),
52+ Pattern .compile ("(?i)(--password=['\" ])([^'\" ]*)(['\" ])" ),
53+ Pattern .compile ("(?i)(--password=)([^\\ s]+)" ),
54+ Pattern .compile ("(?i)(--password\\ s+['\" ])([^'\" ]*)(['\" ])" ),
55+ Pattern .compile ("(?i)(--password\\ s+)([^\\ s]+)" ),
56+ Pattern .compile ("(?i)(\\ s+-u\\ s+['\" ][^,'\" :]+[,:])([^'\" ]*)(['\" ])" ),
57+ Pattern .compile ("(?i)(\\ s+-u\\ s+[^\\ s,:]+[,:])([^\\ s]+)" ),
58+ Pattern .compile ("(?i)(\\ s+-s\\ s+['\" ])([^'\" ]*)(['\" ])" ),
59+ Pattern .compile ("(?i)(\\ s+-s\\ s+)([^\\ s]+)" ),
60+
61+ };
4362
4463 protected static Logger LOGGER = LogManager .getLogger (SshHelper .class );
4564
@@ -145,7 +164,7 @@ public static void scpTo(String host, int port, String user, File pemKeyFile, St
145164 }
146165
147166 public static void scpTo (String host , int port , String user , File pemKeyFile , String password , String remoteTargetDirectory , String [] localFiles , String fileMode ,
148- int connectTimeoutInMs , int kexTimeoutInMs ) throws Exception {
167+ int connectTimeoutInMs , int kexTimeoutInMs ) throws Exception {
149168
150169 com .trilead .ssh2 .Connection conn = null ;
151170 com .trilead .ssh2 .SCPClient scpClient = null ;
@@ -291,13 +310,16 @@ public static Pair<Boolean, String> sshExecute(String host, int port, String use
291310 }
292311
293312 if (sess .getExitStatus () == null ) {
294- //Exit status is NOT available. Returning failure result.
295- LOGGER .error (String .format ("SSH execution of command %s has no exit status set. Result output: %s" , command , result ));
313+ // Exit status is NOT available. Returning failure result.
314+ LOGGER .error (String .format ("SSH execution of command %s has no exit status set. Result output: %s" ,
315+ sanitizeForLogging (command ), sanitizeForLogging (result )));
296316 return new Pair <Boolean , String >(false , result );
297317 }
298318
299319 if (sess .getExitStatus () != null && sess .getExitStatus ().intValue () != 0 ) {
300- LOGGER .error (String .format ("SSH execution of command %s has an error status code in return. Result output: %s" , command , result ));
320+ LOGGER .error (String .format (
321+ "SSH execution of command %s has an error status code in return. Result output: %s" ,
322+ sanitizeForLogging (command ), sanitizeForLogging (result )));
301323 return new Pair <Boolean , String >(false , result );
302324 }
303325 return new Pair <Boolean , String >(true , result );
@@ -366,4 +388,47 @@ protected static void throwSshExceptionIfStdoutOrStdeerIsNull(InputStream stdout
366388 throw new SshException (msg );
367389 }
368390 }
391+
392+ private static String sanitizeForLogging (String value ) {
393+ if (value == null ) {
394+ return null ;
395+ }
396+ String masked = maskSensitiveValue (value );
397+ String cleaned = com .cloud .utils .StringUtils .cleanString (masked );
398+ if (StringUtils .isBlank (cleaned )) {
399+ return masked ;
400+ }
401+ return cleaned ;
402+ }
403+
404+ private static String maskSensitiveValue (String value ) {
405+ String masked = value ;
406+ for (Pattern pattern : SENSITIVE_COMMAND_PATTERNS ) {
407+ masked = replaceWithMask (masked , pattern );
408+ }
409+ return masked ;
410+ }
411+
412+ private static String replaceWithMask (String value , Pattern pattern ) {
413+ Matcher matcher = pattern .matcher (value );
414+ if (!matcher .find ()) {
415+ return value ;
416+ }
417+
418+ StringBuffer buffer = new StringBuffer ();
419+ do {
420+ StringBuilder replacement = new StringBuilder ();
421+ replacement .append (matcher .group (1 ));
422+ if (matcher .groupCount () >= 3 ) {
423+ replacement .append (MASKED_VALUE );
424+ replacement .append (matcher .group (matcher .groupCount ()));
425+ } else {
426+ replacement .append (MASKED_VALUE );
427+ }
428+ matcher .appendReplacement (buffer , Matcher .quoteReplacement (replacement .toString ()));
429+ } while (matcher .find ());
430+
431+ matcher .appendTail (buffer );
432+ return buffer .toString ();
433+ }
369434}
0 commit comments