Fix code quality violations and exclude Manifest from checks

Document application modes (development/debug/production)
Add global file drop handler, order column normalization, SPA hash fix
Serve CDN assets via /_vendor/ URLs instead of merging into bundles
Add production minification with license preservation
Improve JSON formatting for debugging and production optimization
Add CDN asset caching with CSS URL inlining for production builds
Add three-mode system (development, debug, production)
Update Manifest CLAUDE.md to reflect helper class architecture
Refactor Manifest.php into helper classes for better organization
Pre-manifest-refactor checkpoint: Add app_mode documentation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2026-01-14 10:38:22 +00:00
parent bb9046af1b
commit d523f0f600
2355 changed files with 231384 additions and 32223 deletions

View File

@@ -131,7 +131,7 @@ class AssetHandler
/**
* Check if a path is an asset request
*
*
* @param string $path
* @return bool
*/
@@ -144,14 +144,21 @@ class AssetHandler
return preg_match('/^[A-Za-z0-9_]+__(vendor|app)\.[a-f0-9]{8}\.(js|css)$/', $filename) ||
preg_match('/^[A-Za-z0-9_]+__app\.[a-f0-9]{16}\.(js|css)$/', $filename);
}
// Check if this is a vendor CDN cache request
if (str_starts_with($path, '/_vendor/')) {
// Validate filename format: alphanumeric_hash.(js|css)
$filename = substr($path, 9); // Remove '/_vendor/'
return preg_match('/^[A-Za-z0-9_-]+_[a-f0-9]{12}\.(js|css)$/', $filename);
}
// Check if path has a file extension
$extension = pathinfo($path, PATHINFO_EXTENSION);
if (empty($extension)) {
return false;
}
// Check if extension is allowed
return in_array(strtolower($extension), static::$allowed_extensions);
}
@@ -171,6 +178,11 @@ class AssetHandler
return static::__serve_compiled_bundle($path, $request);
}
// Handle vendor CDN cache requests
if (str_starts_with($path, '/_vendor/')) {
return static::__serve_vendor_cdn_cache($path, $request);
}
// Ensure directories are discovered
static::__ensure_directories_discovered();
@@ -677,10 +689,84 @@ class AssetHandler
// Security headers
$response->headers->set('X-Content-Type-Options', 'nosniff');
return $response;
}
/**
* Serve a cached CDN vendor file
*
* Serves files from the CDN cache directory (rsx/resource/.cdn-cache/)
* with strict filename validation to prevent path traversal.
*
* @param string $path The requested path (e.g., /_vendor/lodash_abc123456789.js)
* @param Request $request
* @return Response
* @throws NotFoundHttpException
*/
protected static function __serve_vendor_cdn_cache($path, Request $request)
{
// Extract filename from path
$filename = substr($path, 9); // Remove '/_vendor/'
// Strict filename validation - only allow safe characters
// Format: name_hash.ext where name is alphanumeric/underscore/hyphen, hash is 12 hex chars
if (!preg_match('/^[A-Za-z0-9_-]+_[a-f0-9]{12}\.(js|css)$/', $filename)) {
throw new NotFoundHttpException("Invalid vendor filename: {$filename}");
}
// Additional safety checks - no path traversal characters
if (str_contains($filename, '..') || str_contains($filename, './') || str_contains($filename, '/')) {
Log::warning('Attempted path traversal in vendor request', [
'filename' => $filename
]);
throw new NotFoundHttpException("Invalid vendor filename: {$filename}");
}
// Get the CDN cache directory from the Cdn_Cache class
$cache_dir = \App\RSpade\Core\Bundle\Cdn_Cache::get_cache_directory();
// Build full path - filename is already validated as safe
$file_path = $cache_dir . '/' . $filename;
// Check if file exists
if (!file_exists($file_path)) {
throw new NotFoundHttpException("Vendor file not found: {$filename}");
}
// Verify the file is actually in the cache directory (belt and suspenders)
// @REALPATH-EXCEPTION - Security: path traversal prevention requires symlink resolution
$real_file = realpath($file_path);
$real_cache_dir = realpath($cache_dir);
if (!$real_file || !$real_cache_dir || !str_starts_with($real_file, $real_cache_dir)) {
Log::warning('Vendor file path traversal attempt', [
'filename' => $filename,
'resolved' => $real_file
]);
throw new NotFoundHttpException("Vendor file not found: {$filename}");
}
// Create binary file response
$response = new BinaryFileResponse($file_path);
// Set appropriate content type
$extension = pathinfo($filename, PATHINFO_EXTENSION);
$mime_type = $extension === 'js' ? 'application/javascript' : 'text/css';
$response->headers->set('Content-Type', $mime_type . '; charset=utf-8');
// Set cache headers - 1 month cache for vendor CDN files
// These files are versioned by their content hash in the filename
$cache_seconds = 2592000; // 30 days (1 month)
$response->headers->set('Cache-Control', 'public, max-age=' . $cache_seconds . ', immutable');
$response->headers->set('Expires', gmdate('D, d M Y H:i:s', time() + $cache_seconds) . ' GMT');
// Security headers
$response->headers->set('X-Content-Type-Options', 'nosniff');
return $response;
}
/**
* Compile a bundle on-demand in development mode
*