1 | /**
|
2 | * @fileoverview Validate strings passed to the RegExp constructor
|
3 | * @author Michael Ficarra
|
4 | */
|
5 | ;
|
6 |
|
7 | //------------------------------------------------------------------------------
|
8 | // Requirements
|
9 | //------------------------------------------------------------------------------
|
10 |
|
11 | const espree = require("espree");
|
12 |
|
13 | //------------------------------------------------------------------------------
|
14 | // Rule Definition
|
15 | //------------------------------------------------------------------------------
|
16 |
|
17 | module.exports = {
|
18 | meta: {
|
19 | docs: {
|
20 | description: "disallow invalid regular expression strings in `RegExp` constructors",
|
21 | category: "Possible Errors",
|
22 | recommended: true,
|
23 | url: "https://eslint.org/docs/rules/no-invalid-regexp"
|
24 | },
|
25 |
|
26 | schema: [{
|
27 | type: "object",
|
28 | properties: {
|
29 | allowConstructorFlags: {
|
30 | type: "array",
|
31 | items: {
|
32 | type: "string"
|
33 | }
|
34 | }
|
35 | },
|
36 | additionalProperties: false
|
37 | }]
|
38 | },
|
39 |
|
40 | create(context) {
|
41 |
|
42 | const options = context.options[0];
|
43 | let allowedFlags = "";
|
44 |
|
45 | if (options && options.allowConstructorFlags) {
|
46 | allowedFlags = options.allowConstructorFlags.join("");
|
47 | }
|
48 |
|
49 | /**
|
50 | * Check if node is a string
|
51 | * @param {ASTNode} node node to evaluate
|
52 | * @returns {boolean} True if its a string
|
53 | * @private
|
54 | */
|
55 | function isString(node) {
|
56 | return node && node.type === "Literal" && typeof node.value === "string";
|
57 | }
|
58 |
|
59 | /**
|
60 | * Validate strings passed to the RegExp constructor
|
61 | * @param {ASTNode} node node to evaluate
|
62 | * @returns {void}
|
63 | * @private
|
64 | */
|
65 | function check(node) {
|
66 | if (node.callee.type === "Identifier" && node.callee.name === "RegExp" && isString(node.arguments[0])) {
|
67 | let flags = isString(node.arguments[1]) ? node.arguments[1].value : "";
|
68 |
|
69 | if (allowedFlags) {
|
70 | flags = flags.replace(new RegExp(`[${allowedFlags}]`, "gi"), "");
|
71 | }
|
72 |
|
73 | try {
|
74 | void new RegExp(node.arguments[0].value);
|
75 | } catch (e) {
|
76 | context.report({
|
77 | node,
|
78 | message: "{{message}}.",
|
79 | data: e
|
80 | });
|
81 | }
|
82 |
|
83 | if (flags) {
|
84 |
|
85 | try {
|
86 | espree.parse(`/./${flags}`, context.parserOptions);
|
87 | } catch (ex) {
|
88 | context.report({
|
89 | node,
|
90 | message: "Invalid flags supplied to RegExp constructor '{{flags}}'.",
|
91 | data: {
|
92 | flags
|
93 | }
|
94 | });
|
95 | }
|
96 | }
|
97 |
|
98 | }
|
99 | }
|
100 |
|
101 | return {
|
102 | CallExpression: check,
|
103 | NewExpression: check
|
104 | };
|
105 |
|
106 | }
|
107 | };
|