- Shipped
- August 3, 2026 at 3:56 AM UTC
- Author
- kamo
- Commit
- aa7c32d
Avatars, org logos and backgrounds stopped loading for every tenant. Two separate regressions I introduced in 36ad904b, both live for hours. 1. The per-request img-src policy derived the tenant domain from `request.nextUrl.hostname`. Inside the pod that is 127.0.0.1, so taking the last two labels produced the apex "0.0" and the emitted header was: img-src 'self' data: blob: https://theme.0.0 https://*.0.0 Every real theme.<domain> origin was therefore blocked. The host has to come from the forwarded headers — requests arrive through Traefik and the gateway, so the external hostname survives only there. The rest of this codebase already knew that; the API proxy forwards X-Forwarded-Host explicitly so backends can build theme URLs from it. The deeper mistake was failing CLOSED on a value it could not parse. A policy built from a nonsense apex is indistinguishable from a strict one at the server, so nothing looked wrong from the outside — it just silently blocked images everywhere. tenantApex() now returns null for anything that is not a registrable domain (IP literal, bare label, localhost, numeric TLD) and the caller omits the header entirely. A missing header is the previous behaviour; a wrong header is an outage. 2. dangerouslyAllowSVG: false blocked SVG through the optimizer for ALL sources, not only remote ones — and the org/software logos and the provider icons under /logo are SVG. Re-enabled, and it is much narrower now than when it was first set: remotePatterns is empty, so the optimizer cannot be aimed at a foreign host at all, and external images go through /api/images/proxy, which validates content-type and serves with `default-src 'none'; sandbox` plus nosniff. Tests cover the derivation against the exact input that caused this, the white-label domains, forwarded-host precedence, comma-joined forwarded values, ports, and every shape that must yield null.