Skip to content

Commit

Permalink
Improvements
Browse files Browse the repository at this point in the history
  * Fixed tests (now tests pass again)
  * Removed task ID in favour of MessageChannel
  * Updated dependencies
  • Loading branch information
corrideat committed May 12, 2024
1 parent 130b570 commit e9c101a
Show file tree
Hide file tree
Showing 15 changed files with 519 additions and 441 deletions.
346 changes: 161 additions & 185 deletions package-lock.json

Large diffs are not rendered by default.

20 changes: 10 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -133,23 +133,23 @@
"author": "Exact Realty Limited",
"license": "ISC",
"devDependencies": {
"@exact-realty/esbuild-plugin-closure-compiler": "^1.0.1",
"@exact-realty/esbuild-plugin-inline-js": "^1.1.5",
"@types/selenium-webdriver": "^4.1.21",
"@typescript-eslint/eslint-plugin": "^6.21.0",
"@typescript-eslint/parser": "^6.21.0",
"@exact-realty/esbuild-plugin-closure-compiler": "^1.0.2",
"@exact-realty/esbuild-plugin-inline-js": "^1.1.6",
"@types/selenium-webdriver": "^4.1.22",
"@typescript-eslint/eslint-plugin": "^7.8.0",
"@typescript-eslint/parser": "^7.8.0",
"esbuild": "^0.19.5",
"eslint": "^8.56.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.1.3",
"glob": "^10.3.10",
"google-closure-compiler": "^20230802.0.0",
"glob": "^10.3.14",
"google-closure-compiler": "^20240317.0.0",
"prettier": "^3.2.5",
"selenium-webdriver": "^4.17.0",
"selenium-webdriver": "^4.20.0",
"ts-node": "^10.9.2",
"ts-patch": "^3.1.2",
"typescript": "^5.3.3",
"typescript-transform-paths": "^3.4.6"
"typescript": "^5.4.5",
"typescript-transform-paths": "^3.4.7"
},
"engines": {
"npm": ">=8.0.0",
Expand Down
2 changes: 1 addition & 1 deletion src/trusted/impl/nodejs/nodejsSandbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const nodejsSandbox = async <T>(
);
}

// Needed for CC, which is unware of these properties
// Needed for Closure Compiler, which is unaware of these properties
const on = 'on';
const terminate = 'terminate';

