fix(csp): scope connect-src to API/OIDC origins so all sub-paths are allowed (MAPPS-369) #428
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/MAPPS-369-csp-connect-src-origin"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Fixes MAPPS-369.
The mokosh-www runtime CSP built
connect-srcstraight from the operator env:connect-src 'self' {$MOKOSH_API_BASE:-} {$MOKOSH_OIDC_ISSUER:-}(oci-build/Caddyfile). Two defects made cross-origin API calls fail:MOKOSH_API_BASEis the full API base including its/api/v1path (the SPA'sapi_base()appends/auth/login,/ready, ... to it). A CSP host-source whose path does not end in/is an exact-path match (CSP3 path-part matching), so only/api/v1itself was allowed and every real API call was blocked. The browser aborts the fetch before it is sent, which the SPA surfaces asApiError::Network("Network error. Check your connection and try again.") with nothing reaching the server. This broke all cross-origin API access: any self-hosted or split SPA/API deployment.Caddy's env-placeholder default syntax is
{$VAR:default}, so{$VAR:-}sets the default to a literal-(not empty, as the old comment claimed). An unset OIDC issuer therefore injected a junk-/http://-source.Found while smoke-testing the v0.7.0 self-host compose (standalone login, no bunyip): login failed with "Network error" and clean server logs; the browser console reported the
connect-srcviolation athttp://localhost:25252/api/v1/ready.Fix
oci-build/entrypoint.sh: neworigin_of()reduces a URL toscheme://host[:port](drops path/query; empty for a relative or empty value, which is same-origin and covered by'self'). It derivesMOKOSH_API_ORIGIN/MOKOSH_OIDC_ORIGINfromMOKOSH_API_BASE/MOKOSH_OIDC_ISSUERand exports them beforeexecing Caddy.oci-build/Caddyfile:connect-srcnow reads'self' {$MOKOSH_API_ORIGIN:} {$MOKOSH_OIDC_ORIGIN:}- origin-scoped so all API/OIDC sub-paths match, with correct empty-default syntax so an unset origin drops out cleanly.MOKOSH_API_BASE/MOKOSH_OIDC_ISSUERare unchanged; the origin vars are internal and derived.Note: the canonical
msp.<tld>deploy is cross-origin (SPAmsp.<tld>calls APIapi.msp.<tld>) and relies onMOKOSH_API_BASEbeing set (its entrypoint does) so its origin appears in connect-src. A deploy that leavesMOKOSH_API_BASEunset and relies purely on the SPA's runtime host-prefix derivation would still have no API origin in connect-src; that path is same-as-before (no regression) and documented inline.Verification
sh -n oci-build/entrypoint.shpasses.origin_ofreturns the expected origin for absolute URLs with the port preserved (http://localhost:25252/api/v1->http://localhost:25252,https://api.msp.example:8443/api/v1->https://api.msp.example:8443) and empty for relative (/api/v1) and empty input.caddy validateon the Caddyfile passes withMOKOSH_OIDC_ORIGINboth set and unset.Manual test
Self-host stack (SPA :8080 + API :25252, different origins),
MOKOSH_API_BASE=http://localhost:25252/api/v1:POST /api/v1/auth/loginand subsequent/api/v1/*calls complete with noconnect-srcviolation; console showsconnect-src 'self' http://localhost:25252with no path and no-token.The mokosh-www runtime CSP built connect-src straight from the operator env: `connect-src 'self' {$MOKOSH_API_BASE:-} {$MOKOSH_OIDC_ISSUER:-}`. Two defects: 1. `MOKOSH_API_BASE` is the full API base including its `/api/v1` path (the SPA appends `/auth/login`, `/ready`, ... to it). A CSP host-source whose path does not end in `/` is an exact-path match, so only `/api/v1` itself was allowed and every real call was blocked. The browser aborted the fetch before it left, surfacing as the SPA's `ApiError::Network` ("Network error") with nothing reaching the server. This broke all cross-origin API access, i.e. any self-hosted or split SPA/API deployment. 2. Caddy's env-placeholder default syntax is `{$VAR:default}`, so `{$VAR:-}` sets the default to a literal `-` (not empty, as the old comment claimed). An unset OIDC issuer therefore injected a junk `-` / `http://-` source. Fix: entrypoint.sh derives origin-scoped CSP sources (`origin_of` reduces a URL to `scheme://host[:port]`, dropping the path; empty for a relative or empty value) into `MOKOSH_API_ORIGIN` / `MOKOSH_OIDC_ORIGIN` and exports them; the Caddyfile connect-src now reads `{$MOKOSH_API_ORIGIN:} {$MOKOSH_OIDC_ORIGIN:}` (origin-scoped so all sub-paths match, correct empty-default syntax so an unset origin drops out). Operator-facing `MOKOSH_API_BASE` / `MOKOSH_OIDC_ISSUER` are unchanged. Verified: `sh -n` on entrypoint.sh; `origin_of` returns the expected origin for absolute URLs (port preserved) and empty for relative/empty input; `caddy validate` passes with the OIDC origin both set and unset. #MAPPS-369 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>