Files
flights_web/src/server/middleware/csp.ts
T

50 lines
1.3 KiB
TypeScript

import { createContext } from "react";
import crypto from "node:crypto";
export interface CspMiddlewareOptions {
reportOnly?: boolean;
}
/**
* React context exposing the per-request CSP nonce.
* Default is "" — client-side components read empty string (no-op).
*/
export const CspNonceContext = createContext<string>("");
/**
* Factory returning express-style middleware that:
* 1. Generates a per-request nonce
* 2. Sets the CSP header (or Report-Only variant)
* 3. Attaches `req.cspNonce` for downstream middleware
*/
export function cspMiddleware(options?: CspMiddlewareOptions) {
const headerName = options?.reportOnly
? "Content-Security-Policy-Report-Only"
: "Content-Security-Policy";
return (
req: Record<string, unknown>,
res: { setHeader(name: string, value: string): void },
next: () => void,
): void => {
const nonce = crypto.randomUUID();
const policy = [
`default-src 'self'`,
`script-src 'self' 'nonce-${nonce}'`,
`style-src 'self' 'unsafe-inline'`,
`img-src 'self' data: https:`,
`font-src 'self'`,
`connect-src 'self' https:`,
`frame-ancestors 'self'`,
`base-uri 'self'`,
`form-action 'self'`,
].join("; ");
res.setHeader(headerName, policy);
req["cspNonce"] = nonce;
next();
};
}