项目原始demo,不改动
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
Это архивный репозиторий. Вы можете его клонировать или просматривать файлы, но не вносить изменения или открывать задачи/запросы на слияние.
 
 
 
 

163 строки
6.0 KiB

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