fix: harden security and improve code quality across services Add Zod validation schemas to backend routes, replace z.any() with explicit types, and convert SQL string interpolation to parameterized queries to prevent injection attacks. Security improvements: - Add SSRF protection with domain allowlist in CommunityPlaylistService - Enforce JWT algorithm restriction and token type validation - Sanitize error messages to prevent path leakage in game-service - Add MIME type enforcement based on file extension, not server headers - Add request timeouts with exponential backoff retry logic - Prevent Flashpoint database writes (read-only enforcement) Backend improvements: - Use UPSERT for play tracking to avoid race conditions - Add dispose() method to CachedSystemSettingsService for cleanup - Use crypto.randomUUID() instead of custom UUID generator - Batch fetch games in PlaylistService to fix N+1 query pattern - Use centralized verifyPassword utility in UserService Frontend improvements: - Add PlayerErrorBoundary to prevent app crashes from Ruffle errors - Add theme initialization script to prevent FOUC - Add custom favicon.svg with Flashpoint branding - Fix token refresh race condition with subscriber queue - Add AbortSignal support to games search API - Replace EventSource with fetch for authenticated SSE streams - Improve error handling in UserForm and CreatePlaylistModal - Stream large files instead of buffering in game-service
58 lines
2.2 KiB
HTML
58 lines
2.2 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
<link rel="icon" type="image/png" sizes="32x32" href="/images/logo.png" />
|
|
<link rel="apple-touch-icon" href="/images/logo.png" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<meta
|
|
http-equiv="Content-Security-Policy"
|
|
content="
|
|
default-src 'self';
|
|
script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval';
|
|
style-src 'self' 'unsafe-inline';
|
|
img-src 'self' data: blob: http://localhost:* http://127.0.0.1:*;
|
|
font-src 'self' data:;
|
|
connect-src 'self' http://localhost:* http://127.0.0.1:* ws://localhost:* ws://127.0.0.1:*;
|
|
frame-src 'self' http://localhost:* http://127.0.0.1:*;
|
|
object-src 'none';
|
|
base-uri 'self';
|
|
form-action 'self';
|
|
"
|
|
/>
|
|
<title>Flashpoint Archive</title>
|
|
<!-- Blocking script to prevent FOUC (Flash of Unstyled Content) -->
|
|
<!-- Applies theme before first paint by reading from localStorage -->
|
|
<script>
|
|
(function () {
|
|
try {
|
|
var stored = localStorage.getItem('flashpoint-theme-settings');
|
|
if (stored) {
|
|
var settings = JSON.parse(stored);
|
|
var mode = settings.state && settings.state.mode;
|
|
if (mode === 'dark') {
|
|
document.documentElement.classList.add('dark');
|
|
} else if (mode === 'light') {
|
|
document.documentElement.classList.add('light');
|
|
} else if (mode === 'system') {
|
|
var prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
document.documentElement.classList.add(prefersDark ? 'dark' : 'light');
|
|
}
|
|
} else {
|
|
// Default to dark mode if no preference set
|
|
document.documentElement.classList.add('dark');
|
|
}
|
|
} catch (e) {
|
|
// Fallback to dark mode on error
|
|
document.documentElement.classList.add('dark');
|
|
}
|
|
})();
|
|
</script>
|
|
</head>
|
|
<body>
|
|
<div id="root"></div>
|
|
<script type="module" src="/src/main.tsx"></script>
|
|
</body>
|
|
</html>
|