sanitizeRelativePath($path); if ($path === '') { abort(404); } $fullPath = $this->resolveReadablePath($path); if ($fullPath === null) { abort(404); } return response()->file($fullPath); } private function sanitizeRelativePath(string $path): string { $path = str_replace('\\', '/', $path); $path = ltrim($path, '/'); if ($path === '' || str_contains($path, '..')) { return ''; } return $path; } private function resolveReadablePath(string $path): ?string { foreach ($this->candidatePaths($path) as $fullPath) { if (is_file($fullPath) && is_readable($fullPath)) { return $fullPath; } } return null; } /** * @return list */ private function candidatePaths(string $path): array { return [ storage_path('app/public/'.$path), public_path('storage/'.$path), ]; } }