Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 34 additions & 1 deletion src/Util/PHP/JobRunner.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,12 @@
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;
use function strpos;
use function substr;
use function sys_get_temp_dir;
use function tempnam;
use function trim;
Expand Down Expand Up @@ -325,9 +329,38 @@ private function settingsToParameters(array $settings): array

foreach ($settings as $setting) {
$buffer[] = '-d';
$buffer[] = $setting;
$buffer[] = $this->quoteSettingValue($setting);
}

return $buffer;
}

/**
* 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
{
$position = strpos($setting, '=');

if ($position === false) {
return $setting;
}

$value = substr($setting, $position + 1);

if (!str_contains($value, ';') && !str_contains($value, '"')) {
return $setting;
}

$name = substr($setting, 0, $position);

return $name . '="' . str_replace('"', '\\"', $value) . '"';
}
}
14 changes: 14 additions & 0 deletions tests/unit/Util/PHP/JobRunnerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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'
<?php declare(strict_types=1);
print ini_get('highlight.string');

EOT,
phpSettings: ['highlight.string=' . $obfuscationRegex],
),
];
}

#[DataProvider('provider')]
Expand Down