Skip to content

Commit c21d095

Browse files
committed
JS: Restrict RegExp queries to actual regular expressions
1 parent b8711fc commit c21d095

12 files changed

Lines changed: 83 additions & 40 deletions

File tree

javascript/ql/src/Performance/ReDoS.ql

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,11 +108,7 @@ class RegExpRoot extends RegExpTerm {
108108
// there are no lookbehinds
109109
not exists(RegExpLookbehind lbh | getRoot(lbh) = this) and
110110
// is actually used as a RegExp
111-
(
112-
parent instanceof RegExpLiteral
113-
or
114-
parent.(StringLiteral).flow() instanceof RegExpPatternSource
115-
)
111+
isUsedAsRegExp()
116112
}
117113
}
118114

javascript/ql/src/RegExp/BackrefBeforeGroup.ql

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,6 @@ import javascript
1616
from RegExpBackRef rebr
1717
where
1818
rebr.getLocation().getStartColumn() < rebr.getGroup().getLocation().getEndColumn() and
19-
not rebr.isInBackwardMatchingContext()
19+
not rebr.isInBackwardMatchingContext() and
20+
rebr.isPartOfRegExpLiteral()
2021
select rebr, "This back reference precedes its capture group."

javascript/ql/src/RegExp/BackrefIntoNegativeLookahead.ql

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ from RegExpNegativeLookahead neg, RegExpGroup grp, RegExpBackRef back
1717
where
1818
grp.getParent+() = neg and
1919
grp = back.getGroup() and
20-
not back.getParent+() = neg
20+
not back.getParent+() = neg and
21+
neg.isPartOfRegExpLiteral()
2122
select back,
2223
"This back reference always matches the empty string, since it refers to $@, which is contained in $@.",
2324
grp, "this capture group", neg, "a negative lookahead assertion"

javascript/ql/src/RegExp/BackspaceEscape.ql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,5 @@ import javascript
1515

1616
from RegExpCharEscape rece
1717
where rece.toString() = "\\b"
18+
and rece.isPartOfRegExpLiteral()
1819
select rece, "Backspace escape in regular expression."

javascript/ql/src/RegExp/DuplicateCharacterInCharacterClass.ql

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ from RegExpCharacterClass recc, RegExpConstant first, RegExpConstant repeat, int
2929
where
3030
constantInCharacterClass(recc, 1, first, val) and
3131
constantInCharacterClass(recc, rnk, repeat, val) and
32-
rnk > 1
32+
rnk > 1 and
33+
recc.isPartOfRegExpLiteral()
3334
select first, "Character '" + first + "' is repeated $@ in the same character class.", repeat,
3435
"here"

javascript/ql/src/RegExp/EmptyCharacterClass.ql

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,6 @@ import javascript
1515
from RegExpCharacterClass recc
1616
where
1717
not exists(recc.getAChild()) and
18-
not recc.isInverted()
18+
not recc.isInverted() and
19+
recc.isPartOfRegExpLiteral()
1920
select recc, "Empty character class."

javascript/ql/src/RegExp/IdentityReplacement.ql

Lines changed: 32 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -31,36 +31,39 @@ predicate matchesString(Expr e, string s) {
3131
*/
3232
language[monotonicAggregates]
3333
predicate regExpMatchesString(RegExpTerm t, string s) {
34-
// constants match themselves
35-
s = t.(RegExpConstant).getValue()
36-
or
37-
// assertions match the empty string
34+
t.isPartOfRegExpLiteral() and
3835
(
39-
t instanceof RegExpCaret or
40-
t instanceof RegExpDollar or
41-
t instanceof RegExpWordBoundary or
42-
t instanceof RegExpNonWordBoundary or
43-
t instanceof RegExpLookahead or
44-
t instanceof RegExpLookbehind
45-
) and
46-
s = ""
47-
or
48-
// groups match their content
49-
regExpMatchesString(t.(RegExpGroup).getAChild(), s)
50-
or
51-
// single-character classes match that character
52-
exists(RegExpCharacterClass recc | recc = t and not recc.isInverted() |
53-
recc.getNumChild() = 1 and
54-
regExpMatchesString(recc.getChild(0), s)
55-
)
56-
or
57-
// sequences match the concatenation of their elements
58-
exists(RegExpSequence seq | seq = t |
59-
s = concat(int i, RegExpTerm child |
60-
child = seq.getChild(i)
61-
|
62-
any(string subs | regExpMatchesString(child, subs)) order by i
63-
)
36+
// constants match themselves
37+
s = t.(RegExpConstant).getValue()
38+
or
39+
// assertions match the empty string
40+
(
41+
t instanceof RegExpCaret or
42+
t instanceof RegExpDollar or
43+
t instanceof RegExpWordBoundary or
44+
t instanceof RegExpNonWordBoundary or
45+
t instanceof RegExpLookahead or
46+
t instanceof RegExpLookbehind
47+
) and
48+
s = ""
49+
or
50+
// groups match their content
51+
regExpMatchesString(t.(RegExpGroup).getAChild(), s)
52+
or
53+
// single-character classes match that character
54+
exists(RegExpCharacterClass recc | recc = t and not recc.isInverted() |
55+
recc.getNumChild() = 1 and
56+
regExpMatchesString(recc.getChild(0), s)
57+
)
58+
or
59+
// sequences match the concatenation of their elements
60+
exists(RegExpSequence seq | seq = t |
61+
s = concat(int i, RegExpTerm child |
62+
child = seq.getChild(i)
63+
|
64+
any(string subs | regExpMatchesString(child, subs)) order by i
65+
)
66+
)
6467
)
6568
}
6669

javascript/ql/src/RegExp/UnboundBackref.ql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import javascript
1616

1717
from RegExpBackRef rebr, string ref
1818
where
19+
rebr.isPartOfRegExpLiteral() and
1920
not exists(rebr.getGroup()) and
2021
(
2122
ref = rebr.getNumber().toString()

javascript/ql/src/RegExp/UnmatchableCaret.ql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import javascript
1717

1818
from RegExpCaret caret, RegExpTerm t
1919
where
20+
caret.isPartOfRegExpLiteral() and
2021
t = caret.getPredecessor+() and
2122
not t.isNullable() and
2223
// conservative handling of multi-line regular expressions

javascript/ql/src/RegExp/UnmatchableDollar.ql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import javascript
1717

1818
from RegExpDollar dollar, RegExpTerm t
1919
where
20+
dollar.isPartOfRegExpLiteral() and
2021
t = dollar.getSuccessor+() and
2122
not t.isNullable() and
2223
// conservative handling of multi-line regular expressions

0 commit comments

Comments
 (0)