Skip to content

Commit 59f0a41

Browse files
committed
support more regular expressions in js/incomplete-multi-character-sanitization
1 parent 92804a3 commit 59f0a41

File tree

2 files changed

+49
-18
lines changed

2 files changed

+49
-18
lines changed

javascript/ql/src/Security/CWE-116/IncompleteMultiCharacterSanitization.ql

Lines changed: 48 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -56,31 +56,62 @@ DangerousPrefix getADangerousMatchedPrefix(EmptyReplaceRegExpTerm t) {
5656
not exists(EmptyReplaceRegExpTerm pred | pred = t.getPredecessor+() and not pred.isNullable())
5757
}
5858

59+
private import semmle.javascript.security.performance.ReDoSUtil as ReDoSUtil
60+
61+
/**
62+
* Gets a char from a dangerous prefix that is matched by `t`.
63+
*/
64+
pragma[noinline]
65+
DangerousPrefixSubstring getADangerousMatchedChar(EmptyReplaceRegExpTerm t) {
66+
t.isNullable() and result = ""
67+
or
68+
t.getAMatchedString() = result
69+
or
70+
ReDoSUtil::getCanonicalCharClass(t).(ReDoSUtil::CharacterClass).matches(result)
71+
or
72+
t instanceof RegExpDot and
73+
result.length() = 1
74+
or
75+
(
76+
t instanceof RegExpOpt or
77+
t instanceof RegExpStar or
78+
t instanceof RegExpPlus or
79+
t instanceof RegExpGroup or
80+
t instanceof RegExpAlt
81+
) and
82+
result = getADangerousMatchedChar(t.getAChild())
83+
}
84+
5985
/**
6086
* Gets a substring of a dangerous prefix that is in the language starting at `t` (ignoring lookarounds).
6187
*
6288
* Note that the language of `t` is slightly restricted as not all RegExpTerm types are supported.
6389
*/
6490
DangerousPrefixSubstring getADangerousMatchedPrefixSubstring(EmptyReplaceRegExpTerm t) {
65-
exists(string left |
66-
t.isNullable() and left = ""
67-
or
68-
t.getAMatchedString() = left
69-
or
70-
(
71-
t instanceof RegExpOpt or
72-
t instanceof RegExpStar or
73-
t instanceof RegExpPlus or
74-
t instanceof RegExpGroup or
75-
t instanceof RegExpAlt
76-
) and
77-
left = getADangerousMatchedPrefixSubstring(t.getAChild())
78-
|
79-
result = left + getADangerousMatchedPrefixSubstring(t.getSuccessor()) or
80-
result = left
91+
result = getADangerousMatchedChar(t) + getADangerousMatchedPrefixSubstring(t.getSuccessor())
92+
or
93+
result = getADangerousMatchedChar(t)
94+
or
95+
// loop around for repetitions (only considering alphanumeric characters in the repetition)
96+
exists(RepetitionMatcher repetition | t = repetition |
97+
result = getADangerousMatchedPrefixSubstring(repetition) + repetition.getAChar()
8198
)
8299
}
83100

101+
class RepetitionMatcher extends EmptyReplaceRegExpTerm {
102+
string char;
103+
104+
pragma[noinline]
105+
RepetitionMatcher() {
106+
(this instanceof RegExpPlus or this instanceof RegExpStar) and
107+
char = getADangerousMatchedChar(this.getAChild()) and
108+
char.regexpMatch("\\w")
109+
}
110+
111+
pragma[noinline]
112+
string getAChar() { result = char }
113+
}
114+
84115
/**
85116
* Holds if `t` may match the dangerous `prefix` and some suffix, indicating intent to prevent a vulnerablity of kind `kind`.
86117
*/
@@ -151,7 +182,7 @@ where
151182
// skip leading optional elements
152183
not dangerous.isNullable() and
153184
// only warn about the longest match (presumably the most descriptive)
154-
prefix = max(string m | matchesDangerousPrefix(dangerous, m, kind) | m order by m.length()) and
185+
prefix = max(string m | matchesDangerousPrefix(dangerous, m, kind) | m order by m.length(), m) and
155186
// only warn once per kind
156187
not exists(EmptyReplaceRegExpTerm other |
157188
other = dangerous.getAChild+() or other = dangerous.getPredecessor+()

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ class InputSymbol extends TInputSymbol {
251251
/**
252252
* An abstract input symbol that represents a character class.
253253
*/
254-
abstract private class CharacterClass extends InputSymbol {
254+
abstract class CharacterClass extends InputSymbol {
255255
/**
256256
* Gets a character that is relevant for intersection-tests involving this
257257
* character class.

0 commit comments

Comments
 (0)