2 * @fileoverview Rule to flag use of implied eval via setTimeout and setInterval
3 * @author James Allardice
8 //------------------------------------------------------------------------------
10 //------------------------------------------------------------------------------
12 const astUtils
= require("./utils/ast-utils");
13 const { getStaticValue
} = require("eslint-utils");
15 //------------------------------------------------------------------------------
17 //------------------------------------------------------------------------------
19 /** @type {import('../shared/types').Rule} */
25 description
: "disallow the use of `eval()`-like methods",
27 url
: "https://eslint.org/docs/rules/no-implied-eval"
33 impliedEval
: "Implied eval. Consider passing a function instead of a string."
38 const GLOBAL_CANDIDATES
= Object
.freeze(["global", "window", "globalThis"]);
39 const EVAL_LIKE_FUNC_PATTERN
= /^(?:set(?:Interval|Timeout)|execScript)$/u;
42 * Checks whether a node is evaluated as a string or not.
43 * @param {ASTNode} node A node to check.
44 * @returns {boolean} True if the node is evaluated as a string.
46 function isEvaluatedString(node
) {
48 (node
.type
=== "Literal" && typeof node
.value
=== "string") ||
49 node
.type
=== "TemplateLiteral"
53 if (node
.type
=== "BinaryExpression" && node
.operator
=== "+") {
54 return isEvaluatedString(node
.left
) || isEvaluatedString(node
.right
);
60 * Reports if the `CallExpression` node has evaluated argument.
61 * @param {ASTNode} node A CallExpression to check.
64 function reportImpliedEvalCallExpression(node
) {
65 const [firstArgument
] = node
.arguments
;
69 const staticValue
= getStaticValue(firstArgument
, context
.getScope());
70 const isStaticString
= staticValue
&& typeof staticValue
.value
=== "string";
71 const isString
= isStaticString
|| isEvaluatedString(firstArgument
);
76 messageId
: "impliedEval"
84 * Reports calls of `implied eval` via the global references.
85 * @param {Variable} globalVar A global variable to check.
88 function reportImpliedEvalViaGlobal(globalVar
) {
89 const { references
, name
} = globalVar
;
91 references
.forEach(ref
=> {
92 const identifier
= ref
.identifier
;
93 let node
= identifier
.parent
;
95 while (astUtils
.isSpecificMemberAccess(node
, null, name
)) {
99 if (astUtils
.isSpecificMemberAccess(node
, null, EVAL_LIKE_FUNC_PATTERN
)) {
100 const calleeNode
= node
.parent
.type
=== "ChainExpression" ? node
.parent
: node
;
101 const parent
= calleeNode
.parent
;
103 if (parent
.type
=== "CallExpression" && parent
.callee
=== calleeNode
) {
104 reportImpliedEvalCallExpression(parent
);
110 //--------------------------------------------------------------------------
112 //--------------------------------------------------------------------------
115 CallExpression(node
) {
116 if (astUtils
.isSpecificId(node
.callee
, EVAL_LIKE_FUNC_PATTERN
)) {
117 reportImpliedEvalCallExpression(node
);
121 const globalScope
= context
.getScope();
124 .map(candidate
=> astUtils
.getVariableByName(globalScope
, candidate
))
125 .filter(globalVar
=> !!globalVar
&& globalVar
.defs
.length
=== 0)
126 .forEach(reportImpliedEvalViaGlobal
);