From c2d147442ec0c056a59f1781c6f5cb2b28a23e4f Mon Sep 17 00:00:00 2001 From: velzie Date: Sun, 28 Jul 2024 14:41:44 -0400 Subject: [PATCH] properly rewrite postmessage --- src/client/dom/open.ts | 2 +- src/client/dom/postmessage.ts | 22 +++++++++- src/client/index.ts | 10 +++++ src/client/shared/event.ts | 81 +++++++++++++++++++++++++++++++++++ src/client/shared/import.ts | 2 +- src/client/shared/wrap.ts | 28 +++++++----- src/client/window.ts | 2 +- src/global.d.ts | 2 + src/types.d.ts | 2 - 9 files changed, 134 insertions(+), 17 deletions(-) create mode 100644 src/client/shared/event.ts diff --git a/src/client/dom/open.ts b/src/client/dom/open.ts index 821456e..294adfa 100644 --- a/src/client/dom/open.ts +++ b/src/client/dom/open.ts @@ -25,7 +25,7 @@ export default function (client: ScramjetClient) { get(ctx) { const realwin = ctx.get() as Window; - if (ScramjetClient.SCRAMJET in realwin.self) { + if (realwin && ScramjetClient.SCRAMJET in realwin.self) { return realwin.self[ScramjetClient.SCRAMJET].windowProxy; } else { // the opener has to have been already hooked, so if we reach here then it's a real window diff --git a/src/client/dom/postmessage.ts b/src/client/dom/postmessage.ts index 645dbd9..8f0b213 100644 --- a/src/client/dom/postmessage.ts +++ b/src/client/dom/postmessage.ts @@ -1,8 +1,28 @@ import { ScramjetClient } from "../client"; -export default function (client: ScramjetClient, self: typeof window) { +export default function (client: ScramjetClient) { client.Proxy("window.postMessage", { apply(ctx) { + // so we need to send the real origin here, since the recieving window can't possibly know. + // except, remember that this code is being ran in a different realm than the invoker, so if we ask our `client` it may give us the wrong origin + // but, the first argument given will be polluted with the real realm + + // this obtains a reference to the Function object of the real realm + const { + constructor: { constructor: Function }, + } = ctx.args[0]; + + // and finally, invoking the stolen Function will execute inside the caller's realm + const callerGlobalThis: Self = Function("return globalThis")(); + const callerClient: ScramjetClient = + callerGlobalThis[ScramjetClient.SCRAMJET]; + + ctx.args[0] = { + $scramjet$origin: callerClient.url.origin, + $scramjet$data: ctx.args[0], + }; + + // * origin because obviously if (typeof ctx.args[1] === "string") ctx.args[1] = "*"; }, }); diff --git a/src/client/index.ts b/src/client/index.ts index 1814df2..5ec0071 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -2,6 +2,16 @@ import { ScramjetClient } from "./client"; +export const iswindow = "window" in self; +export const isworker = "WorkerGlobalScope" in self; +export const issw = "ServiceWorkerGlobalScope" in self; +export const isdedicated = "DedicatedWorkerGlobalScope" in self; +export const isshared = "SharedWorkerGlobalScope" in self; + +export const wrapfn = "$scramjet$wrap"; +export const trysetfn = "$scramjet$tryset"; +export const importfn = "$scramjet$import"; + dbg.log("scrammin"); // if it already exists, that means the handlers have probably already been setup by the parent document if (!(ScramjetClient.SCRAMJET in self)) { diff --git a/src/client/shared/event.ts b/src/client/shared/event.ts new file mode 100644 index 0000000..7ac0b58 --- /dev/null +++ b/src/client/shared/event.ts @@ -0,0 +1,81 @@ +import { iswindow } from ".."; +import { ScramjetClient } from "../client"; + +const realOnEvent = Symbol.for("scramjet original onevent function"); + +export default function (client: ScramjetClient, self: Self) { + const handlers = { + message: { + origin() { + return this.data.$scramjet$origin; + }, + data() { + return this.data.$scramjet$data; + }, + }, + }; + + function wraplistener(listener: (...args: any) => any) { + return new Proxy(listener, { + apply(target, thisArg, argArray) { + const realEvent: Event = argArray[0]; + + // we only need to handle events dispatched from the browser + if (realEvent.isTrusted) { + const type = realEvent.type; + + if (type in handlers) { + let handler = handlers[type]; + argArray[0] = new Proxy(realEvent, { + get(_target, prop, reciever) { + if (prop in handler) { + return handler[prop].call(_target); + } + + return Reflect.get(target, prop, reciever); + }, + }); + } + } + + return listener.apply(self, argArray); + }, + }); + } + + client.Proxy("EventTarget.prototype.addEventListener", { + apply(ctx) { + ctx.args[1] = wraplistener(ctx.args[1]); + }, + }); + + if (!iswindow) return; + + const targets = [self.window, self.HTMLElement.prototype]; + + for (const target of targets) { + const keys = Reflect.ownKeys(target); + + for (const key of keys) { + if (typeof key === "string" && key.startsWith("on")) { + const descriptor = Object.getOwnPropertyDescriptor(target, key); + if (!descriptor.get || !descriptor.set || !descriptor.configurable) + continue; + + // these are the `onmessage`, `onclick`, etc. properties + client.RawTrap(target, key, { + get(ctx) { + if (this[realOnEvent]) return this[realOnEvent]; + + return ctx.get(); + }, + set(ctx, value: any) { + this[realOnEvent] = value; + + ctx.set(wraplistener(value)); + }, + }); + } + } + } +} diff --git a/src/client/shared/import.ts b/src/client/shared/import.ts index 2794d7d..543f300 100644 --- a/src/client/shared/import.ts +++ b/src/client/shared/import.ts @@ -1,5 +1,5 @@ import { encodeUrl } from "../shared"; -import { importfn } from "./wrap"; +import { importfn } from "../"; export default function (client, self) { self[importfn] = function (base) { diff --git a/src/client/shared/wrap.ts b/src/client/shared/wrap.ts index 533a0fc..3a4b87f 100644 --- a/src/client/shared/wrap.ts +++ b/src/client/shared/wrap.ts @@ -1,21 +1,16 @@ +import { iswindow, isworker, trysetfn, wrapfn } from ".."; import { ScramjetClient } from "../client"; -export const iswindow = "window" in self; -export const isworker = "WorkerGlobalScope" in self; -export const issw = "ServiceWorkerGlobalScope" in self; -export const isdedicated = "DedicatedWorkerGlobalScope" in self; -export const isshared = "SharedWorkerGlobalScope" in self; - -export const wrapfn = "$scramjet$wrap"; -export const trysetfn = "$scramjet$tryset"; -export const importfn = "$scramjet$import"; - export default function (client: ScramjetClient, self: typeof globalThis) { // the main magic of the proxy. all attempts to access any "banned objects" will be redirected here, and instead served a proxy object // this contrasts from how other proxies will leave the root object alone and instead attempt to catch every member access // this presents some issues (see element.ts), but makes us a good bit faster at runtime! Object.defineProperty(self, wrapfn, { - value: function (identifier: any) { + value: function (identifier: any, args: any) { + if (args && typeof args === "object" && args.length === 0) + for (const arg of args) { + argdbg(arg); + } if (iswindow && identifier instanceof self.Window) { return client.windowProxy; } else if (iswindow && identifier instanceof self.parent.self.Window) { @@ -75,4 +70,15 @@ export default function (client: ScramjetClient, self: typeof globalThis) { writable: false, configurable: false, }); + + function argdbg(arg) { + switch (typeof arg) { + case "string": + if (arg.includes("scramjet")) debugger; + break; + case "object": + for (let ar of arg) argdbg(ar); + break; + } + } } diff --git a/src/client/window.ts b/src/client/window.ts index ab93f97..ec90163 100644 --- a/src/client/window.ts +++ b/src/client/window.ts @@ -1,6 +1,6 @@ import { encodeUrl } from "../shared/rewriters/url"; import { ScramjetClient } from "./client"; -import { wrapfn } from "./shared/wrap"; +import { wrapfn } from "."; export function createWindowProxy( client: ScramjetClient, diff --git a/src/global.d.ts b/src/global.d.ts index 10c5bec..a856636 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -4,3 +4,5 @@ declare const dbg: { error: (message: string, ...args: any[]) => void; debug: (message: string, ...args: any[]) => void; }; + +declare type Self = Window & typeof globalThis; diff --git a/src/types.d.ts b/src/types.d.ts index 3a3ac3b..8a16769 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -51,5 +51,3 @@ declare global { WASM: string; } } - -type Self = Window & typeof globalThis;