123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131 |
- /**
- * @fileoverview Rule to flag use of implied eval via setTimeout and setInterval
- * @author James Allardice
- */
- "use strict";
- //------------------------------------------------------------------------------
- // Requirements
- //------------------------------------------------------------------------------
- const astUtils = require("./utils/ast-utils");
- const { getStaticValue } = require("eslint-utils");
- //------------------------------------------------------------------------------
- // Rule Definition
- //------------------------------------------------------------------------------
- /** @type {import('../shared/types').Rule} */
- module.exports = {
- meta: {
- type: "suggestion",
- docs: {
- description: "Disallow the use of `eval()`-like methods",
- recommended: false,
- url: "https://eslint.org/docs/rules/no-implied-eval"
- },
- schema: [],
- messages: {
- impliedEval: "Implied eval. Consider passing a function instead of a string."
- }
- },
- create(context) {
- const GLOBAL_CANDIDATES = Object.freeze(["global", "window", "globalThis"]);
- const EVAL_LIKE_FUNC_PATTERN = /^(?:set(?:Interval|Timeout)|execScript)$/u;
- /**
- * Checks whether a node is evaluated as a string or not.
- * @param {ASTNode} node A node to check.
- * @returns {boolean} True if the node is evaluated as a string.
- */
- function isEvaluatedString(node) {
- if (
- (node.type === "Literal" && typeof node.value === "string") ||
- node.type === "TemplateLiteral"
- ) {
- return true;
- }
- if (node.type === "BinaryExpression" && node.operator === "+") {
- return isEvaluatedString(node.left) || isEvaluatedString(node.right);
- }
- return false;
- }
- /**
- * Reports if the `CallExpression` node has evaluated argument.
- * @param {ASTNode} node A CallExpression to check.
- * @returns {void}
- */
- function reportImpliedEvalCallExpression(node) {
- const [firstArgument] = node.arguments;
- if (firstArgument) {
- const staticValue = getStaticValue(firstArgument, context.getScope());
- const isStaticString = staticValue && typeof staticValue.value === "string";
- const isString = isStaticString || isEvaluatedString(firstArgument);
- if (isString) {
- context.report({
- node,
- messageId: "impliedEval"
- });
- }
- }
- }
- /**
- * Reports calls of `implied eval` via the global references.
- * @param {Variable} globalVar A global variable to check.
- * @returns {void}
- */
- function reportImpliedEvalViaGlobal(globalVar) {
- const { references, name } = globalVar;
- references.forEach(ref => {
- const identifier = ref.identifier;
- let node = identifier.parent;
- while (astUtils.isSpecificMemberAccess(node, null, name)) {
- node = node.parent;
- }
- if (astUtils.isSpecificMemberAccess(node, null, EVAL_LIKE_FUNC_PATTERN)) {
- const calleeNode = node.parent.type === "ChainExpression" ? node.parent : node;
- const parent = calleeNode.parent;
- if (parent.type === "CallExpression" && parent.callee === calleeNode) {
- reportImpliedEvalCallExpression(parent);
- }
- }
- });
- }
- //--------------------------------------------------------------------------
- // Public
- //--------------------------------------------------------------------------
- return {
- CallExpression(node) {
- if (astUtils.isSpecificId(node.callee, EVAL_LIKE_FUNC_PATTERN)) {
- reportImpliedEvalCallExpression(node);
- }
- },
- "Program:exit"() {
- const globalScope = context.getScope();
- GLOBAL_CANDIDATES
- .map(candidate => astUtils.getVariableByName(globalScope, candidate))
- .filter(globalVar => !!globalVar && globalVar.defs.length === 0)
- .forEach(reportImpliedEvalViaGlobal);
- }
- };
- }
- };
|