method.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. /**
  2. * @fileoverview ESLint rule to disallow unsanitized method calls
  3. * @author Frederik Braun et al.
  4. * @copyright 2015-2017 Mozilla Corporation. All rights reserved.
  5. */
  6. "use strict";
  7. const RuleHelper = require("../ruleHelper");
  8. //------------------------------------------------------------------------------
  9. // Rule Definition
  10. //------------------------------------------------------------------------------
  11. const defaultRuleChecks = {
  12. // check second parameter to .insertAdjacentHTML()
  13. insertAdjacentHTML: {
  14. properties: [1]
  15. },
  16. // check first parameter of import()
  17. import: {
  18. properties: [0]
  19. },
  20. // check first parameter to createContextualFragment()
  21. createContextualFragment: {
  22. properties: [0]
  23. },
  24. // check first parameter to .write(), as long as the preceeding object matches the regex "document"
  25. write: {
  26. objectMatches: [
  27. "document"
  28. ],
  29. properties: [0]
  30. },
  31. // check first parameter to .writeLn(), as long as the preceeding object matches the regex "document"
  32. writeln: {
  33. objectMatches: [
  34. "document"
  35. ],
  36. properties: [0]
  37. }
  38. };
  39. /**
  40. * On newer parsers, `import(foo)` gets parsed as a keyword.
  41. * @param {Object} ruleHelper a RuleHelper instance
  42. * @param {Object} importExpr The ImportExpression we triggered on
  43. * @returns {undefined} Does not return
  44. */
  45. function checkImport(ruleHelper, importExpr) {
  46. const fakeCall = {callee: {type: "Import"}, arguments: [importExpr.source]};
  47. Object.assign(fakeCall, importExpr);
  48. ruleHelper.checkMethod(fakeCall);
  49. }
  50. /**
  51. * Run ruleHelper.checkMethod for all but irrelevant callees (FunctionExpression, etc.)
  52. * @param {Object} ruleHelper a RuleHelper instance
  53. * @param {Object} callExpr The CallExpression we triggered on
  54. * @param {Object} node The callee node
  55. * @returns {undefined} Does not return
  56. */
  57. function checkCallExpression(ruleHelper, callExpr, node) {
  58. switch(node.type) {
  59. case "Identifier":
  60. case "MemberExpression":
  61. if (callExpr.arguments && callExpr.arguments.length > 0) {
  62. ruleHelper.checkMethod(callExpr);
  63. }
  64. break;
  65. case "TSNonNullExpression": {
  66. const newCallExpr = Object.assign({}, callExpr);
  67. newCallExpr.callee = node.expression;
  68. checkCallExpression(ruleHelper, newCallExpr, node.expression);
  69. break;
  70. }
  71. case "TaggedTemplateExpression": {
  72. const newCallExpr = Object.assign({}, callExpr);
  73. newCallExpr.callee = node.tag;
  74. const expressions = node.quasi.expressions;
  75. const strings = node.quasi.quasis;
  76. newCallExpr.arguments = [strings, ...expressions];
  77. checkCallExpression(ruleHelper, newCallExpr, node.tag);
  78. break;
  79. }
  80. case "TypeCastExpression": {
  81. const newCallExpr = Object.assign({}, callExpr);
  82. newCallExpr.callee = node.expression;
  83. checkCallExpression(ruleHelper, newCallExpr, node.expression);
  84. break;
  85. }
  86. case "AssignmentExpression":
  87. if (node.right.type === "MemberExpression") {
  88. const newCallExpr = Object.assign({}, callExpr);
  89. newCallExpr.callee = node.right;
  90. checkCallExpression(ruleHelper, newCallExpr, node.right);
  91. break;
  92. }
  93. checkCallExpression(ruleHelper, callExpr, node.right);
  94. break;
  95. case "Import":
  96. ruleHelper.checkMethod(callExpr);
  97. break;
  98. case "SequenceExpression": {
  99. // the return value of a SequenceExpression is the last expression.
  100. // So, we create a new mock CallExpression with the actually called
  101. // ... expression as the callee node and pass it to checkMethod()
  102. const newCallExpr = Object.assign({}, callExpr);
  103. const idx = node.expressions.length - 1;
  104. const called = node.expressions[idx];
  105. newCallExpr.callee = called;
  106. ruleHelper.checkMethod(newCallExpr);
  107. break;
  108. }
  109. case "TSAsExpression":
  110. break;
  111. // those are fine:
  112. case "LogicalExpression": // Should we scan these? issue #62.
  113. case "ConditionalExpression":
  114. case "ArrowFunctionExpression":
  115. case "FunctionExpression":
  116. case "Super":
  117. case "CallExpression":
  118. case "ThisExpression":
  119. case "NewExpression":
  120. case "TSTypeAssertion":
  121. case "AwaitExpression": // see issue #122
  122. break;
  123. // If we don't cater for this expression throw an error
  124. default:
  125. ruleHelper.reportUnsupported(node, "Unexpected Callee", `Unsupported Callee of type ${node.type} for CallExpression`);
  126. }
  127. }
  128. module.exports = {
  129. meta: {
  130. type: "problem",
  131. docs: {
  132. description: "ESLint rule to disallow unsanitized method calls",
  133. category: "possible-errors",
  134. url: "https://github.com/mozilla/eslint-plugin-no-unsanitized/tree/master/docs/rules/method.md"
  135. },
  136. /* schema statement TBD until we have options
  137. schema: [
  138. {
  139. type: array
  140. }
  141. ]*/
  142. },
  143. create(context) {
  144. const ruleHelper = new RuleHelper(context, defaultRuleChecks);
  145. return {
  146. CallExpression(node) {
  147. checkCallExpression(ruleHelper, node, node.callee);
  148. },
  149. ImportExpression(node) {
  150. checkImport(ruleHelper, node);
  151. },
  152. // Tagged template expressions pass arguments in a special format we need to
  153. // map to our existing function call logic
  154. // foo`bar${var1}${var2}` will run as foo(['bar', ''], var1, var2)
  155. TaggedTemplateExpression(node) {
  156. const newCallExpr = Object.assign({}, node);
  157. newCallExpr.callee = node.tag;
  158. const expressions = node.quasi.expressions;
  159. const strings = node.quasi.quasis;
  160. newCallExpr.arguments = [strings, ...expressions];
  161. checkCallExpression(ruleHelper, newCallExpr, node.tag);
  162. },
  163. };
  164. }
  165. };