.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / rules / no-eval.js
1 /**
2  * @fileoverview Rule to flag use of eval() statement
3  * @author Nicholas C. Zakas
4  */
5
6 "use strict";
7
8 //------------------------------------------------------------------------------
9 // Requirements
10 //------------------------------------------------------------------------------
11
12 const astUtils = require("./utils/ast-utils");
13
14 //------------------------------------------------------------------------------
15 // Helpers
16 //------------------------------------------------------------------------------
17
18 const candidatesOfGlobalObject = Object.freeze([
19     "global",
20     "window",
21     "globalThis"
22 ]);
23
24 /**
25  * Checks a given node is a MemberExpression node which has the specified name's
26  * property.
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
31  */
32 function isMember(node, name) {
33     return astUtils.isSpecificMemberAccess(node, null, name);
34 }
35
36 //------------------------------------------------------------------------------
37 // Rule Definition
38 //------------------------------------------------------------------------------
39
40 module.exports = {
41     meta: {
42         type: "suggestion",
43
44         docs: {
45             description: "disallow the use of `eval()`",
46             category: "Best Practices",
47             recommended: false,
48             url: "https://eslint.org/docs/rules/no-eval"
49         },
50
51         schema: [
52             {
53                 type: "object",
54                 properties: {
55                     allowIndirect: { type: "boolean", default: false }
56                 },
57                 additionalProperties: false
58             }
59         ],
60
61         messages: {
62             unexpected: "eval can be harmful."
63         }
64     },
65
66     create(context) {
67         const allowIndirect = Boolean(
68             context.options[0] &&
69             context.options[0].allowIndirect
70         );
71         const sourceCode = context.getSourceCode();
72         let funcInfo = null;
73
74         /**
75          * Pushs a variable scope (Program or Function) information to the stack.
76          *
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.
81          * @returns {void}
82          */
83         function enterVarScope(node) {
84             const strict = context.getScope().isStrict;
85
86             funcInfo = {
87                 upper: funcInfo,
88                 node,
89                 strict,
90                 defaultThis: false,
91                 initialized: strict
92             };
93         }
94
95         /**
96          * Pops a variable scope from the stack.
97          * @returns {void}
98          */
99         function exitVarScope() {
100             funcInfo = funcInfo.upper;
101         }
102
103         /**
104          * Reports a given node.
105          *
106          * `node` is `Identifier` or `MemberExpression`.
107          * The parent of `node` might be `CallExpression`.
108          *
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.
113          * @returns {void}
114          */
115         function report(node) {
116             const parent = node.parent;
117             const locationNode = node.type === "MemberExpression"
118                 ? node.property
119                 : node;
120
121             const reportNode = parent.type === "CallExpression" && parent.callee === node
122                 ? parent
123                 : node;
124
125             context.report({
126                 node: reportNode,
127                 loc: locationNode.loc,
128                 messageId: "unexpected"
129             });
130         }
131
132         /**
133          * Reports accesses of `eval` via the global object.
134          * @param {eslint-scope.Scope} globalScope The global scope.
135          * @returns {void}
136          */
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);
141
142                 if (!variable) {
143                     continue;
144                 }
145
146                 const references = variable.references;
147
148                 for (let j = 0; j < references.length; ++j) {
149                     const identifier = references[j].identifier;
150                     let node = identifier.parent;
151
152                     // To detect code like `window.window.eval`.
153                     while (isMember(node, name)) {
154                         node = node.parent;
155                     }
156
157                     // Reports.
158                     if (isMember(node, "eval")) {
159                         report(node);
160                     }
161                 }
162             }
163         }
164
165         /**
166          * Reports all accesses of `eval` (excludes direct calls to eval).
167          * @param {eslint-scope.Scope} globalScope The global scope.
168          * @returns {void}
169          */
170         function reportAccessingEval(globalScope) {
171             const variable = astUtils.getVariableByName(globalScope, "eval");
172
173             if (!variable) {
174                 return;
175             }
176
177             const references = variable.references;
178
179             for (let i = 0; i < references.length; ++i) {
180                 const reference = references[i];
181                 const id = reference.identifier;
182
183                 if (id.name === "eval" && !astUtils.isCallee(id)) {
184
185                     // Is accessing to eval (excludes direct calls to eval)
186                     report(id);
187                 }
188             }
189         }
190
191         if (allowIndirect) {
192
193             // Checks only direct calls to eval. It's simple!
194             return {
195                 "CallExpression:exit"(node) {
196                     const callee = node.callee;
197
198                     /*
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
202                      */
203                     if (!node.optional && astUtils.isSpecificId(callee, "eval")) {
204                         report(callee);
205                     }
206                 }
207             };
208         }
209
210         return {
211             "CallExpression:exit"(node) {
212                 const callee = node.callee;
213
214                 if (astUtils.isSpecificId(callee, "eval")) {
215                     report(callee);
216                 }
217             },
218
219             Program(node) {
220                 const scope = context.getScope(),
221                     features = context.parserOptions.ecmaFeatures || {},
222                     strict =
223                         scope.isStrict ||
224                         node.sourceType === "module" ||
225                         (features.globalReturn && scope.childScopes[0].isStrict);
226
227                 funcInfo = {
228                     upper: null,
229                     node,
230                     strict,
231                     defaultThis: true,
232                     initialized: true
233                 };
234             },
235
236             "Program:exit"() {
237                 const globalScope = context.getScope();
238
239                 exitVarScope();
240                 reportAccessingEval(globalScope);
241                 reportAccessingEvalViaGlobalObject(globalScope);
242             },
243
244             FunctionDeclaration: enterVarScope,
245             "FunctionDeclaration:exit": exitVarScope,
246             FunctionExpression: enterVarScope,
247             "FunctionExpression:exit": exitVarScope,
248             ArrowFunctionExpression: enterVarScope,
249             "ArrowFunctionExpression:exit": exitVarScope,
250
251             ThisExpression(node) {
252                 if (!isMember(node.parent, "eval")) {
253                     return;
254                 }
255
256                 /*
257                  * `this.eval` is found.
258                  * Checks whether or not the value of `this` is the global object.
259                  */
260                 if (!funcInfo.initialized) {
261                     funcInfo.initialized = true;
262                     funcInfo.defaultThis = astUtils.isDefaultThisBinding(
263                         funcInfo.node,
264                         sourceCode
265                     );
266                 }
267
268                 if (!funcInfo.strict && funcInfo.defaultThis) {
269
270                     // `this.eval` is possible built-in `eval`.
271                     report(node.parent);
272                 }
273             }
274         };
275
276     }
277 };