tr
All checks were successful
Deploy XIP / deploy (push) Successful in 34s

This commit is contained in:
arussac
2026-05-31 15:51:07 +02:00
parent 0c08e2080f
commit 9354e2022a

View File

@@ -4,9 +4,12 @@
Renders paid HTML/CSS or JS messages inside a FIXED-SIZE sandboxed iframe. Renders paid HTML/CSS or JS messages inside a FIXED-SIZE sandboxed iframe.
Sandbox policy (never deviate): Sandbox policy (never deviate):
- htmlcss tier: sandbox="" (empty) scripts are INERT (honours README "pas de script"). - htmlcss tier: sandbox="" (empty) scripts are INERT. A meta CSP reinforces this.
- js tier: sandbox="allow-scripts" ONLY script runs in a NULL origin and - js tier: sandbox="allow-scripts" ONLY script runs in a NULL origin and
cannot touch the parent (no allow-same-origin, ever). cannot touch the parent (no allow-same-origin, ever).
NO meta CSP for js mode: Chromium silently blocks 'unsafe-inline'
in null-origin srcdoc iframes despite it being declared; the sandbox
without allow-same-origin is the real isolation boundary.
We NEVER combine allow-scripts with allow-same-origin (that would re-grant parent We NEVER combine allow-scripts with allow-same-origin (that would re-grant parent
access and defeat isolation). A runtime assertion below guards against it. access and defeat isolation). A runtime assertion below guards against it.
@@ -44,12 +47,14 @@ if (import.meta.env.DEV) {
} }
const srcdoc = computed(() => { const srcdoc = computed(() => {
// In-document CSP as a second layer (the sandbox is the primary boundary). // For htmlcss mode: meta CSP blocks scripts as a second layer (sandbox="" already blocks them too).
const csp = // For js mode: NO meta CSP — the null-origin sandbox (allow-scripts without allow-same-origin)
props.mode === 'js' // is the real security boundary; adding a meta CSP with default-src 'none' in a null-origin
? "default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; img-src data: https:; font-src data:;" // srcdoc iframe causes Chromium to silently block inline scripts despite 'unsafe-inline'.
: "default-src 'none'; script-src 'none'; style-src 'unsafe-inline'; img-src data: https:; font-src data:;"; const metaCsp = props.mode === 'js'
return `<!doctype html><html><head><meta charset="utf-8"><meta http-equiv="Content-Security-Policy" content="${csp}"><style>html,body{margin:0;padding:8px;color:#ddd;font-family:Arial,sans-serif;background:#0a0a12;overflow:auto;height:100%;box-sizing:border-box}</style></head><body>${props.content}</body></html>`; ? ''
: '<meta http-equiv="Content-Security-Policy" content="default-src \'none\'; script-src \'none\'; style-src \'unsafe-inline\'; img-src data: https:; font-src data:;">';
return `<!doctype html><html><head><meta charset="utf-8">${metaCsp}<style>html,body{margin:0;padding:8px;color:#ddd;font-family:Arial,sans-serif;background:#0a0a12;overflow:auto;height:100%;box-sizing:border-box}</style></head><body>${props.content}</body></html>`;
}); });
</script> </script>