Harden autoload Redis cache path validation
This commit is contained in:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user