From 57faba1b96b2dbee9ca9658d113ac57aa4ceeaff Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Tue, 20 Oct 2026 10:49:29 +0200 Subject: [PATCH] fix(logs): prevent URL redaction from spanning log lines (#12238) --- bootstrap/helpers/remoteProcess.php | 3 ++- tests/Unit/SanitizeLogsForExportTest.php | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/bootstrap/helpers/remoteProcess.php b/bootstrap/helpers/remoteProcess.php index aa5e92d2f1..f1f4638907 100644 --- a/bootstrap/helpers/remoteProcess.php +++ b/bootstrap/helpers/remoteProcess.php @@ -382,7 +382,8 @@ function remove_iip($text) // Generic URLs with passwords (covers database URLs, ftp, amqp, ssh, git basic auth, etc.) // (protocol://user:password@host → protocol://user:@host) - $text = preg_replace('/((?:https?|postgres|mysql|mongodb|rediss?|mariadb|ftp|sftp|ssh|amqp|amqps|ldap|ldaps|s3):\/\/[^:]+:)[^@]+(@)/i', '$1'.REDACTED.'$2', $text); + // User and password cannot contain whitespace, so a match never spans log lines. + $text = preg_replace('/((?:https?|postgres|mysql|mongodb|rediss?|mariadb|ftp|sftp|ssh|amqp|amqps|ldap|ldaps|s3):\/\/[^:\s@\/]+:)[^@\s]+(@)/i', '$1'.REDACTED.'$2', $text); // Email addresses $text = preg_replace('/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/', REDACTED, $text); diff --git a/tests/Unit/SanitizeLogsForExportTest.php b/tests/Unit/SanitizeLogsForExportTest.php index 88c5950896..0ba2f4ef77 100644 --- a/tests/Unit/SanitizeLogsForExportTest.php +++ b/tests/Unit/SanitizeLogsForExportTest.php @@ -184,3 +184,22 @@ it('removes generic URL passwords', function () { expect($result)->toBe($expected); } }); + +it('does not redact across lines from a URL to a later email', function () { + $recordA = '{"category":"api","url":"/","userAgent":"Mozilla/5.0 (compatible; +https://example.com/security-center/)","userId":null,"statusCode":200,"requestId":"11111111-1111-4111-8111-111111111111"}'; + $recordB = '{"email":"user@example.com","eventType":"login_attempt","message":"Authentication event: login_attempt for user@example.com - ATTEMPT"}'; + + $lines = explode("\n", sanitizeLogsForExport($recordA."\n".$recordB)); + + expect($lines)->toHaveCount(2); + expect($lines[0])->toBe($recordA); + expect($lines[1])->toBe('{"email":"'.REDACTED.'","eventType":"login_attempt","message":"Authentication event: login_attempt for '.REDACTED.' - ATTEMPT"}'); + expect(json_decode($lines[1], true))->toBeArray(); +}); + +it('still redacts URL passwords inside JSON log lines', function () { + $input = '{"msg":"connecting","dsn":"postgres://app:s3cr3t:pa/ss@db:5432/app"}'."\n".'{"email":"user@example.com"}'; + + expect(sanitizeLogsForExport($input)) + ->toBe('{"msg":"connecting","dsn":"postgres://app:'.REDACTED.'@db:5432/app"}'."\n".'{"email":"'.REDACTED.'"}'); +});