Ohm-Management - Projektarbeit B-ME
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

no-eval.js 9.3KB

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