summaryrefslogtreecommitdiff
path: root/tools/node_modules/eslint/lib/rules/no-eval.js
diff options
context:
space:
mode:
Diffstat (limited to 'tools/node_modules/eslint/lib/rules/no-eval.js')
-rw-r--r--tools/node_modules/eslint/lib/rules/no-eval.js308
1 files changed, 308 insertions, 0 deletions
diff --git a/tools/node_modules/eslint/lib/rules/no-eval.js b/tools/node_modules/eslint/lib/rules/no-eval.js
new file mode 100644
index 0000000000..ee5f577f47
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/no-eval.js
@@ -0,0 +1,308 @@
+/**
+ * @fileoverview Rule to flag use of eval() statement
+ * @author Nicholas C. Zakas
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+const candidatesOfGlobalObject = Object.freeze([
+ "global",
+ "window"
+]);
+
+/**
+ * Checks a given node is a Identifier node of the specified name.
+ *
+ * @param {ASTNode} node - A node to check.
+ * @param {string} name - A name to check.
+ * @returns {boolean} `true` if the node is a Identifier node of the name.
+ */
+function isIdentifier(node, name) {
+ return node.type === "Identifier" && node.name === name;
+}
+
+/**
+ * Checks a given node is a Literal node of the specified string value.
+ *
+ * @param {ASTNode} node - A node to check.
+ * @param {string} name - A name to check.
+ * @returns {boolean} `true` if the node is a Literal node of the name.
+ */
+function isConstant(node, name) {
+ switch (node.type) {
+ case "Literal":
+ return node.value === name;
+
+ case "TemplateLiteral":
+ return (
+ node.expressions.length === 0 &&
+ node.quasis[0].value.cooked === name
+ );
+
+ default:
+ return false;
+ }
+}
+
+/**
+ * Checks a given node is a MemberExpression node which has the specified name's
+ * property.
+ *
+ * @param {ASTNode} node - A node to check.
+ * @param {string} name - A name to check.
+ * @returns {boolean} `true` if the node is a MemberExpression node which has
+ * the specified name's property
+ */
+function isMember(node, name) {
+ return (
+ node.type === "MemberExpression" &&
+ (node.computed ? isConstant : isIdentifier)(node.property, name)
+ );
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "disallow the use of `eval()`",
+ category: "Best Practices",
+ recommended: false
+ },
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ allowIndirect: { type: "boolean" }
+ },
+ additionalProperties: false
+ }
+ ]
+ },
+
+ create(context) {
+ const allowIndirect = Boolean(
+ context.options[0] &&
+ context.options[0].allowIndirect
+ );
+ const sourceCode = context.getSourceCode();
+ let funcInfo = null;
+
+ /**
+ * Pushs a variable scope (Program or Function) information to the stack.
+ *
+ * This is used in order to check whether or not `this` binding is a
+ * reference to the global object.
+ *
+ * @param {ASTNode} node - A node of the scope. This is one of Program,
+ * FunctionDeclaration, FunctionExpression, and ArrowFunctionExpression.
+ * @returns {void}
+ */
+ function enterVarScope(node) {
+ const strict = context.getScope().isStrict;
+
+ funcInfo = {
+ upper: funcInfo,
+ node,
+ strict,
+ defaultThis: false,
+ initialized: strict
+ };
+ }
+
+ /**
+ * Pops a variable scope from the stack.
+ *
+ * @returns {void}
+ */
+ function exitVarScope() {
+ funcInfo = funcInfo.upper;
+ }
+
+ /**
+ * Reports a given node.
+ *
+ * `node` is `Identifier` or `MemberExpression`.
+ * The parent of `node` might be `CallExpression`.
+ *
+ * The location of the report is always `eval` `Identifier` (or possibly
+ * `Literal`). The type of the report is `CallExpression` if the parent is
+ * `CallExpression`. Otherwise, it's the given node type.
+ *
+ * @param {ASTNode} node - A node to report.
+ * @returns {void}
+ */
+ function report(node) {
+ let locationNode = node;
+ const parent = node.parent;
+
+ if (node.type === "MemberExpression") {
+ locationNode = node.property;
+ }
+ if (parent.type === "CallExpression" && parent.callee === node) {
+ node = parent;
+ }
+
+ context.report({
+ node,
+ loc: locationNode.loc.start,
+ message: "eval can be harmful."
+ });
+ }
+
+ /**
+ * Reports accesses of `eval` via the global object.
+ *
+ * @param {eslint-scope.Scope} globalScope - The global scope.
+ * @returns {void}
+ */
+ function reportAccessingEvalViaGlobalObject(globalScope) {
+ for (let i = 0; i < candidatesOfGlobalObject.length; ++i) {
+ const name = candidatesOfGlobalObject[i];
+ const variable = astUtils.getVariableByName(globalScope, name);
+
+ if (!variable) {
+ continue;
+ }
+
+ const references = variable.references;
+
+ for (let j = 0; j < references.length; ++j) {
+ const identifier = references[j].identifier;
+ let node = identifier.parent;
+
+ // To detect code like `window.window.eval`.
+ while (isMember(node, name)) {
+ node = node.parent;
+ }
+
+ // Reports.
+ if (isMember(node, "eval")) {
+ report(node);
+ }
+ }
+ }
+ }
+
+ /**
+ * Reports all accesses of `eval` (excludes direct calls to eval).
+ *
+ * @param {eslint-scope.Scope} globalScope - The global scope.
+ * @returns {void}
+ */
+ function reportAccessingEval(globalScope) {
+ const variable = astUtils.getVariableByName(globalScope, "eval");
+
+ if (!variable) {
+ return;
+ }
+
+ const references = variable.references;
+
+ for (let i = 0; i < references.length; ++i) {
+ const reference = references[i];
+ const id = reference.identifier;
+
+ if (id.name === "eval" && !astUtils.isCallee(id)) {
+
+ // Is accessing to eval (excludes direct calls to eval)
+ report(id);
+ }
+ }
+ }
+
+ if (allowIndirect) {
+
+ // Checks only direct calls to eval. It's simple!
+ return {
+ "CallExpression:exit"(node) {
+ const callee = node.callee;
+
+ if (isIdentifier(callee, "eval")) {
+ report(callee);
+ }
+ }
+ };
+ }
+
+ return {
+ "CallExpression:exit"(node) {
+ const callee = node.callee;
+
+ if (isIdentifier(callee, "eval")) {
+ report(callee);
+ }
+ },
+
+ Program(node) {
+ const scope = context.getScope(),
+ features = context.parserOptions.ecmaFeatures || {},
+ strict =
+ scope.isStrict ||
+ node.sourceType === "module" ||
+ (features.globalReturn && scope.childScopes[0].isStrict);
+
+ funcInfo = {
+ upper: null,
+ node,
+ strict,
+ defaultThis: true,
+ initialized: true
+ };
+ },
+
+ "Program:exit"() {
+ const globalScope = context.getScope();
+
+ exitVarScope();
+ reportAccessingEval(globalScope);
+ reportAccessingEvalViaGlobalObject(globalScope);
+ },
+
+ FunctionDeclaration: enterVarScope,
+ "FunctionDeclaration:exit": exitVarScope,
+ FunctionExpression: enterVarScope,
+ "FunctionExpression:exit": exitVarScope,
+ ArrowFunctionExpression: enterVarScope,
+ "ArrowFunctionExpression:exit": exitVarScope,
+
+ ThisExpression(node) {
+ if (!isMember(node.parent, "eval")) {
+ return;
+ }
+
+ /*
+ * `this.eval` is found.
+ * Checks whether or not the value of `this` is the global object.
+ */
+ if (!funcInfo.initialized) {
+ funcInfo.initialized = true;
+ funcInfo.defaultThis = astUtils.isDefaultThisBinding(
+ funcInfo.node,
+ sourceCode
+ );
+ }
+
+ if (!funcInfo.strict && funcInfo.defaultThis) {
+
+ // `this.eval` is possible built-in `eval`.
+ report(node.parent);
+ }
+ }
+ };
+
+ }
+};