diff --git a/src/client/client.ts b/src/client/client.ts index dc14748..8403e9a 100644 --- a/src/client/client.ts +++ b/src/client/client.ts @@ -110,7 +110,8 @@ export class ScramjetClient { }); for (const module of modules) { - if (module.enabled()) module.default(this, this.global); + if (!module.enabled || module.enabled()) + module.default(this, this.global); else module.disabled(this, this.global); } } diff --git a/src/client/dom/postmessage.ts b/src/client/dom/postmessage.ts index 8f0b213..64dd96b 100644 --- a/src/client/dom/postmessage.ts +++ b/src/client/dom/postmessage.ts @@ -1,21 +1,35 @@ import { ScramjetClient } from "../client"; +import { POLLUTANT } from "../shared/realm"; 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 + // if we were given any object that came from the real realm we can use that to get the real origin + // and this works in every case EXCEPT for the fact that all three arguments can be strings which are copied instead of cloned + // so we have to use `$setrealm` which will pollute this with an object from the real realm + const pollutant = ctx.this[POLLUTANT] || {}; - // this obtains a reference to the Function object of the real realm + // and now we can steal Function from the caller's realm const { constructor: { constructor: Function }, - } = ctx.args[0]; + } = pollutant; - // and finally, invoking the stolen Function will execute inside the caller's realm - const callerGlobalThis: Self = Function("return globalThis")(); + // invoking stolen function will give us the caller's globalThis, remember scramjet has already proxied it!!! + const callerGlobalThisProxied: Self = Function("return globalThis")(); const callerClient: ScramjetClient = - callerGlobalThis[ScramjetClient.SCRAMJET]; + callerGlobalThisProxied[ScramjetClient.SCRAMJET]; + + // this WOULD be enough but the source argument of MessageEvent has to return the caller's window + // and if we just call it normally it would be coming from here, which WILL NOT BE THE CALLER'S because the accessor is from the parent + // so with the stolen function we wrap postmessage so the source will truly be the caller's window (remember that function is scramjet's!!!) + const wrappedPostMessage = Function( + "data", + "origin", + "transfer", + "this(data, origin, transfer)" + ); ctx.args[0] = { $scramjet$origin: callerClient.url.origin, @@ -24,6 +38,8 @@ export default function (client: ScramjetClient) { // * origin because obviously if (typeof ctx.args[1] === "string") ctx.args[1] = "*"; + + wrappedPostMessage.call(ctx.fn, ctx.args[0], ctx.args[1], ctx.args[2]); }, }); } diff --git a/src/client/helpers.ts b/src/client/helpers.ts index b235775..00cc290 100644 --- a/src/client/helpers.ts +++ b/src/client/helpers.ts @@ -1,6 +1,6 @@ export function getOwnPropertyDescriptorHandler(target, prop) { let realDescriptor = Reflect.getOwnPropertyDescriptor(target, prop); - if (!realDescriptor) return realDescriptor; + return realDescriptor; let d: PropertyDescriptor = {}; diff --git a/src/client/shared/realm.ts b/src/client/shared/realm.ts new file mode 100644 index 0000000..b6300e1 --- /dev/null +++ b/src/client/shared/realm.ts @@ -0,0 +1,26 @@ +import { ScramjetClient } from "../client"; +import { config } from "../shared"; + +export const POLLUTANT = Symbol.for("scramjet realm pollutant"); + +export default function (client: ScramjetClient, self: typeof globalThis) { + // object.$setrealm({}).postMessage(...) + // the empty object is the "pollutant" which can reconstruct the real realm + // i explain more in postmessage.ts + Object.defineProperty(self.Object.prototype, config.setrealmfn, { + value(pollution: {}) { + // this is bad!! sites could detect this + Object.defineProperty(this, POLLUTANT, { + value: pollution, + writable: false, + configurable: true, + enumerable: false, + }); + + return this; + }, + writable: false, + configurable: false, + enumerable: false, + }); +} diff --git a/src/controller/index.ts b/src/controller/index.ts index 46d3fc4..1d55a96 100644 --- a/src/controller/index.ts +++ b/src/controller/index.ts @@ -16,12 +16,16 @@ export class ScramjetController { importfn: "$scramjet$import", rewritefn: "$scramjet$rewrite", metafn: "$scramjet$meta", + setrealmfn: "$scramjet$setrealm", wasm: "/scramjet.wasm.js", shared: "/scramjet.shared.js", worker: "/scramjet.worker.js", thread: "/scramjet.thread.js", client: "/scramjet.client.js", codecs: "/scramjet.codecs.js", + flags: { + serviceworkers: true, + }, }; this.config = Object.assign({}, defaultConfig, config); diff --git a/src/types.d.ts b/src/types.d.ts index 5a2931f..a6e4135 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -28,6 +28,7 @@ interface ScramjetConfig { importfn: string; rewritefn: string; metafn: string; + setrealmfn: string; wasm: string; shared: string; worker: string;