• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/**
2 * @fileoverview Validate strings passed to the RegExp constructor
3 * @author Michael Ficarra
4 */
5"use strict";
6
7//------------------------------------------------------------------------------
8// Requirements
9//------------------------------------------------------------------------------
10
11const RegExpValidator = require("regexpp").RegExpValidator;
12const validator = new RegExpValidator({ ecmaVersion: 2018 });
13const validFlags = /[gimuys]/gu;
14const undefined1 = void 0;
15
16//------------------------------------------------------------------------------
17// Rule Definition
18//------------------------------------------------------------------------------
19
20module.exports = {
21    meta: {
22        type: "problem",
23
24        docs: {
25            description: "disallow invalid regular expression strings in `RegExp` constructors",
26            category: "Possible Errors",
27            recommended: true,
28            url: "https://eslint.org/docs/rules/no-invalid-regexp"
29        },
30
31        schema: [{
32            type: "object",
33            properties: {
34                allowConstructorFlags: {
35                    type: "array",
36                    items: {
37                        type: "string"
38                    }
39                }
40            },
41            additionalProperties: false
42        }],
43
44        messages: {
45            regexMessage: "{{message}}."
46        }
47    },
48
49    create(context) {
50
51        const options = context.options[0];
52        let allowedFlags = null;
53
54        if (options && options.allowConstructorFlags) {
55            const temp = options.allowConstructorFlags.join("").replace(validFlags, "");
56
57            if (temp) {
58                allowedFlags = new RegExp(`[${temp}]`, "giu");
59            }
60        }
61
62        /**
63         * Check if node is a string
64         * @param {ASTNode} node node to evaluate
65         * @returns {boolean} True if its a string
66         * @private
67         */
68        function isString(node) {
69            return node && node.type === "Literal" && typeof node.value === "string";
70        }
71
72        /**
73         * Check syntax error in a given pattern.
74         * @param {string} pattern The RegExp pattern to validate.
75         * @param {boolean} uFlag The Unicode flag.
76         * @returns {string|null} The syntax error.
77         */
78        function validateRegExpPattern(pattern, uFlag) {
79            try {
80                validator.validatePattern(pattern, undefined1, undefined1, uFlag);
81                return null;
82            } catch (err) {
83                return err.message;
84            }
85        }
86
87        /**
88         * Check syntax error in a given flags.
89         * @param {string} flags The RegExp flags to validate.
90         * @returns {string|null} The syntax error.
91         */
92        function validateRegExpFlags(flags) {
93            try {
94                validator.validateFlags(flags);
95                return null;
96            } catch {
97                return `Invalid flags supplied to RegExp constructor '${flags}'`;
98            }
99        }
100
101        return {
102            "CallExpression, NewExpression"(node) {
103                if (node.callee.type !== "Identifier" || node.callee.name !== "RegExp" || !isString(node.arguments[0])) {
104                    return;
105                }
106                const pattern = node.arguments[0].value;
107                let flags = isString(node.arguments[1]) ? node.arguments[1].value : "";
108
109                if (allowedFlags) {
110                    flags = flags.replace(allowedFlags, "");
111                }
112
113                // If flags are unknown, check both are errored or not.
114                const message = validateRegExpFlags(flags) || (
115                    flags
116                        ? validateRegExpPattern(pattern, flags.indexOf("u") !== -1)
117                        : validateRegExpPattern(pattern, true) && validateRegExpPattern(pattern, false)
118                );
119
120                if (message) {
121                    context.report({
122                        node,
123                        messageId: "regexMessage",
124                        data: { message }
125                    });
126                }
127            }
128        };
129    }
130};
131