What the sandbox a listener's song plays in may do (sandbox.mjs), as headers: one source for every place that serves it.
pages/jev/sandbox.astroputs the policy in a meta tag, so it holds on any static host.- The build writes
_headers(astro.config.mjs,jevHeaders), which Cloudflare applies to the deploy's static files: the page gets the policy as a header too, withsandbox(so even opened on its own, the page has an opaque origin) andframe-ancestors(only this site may frame it), which a meta tag cannot carry. - The dev server sends the same headers (astro.config.mjs,
jevSandboxDev).
The frame has an opaque origin, so everything it loads from the site is
a cross-origin request with Origin: null: its scripts (module scripts
and AudioWorklets are fetched with CORS) and the sample pack. Those
public files are served with Access-Control-Allow-Origin: *, which
sends no credentials.
Hosts songs load sounds from: Strudel's CDN (the default banks,
prebakeSounds.mjs), GitHub's raw files (samples('github:…'), as the
site's songs load tidalcycles/dirt-samples), and the soundfonts'.
29export const SANDBOX_PAGE = '/jev/sandbox/';
meta: the policy as a meta tag may hold it (no sandbox,
frame-ancestors); otherwise the header's.
33export function sandboxCsp({ meta = false } = {}) { 34 const directives = [ 35 ...(meta ? [] : ['sandbox allow-scripts']), 36 "default-src 'none'", 37 // the site's own scripts; a song is code, evaluated (unsafe-eval), and 38 // AudioWorklets load from blob: (the meter) and data: (superdough's dough 39 // worklet in dev); their bundled file is 'self' in production 40 "script-src 'self' 'unsafe-eval' blob: data:", 41 `connect-src 'self' ${SAMPLE_HOSTS.join(' ')}`, 42 "style-src 'unsafe-inline'", 43 "img-src 'none'", 44 "media-src 'none'", 45 "font-src 'none'", 46 "object-src 'none'", 47 "frame-src 'none'", 48 "manifest-src 'none'", 49 // AudioWorklet module scripts are governed by worker-src 50 "worker-src 'self' blob: data:", 51 "form-action 'none'", 52 // the page sets <base href="/"> so a song's root-relative sample URL 53 // resolves to the site root, as it does on the main page; 'self' is the 54 // origin the frame was served from (the site), not its opaque runtime 55 // origin, so the tag is allowed but cannot be pointed off-site 56 "base-uri 'self'", 57 ...(meta ? [] : ["frame-ancestors 'self'"]), 58 ]; 59 return directives.join('; '); 60}
Paths the sandbox loads from the site, served to it with CORS.
69export const CORS_PATHS = ['/_astro/*', '/jev-samples/*'];
The deploy's _headers (Workers static assets).
72export function headersFile() { 73 const block = (path, headers) => [path, ...Object.entries(headers).map(([k, v]) => ` ${k}: ${v}`)].join('\n'); 74 return [ 75 '# Written by the build (website/src/jev/sandboxPolicy.mjs); do not edit.', 76 block(`${SANDBOX_PAGE}*`, sandboxHeaders()), 77 ...CORS_PATHS.map((path) => block(path, { 'Access-Control-Allow-Origin': '*' })), 78 '', 79 ].join('\n\n'); 80}