fix postmessage stuff a little more

This commit is contained in:
velzie 2024-09-19 20:04:32 -04:00
parent 4ae8dafccb
commit f48ecdb2b9
No known key found for this signature in database
GPG key ID: 048413F95F0DDE1F
3 changed files with 86 additions and 82 deletions

View file

@ -1,81 +0,0 @@
import { iswindow } from "..";
import { SCRAMJETCLIENT } from "../../symbols";
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
// 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
let pollutant;
if (typeof ctx.args[0] === "object" && ctx.args[0] !== null) {
pollutant = ctx.args[0]; // try to use the first object we can find because it's more reliable
} else if (typeof ctx.args[2] === "object" && ctx.args[2] !== null) {
pollutant = ctx.args[2]; // next try to use transfer
} else if (
POLLUTANT in ctx.this &&
typeof ctx.this[POLLUTANT] === "object" &&
ctx.this[POLLUTANT] !== null
) {
pollutant = ctx.this[POLLUTANT]; // lastly try to use the object from $setrealm
} else {
pollutant = {}; // give up
}
// and now we can steal Function from the caller's realm
const {
constructor: { constructor: Function },
} = pollutant;
// invoking stolen function will give us the caller's globalThis, remember scramjet has already proxied it!!!
const callerGlobalThisProxied: Self = Function("return globalThis")();
const callerClient = callerGlobalThisProxied[SCRAMJETCLIENT];
// 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$messagetype: "window",
$scramjet$origin: callerClient.url.origin,
$scramjet$data: ctx.args[0],
};
// * origin because obviously
if (typeof ctx.args[1] === "string") ctx.args[1] = "*";
ctx.return(
wrappedPostMessage.call(ctx.fn, ctx.args[0], ctx.args[1], ctx.args[2])
);
},
});
const toproxy = [
"Worker.prototype.postMessage",
"MessagePort.prototype.postMessage",
];
if (!iswindow) toproxy.push("self.postMessage"); // only do the generic version if we're in a worker
client.Proxy(toproxy, {
apply(ctx) {
// origin/source doesn't need to be preserved - it's null in the message event
ctx.args[0] = {
$scramjet$messagetype: "worker",
$scramjet$data: ctx.args[0],
};
},
});
}

View file

@ -0,0 +1,82 @@
import { iswindow } from "..";
import { SCRAMJETCLIENT } from "../../symbols";
import { ScramjetClient } from "../client";
import { POLLUTANT } from "../shared/realm";
export default function (client: ScramjetClient) {
if (iswindow)
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
// 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
let pollutant;
if (typeof ctx.args[0] === "object" && ctx.args[0] !== null) {
pollutant = ctx.args[0]; // try to use the first object we can find because it's more reliable
} else if (typeof ctx.args[2] === "object" && ctx.args[2] !== null) {
pollutant = ctx.args[2]; // next try to use transfer
} else if (
POLLUTANT in ctx.this &&
typeof ctx.this[POLLUTANT] === "object" &&
ctx.this[POLLUTANT] !== null
) {
pollutant = ctx.this[POLLUTANT]; // lastly try to use the object from $setrealm
} else {
pollutant = {}; // give up
}
// and now we can steal Function from the caller's realm
const {
constructor: { constructor: Function },
} = pollutant;
// invoking stolen function will give us the caller's globalThis, remember scramjet has already proxied it!!!
const callerGlobalThisProxied: Self = Function("return globalThis")();
const callerClient = callerGlobalThisProxied[SCRAMJETCLIENT];
// 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$messagetype: "window",
$scramjet$origin: callerClient.url.origin,
$scramjet$data: ctx.args[0],
};
// * origin because obviously
if (typeof ctx.args[1] === "string") ctx.args[1] = "*";
ctx.return(
wrappedPostMessage.call(ctx.fn, ctx.args[0], ctx.args[1], ctx.args[2])
);
},
});
const toproxy = [
"Worker.prototype.postMessage",
"MessagePort.prototype.postMessage",
];
if (!iswindow) toproxy.push("self.postMessage"); // only do the generic version if we're in a worker
client.Proxy(toproxy, {
apply(ctx) {
// origin/source doesn't need to be preserved - it's null in the message event
ctx.args[0] = {
$scramjet$messagetype: "worker",
$scramjet$data: ctx.args[0],
};
},
});
}

View file

@ -3,6 +3,8 @@ import { encodeUrl } from "../../shared/rewriters/url";
import type { MessageC2W } from "../../worker";
import { ScramjetClient } from "../client";
const workerpostmessage = Worker.prototype.postMessage;
export default function (client: ScramjetClient, self: typeof globalThis) {
client.Proxy("Worker", {
construct({ args, call }) {
@ -36,7 +38,8 @@ export default function (client: ScramjetClient, self: typeof globalThis) {
(async () => {
const port = await conn.getInnerPort();
worker.postMessage(
workerpostmessage.call(
worker,
{
$scramjet$type: "baremuxinit",
port,