From 00bf14e566e8f1012981ade73045c83ba4990d2f Mon Sep 17 00:00:00 2001 From: 0xPoker Date: Fri, 19 Jun 2026 16:54:31 +0800 Subject: [PATCH 1/5] =?UTF-8?q?fix:=20=E9=80=82=E9=85=8D=E4=B8=8A=E6=B8=B8?= =?UTF-8?q?=20Sub-Store=20=E6=96=B0=E5=A2=9E=E7=9A=84=20yaml=20=E5=92=8C?= =?UTF-8?q?=20dgram=20=E4=BE=9D=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加 yaml 包依赖(上游从 js-yaml 切换到 yaml) - esbuild 存根列表新增 dgram(上游 DNS 模块使用) --- esbuild.js | 1 + package-lock.json | 20 +++++++++++++++++++- package.json | 3 ++- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/esbuild.js b/esbuild.js index e7c53b4..d2aef41 100644 --- a/esbuild.js +++ b/esbuild.js @@ -231,6 +231,7 @@ const nodeStubPlugin = { 'https', 'os', 'crypto', + 'dgram', ]; for (const mod of stubs) { diff --git a/package-lock.json b/package-lock.json index 8d54faf..7ad64d7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,8 @@ "nanoid": "^3.3.3", "peggy": "^2.0.1", "semver": "^7.6.3", - "static-js-yaml": "^1.0.0" + "static-js-yaml": "^1.0.0", + "yaml": "^2.9.0" }, "devDependencies": { "esbuild": "^0.19.8", @@ -2132,6 +2133,7 @@ "integrity": "sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "pathe": "^2.0.3" } @@ -2149,6 +2151,7 @@ "dev": true, "hasInstallScript": true, "license": "Apache-2.0", + "peer": true, "bin": { "workerd": "bin/workerd" }, @@ -2662,6 +2665,21 @@ "node": ">=0.4" } }, + "node_modules/yaml": { + "version": "2.9.0", + "resolved": "https://registry.npmmirror.com/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, "node_modules/youch": { "version": "4.1.0-beta.10", "resolved": "https://registry.npmmirror.com/youch/-/youch-4.1.0-beta.10.tgz", diff --git a/package.json b/package.json index 02a03d8..13380ad 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,8 @@ "nanoid": "^3.3.3", "peggy": "^2.0.1", "semver": "^7.6.3", - "static-js-yaml": "^1.0.0" + "static-js-yaml": "^1.0.0", + "yaml": "^2.9.0" }, "devDependencies": { "esbuild": "^0.19.8", From 6aefffa2f369f2abbcdae9dd4c9f160a7fa24c6c Mon Sep 17 00:00:00 2001 From: 0xPoker Date: Fri, 19 Jun 2026 21:08:53 +0800 Subject: [PATCH 2/5] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=20QuickJS=20WASM?= =?UTF-8?q?=20=E6=B2=99=E7=AE=B1=E6=89=A7=E8=A1=8C=E5=BC=95=E6=93=8E?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E8=84=9A=E6=9C=AC=E8=BF=87=E6=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- esbuild.js | 11 +- package-lock.json | 69 ++++++ package.json | 2 + src/restful/miscs.js | 7 +- src/vendor/quickjs-executor.js | 405 +++++++++++++++++++++++++++++++++ src/vendor/quickjs.wasm | Bin 0 -> 503134 bytes 6 files changed, 491 insertions(+), 3 deletions(-) create mode 100644 src/vendor/quickjs-executor.js create mode 100644 src/vendor/quickjs.wasm diff --git a/esbuild.js b/esbuild.js index d2aef41..78eb6fd 100644 --- a/esbuild.js +++ b/esbuild.js @@ -114,7 +114,12 @@ const evalRewritePlugin = { contents = contents.replace( /function createDynamicFunction\(name, script, \$arguments, \$options\) \{[\s\S]*?\n\}/, `function createDynamicFunction(name, script, $arguments, $options) { - throw new Error('Script Operator is not supported in Cloudflare Workers because dynamic code execution through eval/new Function is disabled. Use built-in filters/operators, mihomo YAML patch, or an external trusted execution service.'); + const engine = globalThis.__workerEnv?.SCRIPT_ENGINE; + if (engine === 'quickjs') { + const { createScriptFunction } = require('@/vendor/quickjs-executor'); + return createScriptFunction(script, name, $arguments, $options); + } + throw new Error('Script Operator is not supported. Set SCRIPT_ENGINE="quickjs" in wrangler.toml [vars] to enable. Alternative: use built-in filters/operators, mihomo YAML patch, or an external trusted execution service.'); }`, ); } @@ -275,6 +280,10 @@ const nodeStubPlugin = { target: 'es2022', outfile: path.join(__dirname, 'dist', 'worker.js'), plugins: [aliasPlugin, peggyPrecompilePlugin, evalRewritePlugin, nodeStubPlugin], + loader: { + '.wasm': 'copy', + }, + assetNames: '[name]', define: { 'process.env.NODE_ENV': '"production"', }, diff --git a/package-lock.json b/package-lock.json index 7ad64d7..66367e9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "sub-store-workers", "version": "1.1.0", "dependencies": { + "@jitl/quickjs-wasmfile-release-sync": "^0.32.0", "age-encryption": "^0.3.0", "buffer": "^6.0.3", "ip-address": "^9.0.5", @@ -18,6 +19,7 @@ "ms": "^2.1.3", "nanoid": "^3.3.3", "peggy": "^2.0.1", + "quickjs-emscripten": "^0.32.0", "semver": "^7.6.3", "static-js-yaml": "^1.0.0", "yaml": "^2.9.0" @@ -1094,6 +1096,48 @@ "url": "https://opencollective.com/libvips" } }, + "node_modules/@jitl/quickjs-ffi-types": { + "version": "0.32.0", + "resolved": "https://registry.npmmirror.com/@jitl/quickjs-ffi-types/-/quickjs-ffi-types-0.32.0.tgz", + "integrity": "sha512-v9T+GQpmk43VDJ7d72sf0Nexhk+ArvtUihW27dy7lqAl0zBObFKtSBBIm5RBjwIhE8VwsPPm9PNuvPvNqLWUEg==", + "license": "MIT" + }, + "node_modules/@jitl/quickjs-wasmfile-debug-asyncify": { + "version": "0.32.0", + "resolved": "https://registry.npmmirror.com/@jitl/quickjs-wasmfile-debug-asyncify/-/quickjs-wasmfile-debug-asyncify-0.32.0.tgz", + "integrity": "sha512-EX8zbXwGqCgAE764M+qvkHtyXDi/FUoMBea0JnES7vCM3P7a2+EOZOjGv85wtZ2sJhI1oJ+nekmqpOODFDY+hw==", + "license": "MIT", + "dependencies": { + "@jitl/quickjs-ffi-types": "0.32.0" + } + }, + "node_modules/@jitl/quickjs-wasmfile-debug-sync": { + "version": "0.32.0", + "resolved": "https://registry.npmmirror.com/@jitl/quickjs-wasmfile-debug-sync/-/quickjs-wasmfile-debug-sync-0.32.0.tgz", + "integrity": "sha512-LeYWrPGC1uNCTBWvibo3ZLJj0CSVNYUXvJpXMCmuQ5Sap2cCACc3uvGvYV4homHHBAzfw5akoTqMMS4YFRtw+Q==", + "license": "MIT", + "dependencies": { + "@jitl/quickjs-ffi-types": "0.32.0" + } + }, + "node_modules/@jitl/quickjs-wasmfile-release-asyncify": { + "version": "0.32.0", + "resolved": "https://registry.npmmirror.com/@jitl/quickjs-wasmfile-release-asyncify/-/quickjs-wasmfile-release-asyncify-0.32.0.tgz", + "integrity": "sha512-3oSwPfja12ICz4aIblB58cuY8JlEq5Txt8Cut4VLo+LH47QN+mzCnSgnbB03hWzg1LBcc+VyyI9UOag7a1NF+Q==", + "license": "MIT", + "dependencies": { + "@jitl/quickjs-ffi-types": "0.32.0" + } + }, + "node_modules/@jitl/quickjs-wasmfile-release-sync": { + "version": "0.32.0", + "resolved": "https://registry.npmmirror.com/@jitl/quickjs-wasmfile-release-sync/-/quickjs-wasmfile-release-sync-0.32.0.tgz", + "integrity": "sha512-BKNDI/TPBfGlLNGYpLrhcDGXmIk4xHm4MRAisOBnOzpXVn9HZWsfmMAc9WMBrAHjvvds6HOikKeaOBKdPdpVrg==", + "license": "MIT", + "dependencies": { + "@jitl/quickjs-ffi-types": "0.32.0" + } + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", @@ -1830,6 +1874,31 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "license": "MIT" }, + "node_modules/quickjs-emscripten": { + "version": "0.32.0", + "resolved": "https://registry.npmmirror.com/quickjs-emscripten/-/quickjs-emscripten-0.32.0.tgz", + "integrity": "sha512-So0Sqw869y/S2oE3Nuc0uT3Dhqgvsj8FSrwBdsuTosVsG8ME5/OcudU1GxsrIFdFABgy17GHnTVO9TYV/bLQcA==", + "license": "MIT", + "dependencies": { + "@jitl/quickjs-wasmfile-debug-asyncify": "0.32.0", + "@jitl/quickjs-wasmfile-debug-sync": "0.32.0", + "@jitl/quickjs-wasmfile-release-asyncify": "0.32.0", + "@jitl/quickjs-wasmfile-release-sync": "0.32.0", + "quickjs-emscripten-core": "0.32.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/quickjs-emscripten-core": { + "version": "0.32.0", + "resolved": "https://registry.npmmirror.com/quickjs-emscripten-core/-/quickjs-emscripten-core-0.32.0.tgz", + "integrity": "sha512-QFnPfjFey8EqknSrSxe1hZrf1/8z7/6s1QzGOmKo6++02r7QRRX7ZoyNaZh7JuVjWsVW87KnQrbZqnHkOAzUyg==", + "license": "MIT", + "dependencies": { + "@jitl/quickjs-ffi-types": "0.32.0" + } + }, "node_modules/quote-stream": { "version": "0.0.0", "resolved": "https://registry.npmmirror.com/quote-stream/-/quote-stream-0.0.0.tgz", diff --git a/package.json b/package.json index 13380ad..45402f6 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "prepush": "node -e \"let f=require('fs'),t=f.readFileSync('wrangler.toml','utf8');if(!t.includes('你的KV命名空间ID')){console.error('\\x1b[31m请先清除 wrangler.toml 中的 KV ID!\\x1b[0m');process.exit(1)}\"" }, "dependencies": { + "@jitl/quickjs-wasmfile-release-sync": "^0.32.0", "age-encryption": "^0.3.0", "buffer": "^6.0.3", "ip-address": "^9.0.5", @@ -23,6 +24,7 @@ "ms": "^2.1.3", "nanoid": "^3.3.3", "peggy": "^2.0.1", + "quickjs-emscripten": "^0.32.0", "semver": "^7.6.3", "static-js-yaml": "^1.0.0", "yaml": "^2.9.0" diff --git a/src/restful/miscs.js b/src/restful/miscs.js index 8d0ac15..ed8af59 100644 --- a/src/restful/miscs.js +++ b/src/restful/miscs.js @@ -179,8 +179,11 @@ function getWorkerStatus(req, res) { }, capabilities: { scriptOperator: { - supported: false, - reason: 'Cloudflare Workers does not allow dynamic code execution through eval/new Function.', + supported: workerEnv.SCRIPT_ENGINE === 'quickjs', + engine: workerEnv.SCRIPT_ENGINE || 'none', + reason: workerEnv.SCRIPT_ENGINE === 'quickjs' + ? 'Enabled via QuickJS WASM sandbox. Set SCRIPT_ENGINE="quickjs" in wrangler.toml [vars] to keep enabled.' + : 'Disabled. Set SCRIPT_ENGINE="quickjs" in wrangler.toml [vars] to enable.', alternatives: [ 'Use built-in filters/operators', 'Use mihomo YAML patch scripts', diff --git a/src/vendor/quickjs-executor.js b/src/vendor/quickjs-executor.js new file mode 100644 index 0000000..1062c4b --- /dev/null +++ b/src/vendor/quickjs-executor.js @@ -0,0 +1,405 @@ +/** + * QuickJS WASM 脚本执行引擎 + * + * 在 Cloudflare Workers 中以沙箱方式执行用户 JavaScript 脚本, + * 替代 Node.js 环境下使用 new Function() / eval() 的方案。 + * + * 依赖: + * - quickjs-emscripten (RELEASE_SYNC 变体) + * - Wrangler 原生 WASM module import + * + * Cloudflare Workers 禁止运行时 WebAssembly.compile(bytes), + * 因此必须让 Wrangler 把 .wasm 作为 WebAssembly.Module 上传。 + * + * 上游执行模式(来自 Sub-Store processors/index.js): + * 1. func 模式:createDynamicFunction 返回函数 → fn(proxies, targetPlatform, context) + * 用户脚本定义 function filter(proxies, ...) 返回 boolean[] + * 2. nodeFunc 模式(回退):将用户脚本包装为快捷脚本遍历 + * const fn = async ($server) => {