Skip to content

Commit 487eb10

Browse files
committed
fix(e2e): prevent prefix-collision bypass in the php -S router
The static-file containment used str_starts_with($candidate, $root) with a non-normalized root and no trailing separator, so a sibling directory sharing the prefix (e.g. ".../phpmyfaq-x") could pass the check. Canonicalize the root with realpath() and compare against a root that ends in a directory separator, so only files genuinely inside the docroot are served directly.
1 parent 4e23d4f commit 487eb10

1 file changed

Lines changed: 9 additions & 6 deletions

File tree

tests/e2e/router.php

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,25 +20,28 @@
2020

2121
declare(strict_types=1);
2222

23-
$root = dirname(__DIR__, 2) . '/phpmyfaq';
23+
$canonicalRoot = realpath(dirname(__DIR__, 2) . '/phpmyfaq');
24+
// Trailing separator so a sibling directory sharing the prefix (e.g.
25+
// ".../phpmyfaq-x") cannot pass the containment check below.
26+
$rootWithSep = rtrim((string) $canonicalRoot, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
2427
$path = (string) parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH);
25-
$candidate = realpath($root . $path);
28+
$candidate = realpath($canonicalRoot . $path);
2629

2730
// Serve existing static assets (css, js, images, fonts, ...) straight from disk.
2831
if (
2932
$candidate !== false
3033
&& is_file($candidate)
31-
&& str_starts_with($candidate, $root)
34+
&& str_starts_with($candidate, $rootWithSep)
3235
&& !str_ends_with($candidate, '.php')
3336
) {
3437
return false;
3538
}
3639

37-
$dispatch = static function (string $frontController) use ($root): void {
40+
$dispatch = static function (string $frontController) use ($canonicalRoot): void {
3841
$_SERVER['SCRIPT_NAME'] = $frontController;
3942
$_SERVER['PHP_SELF'] = $frontController;
40-
$_SERVER['SCRIPT_FILENAME'] = $root . $frontController;
41-
require $root . $frontController;
43+
$_SERVER['SCRIPT_FILENAME'] = $canonicalRoot . $frontController;
44+
require $canonicalRoot . $frontController;
4245
};
4346

4447
if (str_starts_with($path, '/admin/api')) {

0 commit comments

Comments
 (0)