Expand Down
24 changes: 7 additions & 17 deletions src/trusted/lib/setupSandboxListeners.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,10 @@ const setupSandboxListeners = <T>(

const postMessage = messagePort.postMessage.bind(messagePort);

const [performTask, resultHandler, destroyTaskPerformer] =
performTaskFactory<T>(!!abort, postMessage);
const [performTask, destroyTaskPerformer] = performTaskFactory<T>(
!!abort,
postMessage,
);

const eventListener = (event: MessageEvent) => {
if ((!allowUntrusted && !event.isTrusted) || !Array.isArray(event.data))
Expand All @@ -68,35 +70,23 @@ const setupSandboxListeners = <T>(

if (__buildtimeSettings__.bidirectionalMessaging) {
if (data[0] === EMessageTypes.REQUEST) {
Logger.debug(
'Received REQUEST for task [' + data[1] + '] ' + data[2],
);
Logger.debug('Received REQUEST for task ' + data[2]);

if (!externalMethods) {
// This situation should not be possible
Logger.debug(
'Received REQUEST for task [' +
data[1] +
'] ' +
'Received REQUEST for task ' +
data[2] +
', but there are no external methods configured',
);
return;
}

requestHandler(
postMessage,
externalMethods,
data[1],
data[2],
data[3],
);
requestHandler(externalMethods, data[1], data[2], data[3]);

return;
}
}

resultHandler(data);
};

const onDestroy = () => {
Expand Down
51 changes: 48 additions & 3 deletions src/untrusted/impl/nodejs/nodejsSandboxInit.inline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
*/

import {
aForEach,
aFrom,
aIndexOf,
aIsArray,
Expand Down Expand Up @@ -120,7 +121,8 @@ const nativeWrapperFactory =
// environment
// Then, the parent needs to delete these functions, which it can do when
// it receives SANDBOX_READY in the next step
['atob', 'btoa', 'close', 'clearInterval', 'clearTimeout'].forEach(
aForEach(
['atob', 'btoa', 'close', 'clearInterval', 'clearTimeout'],
nativeWrapperFactory(
globalThis as unknown as Parameters<typeof nativeWrapperFactory>[0],
),
Expand Down Expand Up @@ -299,6 +301,23 @@ if (__buildtimeSettings__.contextifyMessagePort) {
typeof Function.prototype
>();

/** Wrapper for structuredClone to preserve the MessagePort argument */
const cloneMsgData =
typeof structuredClone === 'function'
? l_structuredClone
: (data: unknown) => {
const clonedData = l_structuredClone(data);
if (
aIsArray(data) &&
aIsArray(clonedData) &&
clonedData[0] === EMessageTypes.REQUEST &&
typeof clonedData[1] === 'object'
) {
clonedData[1] = data[1];
}
return clonedData;
};

/**
* Wrapper function for the `addEventListener` method.
* This ensures that messages passed to the listeners are cloned to
Expand All @@ -321,7 +340,7 @@ if (__buildtimeSettings__.contextifyMessagePort) {
if (ev.type !== 'message') return;

oDefineProperty(ev, 'data', {
['value']: l_structuredClone((ev as MessageEvent).data),
['value']: cloneMsgData((ev as MessageEvent).data),
});
listener(ev);
};
Expand Down Expand Up @@ -426,7 +445,7 @@ if (__buildtimeSettings__.contextifyMessagePort) {
}.bind(globalThis);
})();

['setInterval', 'setTimeout'].forEach((v) => {
aForEach(['setInterval', 'setTimeout'], (v) => {
const setTimer = (
globalThis as unknown as Record<
string,
Expand Down Expand Up @@ -465,6 +484,32 @@ if (__buildtimeSettings__.contextifyMessagePort) {
}.bind(globalThis);
});

if (__buildtimeSettings__.bidirectionalMessaging && isNaN(1)) {
(() => {
const mc = globalThis.MessageChannel;

if (typeof mc !== 'function') return;

const wmc = function (this: MessageChannel) {
try {
const r = new mc();

aForEach(['port1', 'port2'], (v: keyof MessageChannel) => {
oDefineProperty(this, v, {
configurable: true,
enumerable: true,
get: () => r[v],
});
});
} catch (e) {
throw recreateError(e);
}
} as unknown as typeof MessageChannel;

globalThis.MessageChannel = wmc;
})();
}

Logger.info('Worker started, registering event listener');

globalThis.addEventListener('message', listener, false);
64 changes: 60 additions & 4 deletions src/untrusted/impl/nodejs/nodejsSandboxVm.inline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ import {
moveMessagePortToContext,
workerData,
} from 'node:worker_threads';
import type { MessagePort as WTMessagePort } from 'node:worker_threads';
import createWrapperFn from '~untrusted/lib/createWrapperFn.js';
import hardenGlobals from '~untrusted/lib/hardenGlobals.js';
import scopedTimerFunction from '~untrusted/lib/scopedTimerFunction.js';
import { INTERNAL_SOURCE_STRING } from './constants.js';
import { MC, aIsArray } from '~/untrusted/lib/utils.js';

if (isMainThread) throw new Error('Invalid environment');

Expand Down Expand Up @@ -245,6 +247,40 @@ const nodejsSandbox: TNodejsSandbox = (
['value']: scopedSetTimeout,
},
],
...(__buildtimeSettings__.bidirectionalMessaging
? [
[
'MessageChannel',
{
['writable']: true,
['configurable']: true,
['value']:
__buildtimeSettings__.contextifyMessagePort
? function (this: {
['port1']: WTMessagePort;
['port2']: WTMessagePort;
}) {
const mc = new MC();
this['port1'] =
moveMessagePortToContext(
mc[
'port1'
] as unknown as WTMessagePort,
context,
);
this['port2'] =
moveMessagePortToContext(
mc[
'port2'
] as unknown as WTMessagePort,
context,
);
}
: MC,
},
],
]
: []),
]),
);

Expand All @@ -269,20 +305,40 @@ const nodejsSandbox: TNodejsSandbox = (
if (__buildtimeSettings__.contextifyMessagePortWorkaroundCrash) {
const messageChannel = new MessageChannel();

messageChannel.port1.onmessage = (ev) => {
messagePort.postMessage(ev.data);
messageChannel['port1'].onmessage = (ev) => {
if (
aIsArray(ev.data) &&
ev.data[0] === EMessageTypes.REQUEST &&
typeof ev.data[1] === 'object'
) {
messagePort.postMessage(ev.data, [ev.data[1]]);
} else {
messagePort.postMessage(ev.data);
}
};

messagePort.onmessage = (ev) => {
messageChannel.port1.postMessage(ev.data);
if (
aIsArray(ev.data) &&
ev.data[0] === EMessageTypes.REQUEST &&
typeof ev.data[1] === 'object'
) {
ev.data[1] = moveMessagePortToContext(
ev.data[1] as ReturnType<typeof eval>,
context,
);
messageChannel['port1'].postMessage(ev.data, [ev.data[1]]);
} else {
messageChannel['port1'].postMessage(ev.data);
}
};

oneTimeCtxValue(
sandboxId,
context,
'%__messagePort__',
moveMessagePortToContext(
messageChannel.port2 as ReturnType<typeof eval>,
messageChannel['port2'] as ReturnType<typeof eval>,
context,
),
);
Expand Down
24 changes: 3 additions & 21 deletions src/untrusted/impl/worker/workerSandboxManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,33 +122,15 @@ const workerSandboxManager = async (
const revokeRootMessageEventListener = createMessageEventListener(
messagePort,
(data: unknown[]) => {
if (
[
EMessageTypes.REQUEST,
EMessageTypes.RESULT,
EMessageTypes.ERROR,
].includes(data[0] as EMessageTypes)
) {
if (data[0] === EMessageTypes.REQUEST) {
if (data[0] === EMessageTypes.REQUEST) {
Logger.debug(
'Forwarding REQUEST from parent to worker for task [' +
data[1] +
'] ' +
'Forwarding REQUEST from parent to worker for task' +
data[2],
);
} else {
Logger.debug(
'Forwarding ' +
(data[0] === EMessageTypes.RESULT
? 'RESULT'
: 'ERROR') +
' from parent to worker for task [' +
data[1] +
']',
);
}

worker.postMessage(data);
worker.postMessage(data, [data[1] as MessagePort]);
} else if (data[0] === EMessageTypes.DESTROY) {
Logger.debug('Received DESTROY from parent');

Expand Down
29 changes: 4 additions & 25 deletions src/untrusted/lib/createSandboxedHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,17 +92,7 @@ const createSandboxedHandler = (

const handler = (data: unknown[]) => {
if (
!aIncludes(
__buildtimeSettings__.bidirectionalMessaging
? [
EMessageTypes.REQUEST,
EMessageTypes.DESTROY,
EMessageTypes.RESULT,
EMessageTypes.ERROR,
]
: [EMessageTypes.REQUEST, EMessageTypes.DESTROY],
data[0] as EMessageTypes,
)
!aIncludes([EMessageTypes.REQUEST, EMessageTypes.DESTROY], data[0])
) {
return;
}
Expand All @@ -117,23 +107,20 @@ const createSandboxedHandler = (
postMessage = Boolean;

if (__buildtimeSettings__.bidirectionalMessaging) {
performTaskMethods?.[2]();
performTaskMethods?.[1]();
}

cleanup();

return;
}
case EMessageTypes.REQUEST: {
Logger.debug(
'Received REQUEST for task [' + data[1] + '] ' + data[2],
);
Logger.debug('Received REQUEST for task ' + data[2]);

if (!ctx?.['module']?.['exports']) {
try {
postMessage([
EMessageTypes.ERROR,
data[1],
extractErrorInformation(
RE(`${data[2]} is not defined`),
),
Expand All @@ -145,22 +132,14 @@ const createSandboxedHandler = (
}

requestHandler(
postMessage,
ctx['module']['exports'],
data[1],
data[1] as MessagePort,
data[2],
...aSlice(data, 3),
);

return;
}
case EMessageTypes.RESULT:
case EMessageTypes.ERROR: {
if (__buildtimeSettings__.bidirectionalMessaging) {
performTaskMethods?.[1](data);
}
return;
}
}
};

Expand Down
Loading

0 comments on commit e9c101a

Please sign in to comment.