You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

131 lines
3.6 KiB

/**
* @fileoverview Look for unescaped "literal" dots in regular expressions
* @author Brian White
*/
'use strict';
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
const sourceCode = context.getSourceCode();
const regexpStack = [];
var regexpBuffer = [];
var inRegExp = false;
function report(node, startOffset) {
const indexOfDot = sourceCode.getIndexFromLoc(node.loc.start) + startOffset;
context.report({
node,
loc: sourceCode.getLocFromIndex(indexOfDot),
message: 'Unescaped dot character in regular expression'
});
}
const allowedModifiers = ['+', '*', '?', '{'];
function checkRegExp(nodes) {
var escaping = false;
var inCharClass = false;
for (var n = 0; n < nodes.length; ++n) {
const pair = nodes[n];
const node = pair[0];
const str = pair[1];
for (var i = 0; i < str.length; ++i) {
switch (str[i]) {
case '[':
if (!escaping)
inCharClass = true;
else
escaping = false;
break;
case ']':
if (!escaping) {
if (inCharClass)
inCharClass = false;
} else {
escaping = false;
}
break;
case '\\':
escaping = !escaping;
break;
case '.':
if (!escaping) {
if (!inCharClass &&
((i + 1) === str.length ||
allowedModifiers.indexOf(str[i + 1]) === -1)) {
report(node, i);
}
} else {
escaping = false;
}
break;
default:
if (escaping)
escaping = false;
}
}
}
}
function checkRegExpStart(node) {
if (node.callee && node.callee.name === 'RegExp') {
if (inRegExp) {
regexpStack.push(regexpBuffer);
regexpBuffer = [];
}
inRegExp = true;
}
}
function checkRegExpEnd(node) {
if (node.callee && node.callee.name === 'RegExp') {
checkRegExp(regexpBuffer);
if (regexpStack.length) {
regexpBuffer = regexpStack.pop();
} else {
inRegExp = false;
regexpBuffer = [];
}
}
}
function checkLiteral(node) {
const isTemplate = (node.type === 'TemplateLiteral' && node.quasis &&
node.quasis.length);
if (inRegExp &&
(isTemplate || (typeof node.value === 'string' && node.value.length))) {
var p = node.parent;
while (p && p.type === 'BinaryExpression') {
p = p.parent;
}
if (p && (p.type === 'NewExpression' || p.type === 'CallExpression') &&
p.callee && p.callee.type === 'Identifier' &&
p.callee.name === 'RegExp') {
if (isTemplate) {
const quasis = node.quasis;
for (var i = 0; i < quasis.length; ++i) {
const el = quasis[i];
if (el.type === 'TemplateElement' && el.value && el.value.cooked)
regexpBuffer.push([el, el.value.cooked]);
}
} else {
regexpBuffer.push([node, node.value]);
}
}
} else if (node.regex) {
checkRegExp([[node, node.regex.pattern]]);
}
}
return {
TemplateLiteral: checkLiteral,
Literal: checkLiteral,
CallExpression: checkRegExpStart,
NewExpression: checkRegExpStart,
'CallExpression:exit': checkRegExpEnd,
'NewExpression:exit': checkRegExpEnd
};
};