Skip to content

Commit

Permalink
fix: cross-origin postMessage (#52)
Browse files Browse the repository at this point in the history
  • Loading branch information
adamdbradley authored Jan 22, 2022
1 parent 06a9741 commit 79a25dd
Show file tree
Hide file tree
Showing 5 changed files with 5,858 additions and 52 deletions.
2 changes: 1 addition & 1 deletion src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,6 @@ export interface MediaSelf {
}

export interface PostMessageData {
$winId$: number;
$origin$: string;
$data$: string;
}
71 changes: 51 additions & 20 deletions src/lib/web-worker/worker-exec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { debug, SCRIPT_TYPE } from '../utils';
import { debug, len, SCRIPT_TYPE } from '../utils';
import {
EventHandler,
InitializeScriptData,
Expand All @@ -7,7 +7,7 @@ import {
WebWorkerEnvironment,
WorkerMessageType,
} from '../types';
import { environments, webWorkerCtx } from './worker-constants';
import { environments, postMessages, webWorkerCtx } from './worker-constants';
import { getEnv } from './worker-environment';
import { getInstanceStateValue, setInstanceStateValue } from './worker-state';
import { getOrCreateNodeInstance } from './worker-constructors';
Expand Down Expand Up @@ -41,7 +41,7 @@ export const initNextScriptsInWebWorker = async (initScript: InitializeScriptDat
scriptContent = await rsp.text();

env.$currentScriptId$ = instanceId;
run(env, winId, scriptContent, scriptOrgSrc || scriptSrc);
run(env, scriptContent, scriptOrgSrc || scriptSrc);
runStateLoadHandlers(instance!, StateProp.loadHandlers);
} else {
errorMsg = rsp.statusText;
Expand Down Expand Up @@ -88,7 +88,7 @@ export const runScriptContent = (
}

env.$currentScriptId$ = instanceId;
run(env, winId, scriptContent);
run(env, scriptContent);
} catch (contentError: any) {
console.error(scriptContent, contentError);
errorMsg = String(contentError.stack || contentError);
Expand All @@ -99,13 +99,56 @@ export const runScriptContent = (
return errorMsg;
};

const run = (env: WebWorkerEnvironment, winId: number, scriptContent: string, scriptUrl?: string) =>
const run = (env: WebWorkerEnvironment, scriptContent: string, scriptUrl?: string) => {
const doc: any = new Proxy(env.$document$, {
get: (target: any, propName) => {
if (propName === 'defaultView') {
return win;
} else {
return target[propName];
}
},
});

const win: any = new Proxy(env.$window$, {
get: (target: any, propName) => {
if (propName === 'document') {
return doc;
} else if (propName === 'window' || propName === 'globalThis' || propName === 'self') {
return win;
} else if (propName === 'parent' || propName === 'top') {
return new Proxy(target[propName], {
get: (targetParent: any, parentPropName) => {
if (parentPropName === 'postMessage') {
return (...args: any[]) => {
if (len(postMessages) > 20) {
postMessages.splice(0, 5);
}
postMessages.push({
$data$: JSON.stringify(args[0]),
$origin$: env.$location$.origin,
});
targetParent.postMessage(...args);
};
} else {
return targetParent[parentPropName];
}
},
});
} else {
return target[propName];
}
},
has: () => true,
});

new Function(
`with(this){(function _${winId}_(){${scriptContent
`with(this){${scriptContent
.replace(/\bthis\b/g, 'thi$(this)')
.replace(/\/\/# so/g, '//Xso')};function thi$(t){return t===this?window:t}})()}` +
.replace(/\/\/# so/g, '//Xso')};function thi$(t){return t===this?window:t}}` +
(scriptUrl ? '\n//# sourceURL=' + scriptUrl : '')
).call(env.$window$);
).call(win);
};

const runStateLoadHandlers = (
instance: WorkerInstance,
Expand All @@ -118,18 +161,6 @@ const runStateLoadHandlers = (
}
};

export const getScriptWinIdContext = () => {
try {
throw new Error();
} catch (e: any) {
const r = /_(\d+)_/gm.exec(e.stack);
if (r) {
return parseInt(r[1], 10);
}
}
return 0;
};

export const insertIframe = (winId: number, iframe: WorkerInstance) => {
// an iframe element's instanceId is also
// the winId of its contentWindow
Expand Down
6 changes: 1 addition & 5 deletions src/lib/web-worker/worker-serialization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,12 +184,8 @@ export const deserializeFromMain = (
if (obj.type === 'message' && obj.origin) {
let postMessageKey = JSON.stringify(obj.data);
let postMessageData = postMessages.find((pm) => pm.$data$ === postMessageKey);
let env: WebWorkerEnvironment;
if (postMessageData) {
env = environments[postMessageData.$winId$];
if (env) {
obj.origin = env.$window$.origin;
}
obj.origin = postMessageData.$origin$;
}
}
return new Proxy(new Event(obj.type, obj), {
Expand Down
35 changes: 9 additions & 26 deletions src/lib/web-worker/worker-window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,9 @@ import { createNavigator } from './worker-navigator';
import { createImageConstructor } from './worker-image';
import { createNodeInstance, getOrCreateNodeInstance } from './worker-constructors';
import { debug, defineConstructorName, defineProperty, len, randomId } from '../utils';
import {
envGlobalConstructors,
environments,
postMessages,
webWorkerCtx,
WinIdKey,
} from './worker-constants';
import { envGlobalConstructors, environments, webWorkerCtx, WinIdKey } from './worker-constants';
import { getEnv } from './worker-environment';
import { getScriptWinIdContext, resolveUrl } from './worker-exec';
import { resolveUrl } from './worker-exec';
import { lazyLoadMedia, windowMediaConstructors } from './worker-media';
import { Location } from './worker-location';
import { normalizedWinId } from '../log';
Expand All @@ -26,7 +20,6 @@ export class Window extends WorkerInstance {
let _this: any = this;
let globalName: string;
let value: any;
let win: any;

// assign global properties already in the web worker global
// that we can put onto the environment window
Expand Down Expand Up @@ -83,19 +76,16 @@ export class Window extends WorkerInstance {
_this.trustedTypes = (self as any).trustedTypes;
}

win = new Proxy(_this, {
has() {
// window "has" any and all props, this is especially true for global variables
// that are meant to be assigned to window, but without "window." prefix,
// like: <script>globalProp = true</script>
return true;
},
});

environments[$winId$] = {
$winId$,
$parentWinId$,
$window$: win as any,
$window$: new Proxy(_this, {
has: () =>
// window "has" any and all props, this is especially true for global variables
// that are meant to be assigned to window, but without "window." prefix,
// like: <script>globalProp = true</script>
true,
}),
$document$: createNodeInstance(
$winId$,
PlatformInstanceId.document,
Expand Down Expand Up @@ -139,8 +129,6 @@ export class Window extends WorkerInstance {
addStorageApi(_this, 'sessionStorage', webWorkerCtx.$sessionStorage$);

_this.Worker = undefined;

return win;
}

get body() {
Expand Down Expand Up @@ -214,11 +202,6 @@ export class Window extends WorkerInstance {
}

postMessage(...args: any[]) {
if (len(postMessages) > 20) {
postMessages.splice(0, 5);
}
postMessages.push({ $data$: JSON.stringify(args[0]), $winId$: getScriptWinIdContext() });

callMethod(this, ['postMessage'], args, CallType.NonBlockingNoSideEffect);
}

Expand Down
Loading

1 comment on commit 79a25dd

@vercel
Copy link

@vercel vercel bot commented on 79a25dd Jan 22, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.