update middleware to allow cloudflare and inline script

Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
This commit is contained in:
Hadi
2026-03-07 17:22:06 +01:00
parent 4551254dfd
commit c95f5a182b

View File

@@ -1,7 +1,7 @@
import { defineMiddleware } from "astro:middleware"; import { defineMiddleware } from "astro:middleware";
const RATE_LIMIT_WINDOW_MS = 60_000; // 1 minute const RATE_LIMIT_WINDOW_MS = 60_000;
const RATE_LIMIT_MAX_REQUESTS = 60; // requests per window const RATE_LIMIT_MAX_REQUESTS = 60;
interface RateLimitEntry { interface RateLimitEntry {
count: number; count: number;
@@ -10,7 +10,6 @@ interface RateLimitEntry {
const store = new Map<string, RateLimitEntry>(); const store = new Map<string, RateLimitEntry>();
// Cleanup stale entries every 5 minutes to avoid memory leaks
setInterval(() => { setInterval(() => {
const now = Date.now(); const now = Date.now();
for (const [key, entry] of store) { for (const [key, entry] of store) {
@@ -19,7 +18,6 @@ setInterval(() => {
}, 5 * 60_000); }, 5 * 60_000);
function getClientIp(request: Request): string { function getClientIp(request: Request): string {
// Cloudflare sets CF-Connecting-IP — use it first.
return ( return (
request.headers.get("CF-Connecting-IP") ?? request.headers.get("CF-Connecting-IP") ??
request.headers.get("X-Forwarded-For")?.split(",")[0].trim() ?? request.headers.get("X-Forwarded-For")?.split(",")[0].trim() ??
@@ -47,31 +45,16 @@ function isRateLimited(ip: string): { limited: boolean; retryAfter: number } {
} }
const SECURITY_HEADERS: Record<string, string> = { const SECURITY_HEADERS: Record<string, string> = {
// Prevent MIME-type sniffing
"X-Content-Type-Options": "nosniff", "X-Content-Type-Options": "nosniff",
// Disallow embedding in iframes (clickjacking)
"X-Frame-Options": "DENY", "X-Frame-Options": "DENY",
// Only send referrer on same-origin requests
"Referrer-Policy": "strict-origin-when-cross-origin", "Referrer-Policy": "strict-origin-when-cross-origin",
// Disable browser features you don't use
"Permissions-Policy": "camera=(), microphone=(), geolocation=()", "Permissions-Policy": "camera=(), microphone=(), geolocation=()",
// Content Security Policy
// - default-src 'self' → only load resources from your own origin by default
// - img-src 'self' cdn.jsdelivr.net data: → allow Simple Icons CDN + inline SVGs
// - style-src 'self' 'unsafe-inline' → DaisyUI injects inline styles at runtime
// - script-src 'self' → no third-party scripts
// - connect-src 'self' → API calls only to your own origin
// - frame-ancestors 'none' → redundant with X-Frame-Options but defense-in-depth
"Content-Security-Policy": [ "Content-Security-Policy": [
"default-src 'self'", "default-src 'self'",
"img-src 'self' cdn.jsdelivr.net data:", "img-src 'self' cdn.jsdelivr.net data:",
"style-src 'self' 'unsafe-inline'", "style-src 'self' 'unsafe-inline'",
"script-src 'self'", "script-src 'self' 'unsafe-inline' static.cloudflareinsights.com",
"connect-src 'self'", "connect-src 'self' cloudflareinsights.com",
"frame-ancestors 'none'", "frame-ancestors 'none'",
].join("; "), ].join("; "),
}; };