fix(Code Node): Upgrade pyodide, sandbox it, and prevent JS sandbox escape (#14356)

This commit is contained in:
कारतोफ्फेलस्क्रिप्ट™
2025-04-23 14:41:48 +02:00
committed by GitHub
parent 9021e195fa
commit 6c9c720ae9
6 changed files with 42 additions and 59 deletions

View File

@@ -98,7 +98,6 @@
"patchedDependencies": {
"bull@4.12.1": "patches/bull@4.12.1.patch",
"pkce-challenge@5.0.0": "patches/pkce-challenge@5.0.0.patch",
"pyodide@0.23.4": "patches/pyodide@0.23.4.patch",
"@types/express-serve-static-core@5.0.6": "patches/@types__express-serve-static-core@5.0.6.patch",
"@types/ws@8.5.4": "patches/@types__ws@8.5.4.patch",
"@types/uuencode@0.0.3": "patches/@types__uuencode@0.0.3.patch",

View File

@@ -1,4 +1,5 @@
import { dirname } from 'node:path';
import { createContext, runInContext } from 'node:vm';
import type { PyodideInterface } from 'pyodide';
let pyodideInstance: PyodideInterface | undefined;
@@ -6,8 +7,22 @@ let pyodideInstance: PyodideInterface | undefined;
export async function LoadPyodide(packageCacheDir: string): Promise<PyodideInterface> {
if (pyodideInstance === undefined) {
const { loadPyodide } = await import('pyodide');
const { XMLHttpRequest } = await import('xmlhttprequest-ssl');
const indexURL = dirname(require.resolve('pyodide'));
pyodideInstance = await loadPyodide({ indexURL, packageCacheDir });
const context = createContext({
loadPyodide,
indexURL,
packageCacheDir,
jsglobals: {
Object,
console,
XMLHttpRequest,
},
});
pyodideInstance = (await runInContext(
'loadPyodide({ indexURL, packageCacheDir, jsglobals })',
context,
)) as PyodideInterface;
await pyodideInstance.runPythonAsync(`
from _pyodide_core import jsproxy_typedict

View File

@@ -845,8 +845,8 @@
"@types/cheerio": "^0.22.15",
"@types/eventsource": "^1.1.2",
"@types/express": "catalog:",
"@types/html-to-text": "^9.0.1",
"@types/gm": "^1.25.0",
"@types/html-to-text": "^9.0.1",
"@types/js-nacl": "^1.3.0",
"@types/jsonwebtoken": "catalog:",
"@types/lodash": "catalog:",
@@ -869,11 +869,11 @@
"dependencies": {
"@aws-sdk/client-sso-oidc": "3.666.0",
"@kafkajs/confluent-schema-registry": "3.8.0",
"@mozilla/readability": "0.6.0",
"@n8n/config": "workspace:*",
"@n8n/di": "workspace:*",
"@n8n/imap": "workspace:*",
"@n8n/vm2": "3.9.25",
"@mozilla/readability": "0.6.0",
"alasql": "4.4.0",
"amqplib": "0.10.3",
"aws4": "1.11.0",
@@ -887,8 +887,8 @@
"eventsource": "2.0.2",
"fast-glob": "catalog:",
"fflate": "0.7.4",
"get-system-fonts": "2.0.2",
"generate-schema": "2.6.0",
"get-system-fonts": "2.0.2",
"gm": "1.25.1",
"html-to-text": "9.0.5",
"iconv-lite": "catalog:",
@@ -916,10 +916,10 @@
"nodemailer": "6.9.9",
"otpauth": "9.1.1",
"pdfjs-dist": "2.16.105",
"pg-promise": "11.9.1",
"pg": "8.12.0",
"pg-promise": "11.9.1",
"promise-ftp": "1.3.5",
"pyodide": "0.23.4",
"pyodide": "0.27.5",
"redis": "4.6.14",
"rfc2047": "4.0.1",
"rhea": "1.0.24",
@@ -935,6 +935,7 @@
"ts-ics": "1.2.2",
"uuid": "catalog:",
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz",
"xml2js": "catalog:"
"xml2js": "catalog:",
"xmlhttprequest-ssl": "3.1.0"
}
}

View File

@@ -0,0 +1,3 @@
declare module 'xmlhttprequest-ssl' {
export const XMLHttpRequest: typeof globalThis.XMLHttpRequest;
}

View File

@@ -1,20 +0,0 @@
diff --git a/pyodide.d.ts b/pyodide.d.ts
index d5ed46f6345855a75ec6f2b7ef73237a0af64a7e..e0087c83792558ca30713e9d07a9a37625f68d8d 100644
--- a/pyodide.d.ts
+++ b/pyodide.d.ts
@@ -1118,6 +1118,15 @@ export declare function loadPyodide(options?: {
* (``pyodide.js`` or ``pyodide.mjs``) removed.
*/
indexURL?: string;
+ /**
+ * The file path where packages will be cached in `node.js`. If a package
+ * exists in `packageCacheDir` it is loaded from there, otherwise it is
+ * downloaded from the JsDelivr CDN and then cached into `packageCacheDir`.
+ * Only applies when running in node.js. Ignored in browsers.
+ *
+ * Default: same as indexURL
+ */
+ packageCacheDir?: string;
/**
* file. You can produce custom lock files with :py:func:`micropip.freeze`.
* Default: ```${indexURL}/repodata.json```

47
pnpm-lock.yaml generated
View File

@@ -182,9 +182,6 @@ patchedDependencies:
pkce-challenge@5.0.0:
hash: 651e785d0b7bbf5be9210e1e895c39a16dc3ce8a5a3843b4819565fb6e175b90
path: patches/pkce-challenge@5.0.0.patch
pyodide@0.23.4:
hash: c1002dacf7f6d0827d23aaf6cf2845e1b0c351339306c4ad660b8cd72077976c
path: patches/pyodide@0.23.4.patch
vue-tsc@2.2.8:
hash: e2aee939ccac8a57fe449bfd92bedd8117841579526217bc39aca26c6b8c317f
path: patches/vue-tsc@2.2.8.patch
@@ -2182,8 +2179,8 @@ importers:
specifier: 1.3.5
version: 1.3.5(promise-ftp-common@1.1.5)
pyodide:
specifier: 0.23.4
version: 0.23.4(patch_hash=c1002dacf7f6d0827d23aaf6cf2845e1b0c351339306c4ad660b8cd72077976c)(encoding@0.1.13)
specifier: 0.27.5
version: 0.27.5
redis:
specifier: 4.6.14
version: 4.6.14
@@ -2232,6 +2229,9 @@ importers:
xml2js:
specifier: 'catalog:'
version: 0.6.2
xmlhttprequest-ssl:
specifier: 3.1.0
version: 3.1.0
devDependencies:
'@n8n/typescript-config':
specifier: workspace:*
@@ -7088,9 +7088,6 @@ packages:
balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
base-64@1.0.0:
resolution: {integrity: sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==}
base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
@@ -10929,6 +10926,7 @@ packages:
node-domexception@1.0.0:
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
engines: {node: '>=10.5.0'}
deprecated: Use your platform's native DOMException instead
node-ensure@0.0.0:
resolution: {integrity: sha512-DRI60hzo2oKN1ma0ckc6nQWlHU69RH6xN0sjQTjMpChPfTYvKZdcQFfdYK2RWbJcKyUizSIy/l8OTGxMAM1QDw==}
@@ -10937,15 +10935,6 @@ packages:
resolution: {integrity: sha512-ofRW94Ab0T4AOh5Fk8t0h8OBWrmjb0SSB20xh1H8YnPV9EJ+f5AMoYSUQ2zgJ4Iq2HAK0I2l5/Nequ8YzFS3Hg==}
engines: {node: 4.x || >=6.0.0}
node-fetch@2.6.8:
resolution: {integrity: sha512-RZ6dBYuj8dRSfxpUSu+NsdF1dpPpluJxwOp+6IoDp/sH2QNDSvurYsAa+F1WxY2RjA1iP93xhcsUoYbF2XBqVg==}
engines: {node: 4.x || >=6.0.0}
peerDependencies:
encoding: ^0.1.0
peerDependenciesMeta:
encoding:
optional: true
node-fetch@2.7.0:
resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
engines: {node: 4.x || >=6.0.0}
@@ -11804,8 +11793,9 @@ packages:
pure-rand@6.0.1:
resolution: {integrity: sha512-t+x1zEHDjBwkDGY5v5ApnZ/utcd4XYDiJsaQQoptTXgUXX95sDg1elCdJghzicm7n2mbCBJ3uYWr6M22SO19rg==}
pyodide@0.23.4:
resolution: {integrity: sha512-WpQUHaIXQ1xede5BMqPAjBcmopxN22s5hEsYOR8T7/UW/fkNLFUn07SaemUgthbtvedD5JGymMMj4VpD9sGMTg==}
pyodide@0.27.5:
resolution: {integrity: sha512-nXErpLzEdtQolt+sNQ/5mKuN9XTUwhxR2MRhRhZ6oDRGpYLXrOp5+kkTPGEwK+wn1ZA8+poNmoxKTj2sq/p9og==}
engines: {node: '>=18.0.0'}
python-struct@1.1.3:
resolution: {integrity: sha512-UsI/mNvk25jRpGKYI38Nfbv84z48oiIWwG67DLVvjRhy8B/0aIK+5Ju5WOHgw/o9rnEmbAS00v4rgKFQeC332Q==}
@@ -13875,6 +13865,10 @@ packages:
xmlchars@2.2.0:
resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
xmlhttprequest-ssl@3.1.0:
resolution: {integrity: sha512-UsofFE/khRRAcM9c3FGDEUSwupaQQC3Kme1brtz+B3N+RZHXGbD6AG6QzgWcunHzszqtOSMiZoPNrmHEBB2DjA==}
engines: {node: '>=12.0.0'}
xmllint-wasm@3.0.1:
resolution: {integrity: sha512-t+aKQXJQNAt9/qLgCjhHUmCnPXAyqBKiyh8oV0ZwBMar/uB+5F40tqOJZ97JwLADcqQr5WB2bjCxLKrm+DHz1g==}
engines: {node: '>=10.5.0'}
@@ -20207,8 +20201,6 @@ snapshots:
balanced-match@1.0.2: {}
base-64@1.0.0: {}
base64-js@1.5.1: {}
basic-auth@2.0.1:
@@ -25004,12 +24996,6 @@ snapshots:
dependencies:
http2-client: 1.3.5
node-fetch@2.6.8(encoding@0.1.13):
dependencies:
whatwg-url: 5.0.0
optionalDependencies:
encoding: 0.1.13
node-fetch@2.7.0(encoding@0.1.13):
dependencies:
whatwg-url: 5.0.0
@@ -25935,14 +25921,11 @@ snapshots:
pure-rand@6.0.1: {}
pyodide@0.23.4(patch_hash=c1002dacf7f6d0827d23aaf6cf2845e1b0c351339306c4ad660b8cd72077976c)(encoding@0.1.13):
pyodide@0.27.5:
dependencies:
base-64: 1.0.0
node-fetch: 2.6.8(encoding@0.1.13)
ws: 8.17.1
transitivePeerDependencies:
- bufferutil
- encoding
- utf-8-validate
python-struct@1.1.3:
@@ -28345,6 +28328,8 @@ snapshots:
xmlchars@2.2.0: {}
xmlhttprequest-ssl@3.1.0: {}
xmllint-wasm@3.0.1: {}
xpath@0.0.32: {}