/** * A single HTTP header, in the shape Next.js next.config headers() expects. */ /** * Builds the generic, project-independent security headers every site should * send: HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, * Permissions-Policy. These are identical across projects, so the plugin owns * the boilerplate; the client spreads the result into next.config's headers(). * * Content-Security-Policy is deliberately excluded: a useful CSP enumerates the * exact domains a project loads from (its CDN, analytics, embeds), so it can't * be generic without being either too loose (useless) or too strict (breaks the * site). Add your project's CSP via `additional`. * * @example * // next.config.ts * import { buildSecurityHeaders } from '@intecion/ipal-kit' * const securityHeaders = buildSecurityHeaders({ * hsts: process.env.NODE_ENV === 'production', // off in dev over http * additional: [ * { key: 'Content-Security-Policy', value: "default-src 'self'; ..." }, * ], * }) * const nextConfig = { * async headers() { * return [{ source: '/:path*', headers: securityHeaders }] * }, * } */ export function buildSecurityHeaders(args = {}) { const { additional = [], coop = 'same-origin', frameOptions = 'DENY', hsts = true, hstsIncludeSubDomains = true, hstsMaxAge = 63072000, hstsPreload = false, permissionsPolicy = 'camera=(), microphone=(), geolocation=()', referrerPolicy = 'strict-origin-when-cross-origin' } = args; const headers = []; if (hsts) { const parts = [ `max-age=${hstsMaxAge}` ]; if (hstsIncludeSubDomains) { parts.push('includeSubDomains'); } if (hstsPreload) { parts.push('preload'); } headers.push({ key: 'Strict-Transport-Security', value: parts.join('; ') }); } if (frameOptions) { headers.push({ key: 'X-Frame-Options', value: frameOptions }); } // Prevents MIME-type sniffing — always safe, no project specifics. headers.push({ key: 'X-Content-Type-Options', value: 'nosniff' }); if (referrerPolicy) { headers.push({ key: 'Referrer-Policy', value: referrerPolicy }); } if (permissionsPolicy) { headers.push({ key: 'Permissions-Policy', value: permissionsPolicy }); } // COOP — isolates the browsing context (XS-Leaks / Spectre protection). if (coop) { headers.push({ key: 'Cross-Origin-Opener-Policy', value: coop }); } // Merge additional: same-key entries override the defaults above. for (const extra of additional){ const i = headers.findIndex((h)=>h.key.toLowerCase() === extra.key.toLowerCase()); if (i >= 0) { headers[i] = extra; } else { headers.push(extra); } } return headers; } //# sourceMappingURL=buildSecurityHeaders.js.map