Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit bb56264

Browse files
Support possessive quantifiers, which cannot backtrack.
They are approximated by limiting them to up to one repetition (effectively making *+ like ? and ++ like a no-op).
1 parent 49374b8 commit bb56264

4 files changed

Lines changed: 32 additions & 6 deletions

File tree

java/ql/lib/semmle/code/java/regex/RegexTreeView.qll

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,12 @@ class RegExpQuantifier extends RegExpTerm, TRegExpQuantifier {
253253
predicate mayRepeatForever() { may_repeat_forever = true }
254254

255255
/** Gets the quantifier for this term. That is e.g "?" for "a?". */
256-
string getquantifier() { result = re.getText().substring(part_end, end) }
256+
string getQuantifier() { result = re.getText().substring(part_end, end) }
257+
258+
/** Holds if this is a possessive quantifier, e.g. a*+. */
259+
predicate isPossessive() {
260+
exists(string q | q = this.getQuantifier() | q.length() > 1 and q.charAt(q.length() - 1) = "+")
261+
}
257262

258263
override string getPrimaryQLClass() { result = "RegExpQuantifier" }
259264
}
@@ -275,7 +280,7 @@ class InfiniteRepetitionQuantifier extends RegExpQuantifier {
275280
* ```
276281
*/
277282
class RegExpStar extends InfiniteRepetitionQuantifier {
278-
RegExpStar() { this.getquantifier().charAt(0) = "*" }
283+
RegExpStar() { this.getQuantifier().charAt(0) = "*" }
279284

280285
override string getPrimaryQLClass() { result = "RegExpStar" }
281286
}
@@ -290,7 +295,7 @@ class RegExpStar extends InfiniteRepetitionQuantifier {
290295
* ```
291296
*/
292297
class RegExpPlus extends InfiniteRepetitionQuantifier {
293-
RegExpPlus() { this.getquantifier().charAt(0) = "+" }
298+
RegExpPlus() { this.getQuantifier().charAt(0) = "+" }
294299

295300
override string getPrimaryQLClass() { result = "RegExpPlus" }
296301
}
@@ -305,7 +310,7 @@ class RegExpPlus extends InfiniteRepetitionQuantifier {
305310
* ```
306311
*/
307312
class RegExpOpt extends RegExpQuantifier {
308-
RegExpOpt() { this.getquantifier().charAt(0) = "?" }
313+
RegExpOpt() { this.getQuantifier().charAt(0) = "?" }
309314

310315
override string getPrimaryQLClass() { result = "RegExpOpt" }
311316
}

java/ql/lib/semmle/code/java/security/performance/ReDoSUtil.qll

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -608,10 +608,15 @@ State after(RegExpTerm t) {
608608
or
609609
exists(RegExpGroup grp | t = grp.getAChild() | result = after(grp))
610610
or
611-
exists(EffectivelyStar star | t = star.getAChild() | result = before(star))
611+
exists(EffectivelyStar star | t = star.getAChild() |
612+
not isPossessive(star) and
613+
result = before(star)
614+
)
612615
or
613616
exists(EffectivelyPlus plus | t = plus.getAChild() |
614-
result = before(plus) or
617+
not isPossessive(plus) and
618+
result = before(plus)
619+
or
615620
result = after(plus)
616621
)
617622
or

java/ql/lib/semmle/code/java/security/performance/RegExpTreeView.qll

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ predicate isEscapeClass(RegExpTerm term, string clazz) {
1616
term.(RegExpNamedProperty).getBackslashEquivalent() = clazz
1717
}
1818

19+
/**
20+
* Holds if `term` is a possessive quantifier, e.g. `a*+`.
21+
*/
22+
predicate isPossessive(RegExpQuantifier term) { term.isPossessive() }
23+
1924
/**
2025
* Holds if the regular expression should not be considered.
2126
*

java/ql/test/query-tests/security/CWE-730/ExpRedosTest.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,17 @@ class ExpRedosTest {
418418
"\\A(\\d|0)*x", // $ hasExpRedos
419419
"(\\d|0)*\\Z", // $ hasExpRedos
420420
"\\b(\\d|0)*x", // $ hasExpRedos
421+
422+
// GOOD - possessive quantifiers don't backtrack
423+
"(a*+)*+b",
424+
"(a*)*+b",
425+
"(a*+)*b",
426+
427+
// BAD
428+
"(a*)*b", // $ hasExpRedos
429+
430+
// BAD - but not detected due to the way possessive quantifiers are approximated
431+
"((aa|a*+)b)*c" // $ MISSING: hasExpRedos
421432
};
422433

423434
void test() {

0 commit comments

Comments
 (0)