Skip to content

Commit 28cffa1

Browse files
committed
add comment in isFork about /(a*)*/ regular expressions
1 parent c58f67b commit 28cffa1

File tree

1 file changed

+11
-0
lines changed

1 file changed

+11
-0
lines changed

javascript/ql/src/Performance/ReDoS.ql

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,17 @@ predicate isFork(State q, InputSymbol s1, InputSymbol s2, State r1, State r2) {
187187
or
188188
r1 = r2 and q1 != q2
189189
or
190+
// If q can reach itself by epsilon transitions, then there are two distinct paths to the q1/q2 state:
191+
// one that uses the loop and one that doesn't. The engine will separately attempt to match with each path,
192+
// despite ending in the same state. The "fork" thus arises from the choice of whether to use the loop or not.
193+
// To avoid every state in the loop becoming a fork state,
194+
// we arbitrarily pick the InfiniteRepetitionQuantifier state as the canonical fork state for the loop
195+
// (every epsilon-loop must contain such a state).
196+
//
197+
// We additionally require that the there exists another InfiniteRepetitionQuantifier `mid` on the path from `q` to itself.
198+
// This is done to avoid flagging regular expressions such as `/(a?)*b/` - that only has polynomial runtime.
199+
// The below code is therefore a heuritic, that only flags regular expressions such as `/(a*)*b/`,
200+
// and does not flag regular expressions such as `/(a?b?)c/`, but the latter pattern is not used frequently.
190201
r1 = r2 and
191202
q1 = q2 and
192203
epsilonSucc+(q) = q and

0 commit comments

Comments
 (0)