Skip to content

Commit 3bab8c6

Browse files
authored
Merge pull request #7173 from erik-krogh/getRubyInSync
JS/PY/RB: get ReDoSUtil in sync for ruby
2 parents 7dde52c + 87a1ccd commit 3bab8c6

File tree

9 files changed

+138
-187
lines changed

9 files changed

+138
-187
lines changed

config/identical-files.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -460,9 +460,10 @@
460460
"javascript/ql/lib/semmle/javascript/security/internal/SensitiveDataHeuristics.qll",
461461
"python/ql/lib/semmle/python/security/internal/SensitiveDataHeuristics.qll"
462462
],
463-
"ReDoS Util Python/JS": [
463+
"ReDoS Util Python/JS/Ruby": [
464464
"javascript/ql/lib/semmle/javascript/security/performance/ReDoSUtil.qll",
465-
"python/ql/lib/semmle/python/security/performance/ReDoSUtil.qll"
465+
"python/ql/lib/semmle/python/security/performance/ReDoSUtil.qll",
466+
"ruby/ql/lib/codeql/ruby/security/performance/ReDoSUtil.qll"
466467
],
467468
"ReDoS Exponential Python/JS": [
468469
"javascript/ql/lib/semmle/javascript/security/performance/ExponentialBackTracking.qll",

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

+31-25
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ private newtype TInputSymbol =
218218
recc instanceof RegExpCharacterClass and
219219
not recc.(RegExpCharacterClass).isUniversalClass()
220220
or
221-
recc instanceof RegExpCharacterClassEscape
221+
isEscapeClass(recc, _)
222222
)
223223
} or
224224
/** An input symbol representing all characters matched by `.`. */
@@ -340,13 +340,13 @@ private module CharacterClasses {
340340
char <= hi
341341
)
342342
or
343-
exists(RegExpCharacterClassEscape escape | escape = child |
344-
escape.getValue() = escape.getValue().toLowerCase() and
345-
classEscapeMatches(escape.getValue(), char)
343+
exists(string charClass | isEscapeClass(child, charClass) |
344+
charClass.toLowerCase() = charClass and
345+
classEscapeMatches(charClass, char)
346346
or
347347
char = getARelevantChar() and
348-
escape.getValue() = escape.getValue().toUpperCase() and
349-
not classEscapeMatches(escape.getValue().toLowerCase(), char)
348+
charClass.toUpperCase() = charClass and
349+
not classEscapeMatches(charClass, char)
350350
)
351351
)
352352
}
@@ -409,10 +409,10 @@ private module CharacterClasses {
409409
or
410410
child.(RegExpCharacterRange).isRange(_, result)
411411
or
412-
exists(RegExpCharacterClassEscape escape | child = escape |
413-
result = min(string s | classEscapeMatches(escape.getValue().toLowerCase(), s))
412+
exists(string charClass | isEscapeClass(child, charClass) |
413+
result = min(string s | classEscapeMatches(charClass.toLowerCase(), s))
414414
or
415-
result = max(string s | classEscapeMatches(escape.getValue().toLowerCase(), s))
415+
result = max(string s | classEscapeMatches(charClass.toLowerCase(), s))
416416
)
417417
)
418418
}
@@ -466,33 +466,36 @@ private module CharacterClasses {
466466
* An implementation of `CharacterClass` for \d, \s, and \w.
467467
*/
468468
private class PositiveCharacterClassEscape extends CharacterClass {
469-
RegExpCharacterClassEscape cc;
469+
RegExpTerm cc;
470+
string charClass;
470471

471472
PositiveCharacterClassEscape() {
472-
this = getCanonicalCharClass(cc) and cc.getValue() = ["d", "s", "w"]
473+
isEscapeClass(cc, charClass) and
474+
this = getCanonicalCharClass(cc) and
475+
charClass = ["d", "s", "w"]
473476
}
474477

475478
override string getARelevantChar() {
476-
cc.getValue() = "d" and
479+
charClass = "d" and
477480
result = ["0", "9"]
478481
or
479-
cc.getValue() = "s" and
482+
charClass = "s" and
480483
result = " "
481484
or
482-
cc.getValue() = "w" and
485+
charClass = "w" and
483486
result = ["a", "Z", "_", "0", "9"]
484487
}
485488

486-
override predicate matches(string char) { classEscapeMatches(cc.getValue(), char) }
489+
override predicate matches(string char) { classEscapeMatches(charClass, char) }
487490

488491
override string choose() {
489-
cc.getValue() = "d" and
492+
charClass = "d" and
490493
result = "9"
491494
or
492-
cc.getValue() = "s" and
495+
charClass = "s" and
493496
result = " "
494497
or
495-
cc.getValue() = "w" and
498+
charClass = "w" and
496499
result = "a"
497500
}
498501
}
@@ -501,26 +504,29 @@ private module CharacterClasses {
501504
* An implementation of `CharacterClass` for \D, \S, and \W.
502505
*/
503506
private class NegativeCharacterClassEscape extends CharacterClass {
504-
RegExpCharacterClassEscape cc;
507+
RegExpTerm cc;
508+
string charClass;
505509

506510
NegativeCharacterClassEscape() {
507-
this = getCanonicalCharClass(cc) and cc.getValue() = ["D", "S", "W"]
511+
isEscapeClass(cc, charClass) and
512+
this = getCanonicalCharClass(cc) and
513+
charClass = ["D", "S", "W"]
508514
}
509515

510516
override string getARelevantChar() {
511-
cc.getValue() = "D" and
517+
charClass = "D" and
512518
result = ["a", "Z", "!"]
513519
or
514-
cc.getValue() = "S" and
520+
charClass = "S" and
515521
result = ["a", "9", "!"]
516522
or
517-
cc.getValue() = "W" and
523+
charClass = "W" and
518524
result = [" ", "!"]
519525
}
520526

521527
bindingset[char]
522528
override predicate matches(string char) {
523-
not classEscapeMatches(cc.getValue().toLowerCase(), char)
529+
not classEscapeMatches(charClass.toLowerCase(), char)
524530
}
525531
}
526532
}
@@ -599,7 +605,7 @@ predicate delta(State q1, EdgeLabel lbl, State q2) {
599605
q2 = after(cc)
600606
)
601607
or
602-
exists(RegExpCharacterClassEscape cc |
608+
exists(RegExpTerm cc | isEscapeClass(cc, _) |
603609
q1 = before(cc) and
604610
lbl = CharClass(cc.getRawValue() + "|" + getCanonicalizationFlags(cc.getRootTerm())) and
605611
q2 = after(cc)

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

+8
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@
66

77
import javascript
88

9+
/**
10+
* Holds if `term` is an ecape class representing e.g. `\d`.
11+
* `clazz` is which character class it represents, e.g. "d" for `\d`.
12+
*/
13+
predicate isEscapeClass(RegExpTerm term, string clazz) {
14+
exists(RegExpCharacterClassEscape escape | term = escape | escape.getValue() = clazz)
15+
}
16+
917
/**
1018
* Holds if the regular expression should not be considered.
1119
*

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

+31-25
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ private newtype TInputSymbol =
218218
recc instanceof RegExpCharacterClass and
219219
not recc.(RegExpCharacterClass).isUniversalClass()
220220
or
221-
recc instanceof RegExpCharacterClassEscape
221+
isEscapeClass(recc, _)
222222
)
223223
} or
224224
/** An input symbol representing all characters matched by `.`. */
@@ -340,13 +340,13 @@ private module CharacterClasses {
340340
char <= hi
341341
)
342342
or
343-
exists(RegExpCharacterClassEscape escape | escape = child |
344-
escape.getValue() = escape.getValue().toLowerCase() and
345-
classEscapeMatches(escape.getValue(), char)
343+
exists(string charClass | isEscapeClass(child, charClass) |
344+
charClass.toLowerCase() = charClass and
345+
classEscapeMatches(charClass, char)
346346
or
347347
char = getARelevantChar() and
348-
escape.getValue() = escape.getValue().toUpperCase() and
349-
not classEscapeMatches(escape.getValue().toLowerCase(), char)
348+
charClass.toUpperCase() = charClass and
349+
not classEscapeMatches(charClass, char)
350350
)
351351
)
352352
}
@@ -409,10 +409,10 @@ private module CharacterClasses {
409409
or
410410
child.(RegExpCharacterRange).isRange(_, result)
411411
or
412-
exists(RegExpCharacterClassEscape escape | child = escape |
413-
result = min(string s | classEscapeMatches(escape.getValue().toLowerCase(), s))
412+
exists(string charClass | isEscapeClass(child, charClass) |
413+
result = min(string s | classEscapeMatches(charClass.toLowerCase(), s))
414414
or
415-
result = max(string s | classEscapeMatches(escape.getValue().toLowerCase(), s))
415+
result = max(string s | classEscapeMatches(charClass.toLowerCase(), s))
416416
)
417417
)
418418
}
@@ -466,33 +466,36 @@ private module CharacterClasses {
466466
* An implementation of `CharacterClass` for \d, \s, and \w.
467467
*/
468468
private class PositiveCharacterClassEscape extends CharacterClass {
469-
RegExpCharacterClassEscape cc;
469+
RegExpTerm cc;
470+
string charClass;
470471

471472
PositiveCharacterClassEscape() {
472-
this = getCanonicalCharClass(cc) and cc.getValue() = ["d", "s", "w"]
473+
isEscapeClass(cc, charClass) and
474+
this = getCanonicalCharClass(cc) and
475+
charClass = ["d", "s", "w"]
473476
}
474477

475478
override string getARelevantChar() {
476-
cc.getValue() = "d" and
479+
charClass = "d" and
477480
result = ["0", "9"]
478481
or
479-
cc.getValue() = "s" and
482+
charClass = "s" and
480483
result = " "
481484
or
482-
cc.getValue() = "w" and
485+
charClass = "w" and
483486
result = ["a", "Z", "_", "0", "9"]
484487
}
485488

486-
override predicate matches(string char) { classEscapeMatches(cc.getValue(), char) }
489+
override predicate matches(string char) { classEscapeMatches(charClass, char) }
487490

488491
override string choose() {
489-
cc.getValue() = "d" and
492+
charClass = "d" and
490493
result = "9"
491494
or
492-
cc.getValue() = "s" and
495+
charClass = "s" and
493496
result = " "
494497
or
495-
cc.getValue() = "w" and
498+
charClass = "w" and
496499
result = "a"
497500
}
498501
}
@@ -501,26 +504,29 @@ private module CharacterClasses {
501504
* An implementation of `CharacterClass` for \D, \S, and \W.
502505
*/
503506
private class NegativeCharacterClassEscape extends CharacterClass {
504-
RegExpCharacterClassEscape cc;
507+
RegExpTerm cc;
508+
string charClass;
505509

506510
NegativeCharacterClassEscape() {
507-
this = getCanonicalCharClass(cc) and cc.getValue() = ["D", "S", "W"]
511+
isEscapeClass(cc, charClass) and
512+
this = getCanonicalCharClass(cc) and
513+
charClass = ["D", "S", "W"]
508514
}
509515

510516
override string getARelevantChar() {
511-
cc.getValue() = "D" and
517+
charClass = "D" and
512518
result = ["a", "Z", "!"]
513519
or
514-
cc.getValue() = "S" and
520+
charClass = "S" and
515521
result = ["a", "9", "!"]
516522
or
517-
cc.getValue() = "W" and
523+
charClass = "W" and
518524
result = [" ", "!"]
519525
}
520526

521527
bindingset[char]
522528
override predicate matches(string char) {
523-
not classEscapeMatches(cc.getValue().toLowerCase(), char)
529+
not classEscapeMatches(charClass.toLowerCase(), char)
524530
}
525531
}
526532
}
@@ -599,7 +605,7 @@ predicate delta(State q1, EdgeLabel lbl, State q2) {
599605
q2 = after(cc)
600606
)
601607
or
602-
exists(RegExpCharacterClassEscape cc |
608+
exists(RegExpTerm cc | isEscapeClass(cc, _) |
603609
q1 = before(cc) and
604610
lbl = CharClass(cc.getRawValue() + "|" + getCanonicalizationFlags(cc.getRootTerm())) and
605611
q2 = after(cc)

python/ql/lib/semmle/python/security/performance/RegExpTreeView.qll

+8
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@
55
import python
66
import semmle.python.RegexTreeView
77

8+
/**
9+
* Holds if `term` is an ecape class representing e.g. `\d`.
10+
* `clazz` is which character class it represents, e.g. "d" for `\d`.
11+
*/
12+
predicate isEscapeClass(RegExpTerm term, string clazz) {
13+
exists(RegExpCharacterClassEscape escape | term = escape | escape.getValue() = clazz)
14+
}
15+
816
/**
917
* Holds if the regular expression should not be considered.
1018
*

0 commit comments

Comments
 (0)