| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 |
- /**
- * @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-community/eslint-utils");
- //------------------------------------------------------------------------------
- // Rule Definition
- //------------------------------------------------------------------------------
- /** @type {import('../types').Rule.RuleModule} */
- module.exports = {
- meta: {
- type: "suggestion",
- docs: {
- description: "Disallow the use of `eval()`-like methods",
- recommended: false,
- url: "https://eslint.org/docs/latest/rules/no-implied-eval",
- },
- schema: [],
- messages: {
- impliedEval:
- "Implied eval. Consider passing a function instead of a string.",
- execScript: "Implied eval. Do not use execScript().",
- },
- },
- create(context) {
- const GLOBAL_CANDIDATES = Object.freeze([
- "global",
- "window",
- "globalThis",
- ]);
- const EVAL_LIKE_FUNC_PATTERN =
- /^(?:set(?:Interval|Timeout)|execScript)$/u;
- const sourceCode = context.sourceCode;
- /**
- * 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,
- sourceCode.getScope(node),
- );
- const isStaticString =
- staticValue && typeof staticValue.value === "string";
- const isString =
- isStaticString || isEvaluatedString(firstArgument);
- if (isString) {
- const calleeName =
- node.callee.type === "Identifier"
- ? node.callee.name
- : astUtils.getStaticPropertyName(node.callee);
- const isExecScript = calleeName === "execScript";
- context.report({
- node,
- messageId: isExecScript ? "execScript" : "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,
- ) &&
- sourceCode.isGlobalReference(node.callee)
- ) {
- reportImpliedEvalCallExpression(node);
- }
- },
- "Program:exit"(node) {
- const globalScope = sourceCode.getScope(node);
- GLOBAL_CANDIDATES.map(candidate =>
- astUtils.getVariableByName(globalScope, candidate),
- )
- .filter(
- globalVar => !!globalVar && globalVar.defs.length === 0,
- )
- .forEach(reportImpliedEvalViaGlobal);
- },
- };
- },
- };
|