]> git.proxmox.com Git - pve-eslint.git/blob - eslint/lib/rules/no-control-regex.js
import 8.23.1 source
[pve-eslint.git] / eslint / lib / rules / no-control-regex.js
1 /**
2 * @fileoverview Rule to forbid control characters from regular expressions.
3 * @author Nicholas C. Zakas
4 */
5
6 "use strict";
7
8 const RegExpValidator = require("regexpp").RegExpValidator;
9 const collector = new (class {
10 constructor() {
11 this._source = "";
12 this._controlChars = [];
13 this._validator = new RegExpValidator(this);
14 }
15
16 onPatternEnter() {
17 this._controlChars = [];
18 }
19
20 onCharacter(start, end, cp) {
21 if (cp >= 0x00 &&
22 cp <= 0x1F &&
23 (
24 this._source.codePointAt(start) === cp ||
25 this._source.slice(start, end).startsWith("\\x") ||
26 this._source.slice(start, end).startsWith("\\u")
27 )
28 ) {
29 this._controlChars.push(`\\x${`0${cp.toString(16)}`.slice(-2)}`);
30 }
31 }
32
33 collectControlChars(regexpStr, flags) {
34 const uFlag = typeof flags === "string" && flags.includes("u");
35
36 try {
37 this._source = regexpStr;
38 this._validator.validatePattern(regexpStr, void 0, void 0, uFlag); // Call onCharacter hook
39 } catch {
40
41 // Ignore syntax errors in RegExp.
42 }
43 return this._controlChars;
44 }
45 })();
46
47 //------------------------------------------------------------------------------
48 // Rule Definition
49 //------------------------------------------------------------------------------
50
51 /** @type {import('../shared/types').Rule} */
52 module.exports = {
53 meta: {
54 type: "problem",
55
56 docs: {
57 description: "Disallow control characters in regular expressions",
58 recommended: true,
59 url: "https://eslint.org/docs/rules/no-control-regex"
60 },
61
62 schema: [],
63
64 messages: {
65 unexpected: "Unexpected control character(s) in regular expression: {{controlChars}}."
66 }
67 },
68
69 create(context) {
70
71 /**
72 * Get the regex expression
73 * @param {ASTNode} node `Literal` node to evaluate
74 * @returns {{ pattern: string, flags: string | null } | null} Regex if found (the given node is either a regex literal
75 * or a string literal that is the pattern argument of a RegExp constructor call). Otherwise `null`. If flags cannot be determined,
76 * the `flags` property will be `null`.
77 * @private
78 */
79 function getRegExp(node) {
80 if (node.regex) {
81 return node.regex;
82 }
83 if (typeof node.value === "string" &&
84 (node.parent.type === "NewExpression" || node.parent.type === "CallExpression") &&
85 node.parent.callee.type === "Identifier" &&
86 node.parent.callee.name === "RegExp" &&
87 node.parent.arguments[0] === node
88 ) {
89 const pattern = node.value;
90 const flags =
91 node.parent.arguments.length > 1 &&
92 node.parent.arguments[1].type === "Literal" &&
93 typeof node.parent.arguments[1].value === "string"
94 ? node.parent.arguments[1].value
95 : null;
96
97 return { pattern, flags };
98 }
99
100 return null;
101 }
102
103 return {
104 Literal(node) {
105 const regExp = getRegExp(node);
106
107 if (regExp) {
108 const { pattern, flags } = regExp;
109 const controlCharacters = collector.collectControlChars(pattern, flags);
110
111 if (controlCharacters.length > 0) {
112 context.report({
113 node,
114 messageId: "unexpected",
115 data: {
116 controlChars: controlCharacters.join(", ")
117 }
118 });
119 }
120 }
121 }
122 };
123
124 }
125 };