]>
Commit | Line | Data |
---|---|---|
eb39fafa DC |
1 | /** |
2 | * @fileoverview Validate strings passed to the RegExp constructor | |
3 | * @author Michael Ficarra | |
4 | */ | |
5 | "use strict"; | |
6 | ||
7 | //------------------------------------------------------------------------------ | |
8 | // Requirements | |
9 | //------------------------------------------------------------------------------ | |
10 | ||
11 | const RegExpValidator = require("regexpp").RegExpValidator; | |
456be15e | 12 | const validator = new RegExpValidator(); |
eb39fafa DC |
13 | const validFlags = /[gimuys]/gu; |
14 | const undefined1 = void 0; | |
15 | ||
16 | //------------------------------------------------------------------------------ | |
17 | // Rule Definition | |
18 | //------------------------------------------------------------------------------ | |
19 | ||
20 | module.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; | |
d3726936 | 96 | } catch { |
eb39fafa DC |
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 | }; |