]> git.proxmox.com Git - pve-eslint.git/blame - eslint/lib/rules/no-control-regex.js
import 8.41.0 source
[pve-eslint.git] / eslint / lib / rules / no-control-regex.js
CommitLineData
eb39fafa
DC
1/**
2 * @fileoverview Rule to forbid control characters from regular expressions.
3 * @author Nicholas C. Zakas
4 */
5
6"use strict";
7
f2a92ac6 8const RegExpValidator = require("@eslint-community/regexpp").RegExpValidator;
eb39fafa
DC
9const collector = new (class {
10 constructor() {
eb39fafa
DC
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
8f9d1d4d
DC
33 collectControlChars(regexpStr, flags) {
34 const uFlag = typeof flags === "string" && flags.includes("u");
35
eb39fafa
DC
36 try {
37 this._source = regexpStr;
8f9d1d4d 38 this._validator.validatePattern(regexpStr, void 0, void 0, uFlag); // Call onCharacter hook
d3726936 39 } catch {
eb39fafa
DC
40
41 // Ignore syntax errors in RegExp.
42 }
43 return this._controlChars;
44 }
45})();
46
47//------------------------------------------------------------------------------
48// Rule Definition
49//------------------------------------------------------------------------------
50
34eeec05 51/** @type {import('../shared/types').Rule} */
eb39fafa
DC
52module.exports = {
53 meta: {
54 type: "problem",
55
56 docs: {
8f9d1d4d 57 description: "Disallow control characters in regular expressions",
eb39fafa 58 recommended: true,
f2a92ac6 59 url: "https://eslint.org/docs/latest/rules/no-control-regex"
eb39fafa
DC
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
8f9d1d4d
DC
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`.
eb39fafa
DC
77 * @private
78 */
8f9d1d4d 79 function getRegExp(node) {
eb39fafa 80 if (node.regex) {
8f9d1d4d 81 return node.regex;
eb39fafa
DC
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 ) {
8f9d1d4d
DC
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 };
eb39fafa
DC
98 }
99
100 return null;
101 }
102
103 return {
104 Literal(node) {
8f9d1d4d 105 const regExp = getRegExp(node);
eb39fafa 106
8f9d1d4d
DC
107 if (regExp) {
108 const { pattern, flags } = regExp;
109 const controlCharacters = collector.collectControlChars(pattern, flags);
eb39fafa
DC
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};