/** * @fileoverview Validate strings passed to the RegExp constructor * @author Michael Ficarra */ "use strict"; //------------------------------------------------------------------------------ // Requirements //------------------------------------------------------------------------------ const RegExpValidator = require("regexpp").RegExpValidator; const validator = new RegExpValidator({ ecmaVersion: 2018 }); const validFlags = /[gimuys]/g; const undefined1 = void 0; //------------------------------------------------------------------------------ // Rule Definition //------------------------------------------------------------------------------ module.exports = { meta: { docs: { description: "disallow invalid regular expression strings in `RegExp` constructors", category: "Possible Errors", recommended: true, url: "https://eslint.org/docs/rules/no-invalid-regexp" }, schema: [{ type: "object", properties: { allowConstructorFlags: { type: "array", items: { type: "string" } } }, additionalProperties: false }] }, create(context) { const options = context.options[0]; let allowedFlags = null; if (options && options.allowConstructorFlags) { const temp = options.allowConstructorFlags.join("").replace(validFlags, ""); if (temp) { allowedFlags = new RegExp(`[${temp}]`, "gi"); } } /** * Check if node is a string * @param {ASTNode} node node to evaluate * @returns {boolean} True if its a string * @private */ function isString(node) { return node && node.type === "Literal" && typeof node.value === "string"; } /** * Check syntax error in a given pattern. * @param {string} pattern The RegExp pattern to validate. * @param {boolean} uFlag The Unicode flag. * @returns {string|null} The syntax error. */ function validateRegExpPattern(pattern, uFlag) { try { validator.validatePattern(pattern, undefined1, undefined1, uFlag); return null; } catch (err) { return err.message; } } /** * Check syntax error in a given flags. * @param {string} flags The RegExp flags to validate. * @returns {string|null} The syntax error. */ function validateRegExpFlags(flags) { try { validator.validateFlags(flags); return null; } catch (err) { return `Invalid flags supplied to RegExp constructor '${flags}'`; } } return { "CallExpression, NewExpression"(node) { if (node.callee.type !== "Identifier" || node.callee.name !== "RegExp" || !isString(node.arguments[0])) { return; } const pattern = node.arguments[0].value; let flags = isString(node.arguments[1]) ? node.arguments[1].value : ""; if (allowedFlags) { flags = flags.replace(allowedFlags, ""); } // If flags are unknown, check both are errored or not. const message = validateRegExpFlags(flags) || ( flags ? validateRegExpPattern(pattern, flags.indexOf("u") !== -1) : validateRegExpPattern(pattern, true) && validateRegExpPattern(pattern, false) ); if (message) { context.report({ node, message: "{{message}}.", data: { message } }); } } }; } };