From 49ddf65515714c7f6c269465a64151a63b94f663 Mon Sep 17 00:00:00 2001 From: Riddhesh Sanghvi Date: Mon, 12 Oct 2026 19:20:26 +0530 Subject: [PATCH 1/2] fix(log): mask the secret rows of info tables in logged output site clone runs ee site create in a child process and logs its output, which ends with the new site's info table. The parent never sees the generated passwords, so the DB Password, DB Root Password and WordPress Password values reached ee.log. mask_secret_patterns() now masks the value cell of any table row whose label names a password, secret, token or API key. --- php/utils.php | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/php/utils.php b/php/utils.php index 84568c7ee..7451c69ad 100644 --- a/php/utils.php +++ b/php/utils.php @@ -1562,15 +1562,27 @@ function mask_secret_argv( $argv ) { /** * Last-resort masking for secrets EE has no value for, such as another host's site details or a value shorter than - * `EE::obfuscate()` accepts: `--=value` flags, `"": value` JSON members, mysql `-p` and - * openssl `pass:`. + * `EE::obfuscate()` accepts: `--=value` flags, `"": value` JSON members, mysql `-p`, + * openssl `pass:` and the value cell of a `| DB Password | value |` table row. * * @param string $text Text to be logged. * * @return string */ function mask_secret_patterns( $text ) { - if ( ! is_string( $text ) || ( false === strpos( $text, '-' ) && false === strpos( $text, '"' ) ) ) { + if ( ! is_string( $text ) ) { + return $text; + } + // A nested `ee site create` (clone) prints the new site's info table, whose values the parent process never saw. + if ( false !== strpos( $text, '|' ) ) { + $table = preg_replace( + '/(\|[ \t]*+[^|\n]*?(?:pass|pwd|secret|token|api[-_ ]?key)[^|\n]*+\|[ \t]*+)[^|\s][^|\n]*?([ \t]*+\|)/i', + '$1******$2', + $text + ); + $text = null === $table ? $text : $table; + } + if ( false === strpos( $text, '-' ) && false === strpos( $text, '"' ) ) { return $text; } From db1427439e259ca77a761e62ecf054ff42b57a26 Mon Sep 17 00:00:00 2001 From: Riddhesh Sanghvi Date: Mon, 12 Oct 2026 20:25:57 +0530 Subject: [PATCH 2/2] fix(log): match the secret keyword as a whole word of the table label A label like Cache bypass, Passenger or Tokenizer only contains a keyword, so its row was masked too. The keyword now has to be a word of the label (password, passwd, passphrase, pass, pwd, secret, token or API key, optionally plural). --- php/utils.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/php/utils.php b/php/utils.php index 7451c69ad..120d50050 100644 --- a/php/utils.php +++ b/php/utils.php @@ -1574,9 +1574,10 @@ function mask_secret_patterns( $text ) { return $text; } // A nested `ee site create` (clone) prints the new site's info table, whose values the parent process never saw. + // The keyword must be a whole word of the label, so rows like `Cache bypass` stay readable. if ( false !== strpos( $text, '|' ) ) { $table = preg_replace( - '/(\|[ \t]*+[^|\n]*?(?:pass|pwd|secret|token|api[-_ ]?key)[^|\n]*+\|[ \t]*+)[^|\s][^|\n]*?([ \t]*+\|)/i', + '/(\|[ \t]*+[^|\n]*?(?