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

313 рядки
9.3 KiB

  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("../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. docs: {
  68. description: "disallow the use of `eval()`",
  69. category: "Best Practices",
  70. recommended: false,
  71. url: "https://eslint.org/docs/rules/no-eval"
  72. },
  73. schema: [
  74. {
  75. type: "object",
  76. properties: {
  77. allowIndirect: { type: "boolean" }
  78. },
  79. additionalProperties: false
  80. }
  81. ],
  82. messages: {
  83. unexpected: "eval can be harmful."
  84. }
  85. },
  86. create(context) {
  87. const allowIndirect = Boolean(
  88. context.options[0] &&
  89. context.options[0].allowIndirect
  90. );
  91. const sourceCode = context.getSourceCode();
  92. let funcInfo = null;
  93. /**
  94. * Pushs a variable scope (Program or Function) information to the stack.
  95. *
  96. * This is used in order to check whether or not `this` binding is a
  97. * reference to the global object.
  98. *
  99. * @param {ASTNode} node - A node of the scope. This is one of Program,
  100. * FunctionDeclaration, FunctionExpression, and ArrowFunctionExpression.
  101. * @returns {void}
  102. */
  103. function enterVarScope(node) {
  104. const strict = context.getScope().isStrict;
  105. funcInfo = {
  106. upper: funcInfo,
  107. node,
  108. strict,
  109. defaultThis: false,
  110. initialized: strict
  111. };
  112. }
  113. /**
  114. * Pops a variable scope from the stack.
  115. *
  116. * @returns {void}
  117. */
  118. function exitVarScope() {
  119. funcInfo = funcInfo.upper;
  120. }
  121. /**
  122. * Reports a given node.
  123. *
  124. * `node` is `Identifier` or `MemberExpression`.
  125. * The parent of `node` might be `CallExpression`.
  126. *
  127. * The location of the report is always `eval` `Identifier` (or possibly
  128. * `Literal`). The type of the report is `CallExpression` if the parent is
  129. * `CallExpression`. Otherwise, it's the given node type.
  130. *
  131. * @param {ASTNode} node - A node to report.
  132. * @returns {void}
  133. */
  134. function report(node) {
  135. const parent = node.parent;
  136. const locationNode = node.type === "MemberExpression"
  137. ? node.property
  138. : node;
  139. const reportNode = parent.type === "CallExpression" && parent.callee === node
  140. ? parent
  141. : node;
  142. context.report({
  143. node: reportNode,
  144. loc: locationNode.loc.start,
  145. messageId: "unexpected"
  146. });
  147. }
  148. /**
  149. * Reports accesses of `eval` via the global object.
  150. *
  151. * @param {eslint-scope.Scope} globalScope - The global scope.
  152. * @returns {void}
  153. */
  154. function reportAccessingEvalViaGlobalObject(globalScope) {
  155. for (let i = 0; i < candidatesOfGlobalObject.length; ++i) {
  156. const name = candidatesOfGlobalObject[i];
  157. const variable = astUtils.getVariableByName(globalScope, name);
  158. if (!variable) {
  159. continue;
  160. }
  161. const references = variable.references;
  162. for (let j = 0; j < references.length; ++j) {
  163. const identifier = references[j].identifier;
  164. let node = identifier.parent;
  165. // To detect code like `window.window.eval`.
  166. while (isMember(node, name)) {
  167. node = node.parent;
  168. }
  169. // Reports.
  170. if (isMember(node, "eval")) {
  171. report(node);
  172. }
  173. }
  174. }
  175. }
  176. /**
  177. * Reports all accesses of `eval` (excludes direct calls to eval).
  178. *
  179. * @param {eslint-scope.Scope} globalScope - The global scope.
  180. * @returns {void}
  181. */
  182. function reportAccessingEval(globalScope) {
  183. const variable = astUtils.getVariableByName(globalScope, "eval");
  184. if (!variable) {
  185. return;
  186. }
  187. const references = variable.references;
  188. for (let i = 0; i < references.length; ++i) {
  189. const reference = references[i];
  190. const id = reference.identifier;
  191. if (id.name === "eval" && !astUtils.isCallee(id)) {
  192. // Is accessing to eval (excludes direct calls to eval)
  193. report(id);
  194. }
  195. }
  196. }
  197. if (allowIndirect) {
  198. // Checks only direct calls to eval. It's simple!
  199. return {
  200. "CallExpression:exit"(node) {
  201. const callee = node.callee;
  202. if (isIdentifier(callee, "eval")) {
  203. report(callee);
  204. }
  205. }
  206. };
  207. }
  208. return {
  209. "CallExpression:exit"(node) {
  210. const callee = node.callee;
  211. if (isIdentifier(callee, "eval")) {
  212. report(callee);
  213. }
  214. },
  215. Program(node) {
  216. const scope = context.getScope(),
  217. features = context.parserOptions.ecmaFeatures || {},
  218. strict =
  219. scope.isStrict ||
  220. node.sourceType === "module" ||
  221. (features.globalReturn && scope.childScopes[0].isStrict);
  222. funcInfo = {
  223. upper: null,
  224. node,
  225. strict,
  226. defaultThis: true,
  227. initialized: true
  228. };
  229. },
  230. "Program:exit"() {
  231. const globalScope = context.getScope();
  232. exitVarScope();
  233. reportAccessingEval(globalScope);
  234. reportAccessingEvalViaGlobalObject(globalScope);
  235. },
  236. FunctionDeclaration: enterVarScope,
  237. "FunctionDeclaration:exit": exitVarScope,
  238. FunctionExpression: enterVarScope,
  239. "FunctionExpression:exit": exitVarScope,
  240. ArrowFunctionExpression: enterVarScope,
  241. "ArrowFunctionExpression:exit": exitVarScope,
  242. ThisExpression(node) {
  243. if (!isMember(node.parent, "eval")) {
  244. return;
  245. }
  246. /*
  247. * `this.eval` is found.
  248. * Checks whether or not the value of `this` is the global object.
  249. */
  250. if (!funcInfo.initialized) {
  251. funcInfo.initialized = true;
  252. funcInfo.defaultThis = astUtils.isDefaultThisBinding(
  253. funcInfo.node,
  254. sourceCode
  255. );
  256. }
  257. if (!funcInfo.strict && funcInfo.defaultThis) {
  258. // `this.eval` is possible built-in `eval`.
  259. report(node.parent);
  260. }
  261. }
  262. };
  263. }
  264. };