From 74fa51bcb47c99e4a34c7cca6c0f504de08593d6 Mon Sep 17 00:00:00 2001 From: Kayw Date: Fri, 17 Apr 2026 17:48:35 +0800 Subject: [PATCH 1/2] Quote PHP -d INI values so regex characters like " and ; survive --- src/Util/PHP/JobRunner.php | 24 +++++++++++++++++++++++- tests/unit/Util/PHP/JobRunnerTest.php | 14 ++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/Util/PHP/JobRunner.php b/src/Util/PHP/JobRunner.php index c70147bb0ba..27d01e223ce 100644 --- a/src/Util/PHP/JobRunner.php +++ b/src/Util/PHP/JobRunner.php @@ -26,8 +26,11 @@ use function is_resource; use function proc_close; use function proc_open; +use function str_replace; use function str_starts_with; use function stream_get_contents; +use function strpos; +use function substr; use function sys_get_temp_dir; use function tempnam; use function trim; @@ -325,9 +328,28 @@ private function settingsToParameters(array $settings): array foreach ($settings as $setting) { $buffer[] = '-d'; - $buffer[] = $setting; + $buffer[] = $this->quoteSettingValue($setting); } return $buffer; } + + /** + * Wraps the value portion of a "name=value" INI setting in double quotes + * so PHP's INI parser treats characters such as `;` (comment) and `"` + * (string delimiter) as literal data instead of metacharacters. + */ + private function quoteSettingValue(string $setting): string + { + $position = strpos($setting, '='); + + if ($position === false) { + return $setting; + } + + $name = substr($setting, 0, $position); + $value = substr($setting, $position + 1); + + return $name . '="' . str_replace('"', '\\"', $value) . '"'; + } } diff --git a/tests/unit/Util/PHP/JobRunnerTest.php b/tests/unit/Util/PHP/JobRunnerTest.php index 53c225011b7..17e5083455e 100644 --- a/tests/unit/Util/PHP/JobRunnerTest.php +++ b/tests/unit/Util/PHP/JobRunnerTest.php @@ -110,6 +110,20 @@ public static function provider(): Generator input: 'test', ), ]; + + $obfuscationRegex = '(?i)(?:(?:"|%22)?)(?:(?:old[-_]?|new[-_]?)?p(?:ass)?w(?:or)?d(?:1|2)?|pass(?:[-_]?phrase)?|secret|(?:api_?|private_?|public_?|access_?|secret_?)key(?:_?id)?|token|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)(?:(?:\s|%20)*(?:=|%3D)[^&]+|(?:"|%22)(?:\s|%20)*(?::|%3A)(?:\s|%20)*(?:"|%22)(?:%2[^2]|%[^2]|[^"%])+(?:"|%22))|bearer(?:\s|%20)+[a-z0-9\._\-]+|token(?::|%3A)[a-z0-9]{13}|gh[opsu]_[0-9a-zA-Z]{36}|ey[I-L](?:[\w=-]|%3D)+\.ey[I-L](?:[\w=-]|%3D)+(?:\.(?:[\w.+\/=-]|%3D|%2F|%2B)+)?|-{5}BEGIN(?:[a-z\s]|%20)+PRIVATE(?:\s|%20)KEY-{5}[^\-]+-{5}END(?:[a-z\s]|%20)+PRIVATE(?:\s|%20)KEY(?:-{5})?(?:\n|%0A)?'; + + yield 'php setting value containing INI metacharacters' => [ + new Result($obfuscationRegex, ''), + new Job( + <<<'EOT' + Date: Fri, 17 Apr 2026 17:59:22 +0800 Subject: [PATCH 2/2] Only quote INI value when it contains ; or " (preserve On/Off semantics) --- src/Util/PHP/JobRunner.php | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/Util/PHP/JobRunner.php b/src/Util/PHP/JobRunner.php index 27d01e223ce..7ec61d506af 100644 --- a/src/Util/PHP/JobRunner.php +++ b/src/Util/PHP/JobRunner.php @@ -26,6 +26,7 @@ use function is_resource; use function proc_close; use function proc_open; +use function str_contains; use function str_replace; use function str_starts_with; use function stream_get_contents; @@ -335,9 +336,14 @@ private function settingsToParameters(array $settings): array } /** - * Wraps the value portion of a "name=value" INI setting in double quotes - * so PHP's INI parser treats characters such as `;` (comment) and `"` - * (string delimiter) as literal data instead of metacharacters. + * Quotes the value portion of a "name=value" INI setting only when it + * contains characters PHP's INI parser would otherwise interpret as + * metacharacters (`;` starts a comment, `"` is a string delimiter). + * + * Quoting is avoided for plain values so that boolean keywords such as + * `On` / `Off` keep their special INI semantics; wrapping them in quotes + * turns them into the literal strings `"On"` / `"Off"` and breaks + * settings like `output_buffering`. */ private function quoteSettingValue(string $setting): string { @@ -347,9 +353,14 @@ private function quoteSettingValue(string $setting): string return $setting; } - $name = substr($setting, 0, $position); $value = substr($setting, $position + 1); + if (!str_contains($value, ';') && !str_contains($value, '"')) { + return $setting; + } + + $name = substr($setting, 0, $position); + return $name . '="' . str_replace('"', '\\"', $value) . '"'; } }