v1.58.4
[1.58.4] — 2026-05-19
fix(security): NEW-1 — emit Content-Security-Policy on every response (was loopback-gated). Before v1.58.4 the CSP header was layered on only when isPubliclyExposed() was true (HOST bound beyond loopback); over 127.0.0.1 both / and /api/health returned no CSP, leaving UI.md()'s escape-first contract as the only XSS defence. The v1.58.3 MASTER regression (§5) flagged this as a stop-ship invariant gap. CSP is now unconditional and identical on every response regardless of bind address: default-src 'self'; script-src 'self'; style-src 'self' https://fonts.googleapis.com 'unsafe-inline'; font-src 'self' https://fonts.gstatic.com; img-src 'self' data:; connect-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'; form-action 'self'. script-src never allows 'unsafe-inline'/'unsafe-eval'. The directive set is unchanged from the prior exposed-only policy (already SPA-correct — Google Fonts allowlisted for Inter), so no visual or functional regression. tests/security-headers.test.mjs was rewritten to lock CSP-present-on-loopback; a Playwright route-walk (en/ru/ja/zh-TW × 7 routes) asserts 0 CSP violations. 900 unit · Playwright 58→59 · e2e 20/20+23/23. Next fix-prompt items (NEW-3, BUG-008-tb, NEW-2, M-1…) ship as subsequent one-fix releases per project doctrine. See qa/v158-regression/FIX-PROMPT-v1.58.4_and_beyond.md. (NEW-1)