fix: serverless vm2 safe-eval
This commit is contained in:
@@ -124,6 +124,7 @@
|
||||
"rxjs": "7.5.5",
|
||||
"snakecase-keys": "5.1.2",
|
||||
"ua-parser-js": "1.0.2",
|
||||
"vm2": "3.9.9",
|
||||
"xss": "1.0.11",
|
||||
"zx": "4.3.0"
|
||||
},
|
||||
|
||||
16
pnpm-lock.yaml
generated
16
pnpm-lock.yaml
generated
@@ -111,6 +111,7 @@ specifiers:
|
||||
tsconfig-paths: 3.13.0
|
||||
typescript: 4.6.2
|
||||
ua-parser-js: 1.0.2
|
||||
vm2: 3.9.9
|
||||
webpack-node-externals: 3.0.0
|
||||
xss: 1.0.11
|
||||
zx: 4.3.0
|
||||
@@ -177,6 +178,7 @@ dependencies:
|
||||
rxjs: 7.5.5
|
||||
snakecase-keys: 5.1.2
|
||||
ua-parser-js: 1.0.2
|
||||
vm2: 3.9.9
|
||||
xss: 1.0.11
|
||||
zx: 4.3.0
|
||||
|
||||
@@ -2317,6 +2319,11 @@ packages:
|
||||
engines: {node: '>=0.4.0'}
|
||||
dev: true
|
||||
|
||||
/acorn-walk/8.2.0:
|
||||
resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
dev: false
|
||||
|
||||
/acorn/7.4.1:
|
||||
resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
@@ -8167,6 +8174,15 @@ packages:
|
||||
extsprintf: 1.3.0
|
||||
dev: false
|
||||
|
||||
/vm2/3.9.9:
|
||||
resolution: {integrity: sha512-xwTm7NLh/uOjARRBs8/95H0e8fT3Ukw5D/JJWhxMbhKzNh1Nu981jQKvkep9iKYNxzlVrdzD0mlBGkDKZWprlw==}
|
||||
engines: {node: '>=6.0'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
acorn: 8.7.0
|
||||
acorn-walk: 8.2.0
|
||||
dev: false
|
||||
|
||||
/w3c-hr-time/1.0.2:
|
||||
resolution: {integrity: sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==}
|
||||
dependencies:
|
||||
|
||||
@@ -56,12 +56,14 @@ const remoteModule =
|
||||
|
||||
受信任的三方库,可在 `snippet.service.ts` 中找到。
|
||||
|
||||
**注意**:这是一个完全的执行上下文,你不能编写某些在 NodeJS 运行时正常执行的代码。
|
||||
**注意**:这是一个完全隔离(可能存在逃逸,请及时指出)的执行上下文,你不能编写某些在 NodeJS 运行时正常执行的代码。
|
||||
|
||||
比如: `process` 中只有只读的 env 可以获取,其他方法都被移除; `setTimeout` 等 API 被移除。但是你可以在独立模块中使用这些 API,需要注意,内存泄漏和安全性。
|
||||
|
||||
`require(id, useCache)` require 支持第二个参数,默认为 true,这是 NodeJS 的默认行为,可以设定为 `false` 以禁用 `require` 的缓存,但是会增加性能开销。
|
||||
|
||||
**注意**:你仍然可以在独立模块中使用主线程的 `require` 方法,所以这并不是一个真正隔离的环境。在使用第三方模块和请注意安全。请不要使用不受信任的模块。
|
||||
|
||||
## `Context`
|
||||
|
||||
`handler` 函数的第一个参数接受一个全局上下文对象。
|
||||
|
||||
@@ -273,7 +273,17 @@ export class ServerlessService {
|
||||
// }
|
||||
|
||||
// fin. is built-in module
|
||||
const module = isBuiltinModule(id, ['fs', 'os', 'child_process', 'sys'])
|
||||
const module = isBuiltinModule(id, [
|
||||
'fs',
|
||||
'os',
|
||||
'child_process',
|
||||
'sys',
|
||||
'process',
|
||||
'vm',
|
||||
'v8',
|
||||
'cluster',
|
||||
'fs/promises',
|
||||
])
|
||||
if (!module) {
|
||||
throw new Error(`cannot require ${id}`)
|
||||
} else {
|
||||
|
||||
@@ -1,34 +1,22 @@
|
||||
import { customAlphabet } from 'nanoid'
|
||||
import vm from 'vm'
|
||||
const nanoid = customAlphabet(
|
||||
'0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$',
|
||||
16,
|
||||
)
|
||||
export function safeEval(
|
||||
code: string,
|
||||
context = {},
|
||||
opts?: string | vm.RunningScriptOptions,
|
||||
) {
|
||||
const sandbox = {}
|
||||
const resultKey = 'SAFE_EVAL_' + nanoid()
|
||||
sandbox[resultKey] = {}
|
||||
const clearContext = `
|
||||
(function() {
|
||||
Function = undefined;
|
||||
const keys = Object.getOwnPropertyNames(this).concat(['constructor']);
|
||||
keys.forEach((key) => {
|
||||
const item = this[key];
|
||||
if (!item || typeof item.constructor !== 'function') return;
|
||||
this[key].constructor = undefined;
|
||||
});
|
||||
})();
|
||||
`
|
||||
code = clearContext + resultKey + '=' + `((() => { ${code} })())`
|
||||
import vm2 from 'vm2'
|
||||
export function safeEval(code: string, context = {}) {
|
||||
const sandbox = {
|
||||
global: {},
|
||||
}
|
||||
|
||||
code = `((() => { ${code} })())`
|
||||
if (context) {
|
||||
Object.keys(context).forEach(function (key) {
|
||||
sandbox[key] = context[key]
|
||||
})
|
||||
}
|
||||
vm.runInNewContext(code, sandbox, opts)
|
||||
return sandbox[resultKey]
|
||||
|
||||
const VM = new vm2.VM({
|
||||
timeout: 60_0000,
|
||||
sandbox,
|
||||
|
||||
eval: false,
|
||||
})
|
||||
|
||||
return VM.run(code)
|
||||
}
|
||||
|
||||
@@ -12,9 +12,7 @@ describe.only('test safe-eval', () => {
|
||||
})
|
||||
|
||||
it('should can not access to global or process or require', () => {
|
||||
expect(() => {
|
||||
safeEval(`return global`)
|
||||
}).toThrow()
|
||||
expect(safeEval(`return global`)).toStrictEqual({})
|
||||
|
||||
expect(() => {
|
||||
safeEval(`return process`)
|
||||
@@ -25,6 +23,14 @@ describe.only('test safe-eval', () => {
|
||||
}).toThrow()
|
||||
})
|
||||
|
||||
describe('test escape', () => {
|
||||
it('case1', () => {
|
||||
expect(() =>
|
||||
safeEval(`this.constructor.constructor("return process")().exit()`),
|
||||
).toThrow()
|
||||
})
|
||||
})
|
||||
|
||||
it('should can access mocked global context', () => {
|
||||
const res = safeEval(`return global.a`, { global: { a: 1 } })
|
||||
expect(res).toBe(1)
|
||||
|
||||
Reference in New Issue
Block a user