| @@ -43,6 +43,18 @@ import { browserApiCorsAllowedHeaders, browserApiCorsAllowedMethods } from "./ht · 42 unmodified lines |
| 43 | 43 | const OTLP_TRACES_PROXY_PATH = "/api/observability/v1/traces"; |
| 44 | 44 | const LOOPBACK_HOSTNAMES = new Set(["127.0.0.1", "::1", "localhost"]); |
| 45 | 45 | const DESKTOP_RENDERER_ORIGINS = ["t3code://app", "t3code-dev://app"]; |
| 46 | +const SVG_CONTENT_SECURITY_POLICY = "default-src 'none'; style-src 'unsafe-inline'; sandbox"; |
| 47 | + |
| 48 | +export function assetResponseHeaders(filePath: string): Record<string, string> { |
| 49 | + return { |
| 50 | + "Cache-Control": "private, max-age=3600", |
| 51 | + "X-Content-Type-Options": "nosniff", |
| 52 | + ...(filePath.toLowerCase().endsWith(".svg") |
| 53 | + ? { "Content-Security-Policy": SVG_CONTENT_SECURITY_POLICY } |
| 54 | + : {}), |
| 55 | + }; |
| 56 | +} |
| 57 | + |
| 46 | 58 | export const httpCompressionLayer = HttpRouter.middleware(HttpMiddleware.compression(), { |
| 47 | 59 | global: true, |
| 48 | 60 | }); |
| @@ -207,10 +219,7 @@ export const assetRouteLayer = HttpRouter.add( · 158 unmodified lines |
| 207 | 219 | } |
| 208 | 220 | return yield* HttpServerResponse.file(asset.path, { |
| 209 | 221 | status: 200, |
| 210 | | − headers: { |
| 211 | | − "Cache-Control": "private, max-age=3600", |
| 212 | | − "X-Content-Type-Options": "nosniff", |
| 213 | | − }, |
| 222 | + headers: assetResponseHeaders(asset.path), |
| 214 | 223 | }).pipe( |
| 215 | 224 | Effect.orElseSucceed(() => HttpServerResponse.text("Internal Server Error", { status: 500 })), |
| 216 | 225 | ); |