Skip to content

Commit e48158b

Browse files
committed
JS: Share more code with Ruby
1 parent f2384a6 commit e48158b

File tree

3 files changed

+51
-26
lines changed

3 files changed

+51
-26
lines changed

javascript/ql/lib/semmle/javascript/security/IncompleteMultiCharacterSanitization.qll

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,3 +157,46 @@ private predicate isCommonWordMatcher(RegExpTerm t) {
157157
.getValue() = "w"
158158
)
159159
}
160+
161+
/**
162+
* Holds if `replace` has a pattern argument containing a regular expression
163+
* `dangerous` which matches a dangerous string beginning with `prefix`, in an
164+
* attempt to avoid a vulnerability of kind `kind`.
165+
*/
166+
predicate isResult(
167+
StringSubstitutionCall replace, EmptyReplaceRegExpTerm dangerous, string prefix, string kind
168+
) {
169+
exists(EmptyReplaceRegExpTerm regexp |
170+
replace = regexp.getCall() and
171+
dangerous.getRootTerm() = regexp and
172+
// skip leading optional elements
173+
not dangerous.isNullable() and
174+
// only warn about the longest match
175+
prefix = max(string m | matchesDangerousPrefix(dangerous, m, kind) | m order by m.length(), m) and
176+
// only warn once per kind
177+
not exists(EmptyReplaceRegExpTerm other |
178+
other = dangerous.getAChild+() or other = dangerous.getPredecessor+()
179+
|
180+
matchesDangerousPrefix(other, _, kind) and
181+
not other.isNullable()
182+
) and
183+
// avoid anchored terms
184+
not exists(RegExpAnchor a | regexp = a.getRootTerm()) and
185+
// Don't flag replace operations that are called repeatedly in a loop, as they can actually work correctly.
186+
not replace.flowsTo(replace.getReceiver+())
187+
)
188+
}
189+
190+
/**
191+
* Holds if `replace` has a pattern argument containing a regular expression
192+
* `dangerous` which matches a dangerous string beginning with `prefix`. `msg`
193+
* is the alert we report.
194+
*/
195+
query predicate problems(
196+
StringSubstitutionCall replace, string msg, EmptyReplaceRegExpTerm dangerous, string prefix
197+
) {
198+
exists(string kind |
199+
isResult(replace, dangerous, prefix, kind) and
200+
msg = "This string may still contain $@, which may cause a " + kind + " vulnerability."
201+
)
202+
}

javascript/ql/lib/semmle/javascript/security/IncompleteMultiCharacterSanitizationSpecific.qll

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import javascript
66
import semmle.javascript.security.performance.ReDoSUtil as ReDoSUtil
77

8+
class StringSubstitutionCall = StringReplaceCall;
9+
810
/**
911
* A regexp term that matches substrings that should be replaced with the empty string.
1012
*/
@@ -15,4 +17,9 @@ class EmptyReplaceRegExpTerm extends RegExpTerm {
1517
this = replace.getRegExp().getRoot().getAChild*()
1618
)
1719
}
20+
21+
/**
22+
* Get the substitution call that uses this regexp term.
23+
*/
24+
StringSubstitutionCall getCall() { this = result.getRegExp().getRoot() }
1825
}

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

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -13,29 +13,4 @@
1313
* external/cwe/cwe-116
1414
*/
1515

16-
import javascript
17-
private import semmle.javascript.security.IncompleteMultiCharacterSanitization
18-
19-
from
20-
StringReplaceCall replace, EmptyReplaceRegExpTerm regexp, EmptyReplaceRegExpTerm dangerous,
21-
string prefix, string kind
22-
where
23-
regexp = replace.getRegExp().getRoot() and
24-
dangerous.getRootTerm() = regexp and
25-
// skip leading optional elements
26-
not dangerous.isNullable() and
27-
// only warn about the longest match (presumably the most descriptive)
28-
prefix = max(string m | matchesDangerousPrefix(dangerous, m, kind) | m order by m.length(), m) and
29-
// only warn once per kind
30-
not exists(EmptyReplaceRegExpTerm other |
31-
other = dangerous.getAChild+() or other = dangerous.getPredecessor+()
32-
|
33-
matchesDangerousPrefix(other, _, kind) and
34-
not other.isNullable()
35-
) and
36-
// don't flag replace operations in a loop
37-
not replace.getAMethodCall*().flowsTo(replace.getReceiver()) and
38-
// avoid anchored terms
39-
not exists(RegExpAnchor a | regexp = a.getRootTerm())
40-
select replace, "This string may still contain $@, which may cause a " + kind + " vulnerability.",
41-
dangerous, prefix
16+
import semmle.javascript.security.IncompleteMultiCharacterSanitization

0 commit comments

Comments
 (0)