-
Notifications
You must be signed in to change notification settings - Fork 41
/
Copy pathresolveBuiltinCalls.js
77 lines (69 loc) · 2.74 KB
/
resolveBuiltinCalls.js
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
import {logger} from 'flast';
import {badValue} from '../config.js';
import {Sandbox} from '../utils/sandbox.js';
import {evalInVm} from '../utils/evalInVm.js';
import {createNewNode} from '../utils/createNewNode.js';
import * as safeImplementations from '../utils/safeImplementations.js';
import {skipBuiltinFunctions, skipIdentifiers, skipProperties} from '../config.js';
const availableSafeImplementations = Object.keys(safeImplementations);
function isCallWithOnlyLiteralArguments(node) {
return node.type === 'CallExpression' && !node.arguments.find(a => a.type !== 'Literal');
}
function isBuiltinIdentifier(node) {
return node.type === 'Identifier' && !node.declNode && !skipBuiltinFunctions.includes(node.name);
}
function isSafeCall(node) {
return node.type === 'CallExpression' && availableSafeImplementations.includes((node.callee.name));
}
function isBuiltinMemberExpression(node) {
return node.type === 'MemberExpression' &&
!node.object.declNode &&
!skipBuiltinFunctions.includes(node.object?.name) &&
!skipIdentifiers.includes(node.object?.name) &&
!skipProperties.includes(node.property?.name || node.property?.value);
}
function isUnwantedNode(node) {
return Boolean(node.callee?.declNode || node?.callee?.object?.declNode ||
'ThisExpression' === (node.callee?.object?.type || node.callee?.type) ||
'constructor' === (node.callee?.property?.name || node.callee?.property?.value));
}
/**
* Resolve calls to builtin functions (like atob or String(), etc...).
* Use safe implmentations of known functions when available.
* @param {Arborist} arb
* @param {Function} candidateFilter (optional) a filter to apply on the candidates list
* @return {Arborist}
*/
function resolveBuiltinCalls(arb, candidateFilter = () => true) {
let sharedSb;
const relevantNodes = [
...(arb.ast[0].typeMap.MemberExpression || []),
...(arb.ast[0].typeMap.CallExpression || []),
...(arb.ast[0].typeMap.Identifier || []),
];
for (let i = 0; i < relevantNodes.length; i++) {
const n = relevantNodes[i];
if (!isUnwantedNode(n) && candidateFilter(n) && (isSafeCall(n) ||
(isCallWithOnlyLiteralArguments(n) && (isBuiltinIdentifier(n.callee) || isBuiltinMemberExpression(n.callee)))
)) {
try {
const safeImplementation = safeImplementations[n.callee.name];
if (safeImplementation) {
const args = n.arguments.map(a => a.value);
const tempValue = safeImplementation(...args);
if (tempValue) {
arb.markNode(n, createNewNode(tempValue));
}
} else {
sharedSb = sharedSb || new Sandbox();
const replacementNode = evalInVm(n.src, sharedSb);
if (replacementNode !== badValue) arb.markNode(n, replacementNode);
}
} catch (e) {
logger.debug(e.message);
}
}
}
return arb;
}
export default resolveBuiltinCalls;