]> git.proxmox.com Git - pve-eslint.git/blob - eslint/lib/rules/no-invalid-regexp.js
ee19932896609934ccae25fe9a697889c860f53e
[pve-eslint.git] / eslint / lib / rules / no-invalid-regexp.js
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;
12 const validator = new RegExpValidator();
13 const validFlags = /[dgimsuy]/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 recommended: true,
27 url: "https://eslint.org/docs/rules/no-invalid-regexp"
28 },
29
30 schema: [{
31 type: "object",
32 properties: {
33 allowConstructorFlags: {
34 type: "array",
35 items: {
36 type: "string"
37 }
38 }
39 },
40 additionalProperties: false
41 }],
42
43 messages: {
44 regexMessage: "{{message}}."
45 }
46 },
47
48 create(context) {
49
50 const options = context.options[0];
51 let allowedFlags = null;
52
53 if (options && options.allowConstructorFlags) {
54 const temp = options.allowConstructorFlags.join("").replace(validFlags, "");
55
56 if (temp) {
57 allowedFlags = new RegExp(`[${temp}]`, "giu");
58 }
59 }
60
61 /**
62 * Check if node is a string
63 * @param {ASTNode} node node to evaluate
64 * @returns {boolean} True if its a string
65 * @private
66 */
67 function isString(node) {
68 return node && node.type === "Literal" && typeof node.value === "string";
69 }
70
71 /**
72 * Gets flags of a regular expression created by the given `RegExp()` or `new RegExp()` call
73 * Examples:
74 * new RegExp(".") // => ""
75 * new RegExp(".", "gu") // => "gu"
76 * new RegExp(".", flags) // => null
77 * @param {ASTNode} node `CallExpression` or `NewExpression` node
78 * @returns {string|null} flags if they can be determined, `null` otherwise
79 * @private
80 */
81 function getFlags(node) {
82 if (node.arguments.length < 2) {
83 return "";
84 }
85
86 if (isString(node.arguments[1])) {
87 return node.arguments[1].value;
88 }
89
90 return null;
91 }
92
93 /**
94 * Check syntax error in a given pattern.
95 * @param {string} pattern The RegExp pattern to validate.
96 * @param {boolean} uFlag The Unicode flag.
97 * @returns {string|null} The syntax error.
98 */
99 function validateRegExpPattern(pattern, uFlag) {
100 try {
101 validator.validatePattern(pattern, undefined1, undefined1, uFlag);
102 return null;
103 } catch (err) {
104 return err.message;
105 }
106 }
107
108 /**
109 * Check syntax error in a given flags.
110 * @param {string} flags The RegExp flags to validate.
111 * @returns {string|null} The syntax error.
112 */
113 function validateRegExpFlags(flags) {
114 try {
115 validator.validateFlags(flags);
116 return null;
117 } catch {
118 return `Invalid flags supplied to RegExp constructor '${flags}'`;
119 }
120 }
121
122 return {
123 "CallExpression, NewExpression"(node) {
124 if (node.callee.type !== "Identifier" || node.callee.name !== "RegExp" || !isString(node.arguments[0])) {
125 return;
126 }
127 const pattern = node.arguments[0].value;
128 let flags = getFlags(node);
129
130 if (flags && allowedFlags) {
131 flags = flags.replace(allowedFlags, "");
132 }
133
134 const message =
135 (
136 flags && validateRegExpFlags(flags)
137 ) ||
138 (
139
140 // If flags are unknown, report the regex only if its pattern is invalid both with and without the "u" flag
141 flags === null
142 ? validateRegExpPattern(pattern, true) && validateRegExpPattern(pattern, false)
143 : validateRegExpPattern(pattern, flags.includes("u"))
144 );
145
146 if (message) {
147 context.report({
148 node,
149 messageId: "regexMessage",
150 data: { message }
151 });
152 }
153 }
154 };
155 }
156 };