3.0 KiB
Strict Content-Security-Policy for the Scalar UI shell
(no submodules)
Purpose
The headers module builds the Content-Security-Policy header value
emitted on every HTML response from the Scalar UI handler. The default
posture is conservative: default-src 'self' plus the minimum set of
directive relaxations the Scalar shell needs to render. In
assetMode = "cdn" the policy is widened (per-handler) to whitelist the
configured Scalar CDN origin. In assetMode = "bundled" the policy
stays strict — every script/style/font load resolves to the host's own
origin under <uiEndpoint>/assets/.
Features
buildCspHeader({ assetMode, cdnOrigin?, openapiOrigin? })— emits theContent-Security-Policyheader pair.originOf(url)— extracts the origin portion of a URL or returnsundefinedfor non-URL inputs (used at handler construction to derive the CDN + OpenAPI origins from the parsed options).cspAllowsInlineScripts(headerValue)— conformance predicate the handler test uses to prove the rendered policy never permits inline scripts. The Scalar bootstrap is loaded as an external<script src>so'unsafe-inline'inscript-srcwould be a regression.'unsafe-inline'allowed ONLY instyle-srcfor the upstream Scalar React app's runtime style injection.
Usage: Basic
import { buildCspHeader } from "@suaveplan/api-openapi-ui-scalar/headers";
const csp = buildCspHeader({ assetMode: "bundled" });
console.log(csp.name); // "Content-Security-Policy"
console.log(csp.value); // strict "default-src 'self'; script-src 'self'; …"
Usage: Advanced
import {
buildCspHeader,
cspAllowsInlineScripts,
originOf,
} from "@suaveplan/api-openapi-ui-scalar/headers";
const cdnOrigin = originOf("https://cdn.jsdelivr.net/npm/@scalar/api-reference");
const openapiOrigin = originOf("https://api.example.com/openapi.json");
const { name, value } = buildCspHeader({
assetMode: "cdn",
cdnOrigin,
openapiOrigin,
});
if (cspAllowsInlineScripts(value)) {
throw new Error("Scalar UI must never emit unsafe-inline scripts");
}
API Reference
CSP_HEADER_NAME— the canonical header name string.BuildCspInput—{ assetMode, cdnOrigin?, openapiOrigin? }.buildCspHeader(input)—{ name, value }.originOf(url)—string | undefined.cspAllowsInlineScripts(headerValue)—boolean.
Implementation Notes
The policy structure is built directive-by-directive rather than via a
single template string so future per-environment relaxations
(e.g. nonce-based script-src) can be added without changing every
call site. script-src never includes 'unsafe-inline'; this is a
load-bearing invariant enforced by the conformance predicate exposed
above. The CDN origin is computed once at handler construction so a
malicious cdnUrl value cannot leak into the response header — Zod
rejects non-URL inputs at the options layer before originOf runs.