// Copyright 2026 certctl LLC. All rights reserved. // SPDX-License-Identifier: BUSL-1.1 package middleware import ( "net/http" "strings" ) // SecurityHeadersConfig configures the SecurityHeaders middleware. // // Each field is the literal value to send. An empty string means // "do not send this header" — operators behind a customising reverse // proxy can disable any header per-deployment without touching code. // Defaults are applied via SecurityHeadersDefaults() which encodes // the H-1 closure's recommended baseline for an HTTPS-only API+UI // host: HSTS, deny-frame, no-MIME-sniff, conservative CSP, and a // no-referrer-when-downgrade fallback. // // H-1 closure (cat-s11-missing_security_headers). type SecurityHeadersConfig struct { HSTS string // Strict-Transport-Security FrameOptions string // X-Frame-Options ContentTypeOptions string // X-Content-Type-Options ReferrerPolicy string // Referrer-Policy ContentSecurityPolicy string // Content-Security-Policy } // SecurityHeadersDefaults returns a recommended baseline. // // CSP: default-src 'self' confines fetches to the same origin. // img-src 'self' data: allows inline base64 images (used by the // dashboard's certctl-logo and a few status icons). // style-src 'self' 'unsafe-inline' — the 'unsafe-inline' grant // is required by React's inline `style={...}` attribute model, // which emits HTML `style="..."` attributes that the browser // treats as inline styles for CSP purposes. The dashboard has 5 // load-bearing dynamic-style sites: Tooltip's Floating-UI // position (left/top px values computed per-tick), // AgentFleetPage's dynamic color+width chart bars, // dashboard/charts.tsx Recharts color props, CertificatesPage's // progress-bar percent width, IssuerHierarchyPage's depth-based // marginLeft. The static-pixel uses (UsersPage filter + table UI, // DigestPage iframe min-height, AuthProvider demo-mode banner) // were migrated to Tailwind utility classes via FE-M6 closure // 2026-05-14. // // FE-M6 audit-framing correction: this comment USED TO say // "Tailwind (via Vite) injects per-component