Skip to content

Commit

Permalink
Rework sandbox init
Browse files Browse the repository at this point in the history
  • Loading branch information
lahmatiy committed Jul 10, 2022
1 parent dc9248d commit c5a6f64
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 15 deletions.
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
## next

- Reworked sandbox init:
- UI is loading into a [sandboxed](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#attr-sandbox) frame with `allow-scripts allow-forms allow-popups allow-modals` features enabled. That prevents access to [data storage/cookies](https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy#cross-origin_data_storage_access) and some JavaScript APIs from UI scripts (because `allow-same-origin` is not enabled) but puts UI scripts in the same conditions across environments (e.g. a regular page, a page in "incognito mode", a devtools page etc).
- Added `sandboxSrc` option for `createSandbox()` to specify a sandbox page URL, needed to define a specific origin e.g. in devtools
- Added `rempl/sanbox-init` endpoint which exposes a code to inject into a sandbox page to init UI scripts, e.g.
```html
<!DOCTYPE html>
<script>
import { initSandboxScript } from 'rempl/sandbox-init';
initSandboxScript();
</script>
```

## 1.0.0-alpha.22 (June 29, 2022)

- Fixed crash on UI init in a sandbox when a bundle declares variables with a name as a readonly globals referring to a window object like `top`, `parent` etc.
Expand Down
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@
"import": "./lib/browser.js"
}
},
"./sandbox-init": {
"types": "./lib/sandbox/browser/sandbox-init.d.ts",
"require": "./lib/sandbox/browser/sandbox-init.cjs",
"import": "./lib/sandbox/browser/sandbox-init.js"
},
"./dist/*": "./dist/*.js",
"./package.json": "./package.json"
},
Expand Down
2 changes: 1 addition & 1 deletion scripts/transpile.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ async function transpileAll(options) {
const { watch = false, types = false, bundle = false } = options || {};

await transpile({
entryPoints: ['src/node.ts', 'src/browser.ts'],
entryPoints: ['src/node.ts', 'src/browser.ts', 'src/sandbox/browser/sandbox-init.ts'],
outputDir: './lib',
format: 'esm',
watch,
Expand Down
43 changes: 30 additions & 13 deletions src/sandbox/browser/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
import { Sandbox } from '../../types.js';
import { OnInitCallback, EventTransport } from '../../transport/event.js';
import { globalThis, parent, genUID } from '../../utils/index.js';
import { initSandboxScript } from './sandbox-init.js';

type Global = typeof globalThis;
type SandboxWindow = Window | Global;
type Settings =
| {
type: 'script';
content: Record<string, string>;
sandboxSrc?: string;
window?: Window;
container?: HTMLElement;
}
Expand All @@ -23,23 +25,29 @@ const initEnvSubscriberMessage = new WeakMap();

// TODO: make tree-shaking friendly
if (parent !== globalThis) {
addEventListener('message', function (event: MessageEvent<any>) {
const data = event.data || {};
addEventListener(
'message',
function (event: MessageEvent<any>) {
const data = event.data || {};

if (event.source && data.to === 'rempl-env-publisher:connect') {
initEnvSubscriberMessage.set(event.source, data);
}
});
if (event.source && data.to === 'rempl-env-publisher:connect') {
initEnvSubscriberMessage.set(event.source, data);
}
},
true
);
}

export function createSandbox(settings: Settings, callback: OnInitCallback) {
function initSandbox(sandboxWindow: SandboxWindow) {
if (settings.type === 'script') {
for (const [sourceURL, source] of Object.entries(settings.content)) {
(sandboxWindow as Global).eval(
`(function(){${source}})()\n//# sourceURL=${sourceURL}`
);
}
sandboxWindow.postMessage(
{
action: 'rempl-sandbox-init-scripts',
scripts: settings.content,
},
'*'
);
}

if (parent !== globalThis && sandboxWindow !== globalThis) {
Expand All @@ -60,7 +68,7 @@ export function createSandbox(settings: Settings, callback: OnInitCallback) {
case 'rempl-env-subscriber:connect':
case toSandbox:
toEnv = data.from;
sandboxWindow.postMessage(data);
sandboxWindow.postMessage(data, '*');
break;

case 'rempl-env-publisher:connect':
Expand Down Expand Up @@ -108,11 +116,20 @@ export function createSandbox(settings: Settings, callback: OnInitCallback) {
iframe = document.createElement('iframe');
iframe.name = genUID(); // to avoid cache
iframe.onload = () => iframe?.contentWindow && initSandbox(iframe.contentWindow);
iframe.setAttribute('sandbox', 'allow-scripts allow-forms allow-popups allow-modals');

if (settings.type === 'url') {
iframe.src = settings.content;
} else if (settings.sandboxSrc) {
iframe.src = settings.sandboxSrc;
} else {
iframe.srcdoc = '<!doctype html>';
iframe.srcdoc = '<!doctype html><script>(' + String(initSandboxScript) + ')()</script>';
// iframe.src = URL.createObjectURL(
// new Blob(
// ['<!doctype html><script>(' + String(initSandboxScript) + ')()</script>'],
// { type: 'text/html' }
// )
// );
}

(settings.container || document.documentElement).appendChild(iframe);
Expand Down
21 changes: 21 additions & 0 deletions src/sandbox/browser/sandbox-init.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
type SandboxInitEvent = MessageEvent<{
action: 'rempl-sandbox-init-scripts';
scripts: Record<string, string>;
}>;

export function initSandboxScript() {
addEventListener('message', function handleMessage(event: SandboxInitEvent) {
const { action, scripts } = event.data || {};

if (action === 'rempl-sandbox-init-scripts' && scripts) {
// handle message only once
removeEventListener('message', handleMessage);

// evaluate scripts
for (const [sourceURL, source] of Object.entries(scripts)) {
// indirect eval, see detail: https://esbuild.github.io/content-types/#direct-eval
Function(`${source}\n//# sourceURL=${sourceURL}`)();
}
}
});
}
2 changes: 1 addition & 1 deletion src/transport/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ export class EventTransport {
payload,
};

this.realm.postMessage(message);
this.realm.postMessage(message, '*');
}
}

Expand Down

0 comments on commit c5a6f64

Please sign in to comment.