Actualizacion maquina principal
[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 ]);
22
23 /**
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.
28  */
29 function isIdentifier(node, name) {
30     return node.type === "Identifier" && node.name === name;
31 }
32
33 /**
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.
38  */
39 function isConstant(node, name) {
40     switch (node.type) {
41         case "Literal":
42             return node.value === name;
43
44         case "TemplateLiteral":
45             return (
46                 node.expressions.length === 0 &&
47                 node.quasis[0].value.cooked === name
48             );
49
50         default:
51             return false;
52     }
53 }
54
55 /**
56  * Checks a given node is a MemberExpression node which has the specified name's
57  * property.
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
62  */
63 function isMember(node, name) {
64     return (
65         node.type === "MemberExpression" &&
66         (node.computed ? isConstant : isIdentifier)(node.property, name)
67     );
68 }
69
70 //------------------------------------------------------------------------------
71 // Rule Definition
72 //------------------------------------------------------------------------------
73
74 module.exports = {
75     meta: {
76         type: "suggestion",
77
78         docs: {
79             description: "disallow the use of `eval()`",
80             category: "Best Practices",
81             recommended: false,
82             url: "https://eslint.org/docs/rules/no-eval"
83         },
84
85         schema: [
86             {
87                 type: "object",
88                 properties: {
89                     allowIndirect: { type: "boolean", default: false }
90                 },
91                 additionalProperties: false
92             }
93         ],
94
95         messages: {
96             unexpected: "eval can be harmful."
97         }
98     },
99
100     create(context) {
101         const allowIndirect = Boolean(
102             context.options[0] &&
103             context.options[0].allowIndirect
104         );
105         const sourceCode = context.getSourceCode();
106         let funcInfo = null;
107
108         /**
109          * Pushs a variable scope (Program or Function) information to the stack.
110          *
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.
115          * @returns {void}
116          */
117         function enterVarScope(node) {
118             const strict = context.getScope().isStrict;
119
120             funcInfo = {
121                 upper: funcInfo,
122                 node,
123                 strict,
124                 defaultThis: false,
125                 initialized: strict
126             };
127         }
128
129         /**
130          * Pops a variable scope from the stack.
131          * @returns {void}
132          */
133         function exitVarScope() {
134             funcInfo = funcInfo.upper;
135         }
136
137         /**
138          * Reports a given node.
139          *
140          * `node` is `Identifier` or `MemberExpression`.
141          * The parent of `node` might be `CallExpression`.
142          *
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.
147          * @returns {void}
148          */
149         function report(node) {
150             const parent = node.parent;
151             const locationNode = node.type === "MemberExpression"
152                 ? node.property
153                 : node;
154
155             const reportNode = parent.type === "CallExpression" && parent.callee === node
156                 ? parent
157                 : node;
158
159             context.report({
160                 node: reportNode,
161                 loc: locationNode.loc.start,
162                 messageId: "unexpected"
163             });
164         }
165
166         /**
167          * Reports accesses of `eval` via the global object.
168          * @param {eslint-scope.Scope} globalScope The global scope.
169          * @returns {void}
170          */
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);
175
176                 if (!variable) {
177                     continue;
178                 }
179
180                 const references = variable.references;
181
182                 for (let j = 0; j < references.length; ++j) {
183                     const identifier = references[j].identifier;
184                     let node = identifier.parent;
185
186                     // To detect code like `window.window.eval`.
187                     while (isMember(node, name)) {
188                         node = node.parent;
189                     }
190
191                     // Reports.
192                     if (isMember(node, "eval")) {
193                         report(node);
194                     }
195                 }
196             }
197         }
198
199         /**
200          * Reports all accesses of `eval` (excludes direct calls to eval).
201          * @param {eslint-scope.Scope} globalScope The global scope.
202          * @returns {void}
203          */
204         function reportAccessingEval(globalScope) {
205             const variable = astUtils.getVariableByName(globalScope, "eval");
206
207             if (!variable) {
208                 return;
209             }
210
211             const references = variable.references;
212
213             for (let i = 0; i < references.length; ++i) {
214                 const reference = references[i];
215                 const id = reference.identifier;
216
217                 if (id.name === "eval" && !astUtils.isCallee(id)) {
218
219                     // Is accessing to eval (excludes direct calls to eval)
220                     report(id);
221                 }
222             }
223         }
224
225         if (allowIndirect) {
226
227             // Checks only direct calls to eval. It's simple!
228             return {
229                 "CallExpression:exit"(node) {
230                     const callee = node.callee;
231
232                     if (isIdentifier(callee, "eval")) {
233                         report(callee);
234                     }
235                 }
236             };
237         }
238
239         return {
240             "CallExpression:exit"(node) {
241                 const callee = node.callee;
242
243                 if (isIdentifier(callee, "eval")) {
244                     report(callee);
245                 }
246             },
247
248             Program(node) {
249                 const scope = context.getScope(),
250                     features = context.parserOptions.ecmaFeatures || {},
251                     strict =
252                         scope.isStrict ||
253                         node.sourceType === "module" ||
254                         (features.globalReturn && scope.childScopes[0].isStrict);
255
256                 funcInfo = {
257                     upper: null,
258                     node,
259                     strict,
260                     defaultThis: true,
261                     initialized: true
262                 };
263             },
264
265             "Program:exit"() {
266                 const globalScope = context.getScope();
267
268                 exitVarScope();
269                 reportAccessingEval(globalScope);
270                 reportAccessingEvalViaGlobalObject(globalScope);
271             },
272
273             FunctionDeclaration: enterVarScope,
274             "FunctionDeclaration:exit": exitVarScope,
275             FunctionExpression: enterVarScope,
276             "FunctionExpression:exit": exitVarScope,
277             ArrowFunctionExpression: enterVarScope,
278             "ArrowFunctionExpression:exit": exitVarScope,
279
280             ThisExpression(node) {
281                 if (!isMember(node.parent, "eval")) {
282                     return;
283                 }
284
285                 /*
286                  * `this.eval` is found.
287                  * Checks whether or not the value of `this` is the global object.
288                  */
289                 if (!funcInfo.initialized) {
290                     funcInfo.initialized = true;
291                     funcInfo.defaultThis = astUtils.isDefaultThisBinding(
292                         funcInfo.node,
293                         sourceCode
294                     );
295                 }
296
297                 if (!funcInfo.strict && funcInfo.defaultThis) {
298
299                     // `this.eval` is possible built-in `eval`.
300                     report(node.parent);
301                 }
302             }
303         };
304
305     }
306 };