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 | },
|
24 |
|
25 | schema: [{
|
26 | type: "object",
|
27 | properties: {
|
28 | allowConstructorFlags: {
|
29 | type: "array",
|
30 | items: {
|
31 | type: "string"
|
32 | }
|
33 | }
|
34 | },
|
35 | additionalProperties: false
|
36 | }]
|
37 | },
|
38 |
|
39 | create(context) {
|
40 |
|
41 | const options = context.options[0];
|
42 | let allowedFlags = "";
|
43 |
|
44 | if (options && options.allowConstructorFlags) {
|
45 | allowedFlags = options.allowConstructorFlags.join("");
|
46 | }
|
47 |
|
48 | /**
|
49 | * Check if node is a string
|
50 | * @param {ASTNode} node node to evaluate
|
51 | * @returns {boolean} True if its a string
|
52 | * @private
|
53 | */
|
54 | function isString(node) {
|
55 | return node && node.type === "Literal" && typeof node.value === "string";
|
56 | }
|
57 |
|
58 | /**
|
59 | * Validate strings passed to the RegExp constructor
|
60 | * @param {ASTNode} node node to evaluate
|
61 | * @returns {void}
|
62 | * @private
|
63 | */
|
64 | function check(node) {
|
65 | if (node.callee.type === "Identifier" && node.callee.name === "RegExp" && isString(node.arguments[0])) {
|
66 | let flags = isString(node.arguments[1]) ? node.arguments[1].value : "";
|
67 |
|
68 | if (allowedFlags) {
|
69 | flags = flags.replace(new RegExp(`[${allowedFlags}]`, "gi"), "");
|
70 | }
|
71 |
|
72 | try {
|
73 | void new RegExp(node.arguments[0].value);
|
74 | } catch (e) {
|
75 | context.report({
|
76 | node,
|
77 | message: "{{message}}.",
|
78 | data: e
|
79 | });
|
80 | }
|
81 |
|
82 | if (flags) {
|
83 |
|
84 | try {
|
85 | espree.parse(`/./${flags}`, context.parserOptions);
|
86 | } catch (ex) {
|
87 | context.report({
|
88 | node,
|
89 | message: "Invalid flags supplied to RegExp constructor '{{flags}}'.",
|
90 | data: {
|
91 | flags
|
92 | }
|
93 | });
|
94 | }
|
95 | }
|
96 |
|
97 | }
|
98 | }
|
99 |
|
100 | return {
|
101 | CallExpression: check,
|
102 | NewExpression: check
|
103 | };
|
104 |
|
105 | }
|
106 | };
|