Actualizacion maquina principal
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / rules / no-implied-eval.js
1 /**
2  * @fileoverview Rule to flag use of implied eval via setTimeout and setInterval
3  * @author James Allardice
4  */
5
6 "use strict";
7
8 //------------------------------------------------------------------------------
9 // Rule Definition
10 //------------------------------------------------------------------------------
11
12 module.exports = {
13     meta: {
14         type: "suggestion",
15
16         docs: {
17             description: "disallow the use of `eval()`-like methods",
18             category: "Best Practices",
19             recommended: false,
20             url: "https://eslint.org/docs/rules/no-implied-eval"
21         },
22
23         schema: []
24     },
25
26     create(context) {
27         const CALLEE_RE = /^(setTimeout|setInterval|execScript)$/u;
28
29         /*
30          * Figures out if we should inspect a given binary expression. Is a stack
31          * of stacks, where the first element in each substack is a CallExpression.
32          */
33         const impliedEvalAncestorsStack = [];
34
35         //--------------------------------------------------------------------------
36         // Helpers
37         //--------------------------------------------------------------------------
38
39         /**
40          * Get the last element of an array, without modifying arr, like pop(), but non-destructive.
41          * @param {Array} arr What to inspect
42          * @returns {*} The last element of arr
43          * @private
44          */
45         function last(arr) {
46             return arr ? arr[arr.length - 1] : null;
47         }
48
49         /**
50          * Checks if the given MemberExpression node is a potentially implied eval identifier on window.
51          * @param {ASTNode} node The MemberExpression node to check.
52          * @returns {boolean} Whether or not the given node is potentially an implied eval.
53          * @private
54          */
55         function isImpliedEvalMemberExpression(node) {
56             const object = node.object,
57                 property = node.property,
58                 hasImpliedEvalName = CALLEE_RE.test(property.name) || CALLEE_RE.test(property.value);
59
60             return object.name === "window" && hasImpliedEvalName;
61         }
62
63         /**
64          * Determines if a node represents a call to a potentially implied eval.
65          *
66          * This checks the callee name and that there's an argument, but not the type of the argument.
67          * @param {ASTNode} node The CallExpression to check.
68          * @returns {boolean} True if the node matches, false if not.
69          * @private
70          */
71         function isImpliedEvalCallExpression(node) {
72             const isMemberExpression = (node.callee.type === "MemberExpression"),
73                 isIdentifier = (node.callee.type === "Identifier"),
74                 isImpliedEvalCallee =
75                     (isIdentifier && CALLEE_RE.test(node.callee.name)) ||
76                     (isMemberExpression && isImpliedEvalMemberExpression(node.callee));
77
78             return isImpliedEvalCallee && node.arguments.length;
79         }
80
81         /**
82          * Checks that the parent is a direct descendent of an potential implied eval CallExpression, and if the parent is a CallExpression, that we're the first argument.
83          * @param {ASTNode} node The node to inspect the parent of.
84          * @returns {boolean} Was the parent a direct descendent, and is the child therefore potentially part of a dangerous argument?
85          * @private
86          */
87         function hasImpliedEvalParent(node) {
88
89             // make sure our parent is marked
90             return node.parent === last(last(impliedEvalAncestorsStack)) &&
91
92                 // if our parent is a CallExpression, make sure we're the first argument
93                 (node.parent.type !== "CallExpression" || node === node.parent.arguments[0]);
94         }
95
96         /**
97          * Checks if our parent is marked as part of an implied eval argument. If
98          * so, collapses the top of impliedEvalAncestorsStack and reports on the
99          * original CallExpression.
100          * @param {ASTNode} node The CallExpression to check.
101          * @returns {boolean} True if the node matches, false if not.
102          * @private
103          */
104         function checkString(node) {
105             if (hasImpliedEvalParent(node)) {
106
107                 // remove the entire substack, to avoid duplicate reports
108                 const substack = impliedEvalAncestorsStack.pop();
109
110                 context.report({ node: substack[0], message: "Implied eval. Consider passing a function instead of a string." });
111             }
112         }
113
114         //--------------------------------------------------------------------------
115         // Public
116         //--------------------------------------------------------------------------
117
118         return {
119             CallExpression(node) {
120                 if (isImpliedEvalCallExpression(node)) {
121
122                     // call expressions create a new substack
123                     impliedEvalAncestorsStack.push([node]);
124                 }
125             },
126
127             "CallExpression:exit"(node) {
128                 if (node === last(last(impliedEvalAncestorsStack))) {
129
130                     /*
131                      * Destroys the entire sub-stack, rather than just using
132                      * last(impliedEvalAncestorsStack).pop(), as a CallExpression is
133                      * always the bottom of a impliedEvalAncestorsStack substack.
134                      */
135                     impliedEvalAncestorsStack.pop();
136                 }
137             },
138
139             BinaryExpression(node) {
140                 if (node.operator === "+" && hasImpliedEvalParent(node)) {
141                     last(impliedEvalAncestorsStack).push(node);
142                 }
143             },
144
145             "BinaryExpression:exit"(node) {
146                 if (node === last(last(impliedEvalAncestorsStack))) {
147                     last(impliedEvalAncestorsStack).pop();
148                 }
149             },
150
151             Literal(node) {
152                 if (typeof node.value === "string") {
153                     checkString(node);
154                 }
155             },
156
157             TemplateLiteral(node) {
158                 checkString(node);
159             }
160         };
161
162     }
163 };