From e9c101adccec4a967fbcd2bc3a4996c5df8c7267 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Iv=C3=A1n=20Vieitez=20Parra?= <3857362+corrideat@users.noreply.github.com> Date: Sun, 12 May 2024 12:57:52 +0000 Subject: [PATCH] Improvements * Fixed tests (now tests pass again) * Removed task ID in favour of MessageChannel * Updated dependencies --- package-lock.json | 346 ++++++++---------- package.json | 20 +- src/trusted/impl/nodejs/nodejsSandbox.ts | 2 +- src/trusted/lib/setupSandboxListeners.ts | 24 +- .../impl/nodejs/nodejsSandboxInit.inline.ts | 51 ++- .../impl/nodejs/nodejsSandboxVm.inline.ts | 64 +++- .../impl/worker/workerSandboxManager.ts | 24 +- src/untrusted/lib/createSandboxedHandler.ts | 29 +- src/untrusted/lib/performTaskFactory.spec.ts | 13 +- src/untrusted/lib/performTaskFactory.ts | 154 ++++---- src/untrusted/lib/requestHandler.spec.ts | 106 ++++-- src/untrusted/lib/requestHandler.ts | 50 ++- src/untrusted/lib/utils.ts | 1 + test/lib/runBrowserTest.ts | 16 +- test/lib/webdriverTestSuites.ts | 60 ++- 15 files changed, 519 insertions(+), 441 deletions(-) diff --git a/package-lock.json b/package-lock.json index 517d391..397fcdc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,23 +9,23 @@ "version": "0.0.20", "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": { "node": ">=16.0.0", @@ -421,9 +421,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz", - "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" @@ -462,22 +462,22 @@ } }, "node_modules/@exact-realty/esbuild-plugin-closure-compiler": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@exact-realty/esbuild-plugin-closure-compiler/-/esbuild-plugin-closure-compiler-1.0.1.tgz", - "integrity": "sha512-B1MtLJy84LhHKBaljonQtDFoUZsSSjsytsf1u53/M50LVQIJsuOIafkhovUg3Z3XcKYS87CITVdK1OmscSNNQA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@exact-realty/esbuild-plugin-closure-compiler/-/esbuild-plugin-closure-compiler-1.0.2.tgz", + "integrity": "sha512-0ZUOzQ5oXeLeExOz2cUGG3918sBzn4+n5bhFWbYFFL7uee6NYlBIZjwsG1/Lz0+H1Sh93pIaVZijQa4JOXbteA==", "dev": true, "peerDependencies": { - "esbuild": "^0.17.0 || ^0.18.0 || ^0.19.0", + "esbuild": "^0.17.0 || ^0.18.0 || ^0.19.0 || ^0.20.0", "google-closure-compiler": "*" } }, "node_modules/@exact-realty/esbuild-plugin-inline-js": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@exact-realty/esbuild-plugin-inline-js/-/esbuild-plugin-inline-js-1.1.5.tgz", - "integrity": "sha512-V/7pTxrcz2d/2enXRe11ClZzU/gLC7c8T2BZ5N7AV0X3AssR2gx/HNzdmgTAYuXEaquGq62tmFvbpM4XlzYBDA==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@exact-realty/esbuild-plugin-inline-js/-/esbuild-plugin-inline-js-1.1.6.tgz", + "integrity": "sha512-2uyz1wP4zP/9YWIElf0wGH2e3l24GzsFj8bLv7mfQYUhe+KMggGGVGfPERN8VEUQNEJVWF+jYhOSq4iyLBWbVA==", "dev": true, "peerDependencies": { - "esbuild": "^0.17.0 || ^0.18.0 || ^0.19.0" + "esbuild": "^0.17.0 || ^0.18.0 || ^0.19.0 || ^0.20.0" } }, "node_modules/@humanwhocodes/config-array": { @@ -699,18 +699,18 @@ "dev": true }, "node_modules/@types/selenium-webdriver": { - "version": "4.1.21", - "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-4.1.21.tgz", - "integrity": "sha512-QGURnImvxYlIQz5DVhvHdqpYNLBjhJ2Vm+cnQI2G9QZzkWlZm0LkLcvDcHp+qE6N2KBz4CeuvXgPO7W3XQ0Tyw==", + "version": "4.1.22", + "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-4.1.22.tgz", + "integrity": "sha512-MCL4l7q8dwxejr2Q2NXLyNwHWMPdlWE0Kpn6fFwJtvkJF7PTkG5jkvbH/X1IAAQxgt/L1dA8u2GtDeekvSKvOA==", "dev": true, "dependencies": { "@types/ws": "*" } }, "node_modules/@types/semver": { - "version": "7.5.6", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", - "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", "dev": true }, "node_modules/@types/ws": { @@ -723,33 +723,33 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", - "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.8.0.tgz", + "integrity": "sha512-gFTT+ezJmkwutUPmB0skOj3GZJtlEGnlssems4AjkVweUPGj7jRwwqg0Hhg7++kPGJqKtTYx+R05Ftww372aIg==", "dev": true, "dependencies": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/type-utils": "6.21.0", - "@typescript-eslint/utils": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.8.0", + "@typescript-eslint/type-utils": "7.8.0", + "@typescript-eslint/utils": "7.8.0", + "@typescript-eslint/visitor-keys": "7.8.0", "debug": "^4.3.4", "graphemer": "^1.4.0", - "ignore": "^5.2.4", + "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", - "eslint": "^7.0.0 || ^8.0.0" + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -758,26 +758,26 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", - "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.8.0.tgz", + "integrity": "sha512-KgKQly1pv0l4ltcftP59uQZCi4HUYswCLbTqVZEJu7uLX8CTLyswqMLqLN+2QFz4jCptqWVV4SB7vdxcH2+0kQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", + "@typescript-eslint/scope-manager": "7.8.0", + "@typescript-eslint/types": "7.8.0", + "@typescript-eslint/typescript-estree": "7.8.0", + "@typescript-eslint/visitor-keys": "7.8.0", "debug": "^4.3.4" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -786,16 +786,16 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", - "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.8.0.tgz", + "integrity": "sha512-viEmZ1LmwsGcnr85gIq+FCYI7nO90DVbE37/ll51hjv9aG+YZMb4WDE2fyWpUR4O/UrhGRpYXK/XajcGTk2B8g==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0" + "@typescript-eslint/types": "7.8.0", + "@typescript-eslint/visitor-keys": "7.8.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -803,25 +803,25 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", - "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.8.0.tgz", + "integrity": "sha512-H70R3AefQDQpz9mGv13Uhi121FNMh+WEaRqcXTX09YEDky21km4dV1ZXJIp8QjXc4ZaVkXVdohvWDzbnbHDS+A==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/typescript-estree": "7.8.0", + "@typescript-eslint/utils": "7.8.0", "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" + "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -830,12 +830,12 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", - "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.8.0.tgz", + "integrity": "sha512-wf0peJ+ZGlcH+2ZS23aJbOv+ztjeeP8uQ9GgwMJGVLx/Nj9CJt17GWgWWoSmoRVKAX2X+7fzEnAjxdvK2gqCLw==", "dev": true, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -843,22 +843,22 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", - "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.8.0.tgz", + "integrity": "sha512-5pfUCOwK5yjPaJQNy44prjCwtr981dO8Qo9J9PwYXZ0MosgAbfEMB008dJ5sNo3+/BN6ytBPuSvXUg9SAqB0dg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", + "@typescript-eslint/types": "7.8.0", + "@typescript-eslint/visitor-keys": "7.8.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -880,9 +880,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -895,41 +895,41 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", - "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.8.0.tgz", + "integrity": "sha512-L0yFqOCflVqXxiZyXrDr80lnahQfSOfc9ELAAZ75sqicqp2i36kEZZGuUymHNFoYOqxRT05up760b4iGsl02nQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "semver": "^7.5.4" + "@types/json-schema": "^7.0.15", + "@types/semver": "^7.5.8", + "@typescript-eslint/scope-manager": "7.8.0", + "@typescript-eslint/types": "7.8.0", + "@typescript-eslint/typescript-estree": "7.8.0", + "semver": "^7.6.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^8.56.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", - "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.8.0.tgz", + "integrity": "sha512-q4/gibTNBQNA0lGyYQCmWRS5D15n8rXh4QjK3KV+MBPlTYHpfBUT3D3PaPR/HeNiI9W6R7FvlkcGhNyAoP+caA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.21.0", - "eslint-visitor-keys": "^3.4.1" + "@typescript-eslint/types": "7.8.0", + "eslint-visitor-keys": "^3.4.3" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -1596,16 +1596,16 @@ "dev": true }, "node_modules/glob": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", - "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "version": "10.3.14", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.14.tgz", + "integrity": "sha512-4fkAqu93xe9Mk7le9v0y3VrPDqLKHarNi2s4Pv7f2yOvfhWfhc7hRPHC/JyqMqb8B/Dt/eGS4n7ykwf3fOsl8g==", "dev": true, "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^2.3.5", + "jackspeak": "^2.3.6", "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" + "minipass": "^7.0.4", + "path-scurry": "^1.11.0" }, "bin": { "glob": "dist/esm/bin.mjs" @@ -1743,13 +1743,13 @@ } }, "node_modules/google-closure-compiler": { - "version": "20230802.0.0", - "resolved": "https://registry.npmjs.org/google-closure-compiler/-/google-closure-compiler-20230802.0.0.tgz", - "integrity": "sha512-o2fYoc8lqOBdhm95Ick0vWrtwH2Icd5yLZhbTcQ0T7NfGiBepYvx1BB63hR8ebgzEZemz9Fh+O6Kg/3Mjm28ww==", + "version": "20240317.0.0", + "resolved": "https://registry.npmjs.org/google-closure-compiler/-/google-closure-compiler-20240317.0.0.tgz", + "integrity": "sha512-PlC5aU2vwsypKbxyFNXOW4psDZfhDoOr2dCwuo8VcgQji+HVIgRi2lviO66x2SfTi0ilm3kI6rq/RSdOMFczcQ==", "dev": true, "dependencies": { "chalk": "4.x", - "google-closure-compiler-java": "^20230802.0.0", + "google-closure-compiler-java": "^20240317.0.0", "minimist": "1.x", "vinyl": "2.x", "vinyl-sourcemaps-apply": "^0.2.0" @@ -1761,21 +1761,21 @@ "node": ">=10" }, "optionalDependencies": { - "google-closure-compiler-linux": "^20230802.0.0", - "google-closure-compiler-osx": "^20230802.0.0", - "google-closure-compiler-windows": "^20230802.0.0" + "google-closure-compiler-linux": "^20240317.0.0", + "google-closure-compiler-osx": "^20240317.0.0", + "google-closure-compiler-windows": "^20240317.0.0" } }, "node_modules/google-closure-compiler-java": { - "version": "20230802.0.0", - "resolved": "https://registry.npmjs.org/google-closure-compiler-java/-/google-closure-compiler-java-20230802.0.0.tgz", - "integrity": "sha512-PWKLMLwj7pR/U0yYbiy649LLqAscu+F1gyY4Y/jK6CmSLb8cIJbL8BTJd00828TzTNfWnYwxbkcQw0y9C2YsGw==", + "version": "20240317.0.0", + "resolved": "https://registry.npmjs.org/google-closure-compiler-java/-/google-closure-compiler-java-20240317.0.0.tgz", + "integrity": "sha512-oWURPChjcCrVfiQOuVtpSoUJVvtOYo41JGEQ2qtArsTGmk/DpWh40vS6hitwKRM/0YzJX/jYUuyt9ibuXXJKmg==", "dev": true }, "node_modules/google-closure-compiler-linux": { - "version": "20230802.0.0", - "resolved": "https://registry.npmjs.org/google-closure-compiler-linux/-/google-closure-compiler-linux-20230802.0.0.tgz", - "integrity": "sha512-F13U4iSXiWeGtHOFS25LVem1s6zI+pJvXVPVR7zSib5ppoUJ0JXnABJQezUR3FnpxmnkALG4oIGW0syH9zPLZA==", + "version": "20240317.0.0", + "resolved": "https://registry.npmjs.org/google-closure-compiler-linux/-/google-closure-compiler-linux-20240317.0.0.tgz", + "integrity": "sha512-dYLtcbbJdbbBS0lTy9SzySdVv/aGkpyTekQiW4ADhT/i1p1b4r0wQTKj6kpVVmFvbZ6t9tW/jbXc9EXXNUahZw==", "cpu": [ "x32", "x64" @@ -1787,9 +1787,9 @@ ] }, "node_modules/google-closure-compiler-osx": { - "version": "20230802.0.0", - "resolved": "https://registry.npmjs.org/google-closure-compiler-osx/-/google-closure-compiler-osx-20230802.0.0.tgz", - "integrity": "sha512-ANAi/ux92Tt+Na7vFDLeK2hRzotjC5j+nxoPtE0OcuNcbjji5dREKoJxkq7r0YwRTCzAFZszK5ip/NPdTOdCEg==", + "version": "20240317.0.0", + "resolved": "https://registry.npmjs.org/google-closure-compiler-osx/-/google-closure-compiler-osx-20240317.0.0.tgz", + "integrity": "sha512-0mABwjD4HP11rikFd8JRIb9OgPqn9h3o3wS0otufMfmbwS7zRpnnoJkunifhORl3VoR1gFm6vcTC9YziTEFdOw==", "cpu": [ "x32", "x64", @@ -1802,9 +1802,9 @@ ] }, "node_modules/google-closure-compiler-windows": { - "version": "20230802.0.0", - "resolved": "https://registry.npmjs.org/google-closure-compiler-windows/-/google-closure-compiler-windows-20230802.0.0.tgz", - "integrity": "sha512-ZQPujoNiiUyTGl8zEGR/0yAygWnbMtX/NQ/S/EHVgq5nmYkvDEVuiVbgpPAmO9lzBTq0hvUTRRATZbTU2ISxgA==", + "version": "20240317.0.0", + "resolved": "https://registry.npmjs.org/google-closure-compiler-windows/-/google-closure-compiler-windows-20240317.0.0.tgz", + "integrity": "sha512-fTueVFzNOWURFlXZmrFkAB7yA+jzpA2TeDOYeBEFwVlVGHwi8PV3Q9vCIWlbkE8wLpukKEg5wfRHYrLwVPINCA==", "cpu": [ "x32", "x64" @@ -1843,9 +1843,9 @@ } }, "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "dev": true, "engines": { "node": ">= 4" @@ -2083,15 +2083,12 @@ "dev": true }, "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", + "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, "engines": { - "node": ">=10" + "node": "14 || >=16.14" } }, "node_modules/make-error": { @@ -2144,9 +2141,9 @@ } }, "node_modules/minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.1.tgz", + "integrity": "sha512-UZ7eQ+h8ywIRAW1hIEl2AqdwzJucU/Kp59+8kkZeSvafXhZjul247BvIJjEVFVeON6d7lM46XX1HXCduKAS8VA==", "dev": true, "engines": { "node": ">=16 || 14 >=14.17" @@ -2272,12 +2269,12 @@ "dev": true }, "node_modules/path-scurry": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", - "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.0.tgz", + "integrity": "sha512-LNHTaVkzaYaLGlO+0u3rQTz7QrHTFOuKyba9JMTQutkmtNew8dw8wOD7mTU/5fCPZzCWpfW0XnQKzY61P0aTaw==", "dev": true, "dependencies": { - "lru-cache": "^9.1.1 || ^10.0.0", + "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { @@ -2287,15 +2284,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.1.tgz", - "integrity": "sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==", - "dev": true, - "engines": { - "node": "14 || >=16.14" - } - }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -2519,27 +2507,24 @@ "dev": true }, "node_modules/selenium-webdriver": { - "version": "4.17.0", - "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.17.0.tgz", - "integrity": "sha512-e2E+2XBlGepzwgFbyQfSwo9Cbj6G5fFfs9MzAS00nC99EewmcS2rwn2MwtgfP7I5p1e7DYv4HQJXtWedsu6DvA==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.20.0.tgz", + "integrity": "sha512-s/G44lGQ1xB3tmtX6NNPomlkpL6CxLdmAvp/AGWWwi4qv5Te1+qji7tPSyr6gyuoPpdYiof1rKnWe3luy0MrYA==", "dev": true, "dependencies": { "jszip": "^3.10.1", - "tmp": "^0.2.1", - "ws": ">=8.14.2" + "tmp": "^0.2.3", + "ws": ">=8.16.0" }, "engines": { "node": ">= 14.20.0" } }, "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -2714,15 +2699,12 @@ "dev": true }, "node_modules/tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", "dev": true, - "dependencies": { - "rimraf": "^3.0.0" - }, "engines": { - "node": ">=8.17.0" + "node": ">=14.14" } }, "node_modules/to-regex-range": { @@ -2738,12 +2720,12 @@ } }, "node_modules/ts-api-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.1.tgz", - "integrity": "sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", "dev": true, "engines": { - "node": ">=16.13.0" + "node": ">=16" }, "peerDependencies": { "typescript": ">=4.2.0" @@ -2850,9 +2832,9 @@ } }, "node_modules/typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -2863,9 +2845,9 @@ } }, "node_modules/typescript-transform-paths": { - "version": "3.4.6", - "resolved": "https://registry.npmjs.org/typescript-transform-paths/-/typescript-transform-paths-3.4.6.tgz", - "integrity": "sha512-qdgpCk9oRHkIBhznxaHAapCFapJt5e4FbFik7Y4qdqtp6VyC3smAIPoDEIkjZ2eiF7x5+QxUPYNwJAtw0thsTw==", + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/typescript-transform-paths/-/typescript-transform-paths-3.4.7.tgz", + "integrity": "sha512-1Us1kdkdfKd2unbkBAOV2HHRmbRBYpSuk9nJ7cLD2hP4QmfToiM/VpxNlhJc1eezVwVqSKSBjNSzZsK/fWR/9A==", "dev": true, "dependencies": { "minimatch": "^3.0.4" @@ -3040,9 +3022,9 @@ "dev": true }, "node_modules/ws": { - "version": "8.14.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", - "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==", + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.0.tgz", + "integrity": "sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==", "dev": true, "engines": { "node": ">=10.0.0" @@ -3060,12 +3042,6 @@ } } }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", diff --git a/package.json b/package.json index 0bfca16..e63a416 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/trusted/impl/nodejs/nodejsSandbox.ts b/src/trusted/impl/nodejs/nodejsSandbox.ts index 1c82f0c..12e34df 100644 --- a/src/trusted/impl/nodejs/nodejsSandbox.ts +++ b/src/trusted/impl/nodejs/nodejsSandbox.ts @@ -37,7 +37,7 @@ const nodejsSandbox = async ( ); } - // 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'; diff --git a/src/trusted/lib/setupSandboxListeners.ts b/src/trusted/lib/setupSandboxListeners.ts index cec8e01..b18cf20 100644 --- a/src/trusted/lib/setupSandboxListeners.ts +++ b/src/trusted/lib/setupSandboxListeners.ts @@ -57,8 +57,10 @@ const setupSandboxListeners = ( const postMessage = messagePort.postMessage.bind(messagePort); - const [performTask, resultHandler, destroyTaskPerformer] = - performTaskFactory(!!abort, postMessage); + const [performTask, destroyTaskPerformer] = performTaskFactory( + !!abort, + postMessage, + ); const eventListener = (event: MessageEvent) => { if ((!allowUntrusted && !event.isTrusted) || !Array.isArray(event.data)) @@ -68,35 +70,23 @@ const setupSandboxListeners = ( 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 = () => { diff --git a/src/untrusted/impl/nodejs/nodejsSandboxInit.inline.ts b/src/untrusted/impl/nodejs/nodejsSandboxInit.inline.ts index 2d4180d..ac6b8f1 100644 --- a/src/untrusted/impl/nodejs/nodejsSandboxInit.inline.ts +++ b/src/untrusted/impl/nodejs/nodejsSandboxInit.inline.ts @@ -14,6 +14,7 @@ */ import { + aForEach, aFrom, aIndexOf, aIsArray, @@ -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[0], ), @@ -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 @@ -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); }; @@ -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, @@ -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); diff --git a/src/untrusted/impl/nodejs/nodejsSandboxVm.inline.ts b/src/untrusted/impl/nodejs/nodejsSandboxVm.inline.ts index 5051426..405bdbb 100644 --- a/src/untrusted/impl/nodejs/nodejsSandboxVm.inline.ts +++ b/src/untrusted/impl/nodejs/nodejsSandboxVm.inline.ts @@ -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'); @@ -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, + }, + ], + ] + : []), ]), ); @@ -269,12 +305,32 @@ 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, + context, + ); + messageChannel['port1'].postMessage(ev.data, [ev.data[1]]); + } else { + messageChannel['port1'].postMessage(ev.data); + } }; oneTimeCtxValue( @@ -282,7 +338,7 @@ const nodejsSandbox: TNodejsSandbox = ( context, '%__messagePort__', moveMessagePortToContext( - messageChannel.port2 as ReturnType, + messageChannel['port2'] as ReturnType, context, ), ); diff --git a/src/untrusted/impl/worker/workerSandboxManager.ts b/src/untrusted/impl/worker/workerSandboxManager.ts index d768774..6483380 100644 --- a/src/untrusted/impl/worker/workerSandboxManager.ts +++ b/src/untrusted/impl/worker/workerSandboxManager.ts @@ -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'); diff --git a/src/untrusted/lib/createSandboxedHandler.ts b/src/untrusted/lib/createSandboxedHandler.ts index d509d1e..1f53be3 100644 --- a/src/untrusted/lib/createSandboxedHandler.ts +++ b/src/untrusted/lib/createSandboxedHandler.ts @@ -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; } @@ -117,7 +107,7 @@ const createSandboxedHandler = ( postMessage = Boolean; if (__buildtimeSettings__.bidirectionalMessaging) { - performTaskMethods?.[2](); + performTaskMethods?.[1](); } cleanup(); @@ -125,15 +115,12 @@ const createSandboxedHandler = ( 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`), ), @@ -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; - } } }; diff --git a/src/untrusted/lib/performTaskFactory.spec.ts b/src/untrusted/lib/performTaskFactory.spec.ts index 03c7647..854802f 100644 --- a/src/untrusted/lib/performTaskFactory.spec.ts +++ b/src/untrusted/lib/performTaskFactory.spec.ts @@ -33,13 +33,12 @@ describe('performTaskFactory', () => { capturedData = data; }; - const [performTask, resultHandler, cleanup] = performTaskFactory( + const [performTask, cleanup] = performTaskFactory( true, postMessageOutgoingMock, ); assert(typeof performTask === 'function'); - assert(typeof resultHandler === 'function'); const mockData = [1, 2, 3]; const mockTask = 'mockTask'; @@ -48,20 +47,22 @@ describe('performTaskFactory', () => { const taskPromise = performTask(mockTask, ...mockData); assert.ok(Array.isArray(capturedData)); - const taskId = capturedData[1]; - capturedData[1] = '%[[TASK_ID]]'; + const taskPort = capturedData[1]; + + assert.equal(typeof taskPort, 'object'); + assert.ok(taskPort instanceof MessagePort); // Ensure that the data were sent correctly assert.deepEqual(capturedData, [ EMessageTypes.REQUEST, - capturedData[1], + taskPort, mockTask, ...mockData, ]); // Simulate receiving a result from the worker const mockResult = 'mockResult'; - resultHandler([EMessageTypes.RESULT, taskId, mockResult]); + taskPort.postMessage([EMessageTypes.RESULT, mockResult]); // Ensure that the task promise resolves with the correct result const result = await taskPromise; diff --git a/src/untrusted/lib/performTaskFactory.ts b/src/untrusted/lib/performTaskFactory.ts index 2410212..40c8ce9 100644 --- a/src/untrusted/lib/performTaskFactory.ts +++ b/src/untrusted/lib/performTaskFactory.ts @@ -13,23 +13,10 @@ * PERFORMANCE OF THIS SOFTWARE. */ -import { - aForEach, - aIncludes, - aIsArray, - aMap, - E, - oCreate, - oFreeze, - oHasOwnProperty, - oKeys, - PM, - TE, -} from './utils.js'; +import { aIncludes, aIsArray, aPush, E, MC, PM, TE } from './utils.js'; import type { IPerformTask } from '~/types/index.js'; import { reconstructErrorInformation } from './errorModem.js'; -import getRandomSecret from './getRandomSecret.js'; import * as Logger from './Logger.js'; import proxyMaybeRevocable from './proxyMaybeRevocable.js'; @@ -45,91 +32,100 @@ import proxyMaybeRevocable from './proxyMaybeRevocable.js'; const performTaskFactory = ( revocable: boolean, postMessageOutgoing: MessagePort['postMessage'], -): [ - performTask: IPerformTask, - resultHandler: { (data: unknown[]): void }, - revoke: { (): void }, -] => { - const pendingTasks: Record< - string, - [typeof Function.prototype, typeof Function.prototype] - > = oCreate(null); - - const resultHandler = (data: unknown[]) => { - if ( - !aIsArray(data) || - !aIncludes( - [EMessageTypes.RESULT, EMessageTypes.ERROR], - data[0] as EMessageTypes, - ) || - typeof data[1] !== 'string' || - !oHasOwnProperty(pendingTasks, data[1] as PropertyKey) - ) - return; - - Logger.debug( - 'Received ' + - (data[0] === EMessageTypes.RESULT ? 'RESULT' : 'ERROR') + - ' from executing task [' + - data[1] + - ']', - ); - - const thisTask = pendingTasks[data[1]]; - - delete pendingTasks[data[1]]; - - if (data[0] === EMessageTypes.RESULT) { - thisTask[0](data[2]); - } else { - thisTask[1](reconstructErrorInformation(data[2])); - } - }; +): [performTask: IPerformTask, revoke: { (): void }] => { + const unresolved: [MessagePort, typeof Boolean][] = []; const performTask: IPerformTask = async (op, ...args) => { if (typeof op !== 'string') { throw TE('Operation must be of string type'); } - const taskId = getRandomSecret(); - - Logger.debug('Sending REQUEST for task [' + taskId + '] ' + op); + Logger.debug('Sending REQUEST for task ' + op); // TODO: Fix type with real return type const taskPromise = new PM((resolve, reject) => { - pendingTasks[taskId] = [resolve, reject]; + const channel = new MC(); + const incomingPort = channel['port1']; + const outgoingPort = channel['port2']; + aPush(unresolved, [incomingPort, reject]); + const markAsResolved = () => { + incomingPort.close(); + const idx = unresolved.findIndex((v) => { + return v[0] === incomingPort; + }); + if (idx !== -1) { + unresolved.splice(idx, 1); + } + }; + incomingPort.onmessage = (ev) => { + const data = ev['data']; + if ( + !aIsArray(data) || + !aIncludes( + [EMessageTypes.RESULT, EMessageTypes.ERROR], + data[0] as EMessageTypes, + ) + ) + return; + + Logger.debug( + 'Received ' + + (data[0] === EMessageTypes.RESULT + ? 'RESULT' + : 'ERROR') + + ' from executing task', + ); + + if (data[0] === EMessageTypes.RESULT) { + resolve(data[1]); + } else { + reject(reconstructErrorInformation(data[1])); + } + markAsResolved(); + }; + incomingPort.onmessageerror = (ev) => { + Logger.debug( + 'Error receiving task result after executing task', + ); + + reject(ev['data']); + markAsResolved(); + }; + incomingPort.start(); + try { + postMessageOutgoing( + [EMessageTypes.REQUEST, outgoingPort, op, ...args], + [outgoingPort], + ); + } catch (e) { + reject(e); + markAsResolved(); + } }); - try { - postMessageOutgoing([EMessageTypes.REQUEST, taskId, op, ...args]); - } catch (e) { - pendingTasks[taskId][1](e); - } - return taskPromise; }; - const performTaskMethods: [typeof performTask, typeof resultHandler] = [ + const performTaskProxy = proxyMaybeRevocable( + revocable || null, performTask, - resultHandler, - ]; - - const performTaskMethodsProxy = aMap(performTaskMethods, (m) => - proxyMaybeRevocable(revocable || null, m, {}), + {}, ); return [ - performTaskMethodsProxy[0].proxy as typeof performTask, - performTaskMethodsProxy[1].proxy as typeof resultHandler, + performTaskProxy['proxy'], () => { - performTaskMethodsProxy[0].revoke(); - performTaskMethodsProxy[1].revoke(); - const error = E('Task cancelled'); - aForEach(oKeys(pendingTasks), (id) => { - pendingTasks[id][1](error); - delete pendingTasks[id]; + performTaskProxy.revoke(); + const e = new E('Task aborted'); + // Abort all pending tasks + unresolved.splice(0).forEach((x) => { + try { + x[1](e); + x[0].close(); + } catch (e) { + void e; + } }); - oFreeze(pendingTasks); }, ]; }; diff --git a/src/untrusted/lib/requestHandler.spec.ts b/src/untrusted/lib/requestHandler.spec.ts index 5be43c8..37927c4 100644 --- a/src/untrusted/lib/requestHandler.spec.ts +++ b/src/untrusted/lib/requestHandler.spec.ts @@ -45,19 +45,28 @@ describe('requestHandler', () => { multiply: (a: number, b: number) => a * b, }; - const id = 'task123'; + const ch = new MessageChannel(); const op = 'add'; const args = [2, 3]; - requestHandler(postMessageStub, ctx, id, op, ...args); - // Check that postMessage was called with the expected result message - assert.deepEqual(postMessageStub.calls, [ - { args: [[EMessageTypes.RESULT, id, 5]] }, - ]); + return new Promise((resolve, reject) => { + ch.port1.onmessage = (ev) => { + try { + assert.deepEqual(ev.data, [EMessageTypes.RESULT, 5]); + resolve(); + } catch (e) { + reject(e); + } + }; + ch.port1.onmessageerror = (ev) => { + reject(ev.data); + }; + requestHandler(ctx, ch.port2, op, ...args); + }); }); - it('should handle asynchronous task execution and send the result', async () => { + it('should handle asynchronous task execution and send the result', () => { const ctx = { async asyncTask(v: string) { return new Promise((resolve) => { @@ -66,19 +75,28 @@ describe('requestHandler', () => { }, }; - const id = 'task456'; + const ch = new MessageChannel(); const op = 'asyncTask'; const args: unknown[] = ['a']; - requestHandler(postMessageStub, ctx, id, op, ...args); - - // Wait for the asynchronous result to be sent via postMessage - await new Promise((resolve) => setTimeout(resolve, 40)); - // Check that postMessage was called with the expected result message - assert.deepEqual(postMessageStub.calls, [ - { args: [[EMessageTypes.RESULT, id, 'Async result: a']] }, - ]); + return new Promise((resolve, reject) => { + ch.port1.onmessage = (ev) => { + try { + assert.deepEqual(ev.data, [ + EMessageTypes.RESULT, + 'Async result: a', + ]); + resolve(); + } catch (e) { + reject(e); + } + }; + ch.port1.onmessageerror = (ev) => { + reject(ev.data); + }; + requestHandler(ctx, ch.port2, op, ...args); + }); }); it('should handle an undefined operation and send an error message', () => { @@ -86,32 +104,42 @@ describe('requestHandler', () => { add: (a: number, b: number) => a + b, }; - const id = 'task789'; + const ch = new MessageChannel(); // Operation 'subtract' is not defined in the context. const op = 'subtract'; - requestHandler(postMessageStub, ctx, id, op); - - assert.equal(postMessageStub.calls.length, 1); - assert.equal(postMessageStub.calls[0].args.length, 1); - assert.ok(Array.isArray(postMessageStub.calls[0].args[0])); - assert.equal((postMessageStub.calls[0].args[0] as unknown[]).length, 3); - assert.deepEqual( - (postMessageStub.calls[0].args[0] as unknown[]).slice(0, 2), - [EMessageTypes.ERROR, id], - ); - assert.ok(Array.isArray(postMessageStub.calls[0].args[0][2])); - assert.throws( - () => { - throw reconstructErrorInformation( - (postMessageStub.calls[0].args[0] as unknown[])[2], - ); - }, - { - name: 'ReferenceError', - message: `${op} is not defined`, - }, - ); + return new Promise((resolve, reject) => { + ch.port1.onmessage = (ev) => { + try { + assert.ok(Array.isArray(ev.data)); + assert.equal(ev.data.length, 2); + assert.equal(ev.data[0], EMessageTypes.ERROR); + + assert.throws( + () => { + throw reconstructErrorInformation( + (ev.data as unknown[])[1], + ); + }, + { + name: 'ReferenceError', + message: `${op} is not defined`, + }, + ); + + resolve(); + } catch (e) { + reject(e); + } + }; + ch.port1.onmessageerror = (ev) => { + reject(ev.data); + }; + + requestHandler(ctx, ch.port2, op); + }).finally(() => { + assert.equal(postMessageStub.calls.length, 0); + }); }); }); diff --git a/src/untrusted/lib/requestHandler.ts b/src/untrusted/lib/requestHandler.ts index b467a4a..41f0cb1 100644 --- a/src/untrusted/lib/requestHandler.ts +++ b/src/untrusted/lib/requestHandler.ts @@ -23,7 +23,6 @@ import { fnApply, oHasOwnProperty, RE, TE } from './utils.js'; * The result or error of the task execution is sent back via the postMessage * function. * - * @param postMessage - The function to send messages back to the caller. * @param ctx - The context object containing the operations that can be * executed. * @param id - The unique identifier for the task. @@ -33,18 +32,26 @@ import { fnApply, oHasOwnProperty, RE, TE } from './utils.js'; * @returns This function does not return a value directly, but uses the postMessage function to send results or errors. */ const requestHandler = ( - postMessage: { (data: unknown[]): void }, ctx: unknown, - id: unknown, + port: MessagePort, op: unknown, ...args: unknown[] ): void => { - if (typeof id !== 'string' || typeof op !== 'string') { - Logger.trace('Rejecting REQUEST, invalid id or op', [id, op]); + if (typeof port !== 'object' || typeof op !== 'string') { + Logger.trace('Rejecting REQUEST, invalid port or op', [op]); return; } - Logger.debug('Handling REQUEST for task [' + id + '] ' + op); + Logger.debug('Handling REQUEST for task ' + op); + + const postAndClose: typeof MessagePort.prototype.postMessage = ( + ...args + ) => { + port['postMessage']( + ...(args as unknown as Parameters<(typeof port)['postMessage']>), + ); + port['close'](); + }; try { if (!ctx || !oHasOwnProperty(ctx, op)) { @@ -69,14 +76,10 @@ const requestHandler = ( ) { const thenable = result['then']((result: unknown) => { Logger.debug( - 'Sending RESULT from executing task [' + - id + - '] ' + - op + - ' (async)', + 'Sending RESULT from executing task ' + op + ' (async)', ); - postMessage([EMessageTypes.RESULT, id, result]); + postAndClose([EMessageTypes.RESULT, result]); }); if ( @@ -87,37 +90,26 @@ const requestHandler = ( ) { thenable['catch']((e: unknown) => { Logger.debug( - 'Sending ERROR from executing task [' + - id + - '] ' + - op + - ' (async)', + 'Sending ERROR from executing task ' + op + ' (async)', ); - postMessage([ + postAndClose([ EMessageTypes.ERROR, - id, extractErrorInformation(e), ]); }); } } else { Logger.debug( - 'Sending RESULT from executing task [' + - id + - '] ' + - op + - ' (sync)', + 'Sending RESULT from executing task ' + op + ' (sync)', ); - postMessage([EMessageTypes.RESULT, id, result]); + postAndClose([EMessageTypes.RESULT, result]); } } catch (e) { - Logger.debug( - 'Sending ERROR from executing task [' + id + '] ' + op + ' (sync)', - ); + Logger.debug('Sending ERROR from executing task ' + op + ' (sync)'); - postMessage([EMessageTypes.ERROR, id, extractErrorInformation(e)]); + postAndClose([EMessageTypes.ERROR, extractErrorInformation(e)]); } }; diff --git a/src/untrusted/lib/utils.ts b/src/untrusted/lib/utils.ts index a369401..7dd6a48 100644 --- a/src/untrusted/lib/utils.ts +++ b/src/untrusted/lib/utils.ts @@ -38,6 +38,7 @@ const l_cGRV = l_crypto?.getRandomValues; export const S = String; export const E = Error; export const EE = EvalError; +export const MC = MessageChannel; export const PM = Promise; export const PX = Proxy; export const RE = ReferenceError; diff --git a/test/lib/runBrowserTest.ts b/test/lib/runBrowserTest.ts index f8f3de1..aff14c5 100644 --- a/test/lib/runBrowserTest.ts +++ b/test/lib/runBrowserTest.ts @@ -20,7 +20,7 @@ import { enabledBrowsers, webdriverTestSuites } from './webdriverTestSuites.js'; const __dirname = fileURLToPath(new URL('.', import.meta.url)); -const runBrowserTest = (m: string, commonBundle?: boolean) => { +const runBrowserTest = async (m: string, commonBundle?: boolean) => { const code = commonBundle ? getCodeHelper(__dirname, '../../dist/index.mjs', m) : getCodeHelper( @@ -29,14 +29,12 @@ const runBrowserTest = (m: string, commonBundle?: boolean) => { 'default', ); - return Promise.all( - enabledBrowsers().map(([browserName, browserDisplayName]) => { - return test( - `Browser: ${browserDisplayName}, module: ${m}`, - webdriverTestSuites(code, browserName), - ); - }), - ); + for (const [browserName, browserDisplayName] of enabledBrowsers()) { + await test( + `Browser: ${browserDisplayName}, module: ${m}`, + webdriverTestSuites(code, browserName), + ); + } }; export default runBrowserTest; diff --git a/test/lib/webdriverTestSuites.ts b/test/lib/webdriverTestSuites.ts index ba52604..669bfcd 100644 --- a/test/lib/webdriverTestSuites.ts +++ b/test/lib/webdriverTestSuites.ts @@ -27,11 +27,9 @@ export const enabledBrowsers = () => { process.env.WEBDRIVER_BROWSERS?.split(/[ ,;]/) ?? [ webdriver.Browser.CHROME, - (process.platform === 'win32' && webdriver.Browser.EDGE) || - undefined, + webdriver.Browser.EDGE, webdriver.Browser.FIREFOX, - (process.platform === 'darwin' && webdriver.Browser.SAFARI) || - undefined, + webdriver.Browser.SAFARI, ].filter(Boolean), ); @@ -68,23 +66,49 @@ export const webdriverTestSuites = code = await codePromise; })(), (async () => { - driver = await new webdriver.Builder() - .forBrowser(browserName) - .setChromeOptions(chromeOptions) - .setEdgeOptions(edgeOptions) - .setFirefoxOptions(firefoxOptions) - .setSafariOptions(safariOptions) - .build(); + try { + const driver_ = await new webdriver.Builder() + .forBrowser(browserName) + .setChromeOptions(chromeOptions) + .setEdgeOptions(edgeOptions) + .setFirefoxOptions(firefoxOptions) + .setSafariOptions(safariOptions) + .build(); + + await driver_.get('about:blank'); - await driver.get('about:blank'); + driver = driver_; + } catch (e) { + if ( + e && + e instanceof Error && + (e.message.includes( + 'Unable to obtain browser driver', + ) || + (e.name === 'WebDriverError' && + e.message.includes( + 'unknown error: cannot find', + ))) + ) { + return; + } + throw e; + } })(), ]); + if (!driver) { + (t as unknown as Record)['skip']( + 'Driver unavailable', + ); + return; + } + await driver.executeScript( code + '; console.log("SCRIPT SUCCESSFULLY LOADED");', ); }, - { timeout: 30e3 }, + { timeout: 12e4 }, ); t.after( @@ -99,6 +123,11 @@ export const webdriverTestSuites = ); await t.test('Can run tasks', async (t) => { + if (!driver) { + t.skip(); + return; + } + await t.test('should return result for sync task', async () => { const result = await driver.executeAsyncScript(` (async () => { @@ -161,6 +190,11 @@ export const webdriverTestSuites = }); await t.test('Error conditions', async (t) => { + if (!driver) { + t.skip(); + return; + } + await t.test('invalid syntax causes error', async () => { const result = await driver.executeAsyncScript( `(async () => {