123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189 |
- /**
- * @fileoverview ESLint rule to disallow unsanitized method calls
- * @author Frederik Braun et al.
- * @copyright 2015-2017 Mozilla Corporation. All rights reserved.
- */
- "use strict";
- const RuleHelper = require("../ruleHelper");
- //------------------------------------------------------------------------------
- // Rule Definition
- //------------------------------------------------------------------------------
- const defaultRuleChecks = {
- // check second parameter to .insertAdjacentHTML()
- insertAdjacentHTML: {
- properties: [1]
- },
- // check first parameter of import()
- import: {
- properties: [0]
- },
- // check first parameter to createContextualFragment()
- createContextualFragment: {
- properties: [0]
- },
- // check first parameter to .write(), as long as the preceeding object matches the regex "document"
- write: {
- objectMatches: [
- "document"
- ],
- properties: [0]
- },
- // check first parameter to .writeLn(), as long as the preceeding object matches the regex "document"
- writeln: {
- objectMatches: [
- "document"
- ],
- properties: [0]
- }
- };
- /**
- * On newer parsers, `import(foo)` gets parsed as a keyword.
- * @param {Object} ruleHelper a RuleHelper instance
- * @param {Object} importExpr The ImportExpression we triggered on
- * @returns {undefined} Does not return
- */
- function checkImport(ruleHelper, importExpr) {
- const fakeCall = {callee: {type: "Import"}, arguments: [importExpr.source]};
- Object.assign(fakeCall, importExpr);
- ruleHelper.checkMethod(fakeCall);
- }
- /**
- * Run ruleHelper.checkMethod for all but irrelevant callees (FunctionExpression, etc.)
- * @param {Object} ruleHelper a RuleHelper instance
- * @param {Object} callExpr The CallExpression we triggered on
- * @param {Object} node The callee node
- * @returns {undefined} Does not return
- */
- function checkCallExpression(ruleHelper, callExpr, node) {
- switch(node.type) {
- case "Identifier":
- case "MemberExpression":
- if (callExpr.arguments && callExpr.arguments.length > 0) {
- ruleHelper.checkMethod(callExpr);
- }
- break;
- case "TSNonNullExpression": {
- const newCallExpr = Object.assign({}, callExpr);
- newCallExpr.callee = node.expression;
- checkCallExpression(ruleHelper, newCallExpr, node.expression);
- break;
- }
- case "TaggedTemplateExpression": {
- const newCallExpr = Object.assign({}, callExpr);
- newCallExpr.callee = node.tag;
- const expressions = node.quasi.expressions;
- const strings = node.quasi.quasis;
- newCallExpr.arguments = [strings, ...expressions];
- checkCallExpression(ruleHelper, newCallExpr, node.tag);
- break;
- }
-
- case "TypeCastExpression": {
- const newCallExpr = Object.assign({}, callExpr);
- newCallExpr.callee = node.expression;
- checkCallExpression(ruleHelper, newCallExpr, node.expression);
- break;
- }
- case "AssignmentExpression":
- if (node.right.type === "MemberExpression") {
- const newCallExpr = Object.assign({}, callExpr);
- newCallExpr.callee = node.right;
- checkCallExpression(ruleHelper, newCallExpr, node.right);
- break;
- }
- checkCallExpression(ruleHelper, callExpr, node.right);
- break;
- case "Import":
- ruleHelper.checkMethod(callExpr);
- break;
- case "SequenceExpression": {
- // the return value of a SequenceExpression is the last expression.
- // So, we create a new mock CallExpression with the actually called
- // ... expression as the callee node and pass it to checkMethod()
- const newCallExpr = Object.assign({}, callExpr);
- const idx = node.expressions.length - 1;
- const called = node.expressions[idx];
- newCallExpr.callee = called;
- ruleHelper.checkMethod(newCallExpr);
- break;
- }
- case "TSAsExpression":
- break;
- // those are fine:
- case "LogicalExpression": // Should we scan these? issue #62.
- case "ConditionalExpression":
- case "ArrowFunctionExpression":
- case "FunctionExpression":
- case "Super":
- case "CallExpression":
- case "ThisExpression":
- case "NewExpression":
- case "TSTypeAssertion":
- case "AwaitExpression": // see issue #122
- break;
- // If we don't cater for this expression throw an error
- default:
- ruleHelper.reportUnsupported(node, "Unexpected Callee", `Unsupported Callee of type ${node.type} for CallExpression`);
- }
- }
- module.exports = {
- meta: {
- type: "problem",
- docs: {
- description: "ESLint rule to disallow unsanitized method calls",
- category: "possible-errors",
- url: "https://github.com/mozilla/eslint-plugin-no-unsanitized/tree/master/docs/rules/method.md"
- },
- /* schema statement TBD until we have options
- schema: [
- {
- type: array
- }
- ]*/
- },
- create(context) {
- const ruleHelper = new RuleHelper(context, defaultRuleChecks);
- return {
- CallExpression(node) {
- checkCallExpression(ruleHelper, node, node.callee);
- },
- ImportExpression(node) {
- checkImport(ruleHelper, node);
- },
- // Tagged template expressions pass arguments in a special format we need to
- // map to our existing function call logic
- // foo`bar${var1}${var2}` will run as foo(['bar', ''], var1, var2)
- TaggedTemplateExpression(node) {
- const newCallExpr = Object.assign({}, node);
- newCallExpr.callee = node.tag;
- const expressions = node.quasi.expressions;
- const strings = node.quasi.quasis;
- newCallExpr.arguments = [strings, ...expressions];
- checkCallExpression(ruleHelper, newCallExpr, node.tag);
- },
- };
- }
- };
|