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 //------------------------------------------------------------------------------
24 description: "disallow the use of `eval()`-like methods",
25 category: "Best Practices",
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);