a005acb93d
CI / full-gate (push) Successful in 2m38s
README loses the competitor comparison, the personas and the repeated philosophy; the
five near-identical E2E command blocks become a table plus one command, and the file
map a clause per entry. AGENTS.md keeps every decision but drops the narrative around
them. todo.md's completed items collapse to their task line — git holds the rest.
Comments lose restatement, README duplication and history ("used to", "originally",
dated notes). AGENTS.md gains a Prose discipline section making this a standing pass on
every change rather than a one-off cleanup.
src/compose.test.ts now expects 6 documented E2E run commands, not 10, since the README
states the command once instead of per suite.
35 lines
1.4 KiB
TypeScript
35 lines
1.4 KiB
TypeScript
// Set once per request in app.ts, so every response carries them (writeHead merges with setHeader).
|
|
// A plugin route may override any per-response via RouteResult.headers.
|
|
|
|
// The non-obvious parts of the CSP:
|
|
// - script-src 'self' with no 'unsafe-inline' ⇒ an injected <script> can't run. A plugin may still
|
|
// serve its own /public/<id>/*.js for opt-in progressive enhancement.
|
|
// - style-src adds 'unsafe-inline': a few partials carry inline style= attributes.
|
|
// - no form-action: the themed login form posts to Kratos' (often cross-origin) action URL.
|
|
const CSP = [
|
|
"base-uri 'self'",
|
|
"default-src 'self'",
|
|
"frame-ancestors 'none'",
|
|
"img-src 'self' data:",
|
|
"object-src 'none'",
|
|
"script-src 'self'",
|
|
"style-src 'self' 'unsafe-inline'",
|
|
].join("; ");
|
|
|
|
export interface SecurityHeaderOptions {
|
|
secure?: boolean; // https deployment (mirrors SECURE_COOKIES) → also emit HSTS
|
|
}
|
|
|
|
export function securityHeaders(options: SecurityHeaderOptions = {}): Record<string, string> {
|
|
const headers: Record<string, string> = {
|
|
"content-security-policy": CSP,
|
|
"cross-origin-opener-policy": "same-origin",
|
|
"referrer-policy": "strict-origin-when-cross-origin",
|
|
"x-content-type-options": "nosniff",
|
|
"x-frame-options": "DENY",
|
|
};
|
|
// HSTS only over https — ignored (and meaningless) on the dev http origin.
|
|
if (options.secure) headers["strict-transport-security"] = "max-age=31536000; includeSubDomains";
|
|
return headers;
|
|
}
|