UNPKG

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