Audit 2026-06-02 — security (Medium)
frontend/apps/web/src/pages/detail/ChangeForm.tsx:71 passes spec.legacy_url straight into LegacyIframe.tsx, which uses it as both an <iframe src> (no sandbox) and an <a href target="_blank"> (LegacyIframe.tsx:29-46) with no scheme/origin validation. Every other navigational sink in the SPA validates (action-redirect.ts, the backend ?next=); this one regressed that bar. A compromised, buggy, or request-influenced form-spec backend (separately mountable via API_URL_PREFIX) could emit a javascript: URL (executes on anchor click) or an off-origin URL (attacker content inside the authenticated admin chrome → phishing).
Fix: reject non-same-origin / non-http(s) / out-of-mount URLs (mirror action-redirect.ts) and add sandbox="allow-forms allow-scripts allow-same-origin" to the iframe.
Related (docs): SECURITY.md §QSEC-03 still recommends X_FRAME_OPTIONS="DENY" / frame-ancestors 'none' and its sample CSP has no frame-src — written before 1.9.0's iframe. Update it to add frame-src 'self' (permits the intended same-origin frame, blocks an off-origin legacy_url) and document the X-Frame-Options interaction.
Audit 2026-06-02 — security (Medium)
frontend/apps/web/src/pages/detail/ChangeForm.tsx:71passesspec.legacy_urlstraight intoLegacyIframe.tsx, which uses it as both an<iframe src>(nosandbox) and an<a href target="_blank">(LegacyIframe.tsx:29-46) with no scheme/origin validation. Every other navigational sink in the SPA validates (action-redirect.ts, the backend?next=); this one regressed that bar. A compromised, buggy, or request-influenced form-spec backend (separately mountable viaAPI_URL_PREFIX) could emit ajavascript:URL (executes on anchor click) or an off-origin URL (attacker content inside the authenticated admin chrome → phishing).Fix: reject non-same-origin / non-
http(s)/ out-of-mount URLs (mirroraction-redirect.ts) and addsandbox="allow-forms allow-scripts allow-same-origin"to the iframe.Related (docs):
SECURITY.md§QSEC-03 still recommendsX_FRAME_OPTIONS="DENY"/frame-ancestors 'none'and its sample CSP has noframe-src— written before 1.9.0's iframe. Update it to addframe-src 'self'(permits the intended same-origin frame, blocks an off-originlegacy_url) and document the X-Frame-Options interaction.