source: imaps-frontend/node_modules/eslint/lib/rules/no-eval.js@ d565449

main
Last change on this file since d565449 was d565449, checked in by stefan toskovski <stefantoska84@…>, 4 weeks ago

Update repo after prototype presentation

  • Property mode set to 100644
File size: 9.3 KB
RevLine 
[d565449]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
12const astUtils = require("./utils/ast-utils");
13
14//------------------------------------------------------------------------------
15// Helpers
16//------------------------------------------------------------------------------
17
18const 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 */
32function isMember(node, name) {
33 return astUtils.isSpecificMemberAccess(node, null, name);
34}
35
36//------------------------------------------------------------------------------
37// Rule Definition
38//------------------------------------------------------------------------------
39
40/** @type {import('../shared/types').Rule} */
41module.exports = {
42 meta: {
43 type: "suggestion",
44
45 docs: {
46 description: "Disallow the use of `eval()`",
47 recommended: false,
48 url: "https://eslint.org/docs/latest/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.sourceCode;
72 let funcInfo = null;
73
74 /**
75 * Pushes a `this` scope (non-arrow function, class static block, or class field initializer) information to the stack.
76 * Top-level scopes are handled separately.
77 *
78 * This is used in order to check whether or not `this` binding is a
79 * reference to the global object.
80 * @param {ASTNode} node A node of the scope.
81 * For functions, this is one of FunctionDeclaration, FunctionExpression.
82 * For class static blocks, this is StaticBlock.
83 * For class field initializers, this can be any node that is PropertyDefinition#value.
84 * @returns {void}
85 */
86 function enterThisScope(node) {
87 const strict = sourceCode.getScope(node).isStrict;
88
89 funcInfo = {
90 upper: funcInfo,
91 node,
92 strict,
93 isTopLevelOfScript: false,
94 defaultThis: false,
95 initialized: strict
96 };
97 }
98
99 /**
100 * Pops a variable scope from the stack.
101 * @returns {void}
102 */
103 function exitThisScope() {
104 funcInfo = funcInfo.upper;
105 }
106
107 /**
108 * Reports a given node.
109 *
110 * `node` is `Identifier` or `MemberExpression`.
111 * The parent of `node` might be `CallExpression`.
112 *
113 * The location of the report is always `eval` `Identifier` (or possibly
114 * `Literal`). The type of the report is `CallExpression` if the parent is
115 * `CallExpression`. Otherwise, it's the given node type.
116 * @param {ASTNode} node A node to report.
117 * @returns {void}
118 */
119 function report(node) {
120 const parent = node.parent;
121 const locationNode = node.type === "MemberExpression"
122 ? node.property
123 : node;
124
125 const reportNode = parent.type === "CallExpression" && parent.callee === node
126 ? parent
127 : node;
128
129 context.report({
130 node: reportNode,
131 loc: locationNode.loc,
132 messageId: "unexpected"
133 });
134 }
135
136 /**
137 * Reports accesses of `eval` via the global object.
138 * @param {eslint-scope.Scope} globalScope The global scope.
139 * @returns {void}
140 */
141 function reportAccessingEvalViaGlobalObject(globalScope) {
142 for (let i = 0; i < candidatesOfGlobalObject.length; ++i) {
143 const name = candidatesOfGlobalObject[i];
144 const variable = astUtils.getVariableByName(globalScope, name);
145
146 if (!variable) {
147 continue;
148 }
149
150 const references = variable.references;
151
152 for (let j = 0; j < references.length; ++j) {
153 const identifier = references[j].identifier;
154 let node = identifier.parent;
155
156 // To detect code like `window.window.eval`.
157 while (isMember(node, name)) {
158 node = node.parent;
159 }
160
161 // Reports.
162 if (isMember(node, "eval")) {
163 report(node);
164 }
165 }
166 }
167 }
168
169 /**
170 * Reports all accesses of `eval` (excludes direct calls to eval).
171 * @param {eslint-scope.Scope} globalScope The global scope.
172 * @returns {void}
173 */
174 function reportAccessingEval(globalScope) {
175 const variable = astUtils.getVariableByName(globalScope, "eval");
176
177 if (!variable) {
178 return;
179 }
180
181 const references = variable.references;
182
183 for (let i = 0; i < references.length; ++i) {
184 const reference = references[i];
185 const id = reference.identifier;
186
187 if (id.name === "eval" && !astUtils.isCallee(id)) {
188
189 // Is accessing to eval (excludes direct calls to eval)
190 report(id);
191 }
192 }
193 }
194
195 if (allowIndirect) {
196
197 // Checks only direct calls to eval. It's simple!
198 return {
199 "CallExpression:exit"(node) {
200 const callee = node.callee;
201
202 /*
203 * Optional call (`eval?.("code")`) is not direct eval.
204 * The direct eval is only step 6.a.vi of https://tc39.es/ecma262/#sec-function-calls-runtime-semantics-evaluation
205 * But the optional call is https://tc39.es/ecma262/#sec-optional-chaining-chain-evaluation
206 */
207 if (!node.optional && astUtils.isSpecificId(callee, "eval")) {
208 report(callee);
209 }
210 }
211 };
212 }
213
214 return {
215 "CallExpression:exit"(node) {
216 const callee = node.callee;
217
218 if (astUtils.isSpecificId(callee, "eval")) {
219 report(callee);
220 }
221 },
222
223 Program(node) {
224 const scope = sourceCode.getScope(node),
225 features = context.parserOptions.ecmaFeatures || {},
226 strict =
227 scope.isStrict ||
228 node.sourceType === "module" ||
229 (features.globalReturn && scope.childScopes[0].isStrict),
230 isTopLevelOfScript = node.sourceType !== "module" && !features.globalReturn;
231
232 funcInfo = {
233 upper: null,
234 node,
235 strict,
236 isTopLevelOfScript,
237 defaultThis: true,
238 initialized: true
239 };
240 },
241
242 "Program:exit"(node) {
243 const globalScope = sourceCode.getScope(node);
244
245 exitThisScope();
246 reportAccessingEval(globalScope);
247 reportAccessingEvalViaGlobalObject(globalScope);
248 },
249
250 FunctionDeclaration: enterThisScope,
251 "FunctionDeclaration:exit": exitThisScope,
252 FunctionExpression: enterThisScope,
253 "FunctionExpression:exit": exitThisScope,
254 "PropertyDefinition > *.value": enterThisScope,
255 "PropertyDefinition > *.value:exit": exitThisScope,
256 StaticBlock: enterThisScope,
257 "StaticBlock:exit": exitThisScope,
258
259 ThisExpression(node) {
260 if (!isMember(node.parent, "eval")) {
261 return;
262 }
263
264 /*
265 * `this.eval` is found.
266 * Checks whether or not the value of `this` is the global object.
267 */
268 if (!funcInfo.initialized) {
269 funcInfo.initialized = true;
270 funcInfo.defaultThis = astUtils.isDefaultThisBinding(
271 funcInfo.node,
272 sourceCode
273 );
274 }
275
276 // `this` at the top level of scripts always refers to the global object
277 if (funcInfo.isTopLevelOfScript || (!funcInfo.strict && funcInfo.defaultThis)) {
278
279 // `this.eval` is possible built-in `eval`.
280 report(node.parent);
281 }
282 }
283 };
284
285 }
286};
Note: See TracBrowser for help on using the repository browser.