2828use function is_resource ;
2929use function proc_close ;
3030use function proc_open ;
31+ use function sprintf ;
3132use function str_contains ;
3233use function str_replace ;
3334use function str_starts_with ;
@@ -321,6 +322,8 @@ private function cliIniOverrides(array $alreadySet): array
321322 /**
322323 * @param list<string> $settings
323324 *
325+ * @throws PhpProcessException
326+ *
324327 * @return list<string>
325328 */
326329 private function settingsToParameters (array $ settings ): array
@@ -329,23 +332,31 @@ private function settingsToParameters(array $settings): array
329332
330333 foreach ($ settings as $ setting ) {
331334 $ buffer [] = '-d ' ;
332- $ buffer [] = $ this ->quoteSettingValue ($ setting );
335+ $ buffer [] = $ this ->processSettingValue ($ setting );
333336 }
334337
335338 return $ buffer ;
336339 }
337340
338341 /**
339- * Quotes the value portion of a "name=value" INI setting only when it
340- * contains characters PHP's INI parser would otherwise interpret as
341- * metacharacters (`;` starts a comment, `"` is a string delimiter).
342+ * Rejects "name=value" INI settings whose value contains a line-break
343+ * character. A newline cannot legitimately appear in a PHP INI value and
344+ * would, if forwarded unchanged, be parsed by the child process as a
345+ * directive separator — turning a single setting into an attacker-
346+ * controlled sequence of directives.
347+ *
348+ * Otherwise quotes the value portion only when it contains characters
349+ * PHP's INI parser would interpret as metacharacters (`;` starts a
350+ * comment, `"` is a string delimiter).
342351 *
343352 * Quoting is avoided for plain values so that boolean keywords such as
344353 * `On` / `Off` keep their special INI semantics; wrapping them in quotes
345354 * turns them into the literal strings `"On"` / `"Off"` and breaks
346355 * settings like `output_buffering`.
356+ *
357+ * @throws PhpProcessException
347358 */
348- private function quoteSettingValue (string $ setting ): string
359+ private function processSettingValue (string $ setting ): string
349360 {
350361 $ parts = explode ('= ' , $ setting , 2 );
351362
@@ -355,6 +366,15 @@ private function quoteSettingValue(string $setting): string
355366
356367 [$ name , $ value ] = $ parts ;
357368
369+ if (str_contains ($ value , "\n" ) || str_contains ($ value , "\r" )) {
370+ throw new PhpProcessException (
371+ sprintf (
372+ 'PHP setting "%s" contains a line-break character, which is not permitted ' ,
373+ $ name ,
374+ ),
375+ );
376+ }
377+
358378 if (!str_contains ($ value , '; ' ) && !str_contains ($ value , '" ' )) {
359379 return $ setting ;
360380 }
0 commit comments