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-implied-eval.js 6.0KB

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