JS: Make ReDoS check string-based regexes

This commit is contained in:
Asger F
2019-10-18 22:08:25 +01:00
parent 97e5da1046
commit e0bdc777b9
4 changed files with 33 additions and 10 deletions

View File

@@ -84,16 +84,19 @@ import javascript
* whose root node is not a disjunction.
*/
class RegExpRoot extends @regexpterm {
RegExpParent parent;
// RegExpTerm is abstract, so do not extend it.
RegExpRoot() {
exists(RegExpLiteral literal, RegExpAlt alt | alt.getParent() = literal |
this = alt.getAChild()
exists(RegExpAlt alt |
alt.isRootTerm() and
this = alt.getAChild() and
parent = alt.getParent()
)
or
exists(RegExpLiteral literal |
not exists(RegExpAlt alt | alt.getParent() = literal) and
this.(RegExpTerm).getParent() = literal
)
this.(RegExpTerm).isRootTerm() and
not this instanceof RegExpAlt and
parent = this.(RegExpTerm).getParent()
}
/**
@@ -103,7 +106,13 @@ class RegExpRoot extends @regexpterm {
// there is at least one repetition
exists(RegExpRepetition rep | getRoot(rep) = this) and
// there are no lookbehinds
not exists(RegExpLookbehind lbh | getRoot(lbh) = this)
not exists(RegExpLookbehind lbh | getRoot(lbh) = this) and
// is actually used as a RegExp
(
parent instanceof RegExpLiteral
or
parent.(StringLiteral).flow() instanceof RegExpPatternSource
)
}
string toString() { result = this.(RegExpTerm).toString() }
@@ -352,10 +361,10 @@ predicate delta(State q1, EdgeLabel lbl, State q2) {
)
)
or
exists(RegExpDot dot, RegExpLiteral rel |
q1 = before(dot) and q2 = after(dot) and rel = dot.getLiteral()
exists(RegExpDot dot |
q1 = before(dot) and q2 = after(dot)
|
if rel.isDotAll() then lbl = Any() else lbl = Dot()
if dot.getLiteral().isDotAll() then lbl = Any() else lbl = Dot()
)
or
exists(RegExpCharacterClass cc |

View File

@@ -105,6 +105,13 @@ abstract class RegExpTerm extends Locatable, @regexpterm {
* it has an enclosing lookbehind assertions.
*/
predicate isInBackwardMatchingContext() { this = any(RegExpLookbehind lbh).getAChild+() }
/**
* Holds if this is the root term of a regular expression.
*/
predicate isRootTerm() {
not getParent() instanceof RegExpTerm
}
}
/**

View File

@@ -49,3 +49,4 @@
| tst.js:74:14:74:21 | (b\|a?b)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'b'. |
| tst.js:77:14:77:21 | (a\|aa?)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. |
| tst.js:83:14:83:20 | (.\|\\n)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\\n'. |
| tst.js:89:25:89:32 | (a\|aa?)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. |

View File

@@ -84,3 +84,9 @@ var bad16 = /(.|\n)*!/s;
// GOOD
var good8 = /([\w.]+)*/;
// NOT GOOD
var bad17 = new RegExp('(a|aa?)*b');
// GOOD - not used as regexp
var good9 = '(a|aa?)*b';