Skip to content

Commit acaf294

Browse files
committed
support a limited number of regexp ranges
1 parent 4ee6533 commit acaf294

File tree

7 files changed

+187
-23
lines changed

7 files changed

+187
-23
lines changed

javascript/ql/lib/semmle/javascript/security/performance/ReDoSUtil.qll

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ predicate matchesEpsilon(RegExpTerm t) {
9898
matchesEpsilon(t.(RegExpBackRef).getGroup())
9999
or
100100
forex(RegExpTerm child | child = t.(RegExpSequence).getAChild() | matchesEpsilon(child))
101+
or
102+
t.(RegExpRange).getLowerBound() = 0
101103
}
102104

103105
/**
@@ -539,6 +541,49 @@ private class EdgeLabel extends TInputSymbol {
539541
}
540542
}
541543

544+
/**
545+
* A RegExp term that acts like a plus.
546+
* Either it's a RegExpPlus, or it is a range {1,X} where X is >= 30.
547+
* 30 has been chosen as a threshold because for exponential blowup 2^30 is enough to get a decent DOS attack.
548+
*/
549+
private class EffectivelyPlus extends RegExpTerm {
550+
EffectivelyPlus() {
551+
this instanceof RegExpPlus
552+
or
553+
exists(RegExpRange range | range.getLowerBound() = 1 and range.getUpperBound() >= 30 |
554+
this = range
555+
)
556+
}
557+
}
558+
559+
/**
560+
* A RegExp term that acts like a star.
561+
* Either it's a RegExpStar, or it is a range {0,X} where X is >= 30.
562+
*/
563+
private class EffectivelyStar extends RegExpTerm {
564+
EffectivelyStar() {
565+
this instanceof RegExpStar
566+
or
567+
exists(RegExpRange range | range.getLowerBound() = 0 and range.getUpperBound() >= 30 |
568+
this = range
569+
)
570+
}
571+
}
572+
573+
/**
574+
* A RegExp term that acts like a question mark.
575+
* Either it's a RegExpQuestion, or it is a range {0,1}.
576+
*/
577+
private class EffectivelyQuestion extends RegExpTerm {
578+
EffectivelyQuestion() {
579+
this instanceof RegExpOpt
580+
or
581+
exists(RegExpRange range | range.getLowerBound() = 0 and range.getUpperBound() = 1 |
582+
this = range
583+
)
584+
}
585+
}
586+
542587
/**
543588
* Gets the state before matching `t`.
544589
*/
@@ -559,14 +604,14 @@ State after(RegExpTerm t) {
559604
or
560605
exists(RegExpGroup grp | t = grp.getAChild() | result = after(grp))
561606
or
562-
exists(RegExpStar star | t = star.getAChild() | result = before(star))
607+
exists(EffectivelyStar star | t = star.getAChild() | result = before(star))
563608
or
564-
exists(RegExpPlus plus | t = plus.getAChild() |
609+
exists(EffectivelyPlus plus | t = plus.getAChild() |
565610
result = before(plus) or
566611
result = after(plus)
567612
)
568613
or
569-
exists(RegExpOpt opt | t = opt.getAChild() | result = after(opt))
614+
exists(EffectivelyQuestion opt | t = opt.getAChild() | result = after(opt))
570615
or
571616
exists(RegExpRoot root | t = root | result = AcceptAnySuffix(root))
572617
}
@@ -617,15 +662,17 @@ predicate delta(State q1, EdgeLabel lbl, State q2) {
617662
or
618663
exists(RegExpGroup grp | lbl = Epsilon() | q1 = before(grp) and q2 = before(grp.getChild(0)))
619664
or
620-
exists(RegExpStar star | lbl = Epsilon() |
665+
exists(EffectivelyStar star | lbl = Epsilon() |
621666
q1 = before(star) and q2 = before(star.getChild(0))
622667
or
623668
q1 = before(star) and q2 = after(star)
624669
)
625670
or
626-
exists(RegExpPlus plus | lbl = Epsilon() | q1 = before(plus) and q2 = before(plus.getChild(0)))
671+
exists(EffectivelyPlus plus | lbl = Epsilon() |
672+
q1 = before(plus) and q2 = before(plus.getChild(0))
673+
)
627674
or
628-
exists(RegExpOpt opt | lbl = Epsilon() |
675+
exists(EffectivelyQuestion opt | lbl = Epsilon() |
629676
q1 = before(opt) and q2 = before(opt.getChild(0))
630677
or
631678
q1 = before(opt) and q2 = after(opt)

javascript/ql/test/query-tests/Performance/ReDoS/PolynomialBackTracking.expected

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@
201201
| regexplib/markup.js:13:14:13:16 | .+? | Strings starting with '<' and with many repetitions of '!' can start matching anywhere after the start of the preceeding .*? |
202202
| regexplib/markup.js:14:13:14:14 | .* | Strings starting with '<' and with many repetitions of 'a' can start matching anywhere after the start of the preceeding .* |
203203
| regexplib/markup.js:14:24:14:25 | .* | Strings starting with '<>' and with many repetitions of '>a' can start matching anywhere after the start of the preceeding .* |
204+
| regexplib/markup.js:15:16:15:18 | .*? | Strings starting with '<img' and with many repetitions of '<imga' can start matching anywhere after the start of the preceeding <(\\/{0,1})img(.*?)(\\/{0,1})\\> |
204205
| regexplib/markup.js:16:5:16:9 | [^>]* | Strings starting with 'src' and with many repetitions of 'src' can start matching anywhere after the start of the preceeding src[^>]*[^/].(?:jpg\|bmp\|gif)(?:\\"\|\\') |
205206
| regexplib/markup.js:17:8:17:24 | (\\s(\\w*=".*?")?)* | Strings starting with '<a' and with many repetitions of ' =""' can start matching anywhere after the start of the preceeding .*? |
206207
| regexplib/markup.js:17:12:17:14 | \\w* | Strings starting with '<a ' and with many repetitions of '="" a' can start matching anywhere after the start of the preceeding .*? |
@@ -213,6 +214,10 @@
213214
| regexplib/markup.js:20:197:20:198 | "+ | Strings with many repetitions of '""' can start matching anywhere after the start of the preceeding "+ |
214215
| regexplib/markup.js:20:245:20:247 | .*? | Strings with many repetitions of 'color: # IF found THEN move ahead "" # single or double # or no quotes\\t' can start matching anywhere after the start of the preceeding .*? |
215216
| regexplib/markup.js:20:274:20:276 | .*? | Strings starting with '<font # Match start of Font Tag ' and with many repetitions of '<font # Match start of Font Tag a' can start matching anywhere after the start of the preceeding <\\*?font # Match start of Font Tag (?(?=[^>]+color.*>) #IF\\/THEN lookahead color in tag (.*?color\\s*?[=\|:]\\s*?) # IF found THEN move ahead ('+\\#*?[\\w\\s]*'+ # CAPTURE ColorName\\/Hex \|"+\\#*?[\\w\\s]*"+ # single or double \|\\#*\\w*\\b) # or no quotes\t.*?> # & move to end of tag \|.*?> # ELSE move to end of Tag ) # Close the If\\/Then lookahead # Use Multiline and IgnoreCase # Replace the matches from RE with MatchEvaluator below: # if m.Groups(1).Value<>"" then # Return "<font color=" & m.Groups(1).Value & ">" # else # Return "<font>" # end if |
217+
| regexplib/markup.js:24:39:24:41 | \\s+ | Strings starting with '&lt;A' and with many repetitions of ' - != ' can start matching anywhere after the start of the preceeding \\s* |
218+
| regexplib/markup.js:24:43:24:45 | \\S+ | Strings starting with '&lt;A ' and with many repetitions of '- !=' can start matching anywhere after the start of the preceeding \\s* |
219+
| regexplib/markup.js:24:48:24:50 | \\s* | Strings starting with '&lt;A !' and with many repetitions of ' =- ! ' can start matching anywhere after the start of the preceeding \\s+ |
220+
| regexplib/markup.js:24:52:24:54 | \\s* | Strings starting with '&lt;A !=' and with many repetitions of '- !=' can start matching anywhere after the start of the preceeding \\S+ |
216221
| regexplib/markup.js:25:11:25:15 | [^>]* | Strings starting with '<A' and with many repetitions of '<A' can start matching anywhere after the start of the preceeding <[a-zA-Z][^>]*\\son\\w+=(\\w+\|'[^']*'\|"[^"]*")[^>]*> |
217222
| regexplib/markup.js:25:45:25:49 | [^>]* | Strings starting with '<A ona=a' and with many repetitions of '0' can start matching anywhere after the start of the preceeding \\w+ |
218223
| regexplib/markup.js:27:3:27:7 | [^>]* | Strings starting with '<' and with many repetitions of '<' can start matching anywhere after the start of the preceeding <[^>]*name[\\s]*=[\\s]*"?[^\\w_]*"?[^>]*> |
@@ -228,6 +233,10 @@
228233
| regexplib/markup.js:44:3:44:7 | [^>]* | Strings starting with '<' and with many repetitions of '<' can start matching anywhere after the start of the preceeding <[^>]*name[\\s]*=[\\s]*"?[^\\w_]*"?[^>]*> |
229234
| regexplib/markup.js:44:34:44:38 | [^>]* | Strings starting with '<name=' and with many repetitions of '\\t' can start matching anywhere after the start of the preceeding [\\s]* |
230235
| regexplib/markup.js:45:6:45:13 | [\\d\\D]*? | Strings starting with '/*' and with many repetitions of 'a/*' can start matching anywhere after the start of the preceeding \\/\\*[\\d\\D]*?\\*\\/ |
236+
| regexplib/markup.js:47:39:47:41 | \\s+ | Strings starting with '&lt;A' and with many repetitions of ' - != ' can start matching anywhere after the start of the preceeding \\s* |
237+
| regexplib/markup.js:47:43:47:45 | \\S+ | Strings starting with '&lt;A ' and with many repetitions of '- !=' can start matching anywhere after the start of the preceeding \\s* |
238+
| regexplib/markup.js:47:48:47:50 | \\s* | Strings starting with '&lt;A !' and with many repetitions of ' =- ! ' can start matching anywhere after the start of the preceeding \\s+ |
239+
| regexplib/markup.js:47:52:47:54 | \\s* | Strings starting with '&lt;A !=' and with many repetitions of '- !=' can start matching anywhere after the start of the preceeding \\S+ |
231240
| regexplib/markup.js:48:6:48:13 | [\\s\\S]*? | Strings starting with '<!--' and with many repetitions of '<!--' can start matching anywhere after the start of the preceeding <!--[\\s\\S]*?--> |
232241
| regexplib/markup.js:53:15:53:19 | [\\w]* | Strings starting with '[a' and with many repetitions of '0' can start matching anywhere after the start of the preceeding \\w+ |
233242
| regexplib/markup.js:56:23:56:25 | \\w+ | Strings with many repetitions of 'a' can start matching anywhere after the start of the preceeding (\\/?(?<step>\\w+))+ |
@@ -300,6 +309,7 @@
300309
| regexplib/strings.js:14:61:14:63 | \\w* | Strings starting with 'AA' and with many repetitions of 'A' can start matching anywhere after the start of the preceeding \\w* |
301310
| regexplib/strings.js:14:107:14:109 | \\w* | Strings starting with 'AAA' and with many repetitions of 'A' can start matching anywhere after the start of the preceeding \\w* |
302311
| regexplib/strings.js:19:31:19:57 | [a-z&#230;&#248;&#229;0-9]+ | Strings starting with '#@' and with many repetitions of '##' can start matching anywhere after the start of the preceeding [a-z&#230;&#248;&#229;0-9]+ |
312+
| regexplib/strings.js:19:69:19:95 | [a-z&#230;&#248;&#229;0-9]+ | Strings starting with '#@#' and with many repetitions of '##' can start matching anywhere after the start of the preceeding [a-z&#230;&#248;&#229;0-9]+ |
303313
| regexplib/strings.js:20:3:20:20 | ((\\\\")\|[^"(\\\\")])+ | Strings starting with '"' and with many repetitions of '\\\\"' can start matching anywhere after the start of the preceeding "((\\\\")\|[^"(\\\\")])+" |
304314
| regexplib/strings.js:21:3:21:7 | [^>]+ | Strings starting with '<' and with many repetitions of '<' can start matching anywhere after the start of the preceeding <[^>]+> |
305315
| regexplib/strings.js:23:3:23:20 | ((\\\\")\|[^"(\\\\")])+ | Strings starting with '"' and with many repetitions of '\\\\"' can start matching anywhere after the start of the preceeding "((\\\\")\|[^"(\\\\")])+" |
@@ -314,6 +324,7 @@
314324
| regexplib/strings.js:48:3:48:12 | [^\\.\\?\\!]* | Strings with many repetitions of ' ' can start matching anywhere after the start of the preceeding ([^\\.\\?\\!]*)[\\.\\?\\!] |
315325
| regexplib/strings.js:49:3:49:5 | \\S+ | Strings with many repetitions of '!' can start matching anywhere after the start of the preceeding (\\S+)\\x20{2,}(?=\\S+) |
316326
| regexplib/strings.js:53:25:53:33 | [a-z0-9]+ | Strings with many repetitions of '0' can start matching anywhere after the start of the preceeding [a-z0-9]+ |
327+
| regexplib/strings.js:53:44:53:52 | [a-z0-9]+ | Strings with many repetitions of '00' can start matching anywhere after the start of the preceeding [a-z0-9]+ |
317328
| regexplib/strings.js:53:65:53:73 | [a-z0-9]+ | Strings with many repetitions of '0' can start matching anywhere after the start of the preceeding [a-z0-9]+ |
318329
| regexplib/strings.js:54:20:54:22 | \\w+ | Strings with many repetitions of 'a' can start matching anywhere after the start of the preceeding (NOT)?(\\s*\\(*)\\s*(\\w+)\\s*(=\|<>\|<\|>\|LIKE\|IN)\\s*(\\(([^\\)]*)\\)\|'([^']*)'\|(-?\\d*\\.?\\d+))(\\s*\\)*\\s*)(AND\|OR)? |
319330
| regexplib/strings.js:56:52:56:53 | .+ | Strings starting with 'PRN.' and with many repetitions of '.' can start matching anywhere after the start of the preceeding .* |
@@ -519,3 +530,5 @@
519530
| tst.js:399:6:399:12 | (d\|dd)* | Strings with many repetitions of 'd' can start matching anywhere after the start of the preceeding ((c\|cc)*\|(d\|dd)*\|(e\|ee)*)f$ |
520531
| tst.js:400:6:401:1 | (e\|ee)* | Strings with many repetitions of 'e' can start matching anywhere after the start of the preceeding ((c\|cc)*\|(d\|dd)*\|(e\|ee)*)f$ |
521532
| tst.js:404:6:405:7 | (g\|gg)* | Strings with many repetitions of 'g' can start matching anywhere after the start of the preceeding (g\|gg)*h$ |
533+
| tst.js:407:128:407:129 | * | Strings starting with '0/*' and with many repetitions of ' ' can start matching anywhere after the start of the preceeding \\s* |
534+
| tst.js:409:23:409:29 | [\\w.-]* | Strings starting with '//' and with many repetitions of '//' can start matching anywhere after the start of the preceeding (\\/(?:\\/[\\w.-]*)*){0,1}:([\\w.-]+) |

javascript/ql/test/query-tests/Performance/ReDoS/ReDoS.expected

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,14 @@
2929
| regexplib/email.js:5:24:5:35 | [a-zA-Z0-9]+ | This part of the regular expression may cause exponential backtracking on strings starting with '0' and containing many repetitions of '0'. |
3030
| regexplib/email.js:5:63:5:74 | [a-zA-Z0-9]+ | This part of the regular expression may cause exponential backtracking on strings starting with '0@0' and containing many repetitions of '0'. |
3131
| regexplib/email.js:6:10:6:35 | (?:[a-zA-Z0-9][\\.\\-\\+_]?)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '0'. |
32+
| regexplib/email.js:6:60:6:88 | (?:[a-zA-Z0-9][\\.\\-_]?){0,62} | This part of the regular expression may cause exponential backtracking on strings starting with '0@' and containing many repetitions of '0'. |
3233
| regexplib/email.js:13:36:13:44 | [a-zA-Z]* | This part of the regular expression may cause exponential backtracking on strings starting with 'A' and containing many repetitions of 'A'. |
3334
| regexplib/email.js:25:67:25:78 | [a-zA-Z0-9]+ | This part of the regular expression may cause exponential backtracking on strings starting with '0' and containing many repetitions of '0'. |
3435
| regexplib/email.js:25:106:25:117 | [a-zA-Z0-9]+ | This part of the regular expression may cause exponential backtracking on strings starting with '0@0' and containing many repetitions of '0'. |
3536
| regexplib/email.js:25:212:25:223 | [a-zA-Z0-9]+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '0'. |
3637
| regexplib/email.js:25:251:25:262 | [a-zA-Z0-9]+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '0'. |
3738
| regexplib/email.js:32:10:32:25 | (?:\\w[\\.\\-\\+]?)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. |
39+
| regexplib/email.js:32:41:32:61 | (?:\\w[\\.\\-\\+]?){0,62} | This part of the regular expression may cause exponential backtracking on strings starting with 'a@' and containing many repetitions of 'a'. |
3840
| regexplib/email.js:33:16:33:22 | [-.\\w]* | This part of the regular expression may cause exponential backtracking on strings starting with '0' and containing many repetitions of '0'. |
3941
| regexplib/email.js:33:38:33:51 | ([0-9a-zA-Z])+ | This part of the regular expression may cause exponential backtracking on strings starting with '0@' and containing many repetitions of '00.'. |
4042
| regexplib/email.js:33:53:33:58 | [-\\w]* | This part of the regular expression may cause exponential backtracking on strings starting with '0@0' and containing many repetitions of '0'. |
@@ -45,9 +47,13 @@
4547
| regexplib/markup.js:13:6:13:12 | [^"']+? | This part of the regular expression may cause exponential backtracking on strings starting with '<' and containing many repetitions of '!'. |
4648
| regexplib/markup.js:13:14:13:16 | .+? | This part of the regular expression may cause exponential backtracking on strings starting with '<' and containing many repetitions of 'a"'. |
4749
| regexplib/markup.js:17:17:17:19 | .*? | This part of the regular expression may cause exponential backtracking on strings starting with '<a ="' and containing many repetitions of '" ="'. |
50+
| regexplib/markup.js:24:43:24:45 | \\S+ | This part of the regular expression may cause exponential backtracking on strings starting with '&lt;A ' and containing many repetitions of '!=- '. |
51+
| regexplib/markup.js:24:47:24:118 | (\\s*=\\s*([-\\w\\.]{1,1024}\|&quot;[^&quot;]{0,1024}&quot;\|'[^']{0,1024}'))? | This part of the regular expression may cause exponential backtracking on strings starting with '&lt;A !' and containing many repetitions of ' =- !'. |
4852
| regexplib/markup.js:37:29:37:56 | [a-zA-Z0-9\|:\|\\/\|=\|-\|.\|\\?\|&]* | This part of the regular expression may cause exponential backtracking on strings starting with '[a=' and containing many repetitions of '='. |
4953
| regexplib/markup.js:40:23:40:25 | \\w+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. |
5054
| regexplib/markup.js:40:132:40:134 | \\s* | This part of the regular expression may cause exponential backtracking on strings starting with 'a[@a=''' and containing many repetitions of ' @a<""'. |
55+
| regexplib/markup.js:47:43:47:45 | \\S+ | This part of the regular expression may cause exponential backtracking on strings starting with '&lt;A ' and containing many repetitions of '!=- '. |
56+
| regexplib/markup.js:47:47:47:118 | (\\s*=\\s*([-\\w\\.]{1,1024}\|&quot;[^&quot;]{0,1024}&quot;\|'[^']{0,1024}'))? | This part of the regular expression may cause exponential backtracking on strings starting with '&lt;A !' and containing many repetitions of ' =- !'. |
5157
| regexplib/markup.js:53:29:53:56 | [a-zA-Z0-9\|:\|\\/\|=\|-\|.\|\\?\|&]* | This part of the regular expression may cause exponential backtracking on strings starting with '[a=' and containing many repetitions of '='. |
5258
| regexplib/markup.js:56:23:56:25 | \\w+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. |
5359
| regexplib/markup.js:56:132:56:134 | \\s* | This part of the regular expression may cause exponential backtracking on strings starting with 'a[@a=''' and containing many repetitions of ' @a<""'. |
@@ -62,6 +68,7 @@
6268
| regexplib/misc.js:148:23:148:29 | [^"'=]+ | This part of the regular expression may cause exponential backtracking on strings starting with '<! ' and containing many repetitions of '! '. |
6369
| regexplib/misc.js:173:4:173:11 | ([a-z])+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'aa'. |
6470
| regexplib/strings.js:19:31:19:57 | [a-z&#230;&#248;&#229;0-9]+ | This part of the regular expression may cause exponential backtracking on strings starting with '#@' and containing many repetitions of '#'. |
71+
| regexplib/strings.js:19:69:19:95 | [a-z&#230;&#248;&#229;0-9]+ | This part of the regular expression may cause exponential backtracking on strings starting with '#@#' and containing many repetitions of '##'. |
6572
| regexplib/strings.js:47:3:47:5 | \\S* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '!'. |
6673
| regexplib/strings.js:57:17:57:19 | \\d+ | This part of the regular expression may cause exponential backtracking on strings starting with '?se[' and containing many repetitions of '9'. |
6774
| regexplib/strings.js:81:17:81:19 | \\d+ | This part of the regular expression may cause exponential backtracking on strings starting with '?se[' and containing many repetitions of '9'. |
@@ -188,3 +195,4 @@
188195
| tst.js:399:6:399:12 | (d\|dd)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'dd'. |
189196
| tst.js:400:6:401:1 | (e\|ee)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'ee'. |
190197
| tst.js:404:6:405:7 | (g\|gg)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'gg'. |
198+
| tst.js:407:125:407:127 | \\s* | This part of the regular expression may cause exponential backtracking on strings starting with '0/*' and containing many repetitions of ' ;0'. |

0 commit comments

Comments
 (0)