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([
24 * Checks a given node is a Identifier node of the specified name.
25 * @param {ASTNode} node A node to check.
26 * @param {string} name A name to check.
27 * @returns {boolean} `true` if the node is a Identifier node of the name.
29 function isIdentifier(node, name) {
30 return node.type === "Identifier" && node.name === name;
34 * Checks a given node is a Literal node of the specified string value.
35 * @param {ASTNode} node A node to check.
36 * @param {string} name A name to check.
37 * @returns {boolean} `true` if the node is a Literal node of the name.
39 function isConstant(node, name) {
42 return node.value === name;
44 case "TemplateLiteral":
46 node.expressions.length === 0 &&
47 node.quasis[0].value.cooked === name
56 * Checks a given node is a MemberExpression node which has the specified name's
58 * @param {ASTNode} node A node to check.
59 * @param {string} name A name to check.
60 * @returns {boolean} `true` if the node is a MemberExpression node which has
61 * the specified name's property
63 function isMember(node, name) {
65 node.type === "MemberExpression" &&
66 (node.computed ? isConstant : isIdentifier)(node.property, name)
70 //------------------------------------------------------------------------------
72 //------------------------------------------------------------------------------
79 description: "disallow the use of `eval()`",
80 category: "Best Practices",
82 url: "https://eslint.org/docs/rules/no-eval"
89 allowIndirect: { type: "boolean", default: false }
91 additionalProperties: false
96 unexpected: "eval can be harmful."
101 const allowIndirect = Boolean(
102 context.options[0] &&
103 context.options[0].allowIndirect
105 const sourceCode = context.getSourceCode();
109 * Pushs a variable scope (Program or Function) information to the stack.
111 * This is used in order to check whether or not `this` binding is a
112 * reference to the global object.
113 * @param {ASTNode} node A node of the scope. This is one of Program,
114 * FunctionDeclaration, FunctionExpression, and ArrowFunctionExpression.
117 function enterVarScope(node) {
118 const strict = context.getScope().isStrict;
130 * Pops a variable scope from the stack.
133 function exitVarScope() {
134 funcInfo = funcInfo.upper;
138 * Reports a given node.
140 * `node` is `Identifier` or `MemberExpression`.
141 * The parent of `node` might be `CallExpression`.
143 * The location of the report is always `eval` `Identifier` (or possibly
144 * `Literal`). The type of the report is `CallExpression` if the parent is
145 * `CallExpression`. Otherwise, it's the given node type.
146 * @param {ASTNode} node A node to report.
149 function report(node) {
150 const parent = node.parent;
151 const locationNode = node.type === "MemberExpression"
155 const reportNode = parent.type === "CallExpression" && parent.callee === node
161 loc: locationNode.loc.start,
162 messageId: "unexpected"
167 * Reports accesses of `eval` via the global object.
168 * @param {eslint-scope.Scope} globalScope The global scope.
171 function reportAccessingEvalViaGlobalObject(globalScope) {
172 for (let i = 0; i < candidatesOfGlobalObject.length; ++i) {
173 const name = candidatesOfGlobalObject[i];
174 const variable = astUtils.getVariableByName(globalScope, name);
180 const references = variable.references;
182 for (let j = 0; j < references.length; ++j) {
183 const identifier = references[j].identifier;
184 let node = identifier.parent;
186 // To detect code like `window.window.eval`.
187 while (isMember(node, name)) {
192 if (isMember(node, "eval")) {
200 * Reports all accesses of `eval` (excludes direct calls to eval).
201 * @param {eslint-scope.Scope} globalScope The global scope.
204 function reportAccessingEval(globalScope) {
205 const variable = astUtils.getVariableByName(globalScope, "eval");
211 const references = variable.references;
213 for (let i = 0; i < references.length; ++i) {
214 const reference = references[i];
215 const id = reference.identifier;
217 if (id.name === "eval" && !astUtils.isCallee(id)) {
219 // Is accessing to eval (excludes direct calls to eval)
227 // Checks only direct calls to eval. It's simple!
229 "CallExpression:exit"(node) {
230 const callee = node.callee;
232 if (isIdentifier(callee, "eval")) {
240 "CallExpression:exit"(node) {
241 const callee = node.callee;
243 if (isIdentifier(callee, "eval")) {
249 const scope = context.getScope(),
250 features = context.parserOptions.ecmaFeatures || {},
253 node.sourceType === "module" ||
254 (features.globalReturn && scope.childScopes[0].isStrict);
266 const globalScope = context.getScope();
269 reportAccessingEval(globalScope);
270 reportAccessingEvalViaGlobalObject(globalScope);
273 FunctionDeclaration: enterVarScope,
274 "FunctionDeclaration:exit": exitVarScope,
275 FunctionExpression: enterVarScope,
276 "FunctionExpression:exit": exitVarScope,
277 ArrowFunctionExpression: enterVarScope,
278 "ArrowFunctionExpression:exit": exitVarScope,
280 ThisExpression(node) {
281 if (!isMember(node.parent, "eval")) {
286 * `this.eval` is found.
287 * Checks whether or not the value of `this` is the global object.
289 if (!funcInfo.initialized) {
290 funcInfo.initialized = true;
291 funcInfo.defaultThis = astUtils.isDefaultThisBinding(
297 if (!funcInfo.strict && funcInfo.defaultThis) {
299 // `this.eval` is possible built-in `eval`.