Patch PHPUnit - GHSA-qrr6-mg7r-m243
This commit is contained in:
@@ -222,7 +222,9 @@
|
||||
"TijsVerkoyen\\CssToInlineStyles\\": "overrides/tijsverkoyen/css-to-inline-styles/src/",
|
||||
"TijsVerkoyen\\CssToInlineStyles\\Css\\Property\\": "overrides/tijsverkoyen/css-to-inline-styles/src/Css/Property/",
|
||||
"Html2Text\\": "overrides/html2text/html2text/src/",
|
||||
"PHPUnit\\Runner\\": "overrides/phpunit/phpunit/src/Runner/"
|
||||
"PHPUnit\\Runner\\": "overrides/phpunit/phpunit/src/Runner/",
|
||||
"PHPUnit\\Util\\PHP\\": "overrides/phpunit/phpunit/src/Util/PHP/",
|
||||
"PHPUnit\\Util\\Xml\\": "overrides/phpunit/phpunit/src/Util/Xml/"
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"Modules/",
|
||||
@@ -516,7 +518,9 @@
|
||||
"vendor/tijsverkoyen/css-to-inline-styles/src/CssToInlineStyles.php",
|
||||
"vendor/tijsverkoyen/css-to-inline-styles/src/Css/Property/Processor.php",
|
||||
"vendor/html2text/html2text/src/Html2Text.php",
|
||||
"vendor/phpunit/phpunit/src/Runner/PhptTestCase.php"
|
||||
"vendor/phpunit/phpunit/src/Runner/PhpTestCase.php",
|
||||
"vendor/phpunit/phpunit/src/Util/PHP/AbstractPhpProcess.php",
|
||||
"vendor/phpunit/phpunit/src/Util/Xml/Loader.php"
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
|
||||
460
overrides/phpunit/phpunit/src/Util/PHP/AbstractPhpProcess.php
Normal file
460
overrides/phpunit/phpunit/src/Util/PHP/AbstractPhpProcess.php
Normal file
@@ -0,0 +1,460 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of PHPUnit.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace PHPUnit\Util\PHP;
|
||||
|
||||
use const DIRECTORY_SEPARATOR;
|
||||
use const PHP_SAPI;
|
||||
use function array_keys;
|
||||
use function array_merge;
|
||||
use function assert;
|
||||
use function escapeshellarg;
|
||||
use function ini_get_all;
|
||||
use function restore_error_handler;
|
||||
use function set_error_handler;
|
||||
use function sprintf;
|
||||
use function str_replace;
|
||||
use function strpos;
|
||||
use function strrpos;
|
||||
use function substr;
|
||||
use function trim;
|
||||
use function unserialize;
|
||||
use __PHP_Incomplete_Class;
|
||||
use ErrorException;
|
||||
use PHPUnit\Framework\AssertionFailedError;
|
||||
use PHPUnit\Framework\Exception;
|
||||
use PHPUnit\Framework\SyntheticError;
|
||||
use PHPUnit\Framework\Test;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use PHPUnit\Framework\TestFailure;
|
||||
use PHPUnit\Framework\TestResult;
|
||||
use SebastianBergmann\Environment\Runtime;
|
||||
|
||||
/**
|
||||
* @internal This class is not covered by the backward compatibility promise for PHPUnit
|
||||
*/
|
||||
abstract class AbstractPhpProcess
|
||||
{
|
||||
/**
|
||||
* @var Runtime
|
||||
*/
|
||||
protected $runtime;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $stderrRedirection = false;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $stdin = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $args = '';
|
||||
|
||||
/**
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $env = [];
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $timeout = 0;
|
||||
|
||||
public static function factory(): self
|
||||
{
|
||||
if (DIRECTORY_SEPARATOR === '\\') {
|
||||
return new WindowsPhpProcess;
|
||||
}
|
||||
|
||||
return new DefaultPhpProcess;
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->runtime = new Runtime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines if should use STDERR redirection or not.
|
||||
*
|
||||
* Then $stderrRedirection is TRUE, STDERR is redirected to STDOUT.
|
||||
*/
|
||||
public function setUseStderrRedirection(bool $stderrRedirection): void
|
||||
{
|
||||
$this->stderrRedirection = $stderrRedirection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns TRUE if uses STDERR redirection or FALSE if not.
|
||||
*/
|
||||
public function useStderrRedirection(): bool
|
||||
{
|
||||
return $this->stderrRedirection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the input string to be sent via STDIN.
|
||||
*/
|
||||
public function setStdin(string $stdin): void
|
||||
{
|
||||
$this->stdin = $stdin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the input string to be sent via STDIN.
|
||||
*/
|
||||
public function getStdin(): string
|
||||
{
|
||||
return $this->stdin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the string of arguments to pass to the php job.
|
||||
*/
|
||||
public function setArgs(string $args): void
|
||||
{
|
||||
$this->args = $args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string of arguments to pass to the php job.
|
||||
*/
|
||||
public function getArgs(): string
|
||||
{
|
||||
return $this->args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the array of environment variables to start the child process with.
|
||||
*
|
||||
* @param array<string, string> $env
|
||||
*/
|
||||
public function setEnv(array $env): void
|
||||
{
|
||||
$this->env = $env;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the array of environment variables to start the child process with.
|
||||
*/
|
||||
public function getEnv(): array
|
||||
{
|
||||
return $this->env;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the amount of seconds to wait before timing out.
|
||||
*/
|
||||
public function setTimeout(int $timeout): void
|
||||
{
|
||||
$this->timeout = $timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the amount of seconds to wait before timing out.
|
||||
*/
|
||||
public function getTimeout(): int
|
||||
{
|
||||
return $this->timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a single test in a separate PHP process.
|
||||
*
|
||||
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
|
||||
*/
|
||||
public function runTestJob(string $job, Test $test, TestResult $result): void
|
||||
{
|
||||
$result->startTest($test);
|
||||
|
||||
$_result = $this->runJob($job);
|
||||
|
||||
$this->processChildResult(
|
||||
$test,
|
||||
$result,
|
||||
$_result['stdout'],
|
||||
$_result['stderr']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the command based into the configurations.
|
||||
*/
|
||||
public function getCommand(array $settings, string $file = null): string
|
||||
{
|
||||
$command = $this->runtime->getBinary();
|
||||
|
||||
if ($this->runtime->hasPCOV()) {
|
||||
$settings = array_merge(
|
||||
$settings,
|
||||
$this->runtime->getCurrentSettings(
|
||||
array_keys(ini_get_all('pcov'))
|
||||
)
|
||||
);
|
||||
} elseif ($this->runtime->hasXdebug()) {
|
||||
$settings = array_merge(
|
||||
$settings,
|
||||
$this->runtime->getCurrentSettings(
|
||||
array_keys(ini_get_all('xdebug'))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$command .= $this->settingsToParameters($settings);
|
||||
|
||||
if (PHP_SAPI === 'phpdbg') {
|
||||
$command .= ' -qrr';
|
||||
|
||||
if (!$file) {
|
||||
$command .= 's=';
|
||||
}
|
||||
}
|
||||
|
||||
if ($file) {
|
||||
$command .= ' ' . escapeshellarg($file);
|
||||
}
|
||||
|
||||
if ($this->args) {
|
||||
if (!$file) {
|
||||
$command .= ' --';
|
||||
}
|
||||
$command .= ' ' . $this->args;
|
||||
}
|
||||
|
||||
if ($this->stderrRedirection) {
|
||||
$command .= ' 2>&1';
|
||||
}
|
||||
|
||||
return $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a single job (PHP code) using a separate PHP process.
|
||||
*/
|
||||
abstract public function runJob(string $job, array $settings = []): array;
|
||||
|
||||
protected function settingsToParameters(array $settings): string
|
||||
{
|
||||
$buffer = '';
|
||||
|
||||
foreach ($settings as $setting) {
|
||||
$buffer .= ' -d ' . $this->processSettingValue(escapeshellarg($setting));
|
||||
}
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rejects "name=value" INI settings whose value contains a line-break
|
||||
* character. A newline cannot legitimately appear in a PHP INI value and
|
||||
* would, if forwarded unchanged, be parsed by the child process as a
|
||||
* directive separator — turning a single setting into an attacker-
|
||||
* controlled sequence of directives.
|
||||
*
|
||||
* Otherwise quotes the value portion only when it contains characters
|
||||
* PHP's INI parser would 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`.
|
||||
*
|
||||
* @throws PhpProcessException
|
||||
*/
|
||||
private function processSettingValue(string $setting): string
|
||||
{
|
||||
$parts = explode('=', $setting, 2);
|
||||
|
||||
if (count($parts) !== 2) {
|
||||
return $setting;
|
||||
}
|
||||
|
||||
[$name, $value] = $parts;
|
||||
|
||||
if (str_contains($value, "\n") || str_contains($value, "\r")) {
|
||||
throw new PhpProcessException(
|
||||
sprintf(
|
||||
'PHP setting "%s" contains a line-break character, which is not permitted',
|
||||
$name,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (!str_contains($value, ';') && !str_contains($value, '"')) {
|
||||
return $setting;
|
||||
}
|
||||
|
||||
return $name . '="' . str_replace('"', '\\"', $value) . '"';
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the TestResult object from an isolated process.
|
||||
*
|
||||
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
|
||||
*/
|
||||
private function processChildResult(Test $test, TestResult $result, string $stdout, string $stderr): void
|
||||
{
|
||||
$time = 0;
|
||||
|
||||
if (!empty($stderr)) {
|
||||
$result->addError(
|
||||
$test,
|
||||
new Exception(trim($stderr)),
|
||||
$time
|
||||
);
|
||||
} else {
|
||||
set_error_handler(
|
||||
/**
|
||||
* @throws ErrorException
|
||||
*/
|
||||
static function ($errno, $errstr, $errfile, $errline): void
|
||||
{
|
||||
throw new ErrorException($errstr, $errno, $errno, $errfile, $errline);
|
||||
}
|
||||
);
|
||||
|
||||
try {
|
||||
if (strpos($stdout, "#!/usr/bin/env php\n") === 0) {
|
||||
$stdout = substr($stdout, 19);
|
||||
}
|
||||
|
||||
$childResult = unserialize(str_replace("#!/usr/bin/env php\n", '', $stdout));
|
||||
restore_error_handler();
|
||||
|
||||
if ($childResult === false) {
|
||||
$result->addFailure(
|
||||
$test,
|
||||
new AssertionFailedError('Test was run in child process and ended unexpectedly'),
|
||||
$time
|
||||
);
|
||||
}
|
||||
} catch (ErrorException $e) {
|
||||
restore_error_handler();
|
||||
$childResult = false;
|
||||
|
||||
$result->addError(
|
||||
$test,
|
||||
new Exception(trim($stdout), 0, $e),
|
||||
$time
|
||||
);
|
||||
}
|
||||
|
||||
if ($childResult !== false) {
|
||||
if (!empty($childResult['output'])) {
|
||||
$output = $childResult['output'];
|
||||
}
|
||||
|
||||
/* @var TestCase $test */
|
||||
|
||||
$test->setResult($childResult['testResult']);
|
||||
$test->addToAssertionCount($childResult['numAssertions']);
|
||||
|
||||
$childResult = $childResult['result'];
|
||||
assert($childResult instanceof TestResult);
|
||||
|
||||
if ($result->getCollectCodeCoverageInformation()) {
|
||||
$result->getCodeCoverage()->merge(
|
||||
$childResult->getCodeCoverage()
|
||||
);
|
||||
}
|
||||
|
||||
$time = $childResult->time();
|
||||
$notImplemented = $childResult->notImplemented();
|
||||
$risky = $childResult->risky();
|
||||
$skipped = $childResult->skipped();
|
||||
$errors = $childResult->errors();
|
||||
$warnings = $childResult->warnings();
|
||||
$failures = $childResult->failures();
|
||||
|
||||
if (!empty($notImplemented)) {
|
||||
$result->addError(
|
||||
$test,
|
||||
$this->getException($notImplemented[0]),
|
||||
$time
|
||||
);
|
||||
} elseif (!empty($risky)) {
|
||||
$result->addError(
|
||||
$test,
|
||||
$this->getException($risky[0]),
|
||||
$time
|
||||
);
|
||||
} elseif (!empty($skipped)) {
|
||||
$result->addError(
|
||||
$test,
|
||||
$this->getException($skipped[0]),
|
||||
$time
|
||||
);
|
||||
} elseif (!empty($errors)) {
|
||||
$result->addError(
|
||||
$test,
|
||||
$this->getException($errors[0]),
|
||||
$time
|
||||
);
|
||||
} elseif (!empty($warnings)) {
|
||||
$result->addWarning(
|
||||
$test,
|
||||
$this->getException($warnings[0]),
|
||||
$time
|
||||
);
|
||||
} elseif (!empty($failures)) {
|
||||
$result->addFailure(
|
||||
$test,
|
||||
$this->getException($failures[0]),
|
||||
$time
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$result->endTest($test, $time);
|
||||
|
||||
if (!empty($output)) {
|
||||
print $output;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the thrown exception from a PHPUnit\Framework\TestFailure.
|
||||
*
|
||||
* @see https://github.com/sebastianbergmann/phpunit/issues/74
|
||||
*/
|
||||
private function getException(TestFailure $error): Exception
|
||||
{
|
||||
$exception = $error->thrownException();
|
||||
|
||||
if ($exception instanceof __PHP_Incomplete_Class) {
|
||||
$exceptionArray = [];
|
||||
|
||||
foreach ((array) $exception as $key => $value) {
|
||||
$key = substr($key, strrpos($key, "\0") + 1);
|
||||
$exceptionArray[$key] = $value;
|
||||
}
|
||||
|
||||
$exception = new SyntheticError(
|
||||
sprintf(
|
||||
'%s: %s',
|
||||
$exceptionArray['_PHP_Incomplete_Class_Name'],
|
||||
$exceptionArray['message']
|
||||
),
|
||||
$exceptionArray['code'],
|
||||
$exceptionArray['file'],
|
||||
$exceptionArray['line'],
|
||||
$exceptionArray['trace']
|
||||
);
|
||||
}
|
||||
|
||||
return $exception;
|
||||
}
|
||||
}
|
||||
120
overrides/phpunit/phpunit/src/Util/Xml/Loader.php
Normal file
120
overrides/phpunit/phpunit/src/Util/Xml/Loader.php
Normal file
@@ -0,0 +1,120 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of PHPUnit.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace PHPUnit\Util\Xml;
|
||||
|
||||
use const LIBXML_NONET;
|
||||
use function chdir;
|
||||
use function dirname;
|
||||
use function error_reporting;
|
||||
use function file_get_contents;
|
||||
use function getcwd;
|
||||
use function libxml_get_errors;
|
||||
use function libxml_use_internal_errors;
|
||||
use function sprintf;
|
||||
use DOMDocument;
|
||||
|
||||
/**
|
||||
* @internal This class is not covered by the backward compatibility promise for PHPUnit
|
||||
*/
|
||||
final class Loader
|
||||
{
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function loadFile(string $filename, bool $isHtml = false, bool $xinclude = false, bool $strict = false): DOMDocument
|
||||
{
|
||||
$reporting = error_reporting(0);
|
||||
$contents = file_get_contents($filename);
|
||||
|
||||
error_reporting($reporting);
|
||||
|
||||
if ($contents === false) {
|
||||
throw new Exception(
|
||||
sprintf(
|
||||
'Could not read "%s".',
|
||||
$filename
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return $this->load($contents, $isHtml, $filename, $xinclude, $strict);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function load(string $actual, bool $isHtml = false, string $filename = '', bool $xinclude = false, bool $strict = false): DOMDocument
|
||||
{
|
||||
if ($actual === '') {
|
||||
throw new Exception('Could not load XML from empty string');
|
||||
}
|
||||
|
||||
// Required for XInclude on Windows.
|
||||
if ($xinclude) {
|
||||
$cwd = getcwd();
|
||||
@chdir(dirname($filename));
|
||||
}
|
||||
|
||||
$document = new DOMDocument;
|
||||
$document->preserveWhiteSpace = false;
|
||||
|
||||
$internal = libxml_use_internal_errors(true);
|
||||
$message = '';
|
||||
$reporting = error_reporting(0);
|
||||
|
||||
if ($filename !== '') {
|
||||
// Required for XInclude
|
||||
$document->documentURI = $filename;
|
||||
}
|
||||
|
||||
if ($isHtml) {
|
||||
$loaded = $document->loadHTML($actual);
|
||||
} else {
|
||||
// https://github.com/advisories/GHSA-qrr6-mg7r-m243
|
||||
//$loaded = $document->loadXML($actual);
|
||||
$loaded = $document->loadXML($actual, LIBXML_NONET);
|
||||
}
|
||||
|
||||
if (!$isHtml && $xinclude) {
|
||||
$document->xinclude();
|
||||
}
|
||||
|
||||
foreach (libxml_get_errors() as $error) {
|
||||
$message .= "\n" . $error->message;
|
||||
}
|
||||
|
||||
libxml_use_internal_errors($internal);
|
||||
error_reporting($reporting);
|
||||
|
||||
if (isset($cwd)) {
|
||||
@chdir($cwd);
|
||||
}
|
||||
|
||||
if ($loaded === false || ($strict && $message !== '')) {
|
||||
if ($filename !== '') {
|
||||
throw new Exception(
|
||||
sprintf(
|
||||
'Could not load "%s".%s',
|
||||
$filename,
|
||||
$message !== '' ? "\n" . $message : ''
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ($message === '') {
|
||||
$message = 'Could not load XML for unknown reason';
|
||||
}
|
||||
|
||||
throw new Exception($message);
|
||||
}
|
||||
|
||||
return $document;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user