2017-03-13 18:50:57 +00:00
|
|
|
/**
|
|
|
|
* @fileoverview Look for unescaped "literal" dots in regular expressions
|
|
|
|
* @author Brian White
|
|
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
// Rule Definition
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
2022-09-27 21:26:54 +00:00
|
|
|
module.exports = {
|
|
|
|
create(context) {
|
2024-05-09 15:27:39 +00:00
|
|
|
const sourceCode = context.sourceCode;
|
2022-09-27 21:26:54 +00:00
|
|
|
const regexpStack = [];
|
|
|
|
let regexpBuffer = [];
|
|
|
|
let inRegExp = false;
|
2017-03-13 18:50:57 +00:00
|
|
|
|
2022-09-27 21:26:54 +00:00
|
|
|
function report(node, startOffset) {
|
|
|
|
const indexOfDot = sourceCode.getIndexFromLoc(node.loc.start) + startOffset;
|
|
|
|
context.report({
|
|
|
|
node,
|
|
|
|
loc: sourceCode.getLocFromIndex(indexOfDot),
|
2022-12-18 16:39:39 +00:00
|
|
|
message: 'Unescaped dot character in regular expression',
|
2022-09-27 21:26:54 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
const allowedModifiers = ['+', '*', '?', '{'];
|
2017-03-13 18:50:57 +00:00
|
|
|
|
2022-09-27 21:26:54 +00:00
|
|
|
function checkRegExp(nodes) {
|
|
|
|
let escaping = false;
|
|
|
|
let inCharClass = false;
|
|
|
|
for (let n = 0; n < nodes.length; ++n) {
|
|
|
|
const pair = nodes[n];
|
|
|
|
const node = pair[0];
|
|
|
|
const str = pair[1];
|
|
|
|
for (let i = 0; i < str.length; ++i) {
|
|
|
|
switch (str[i]) {
|
|
|
|
case '[':
|
|
|
|
if (!escaping)
|
|
|
|
inCharClass = true;
|
|
|
|
else
|
|
|
|
escaping = false;
|
|
|
|
break;
|
|
|
|
case ']':
|
|
|
|
if (!escaping) {
|
2024-10-09 06:42:16 +00:00
|
|
|
inCharClass &&= false;
|
2022-09-27 21:26:54 +00:00
|
|
|
} else {
|
|
|
|
escaping = false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case '\\':
|
|
|
|
escaping = !escaping;
|
|
|
|
break;
|
|
|
|
case '.':
|
|
|
|
if (!escaping) {
|
|
|
|
if (!inCharClass &&
|
2017-03-13 18:50:57 +00:00
|
|
|
((i + 1) === str.length ||
|
|
|
|
allowedModifiers.indexOf(str[i + 1]) === -1)) {
|
2022-09-27 21:26:54 +00:00
|
|
|
report(node, i);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
escaping = false;
|
2017-03-13 18:50:57 +00:00
|
|
|
}
|
2022-09-27 21:26:54 +00:00
|
|
|
break;
|
|
|
|
default:
|
2024-10-09 06:42:16 +00:00
|
|
|
escaping &&= false;
|
2022-09-27 21:26:54 +00:00
|
|
|
}
|
2017-03-13 18:50:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-27 21:26:54 +00:00
|
|
|
function checkRegExpStart(node) {
|
|
|
|
if (node.callee && node.callee.name === 'RegExp') {
|
|
|
|
if (inRegExp) {
|
|
|
|
regexpStack.push(regexpBuffer);
|
|
|
|
regexpBuffer = [];
|
|
|
|
}
|
|
|
|
inRegExp = true;
|
2017-03-13 18:50:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-27 21:26:54 +00:00
|
|
|
function checkRegExpEnd(node) {
|
|
|
|
if (node.callee && node.callee.name === 'RegExp') {
|
|
|
|
checkRegExp(regexpBuffer);
|
|
|
|
if (regexpStack.length) {
|
|
|
|
regexpBuffer = regexpStack.pop();
|
|
|
|
} else {
|
|
|
|
inRegExp = false;
|
|
|
|
regexpBuffer = [];
|
|
|
|
}
|
2017-03-13 18:50:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-27 21:26:54 +00:00
|
|
|
function checkLiteral(node) {
|
2024-09-24 19:48:15 +00:00
|
|
|
const isTemplate = (node.type === 'TemplateLiteral' && node.quasis?.length);
|
2022-09-27 21:26:54 +00:00
|
|
|
if (inRegExp &&
|
2017-03-13 18:50:57 +00:00
|
|
|
(isTemplate || (typeof node.value === 'string' && node.value.length))) {
|
2022-09-27 21:26:54 +00:00
|
|
|
let p = node.parent;
|
|
|
|
while (p && p.type === 'BinaryExpression') {
|
|
|
|
p = p.parent;
|
|
|
|
}
|
|
|
|
if (p && (p.type === 'NewExpression' || p.type === 'CallExpression') &&
|
2017-03-13 18:50:57 +00:00
|
|
|
p.callee && p.callee.type === 'Identifier' &&
|
|
|
|
p.callee.name === 'RegExp') {
|
2022-09-27 21:26:54 +00:00
|
|
|
if (isTemplate) {
|
|
|
|
const quasis = node.quasis;
|
|
|
|
for (let i = 0; i < quasis.length; ++i) {
|
|
|
|
const el = quasis[i];
|
2024-09-24 19:48:15 +00:00
|
|
|
if (el.type === 'TemplateElement' && el.value?.cooked)
|
2022-09-27 21:26:54 +00:00
|
|
|
regexpBuffer.push([el, el.value.cooked]);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
regexpBuffer.push([node, node.value]);
|
2017-03-13 18:50:57 +00:00
|
|
|
}
|
|
|
|
}
|
2022-09-27 21:26:54 +00:00
|
|
|
} else if (node.regex) {
|
|
|
|
checkRegExp([[node, node.regex.pattern]]);
|
2017-03-13 18:50:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-27 21:26:54 +00:00
|
|
|
|
|
|
|
return {
|
|
|
|
'TemplateLiteral': checkLiteral,
|
|
|
|
'Literal': checkLiteral,
|
|
|
|
'CallExpression': checkRegExpStart,
|
|
|
|
'NewExpression': checkRegExpStart,
|
|
|
|
'CallExpression:exit': checkRegExpEnd,
|
2022-12-18 16:39:39 +00:00
|
|
|
'NewExpression:exit': checkRegExpEnd,
|
2022-09-27 21:26:54 +00:00
|
|
|
};
|
2022-12-18 16:39:39 +00:00
|
|
|
},
|
2017-03-13 18:50:57 +00:00
|
|
|
};
|