2 * @fileoverview Rule to flag use of eval() statement
3 * @author Nicholas C. Zakas
8 //------------------------------------------------------------------------------
10 //------------------------------------------------------------------------------
12 const astUtils = require("./utils/ast-utils");
14 //------------------------------------------------------------------------------
16 //------------------------------------------------------------------------------
18 const candidatesOfGlobalObject = Object.freeze([
25 * Checks a given node is a MemberExpression node which has the specified name's
27 * @param {ASTNode} node A node to check.
28 * @param {string} name A name to check.
29 * @returns {boolean} `true` if the node is a MemberExpression node which has
30 * the specified name's property
32 function isMember(node, name) {
33 return astUtils.isSpecificMemberAccess(node, null, name);
36 //------------------------------------------------------------------------------
38 //------------------------------------------------------------------------------
45 description: "disallow the use of `eval()`",
46 category: "Best Practices",
48 url: "https://eslint.org/docs/rules/no-eval"
55 allowIndirect: { type: "boolean", default: false }
57 additionalProperties: false
62 unexpected: "eval can be harmful."
67 const allowIndirect = Boolean(
69 context.options[0].allowIndirect
71 const sourceCode = context.getSourceCode();
75 * Pushs a variable scope (Program or Function) information to the stack.
77 * This is used in order to check whether or not `this` binding is a
78 * reference to the global object.
79 * @param {ASTNode} node A node of the scope. This is one of Program,
80 * FunctionDeclaration, FunctionExpression, and ArrowFunctionExpression.
83 function enterVarScope(node) {
84 const strict = context.getScope().isStrict;
96 * Pops a variable scope from the stack.
99 function exitVarScope() {
100 funcInfo = funcInfo.upper;
104 * Reports a given node.
106 * `node` is `Identifier` or `MemberExpression`.
107 * The parent of `node` might be `CallExpression`.
109 * The location of the report is always `eval` `Identifier` (or possibly
110 * `Literal`). The type of the report is `CallExpression` if the parent is
111 * `CallExpression`. Otherwise, it's the given node type.
112 * @param {ASTNode} node A node to report.
115 function report(node) {
116 const parent = node.parent;
117 const locationNode = node.type === "MemberExpression"
121 const reportNode = parent.type === "CallExpression" && parent.callee === node
127 loc: locationNode.loc,
128 messageId: "unexpected"
133 * Reports accesses of `eval` via the global object.
134 * @param {eslint-scope.Scope} globalScope The global scope.
137 function reportAccessingEvalViaGlobalObject(globalScope) {
138 for (let i = 0; i < candidatesOfGlobalObject.length; ++i) {
139 const name = candidatesOfGlobalObject[i];
140 const variable = astUtils.getVariableByName(globalScope, name);
146 const references = variable.references;
148 for (let j = 0; j < references.length; ++j) {
149 const identifier = references[j].identifier;
150 let node = identifier.parent;
152 // To detect code like `window.window.eval`.
153 while (isMember(node, name)) {
158 if (isMember(node, "eval")) {
166 * Reports all accesses of `eval` (excludes direct calls to eval).
167 * @param {eslint-scope.Scope} globalScope The global scope.
170 function reportAccessingEval(globalScope) {
171 const variable = astUtils.getVariableByName(globalScope, "eval");
177 const references = variable.references;
179 for (let i = 0; i < references.length; ++i) {
180 const reference = references[i];
181 const id = reference.identifier;
183 if (id.name === "eval" && !astUtils.isCallee(id)) {
185 // Is accessing to eval (excludes direct calls to eval)
193 // Checks only direct calls to eval. It's simple!
195 "CallExpression:exit"(node) {
196 const callee = node.callee;
199 * Optional call (`eval?.("code")`) is not direct eval.
200 * The direct eval is only step 6.a.vi of https://tc39.es/ecma262/#sec-function-calls-runtime-semantics-evaluation
201 * But the optional call is https://tc39.es/ecma262/#sec-optional-chaining-chain-evaluation
203 if (!node.optional && astUtils.isSpecificId(callee, "eval")) {
211 "CallExpression:exit"(node) {
212 const callee = node.callee;
214 if (astUtils.isSpecificId(callee, "eval")) {
220 const scope = context.getScope(),
221 features = context.parserOptions.ecmaFeatures || {},
224 node.sourceType === "module" ||
225 (features.globalReturn && scope.childScopes[0].isStrict);
237 const globalScope = context.getScope();
240 reportAccessingEval(globalScope);
241 reportAccessingEvalViaGlobalObject(globalScope);
244 FunctionDeclaration: enterVarScope,
245 "FunctionDeclaration:exit": exitVarScope,
246 FunctionExpression: enterVarScope,
247 "FunctionExpression:exit": exitVarScope,
248 ArrowFunctionExpression: enterVarScope,
249 "ArrowFunctionExpression:exit": exitVarScope,
251 ThisExpression(node) {
252 if (!isMember(node.parent, "eval")) {
257 * `this.eval` is found.
258 * Checks whether or not the value of `this` is the global object.
260 if (!funcInfo.initialized) {
261 funcInfo.initialized = true;
262 funcInfo.defaultThis = astUtils.isDefaultThisBinding(
268 if (!funcInfo.strict && funcInfo.defaultThis) {
270 // `this.eval` is possible built-in `eval`.