Harden autoload Redis cache path validation

This commit is contained in:
Jeppe B
2026-06-02 00:02:40 +02:00
parent dd4c9da86c
commit e363f27da9
+24 -1
View File
@@ -61,12 +61,23 @@ try {
*/
spl_autoload_register(function (string $class): void {
$class = ltrim($class, '\\');
$wdReal = rtrim((string) realpath(WD), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
$modulesRoot = $wdReal . 'modules' . DIRECTORY_SEPARATOR;
$isPathInside = static function (string $path, string $root): bool {
$resolved = realpath($path);
if ($resolved === false) {
return false;
}
$resolved = rtrim($resolved, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
return str_starts_with($resolved, $root);
};
// Check Redis cache first
if (defined('redis')) {
try {
$cached = redis->get('autoload:' . $class);
if ($cached && is_file($cached)) {
if ($cached && is_file($cached) && $isPathInside($cached, $wdReal)) {
require_once $cached;
return;
}
@@ -104,6 +115,18 @@ spl_autoload_register(function (string $class): void {
$module_dirs = redis->get_array('autoload:module_dirs');
} catch (\Throwable $e) {}
}
if (is_array($module_dirs)) {
$module_dirs = array_values(array_filter($module_dirs, static function ($item) use ($base, $modulesRoot, $isPathInside): bool {
if (!is_string($item) || $item === '' || str_contains($item, DIRECTORY_SEPARATOR) || str_contains($item, '..')) {
return false;
}
$candidate = $base . 'modules' . DIRECTORY_SEPARATOR . $item;
return is_dir($candidate) && $isPathInside($candidate, $modulesRoot);
}));
}
if ($module_dirs === null) {
$module_dirs = array_filter(scandir($base . 'modules'), function($item) use ($base) {
return $item !== '.' && $item !== '..' && is_dir($base . 'modules' . DIRECTORY_SEPARATOR . $item);