UNPKG

4.18 kBJavaScriptView Raw
1/**
2 * @fileoverview Rule to forbid control charactes from regular expressions.
3 * @author Nicholas C. Zakas
4 */
5
6"use strict";
7
8//------------------------------------------------------------------------------
9// Rule Definition
10//------------------------------------------------------------------------------
11
12module.exports = {
13 meta: {
14 docs: {
15 description: "disallow control characters in regular expressions",
16 category: "Possible Errors",
17 recommended: true
18 },
19
20 schema: []
21 },
22
23 create(context) {
24
25 /**
26 * Get the regex expression
27 * @param {ASTNode} node node to evaluate
28 * @returns {*} Regex if found else null
29 * @private
30 */
31 function getRegExp(node) {
32 if (node.value instanceof RegExp) {
33 return node.value;
34 } else if (typeof node.value === "string") {
35
36 const parent = context.getAncestors().pop();
37
38 if ((parent.type === "NewExpression" || parent.type === "CallExpression") &&
39 parent.callee.type === "Identifier" && parent.callee.name === "RegExp"
40 ) {
41
42 // there could be an invalid regular expression string
43 try {
44 return new RegExp(node.value);
45 } catch (ex) {
46 return null;
47 }
48 }
49 }
50
51 return null;
52 }
53
54
55 const controlChar = /[\x00-\x1f]/g; // eslint-disable-line no-control-regex
56 const consecutiveSlashes = /\\+/g;
57 const consecutiveSlashesAtEnd = /\\+$/g;
58 const stringControlChar = /\\x[01][0-9a-f]/ig;
59 const stringControlCharWithoutSlash = /x[01][0-9a-f]/ig;
60
61 /**
62 * Return a list of the control characters in the given regex string
63 * @param {string} regexStr regex as string to check
64 * @returns {array} returns a list of found control characters on given string
65 * @private
66 */
67 function getControlCharacters(regexStr) {
68
69 // check control characters, if RegExp object used
70 const controlChars = regexStr.match(controlChar) || [];
71
72 let stringControlChars = [];
73
74 // check substr, if regex literal used
75 const subStrIndex = regexStr.search(stringControlChar);
76
77 if (subStrIndex > -1) {
78
79 // is it escaped, check backslash count
80 const possibleEscapeCharacters = regexStr.slice(0, subStrIndex).match(consecutiveSlashesAtEnd);
81
82 const hasControlChars = possibleEscapeCharacters === null || !(possibleEscapeCharacters[0].length % 2);
83
84 if (hasControlChars) {
85 stringControlChars = regexStr.slice(subStrIndex, -1)
86 .split(consecutiveSlashes)
87 .filter(Boolean)
88 .map(function(x) {
89 const match = x.match(stringControlCharWithoutSlash) || [x];
90
91 return `\\${match[0]}`;
92 });
93 }
94 }
95
96 return controlChars.map(function(x) {
97 const hexCode = `0${x.charCodeAt(0).toString(16)}`.slice(-2);
98
99 return `\\x${hexCode}`;
100 }).concat(stringControlChars);
101 }
102
103 return {
104 Literal(node) {
105 const regex = getRegExp(node);
106
107 if (regex) {
108 const computedValue = regex.toString();
109
110 const controlCharacters = getControlCharacters(computedValue);
111
112 if (controlCharacters.length > 0) {
113 context.report({
114 node,
115 message: "Unexpected control character(s) in regular expression: {{controlChars}}.",
116 data: {
117 controlChars: controlCharacters.join(", ")
118 }
119 });
120 }
121 }
122 }
123 };
124
125 }
126};