-
Notifications
You must be signed in to change notification settings - Fork 146
/
Copy pathjsSandbox.ts
executable file
·84 lines (79 loc) · 2.55 KB
/
jsSandbox.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import vm from 'vm';
import { getCurLoop } from './reportUtils';
import { ReportData, Context, SandBox } from './types';
import {
isError,
CommandExecutionError,
NullishCommandResultError,
} from './errors';
import { logger } from './debug';
// Runs a user snippet in a sandbox, and returns the result.
// The snippet can return a Promise, which is then awaited.
// The sandbox is kept for the execution of snippets later on
// in the template. Sandboxing can also be disabled via
// ctx.options.noSandbox.
export async function runUserJsAndGetRaw(
data: ReportData | undefined,
code: string,
ctx: Context
): Promise<any> {
// Retrieve the current JS sandbox contents (if any) and add
// the code to be run, and a placeholder for the result,
// as well as all data defined by the user
const sandbox: SandBox = {
...(ctx.jsSandbox || {}),
__code__: code,
__result__: undefined,
...data,
...ctx.options.additionalJsContext,
};
// Add currently defined vars, including loop vars and the index
// of the innermost loop
const curLoop = getCurLoop(ctx);
if (curLoop) sandbox.$idx = curLoop.idx;
Object.keys(ctx.vars).forEach(varName => {
sandbox[`$${varName}`] = ctx.vars[varName];
});
// Run the JS snippet and extract the result
let context;
let result;
try {
if (ctx.options.runJs) {
const temp = ctx.options.runJs({ sandbox, ctx });
context = temp.modifiedSandbox;
result = await temp.result;
} else if (ctx.options.noSandbox) {
context = sandbox;
const wrapper = new Function('with(this) { return eval(__code__); }');
result = await wrapper.call(context);
} else {
const script = new vm.Script(sandbox.__code__ ?? '');
context = vm.createContext(sandbox);
result = await script.runInContext(context);
}
} catch (err) {
const e = isError(err) ? err : new Error(`${err}`);
if (ctx.options.errorHandler != null) {
context = sandbox;
result = await ctx.options.errorHandler(e, code);
} else {
throw new CommandExecutionError(e, code);
}
}
if (ctx.options.rejectNullish && result == null) {
const nerr = new NullishCommandResultError(code);
if (ctx.options.errorHandler != null) {
result = await ctx.options.errorHandler(nerr, code);
} else {
throw nerr;
}
}
// Save the sandbox for later use, omitting the __code__ and __result__ properties.
ctx.jsSandbox = {
...context,
__code__: undefined,
__result__: undefined,
};
logger.debug('Command returned: ', result);
return result;
}