// ── Content-Security-Policy ────────────────────────────────────────────────
//
// Two policies ship at once, on two different headers:
//
//   Content-Security-Policy              → `enforced`   (today's policy, unchanged)
//   Content-Security-Policy-Report-Only  → `reportOnly` (the target, + a report sink)
//
// Report-only first, one release of observation, then the two collapse into one
// enforced policy (docs/website/API_V2_PLAN.md § Phase 1). Shipping the tightened
// policy straight to `Content-Security-Policy` would mean discovering any legitimate
// use we forgot as a broken page in production; shipping it *alongside* the current
// one means a violation report instead, with the live policy still protecting users
// the whole time.
//
// Notes on each non-'self' allowance in the base policy:
//   • style-src 'unsafe-inline' — React renders pervasive inline `style={{…}}`
//     attributes, and CSP style *attributes* cannot be nonce'd; this is required.
//     It permits inline styling, not script execution. Also whitelists the Google
//     Fonts stylesheet host.
//   • font-src — Google Fonts (Cinzel) serves the font files from gstatic.
//   • img-src https:/data: — uploaded images are same-origin, but wiki/news bodies
//     (sanitizeHtml allows <img> over http/https) and BRAND_* logo/hero/favicon may
//     point at external https images. http images are blocked by mixed-content on
//     the https site anyway.
//   • connect-src 'self' — the REST API and SSE streams are same-origin. This is the
//     exfiltration channel; do not widen it unless the API genuinely becomes
//     cross-origin (which would also reopen the auth-merge question — see the plan).
//   • script-src 'self' with no 'unsafe-inline'/'unsafe-eval' is the primary defense.
//     Vite is configured with `modulePreload: { polyfill: false }` (client/vite.config.js)
//     precisely so the build emits no inline bootstrap script for this to trip on.
//   • upgrade-insecure-requests is intentionally dropped: TLS is terminated at the
//     proxy, there are no mixed-content subresources to upgrade, and leaving it on
//     breaks a local `npm start` served over plain http.
//
// The interactive API docs at /api/docs get their own looser policy (swagger-ui
// injects an inline bootstrap script); that carve-out lives in app.js and stays
// scoped to the one route.

// Where violation reports are POSTed, and the Reporting-API group name that points
// at it. Same-origin on purpose — reports describe attacks against this site and
// must not be shipped to a third party.
const REPORT_PATH = '/api/csp-report'
const REPORT_GROUP = 'csp-endpoint'

// The policy in force today. Behaviourally unchanged by this phase — it is the safety
// net while the tightened twin is only being observed.
const enforced = {
  'default-src': ["'self'"],
  'script-src': ["'self'"],
  'style-src': ["'self'", "'unsafe-inline'", 'https://fonts.googleapis.com'],
  'font-src': ["'self'", 'https://fonts.gstatic.com'],
  'img-src': ["'self'", 'data:', 'https:'],
  'connect-src': ["'self'"],
  'frame-ancestors': ["'self'"],
  'object-src': ["'none'"],
  'base-uri': ["'self'"],
  // Blocks an injected `<form action="https://evil">` from POSTing credentials
  // off-origin — an exfil path connect-src does not cover. Already emitted today via
  // helmet's `useDefaults`, and pinned here on purpose: a security directive should
  // not depend on a third-party library's default surviving its next major version.
  // Adding it changes the header's *contents* not at all.
  'form-action': ["'self'"],
  'upgrade-insecure-requests': null,
}

// The one directive this phase actually changes, and so the only thing a report can
// legitimately be about:
//
//   • frame-ancestors 'self' → 'none'. Nothing legitimately frames the site, and
//     'self' only means anything if some same-origin page frames another; none does.
//     Worth a soak rather than a straight flip precisely because a violation report
//     is how we would find out that something *does* — the report comes from the
//     browser of whoever framed us, which is information we cannot get any other way.
//
// Derived from `enforced` rather than written out again, so the two policies cannot
// silently drift apart and this object stays a readable diff of the change.
const tightened = {
  ...enforced,
  'frame-ancestors': ["'none'"],
}

const reportOnly = {
  ...tightened,
  // Both mechanisms, deliberately: `report-to` is the current Reporting API (Chrome,
  // needs the Reporting-Endpoints header below), `report-uri` is deprecated but is
  // still the only one Firefox and Safari implement. Browsers that support both send
  // one report, not two.
  'report-to': [REPORT_GROUP],
  'report-uri': [REPORT_PATH],
}

/**
 * Names the Reporting-API group that `report-to` refers to. Without this header the
 * `report-to` directive is inert, and only the `report-uri` fallback would fire.
 */
function reportingEndpoints(req, res, next) {
  res.setHeader('Reporting-Endpoints', `${REPORT_GROUP}="${REPORT_PATH}"`)
  next()
}

module.exports = { enforced, tightened, reportOnly, reportingEndpoints, REPORT_PATH, REPORT_GROUP }
