diff --git a/apps/svelte.dev/src/routes/(authed)/playground/[id]/+page.svelte b/apps/svelte.dev/src/routes/(authed)/playground/[id]/+page.svelte index 6c17f996ad..1d87435f68 100644 --- a/apps/svelte.dev/src/routes/(authed)/playground/[id]/+page.svelte +++ b/apps/svelte.dev/src/routes/(authed)/playground/[id]/+page.svelte @@ -8,6 +8,7 @@ import { mapbox_setup } from '../../../../config.js'; import AppControls from './AppControls.svelte'; import { compress_and_encode_text, decode_and_decompress_text } from './gzip.js'; + import { page } from '$app/stores'; let { data } = $props(); @@ -18,6 +19,10 @@ let version = data.version; let setting_hash: any = null; + // Hashed URLs are less safe (we can't delete malicious REPLs), therefore + // don't allow links to escape the sandbox restrictions + const can_escape = browser && !$page.url.hash; + onMount(() => { if (version !== 'local') { fetch(`https://unpkg.com/svelte@${version}/package.json`) @@ -127,6 +132,7 @@ bind:this={repl} {svelteUrl} {relaxed} + {can_escape} vim={data.vim} injectedJS={mapbox_setup} showModified diff --git a/apps/svelte.dev/src/routes/(authed)/playground/[id]/embed/+page.svelte b/apps/svelte.dev/src/routes/(authed)/playground/[id]/embed/+page.svelte index 65d6c17850..50d2a85163 100644 --- a/apps/svelte.dev/src/routes/(authed)/playground/[id]/embed/+page.svelte +++ b/apps/svelte.dev/src/routes/(authed)/playground/[id]/embed/+page.svelte @@ -50,6 +50,7 @@ bind:this={repl} {svelteUrl} {relaxed} + can_escape injectedJS={mapbox_setup} showModified showAst diff --git a/apps/svelte.dev/src/routes/tutorial/[slug]/OutputRollup.svelte b/apps/svelte.dev/src/routes/tutorial/[slug]/OutputRollup.svelte index 0782cd7310..89b57998f1 100644 --- a/apps/svelte.dev/src/routes/tutorial/[slug]/OutputRollup.svelte +++ b/apps/svelte.dev/src/routes/tutorial/[slug]/OutputRollup.svelte @@ -34,6 +34,7 @@ {#if browser} logs = l} {bundle} theme={$theme.current} diff --git a/packages/repl/src/lib/Output/Output.svelte b/packages/repl/src/lib/Output/Output.svelte index bb5a941f09..609dc7b908 100644 --- a/packages/repl/src/lib/Output/Output.svelte +++ b/packages/repl/src/lib/Output/Output.svelte @@ -13,6 +13,7 @@ export let runtimeError: Error | null = null; export let embedded = false; export let relaxed = false; + export let can_escape = false; export let injectedJS: string; export let injectedCSS: string; export let showAst = false; @@ -61,6 +62,7 @@ bind:error={runtimeError} {status} {relaxed} + {can_escape} {injectedJS} {injectedCSS} theme={previewTheme} diff --git a/packages/repl/src/lib/Output/Viewer.svelte b/packages/repl/src/lib/Output/Viewer.svelte index 6c72f4ba6b..134f9cc57d 100644 --- a/packages/repl/src/lib/Output/Viewer.svelte +++ b/packages/repl/src/lib/Output/Viewer.svelte @@ -18,6 +18,8 @@ export let status: string | null; /** sandbox allow-same-origin */ export let relaxed = false; + /** sandbox allow-popups-to-escape-sandbox (i.e. links within the REPL to other pages work) */ + export let can_escape = false; /** Any additional JS you may want to inject */ export let injectedJS = ''; /** Any additional CSS you may want to inject */ @@ -247,13 +249,12 @@ class:inited bind:this={iframe} sandbox={[ - 'allow-popups-to-escape-sandbox', 'allow-scripts', 'allow-popups', 'allow-forms', 'allow-pointer-lock', - 'allow-top-navigation', 'allow-modals', + can_escape ? 'allow-popups-to-escape-sandbox' : '', relaxed ? 'allow-same-origin' : '' ].join(' ')} class={error || pending || pending_imports ? 'greyed-out' : ''} @@ -269,27 +270,7 @@ {#if !onLog}
- - - {#if $bundle?.error} - - {/if} + {@render main()}
diff --git a/packages/repl/src/lib/Repl.svelte b/packages/repl/src/lib/Repl.svelte index 2c9c56eeb0..748a16a240 100644 --- a/packages/repl/src/lib/Repl.svelte +++ b/packages/repl/src/lib/Repl.svelte @@ -20,6 +20,7 @@ export let embedded = false; export let orientation: 'columns' | 'rows' = 'columns'; export let relaxed = false; + export let can_escape = false; export let fixed = false; export let fixedPos = 50; export let injectedJS = ''; @@ -312,6 +313,7 @@ status={status_visible ? status : null} {embedded} {relaxed} + {can_escape} {injectedJS} {injectedCSS} {showAst}