Skip to content

Commit a082ed9

Browse files
committed
track flow through string replace calls that just replace single chars
1 parent 3123abf commit a082ed9

File tree

4 files changed

+46
-3
lines changed

4 files changed

+46
-3
lines changed

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

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,15 +95,28 @@ module PolynomialReDoS {
9595
this.(StringReplaceCall).isGlobal() and
9696
// not lone char classes - they don't remove any repeated pattern.
9797
not exists(RegExpTerm root | root = this.(StringReplaceCall).getRegExp().getRoot() |
98-
root instanceof RegExpCharacterClass
99-
or
100-
root instanceof RegExpCharacterClassEscape
98+
isCharClassLike(root)
10199
)
102100
or
103101
this.(DataFlow::MethodCallNode).getMethodName() = StringOps::substringMethodName()
104102
}
105103
}
106104

105+
/**
106+
* Holds if `term` matches a set of strings of length 1.
107+
*/
108+
predicate isCharClassLike(RegExpTerm term) {
109+
term instanceof RegExpCharacterClass
110+
or
111+
term instanceof RegExpCharacterClassEscape
112+
or
113+
term.(RegExpConstant).getValue().length() = 1
114+
or
115+
exists(RegExpAlt alt | term = alt |
116+
forall(RegExpTerm choice | choice = alt.getAlternative() | isCharClassLike(choice))
117+
)
118+
}
119+
107120
/**
108121
* An check on the length of a string, seen as a sanitizer guard.
109122
*/

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@
113113
| polynomial-redos.js:116:21:116:28 | [\\d\\D]*? | Strings starting with '/*' and with many repetitions of 'a/*' can start matching anywhere after the start of the preceeding \\/\\*[\\d\\D]*?\\*\\/ |
114114
| polynomial-redos.js:118:17:118:23 | (#\\d+)+ | Strings with many repetitions of '9' can start matching anywhere after the start of the preceeding \\d+ |
115115
| polynomial-redos.js:124:33:124:35 | \\s+ | Strings with many repetitions of ' ' can start matching anywhere after the start of the preceeding \\s+$ |
116+
| polynomial-redos.js:130:21:130:22 | c+ | Strings starting with 'c' and with many repetitions of 'c' can start matching anywhere after the start of the preceeding cc+D |
117+
| polynomial-redos.js:133:22:133:23 | f+ | Strings starting with 'f' and with many repetitions of 'f' can start matching anywhere after the start of the preceeding ff+G |
116118
| regexplib/address.js:27:3:27:5 | \\s* | Strings with many repetitions of ' ' can start matching anywhere after the start of the preceeding (\\s*\\(?0\\d{4}\\)?(\\s*\|-)\\d{3}(\\s*\|-)\\d{3}\\s*) |
117119
| regexplib/address.js:27:48:27:50 | \\s* | Strings with many repetitions of ' ' can start matching anywhere after the start of the preceeding (\\s*\\(?0\\d{3}\\)?(\\s*\|-)\\d{3}(\\s*\|-)\\d{4}\\s*) |
118120
| regexplib/address.js:27:93:27:95 | \\s* | Strings with many repetitions of ' ' can start matching anywhere after the start of the preceeding (\\s*(7\|8)(\\d{7}\|\\d{3}(\\-\|\\s{1})\\d{4})\\s*) |

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,16 @@ nodes
165165
| polynomial-redos.js:123:13:123:20 | replaced |
166166
| polynomial-redos.js:124:12:124:17 | result |
167167
| polynomial-redos.js:124:12:124:17 | result |
168+
| polynomial-redos.js:129:6:129:42 | modified |
169+
| polynomial-redos.js:129:17:129:23 | tainted |
170+
| polynomial-redos.js:129:17:129:42 | tainted ... g, "b") |
171+
| polynomial-redos.js:130:2:130:9 | modified |
172+
| polynomial-redos.js:130:2:130:9 | modified |
173+
| polynomial-redos.js:132:6:132:50 | modified2 |
174+
| polynomial-redos.js:132:18:132:24 | tainted |
175+
| polynomial-redos.js:132:18:132:50 | tainted ... g, "e") |
176+
| polynomial-redos.js:133:2:133:10 | modified2 |
177+
| polynomial-redos.js:133:2:133:10 | modified2 |
168178
edges
169179
| lib/closure.js:3:21:3:21 | x | lib/closure.js:4:16:4:16 | x |
170180
| lib/closure.js:3:21:3:21 | x | lib/closure.js:4:16:4:16 | x |
@@ -317,6 +327,8 @@ edges
317327
| polynomial-redos.js:5:6:5:32 | tainted | polynomial-redos.js:118:2:118:8 | tainted |
318328
| polynomial-redos.js:5:6:5:32 | tainted | polynomial-redos.js:118:2:118:8 | tainted |
319329
| polynomial-redos.js:5:6:5:32 | tainted | polynomial-redos.js:121:18:121:24 | tainted |
330+
| polynomial-redos.js:5:6:5:32 | tainted | polynomial-redos.js:129:17:129:23 | tainted |
331+
| polynomial-redos.js:5:6:5:32 | tainted | polynomial-redos.js:132:18:132:24 | tainted |
320332
| polynomial-redos.js:5:16:5:32 | req.query.tainted | polynomial-redos.js:5:6:5:32 | tainted |
321333
| polynomial-redos.js:5:16:5:32 | req.query.tainted | polynomial-redos.js:5:6:5:32 | tainted |
322334
| polynomial-redos.js:68:18:68:24 | req.url | polynomial-redos.js:68:18:68:24 | req.url |
@@ -327,6 +339,14 @@ edges
327339
| polynomial-redos.js:123:3:123:20 | result | polynomial-redos.js:124:12:124:17 | result |
328340
| polynomial-redos.js:123:3:123:20 | result | polynomial-redos.js:124:12:124:17 | result |
329341
| polynomial-redos.js:123:13:123:20 | replaced | polynomial-redos.js:123:3:123:20 | result |
342+
| polynomial-redos.js:129:6:129:42 | modified | polynomial-redos.js:130:2:130:9 | modified |
343+
| polynomial-redos.js:129:6:129:42 | modified | polynomial-redos.js:130:2:130:9 | modified |
344+
| polynomial-redos.js:129:17:129:23 | tainted | polynomial-redos.js:129:17:129:42 | tainted ... g, "b") |
345+
| polynomial-redos.js:129:17:129:42 | tainted ... g, "b") | polynomial-redos.js:129:6:129:42 | modified |
346+
| polynomial-redos.js:132:6:132:50 | modified2 | polynomial-redos.js:133:2:133:10 | modified2 |
347+
| polynomial-redos.js:132:6:132:50 | modified2 | polynomial-redos.js:133:2:133:10 | modified2 |
348+
| polynomial-redos.js:132:18:132:24 | tainted | polynomial-redos.js:132:18:132:50 | tainted ... g, "e") |
349+
| polynomial-redos.js:132:18:132:50 | tainted ... g, "e") | polynomial-redos.js:132:6:132:50 | modified2 |
330350
#select
331351
| lib/closure.js:4:5:4:17 | /u*o/.test(x) | lib/closure.js:3:21:3:21 | x | lib/closure.js:4:16:4:16 | x | This $@ that depends on $@ may run slow on strings with many repetitions of 'u'. | lib/closure.js:4:6:4:7 | u* | regular expression | lib/closure.js:3:21:3:21 | x | library input |
332352
| lib/lib.js:4:2:4:18 | regexp.test(name) | lib/lib.js:3:28:3:31 | name | lib/lib.js:4:14:4:17 | name | This $@ that depends on $@ may run slow on strings with many repetitions of 'a'. | lib/lib.js:1:15:1:16 | a* | regular expression | lib/lib.js:3:28:3:31 | name | library input |
@@ -409,3 +429,5 @@ edges
409429
| polynomial-redos.js:116:2:116:35 | tainted ... \\*\\//g) | polynomial-redos.js:5:16:5:32 | req.query.tainted | polynomial-redos.js:116:2:116:8 | tainted | This $@ that depends on $@ may run slow on strings starting with '/*' and with many repetitions of 'a/*'. | polynomial-redos.js:116:21:116:28 | [\\d\\D]*? | regular expression | polynomial-redos.js:5:16:5:32 | req.query.tainted | a user-provided value |
410430
| polynomial-redos.js:118:2:118:25 | tainted ... \\d+)+/) | polynomial-redos.js:5:16:5:32 | req.query.tainted | polynomial-redos.js:118:2:118:8 | tainted | This $@ that depends on $@ may run slow on strings with many repetitions of '9'. | polynomial-redos.js:118:17:118:23 | (#\\d+)+ | regular expression | polynomial-redos.js:5:16:5:32 | req.query.tainted | a user-provided value |
411431
| polynomial-redos.js:124:12:124:43 | result. ... /g, '') | polynomial-redos.js:5:16:5:32 | req.query.tainted | polynomial-redos.js:124:12:124:17 | result | This $@ that depends on $@ may run slow on strings with many repetitions of ' '. | polynomial-redos.js:124:33:124:35 | \\s+ | regular expression | polynomial-redos.js:5:16:5:32 | req.query.tainted | a user-provided value |
432+
| polynomial-redos.js:130:2:130:31 | modifie ... g, "b") | polynomial-redos.js:5:16:5:32 | req.query.tainted | polynomial-redos.js:130:2:130:9 | modified | This $@ that depends on $@ may run slow on strings starting with 'c' and with many repetitions of 'c'. | polynomial-redos.js:130:21:130:22 | c+ | regular expression | polynomial-redos.js:5:16:5:32 | req.query.tainted | a user-provided value |
433+
| polynomial-redos.js:133:2:133:32 | modifie ... g, "b") | polynomial-redos.js:5:16:5:32 | req.query.tainted | polynomial-redos.js:133:2:133:10 | modified2 | This $@ that depends on $@ may run slow on strings starting with 'f' and with many repetitions of 'f'. | polynomial-redos.js:133:22:133:23 | f+ | regular expression | polynomial-redos.js:5:16:5:32 | req.query.tainted | a user-provided value |

javascript/ql/test/query-tests/Performance/ReDoS/polynomial-redos.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,4 +125,10 @@ app.use(function(req, res) {
125125
})();
126126

127127
tainted.match(/(https?:\/\/[^\s]+)/gm); // OK
128+
129+
var modified = tainted.replace(/a/g, "b");
130+
modified.replace(/cc+D/g, "b"); // NOT OK
131+
132+
var modified2 = tainted.replace(/a|b|c|\d/g, "e");
133+
modified2.replace(/ff+G/g, "b"); // NOT OK
128134
});

0 commit comments

Comments
 (0)