Skip to content

Commit 72c74a0

Browse files
Merge branch '10.5' into 11.5
* 10.5: Do not run PHPT test when its temporary file for code coverage information exists We do not need to unserialize() objects here Extract method Fix CS/WS issue
2 parents 4f17506 + a8b932b commit 72c74a0

File tree

6 files changed

+79
-1
lines changed

6 files changed

+79
-1
lines changed

ChangeLog-11.5.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
All notable changes of the PHPUnit 11.5 release series are documented in this file using the [Keep a CHANGELOG](https://keepachangelog.com/) principles.
44

5+
## [11.5.50] - 2026-MM-DD
6+
7+
### Changed
8+
9+
* To prevent Poisoned Pipeline Execution (PPE) attacks using prepared `.coverage` files in pull requests, a PHPT test will no longer be run if the temporary file for writing code coverage information already exists before the test runs
10+
511
## [11.5.49] - 2026-01-24
612

713
### Fixed
@@ -413,6 +419,7 @@ All notable changes of the PHPUnit 11.5 release series are documented in this fi
413419
* [#6055](https://github.com/sebastianbergmann/phpunit/issues/6055): `assertNotContainsOnly()` (use `assertContainsNotOnlyArray()`, `assertContainsNotOnlyBool()`, `assertContainsNotOnlyCallable()`, `assertContainsNotOnlyFloat()`, `assertContainsNotOnlyInt()`, `assertContainsNotOnlyIterable()`, `assertContainsNotOnlyNumeric()`, `assertContainsNotOnlyObject()`, `assertContainsNotOnlyResource()`, `assertContainsNotOnlyClosedResource()`, `assertContainsNotOnlyScalar()`, or `assertContainsNotOnlyString()` instead)
414420
* [#6059](https://github.com/sebastianbergmann/phpunit/issues/6059): `containsOnly()` (use `containsOnlyArray()`, `containsOnlyBool()`, `containsOnlyCallable()`, `containsOnlyFloat()`, `containsOnlyInt()`, `containsOnlyIterable()`, `containsOnlyNumeric()`, `containsOnlyObject()`, `containsOnlyResource()`, `containsOnlyClosedResource()`, `containsOnlyScalar()`, or `containsOnlyString()` instead)
415421

422+
[11.5.50]: https://github.com/sebastianbergmann/phpunit/compare/11.5.49...11.5
416423
[11.5.49]: https://github.com/sebastianbergmann/phpunit/compare/11.5.48...11.5.49
417424
[11.5.48]: https://github.com/sebastianbergmann/phpunit/compare/11.5.47...11.5.48
418425
[11.5.47]: https://github.com/sebastianbergmann/phpunit/compare/11.5.46...11.5.47
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php declare(strict_types=1);
2+
/*
3+
* This file is part of PHPUnit.
4+
*
5+
* (c) Sebastian Bergmann <[email protected]>
6+
*
7+
* For the full copyright and license information, please view the LICENSE
8+
* file that was distributed with this source code.
9+
*/
10+
namespace PHPUnit\Runner;
11+
12+
use RuntimeException;
13+
14+
/**
15+
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit
16+
*
17+
* @internal This class is not covered by the backward compatibility promise for PHPUnit
18+
*/
19+
final class CodeCoverageFileExistsException extends RuntimeException implements Exception
20+
{
21+
}

src/Runner/PHPT/PhptTestCase.php

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use function explode;
2121
use function extension_loaded;
2222
use function file;
23+
use function file_exists;
2324
use function file_get_contents;
2425
use function file_put_contents;
2526
use function is_array;
@@ -34,6 +35,7 @@
3435
use function preg_split;
3536
use function realpath;
3637
use function rtrim;
38+
use function sprintf;
3739
use function str_contains;
3840
use function str_replace;
3941
use function str_starts_with;
@@ -93,6 +95,8 @@ final class PhptTestCase implements Reorderable, SelfDescribing, Test
9395
public function __construct(string $filename)
9496
{
9597
$this->filename = $filename;
98+
99+
$this->ensureCoverageFileDoesNotExist();
96100
}
97101

98102
/**
@@ -784,7 +788,7 @@ private function cleanupForCoverage(): RawCodeCoverageData
784788
}
785789

786790
if ($buffer !== false) {
787-
$coverage = @unserialize($buffer);
791+
$coverage = @unserialize($buffer, ['allowed_class' => false]);
788792

789793
if ($coverage === false) {
790794
/**
@@ -986,4 +990,22 @@ private function settings(bool $collectCoverage): array
986990

987991
return $settings;
988992
}
993+
994+
/**
995+
* @throws CodeCoverageFileExistsException
996+
*/
997+
private function ensureCoverageFileDoesNotExist(): void
998+
{
999+
$files = $this->coverageFiles();
1000+
1001+
if (file_exists($files['coverage'])) {
1002+
throw new CodeCoverageFileExistsException(
1003+
sprintf(
1004+
'File %s exists, PHPT test %s will not be executed',
1005+
$files['coverage'],
1006+
$this->filename,
1007+
),
1008+
);
1009+
}
1010+
}
9891011
}

tests/end-to-end/_files/phpt-coverage-file-exists/test.coverage

Whitespace-only changes.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
--TEST--
2+
test
3+
--FILE--
4+
<?php declare(strict_types=1);
5+
print 'test';
6+
--EXPECT--
7+
test
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
--TEST--
2+
Error when code coverage file exists
3+
--FILE--
4+
<?php declare(strict_types=1);
5+
$_SERVER['argv'][] = '--do-not-cache-result';
6+
$_SERVER['argv'][] = '--no-configuration';
7+
$_SERVER['argv'][] = \realpath(__DIR__ . '/../_files/phpt-coverage-file-exists/test.phpt');
8+
9+
require_once __DIR__ . '/../../bootstrap.php';
10+
11+
(new PHPUnit\TextUI\Application)->run($_SERVER['argv']);
12+
--EXPECTF--
13+
PHPUnit %s by Sebastian Bergmann and contributors.
14+
15+
Runtime: %s
16+
17+
There was 1 PHPUnit test runner warning:
18+
19+
1) File %stest.coverage exists, PHPT test %stest.phpt will not be executed
20+
21+
No tests executed!

0 commit comments

Comments
 (0)