Skip to content

Commit 1ae1748

Browse files
committed
JS: incomplete sanitization now also works with RegExp objects
1 parent 7631803 commit 1ae1748

File tree

3 files changed

+20
-11
lines changed

3 files changed

+20
-11
lines changed

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ string metachar() { result = "'\"\\&<>\n\r\t*|{}[]%$".charAt(_) }
2323

2424
/** Gets a string matched by `e` in a `replace` call. */
2525
string getAMatchedString(DataFlow::Node e) {
26-
result = e.(DataFlow::RegExpLiteralNode).getRoot().getAMatchedString()
26+
result = e.(DataFlow::RegExpCreationNode).getRoot().getAMatchedString()
2727
or
2828
result = e.getStringValue()
2929
}
@@ -52,7 +52,7 @@ predicate isSimpleAlt(RegExpAlt t) { forall(RegExpTerm ch | ch = t.getAChild() |
5252
* Holds if `mce` is of the form `x.replace(re, new)`, where `re` is a global
5353
* regular expression and `new` prefixes the matched string with a backslash.
5454
*/
55-
predicate isBackslashEscape(StringReplaceCall mce, DataFlow::RegExpLiteralNode re) {
55+
predicate isBackslashEscape(StringReplaceCall mce, DataFlow::RegExpCreationNode re) {
5656
mce.isGlobal() and
5757
re = mce.getRegExp() and
5858
(
@@ -72,7 +72,7 @@ predicate allBackslashesEscaped(DataFlow::Node nd) {
7272
nd instanceof JsonStringifyCall
7373
or
7474
// check whether `nd` itself escapes backslashes
75-
exists(DataFlow::RegExpLiteralNode rel | isBackslashEscape(nd, rel) |
75+
exists(DataFlow::RegExpCreationNode rel | isBackslashEscape(nd, rel) |
7676
// if it's a complex regexp, we conservatively assume that it probably escapes backslashes
7777
not isSimple(rel.getRoot()) or
7878
getAMatchedString(rel) = "\\"

javascript/ql/test/query-tests/Security/CWE-116/IncompleteSanitization/IncompleteSanitization.expected

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,12 @@
2929
| tst.js:149:2:149:24 | x.repla ... replace | This replaces only the first occurrence of "\\n". |
3030
| tst.js:193:9:193:17 | s.replace | This replaces only the first occurrence of /'/. |
3131
| tst.js:202:10:202:18 | p.replace | This replaces only the first occurrence of "/../". |
32+
| tst.js:337:2:337:12 | s().replace | This replaces only the first occurrence of new Reg ... nown()). |
33+
| tst.js:341:9:341:17 | p.replace | This replaces only the first occurrence of new Reg ... .\\\\./"). |
34+
| tst.js:345:9:345:17 | s.replace | This does not escape backslash characters in the input. |
35+
| tst.js:349:9:349:17 | s.replace | This replaces only the first occurrence of new RegExp("\\'"). |
36+
| tst.js:353:9:353:17 | s.replace | This replaces only the first occurrence of new Reg ... lags()). |
37+
| tst.js:362:2:362:10 | x.replace | This replaces only the first occurrence of new RegExp("\\n"). |
38+
| tst.js:363:2:363:24 | x.repla ... replace | This replaces only the first occurrence of new RegExp("\\n"). |
39+
| tst.js:365:2:365:10 | x.replace | This replaces only the first occurrence of new Reg ... lags()). |
40+
| tst.js:366:2:366:24 | x.repla ... replace | This replaces only the first occurrence of new Reg ... lags()). |

javascript/ql/test/query-tests/Security/CWE-116/IncompleteSanitization/tst.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -338,19 +338,19 @@ function typicalBadHtmlSanitizers(s) {
338338
}
339339

340340
function bad18NewRegExp(p) {
341-
return p.replace(new RegExp("\\.\\./"), ""); // NOT OK -- should be flagged, but currently checking only for literals
341+
return p.replace(new RegExp("\\.\\./"), ""); // NOT OK
342342
}
343343

344344
function bad4NewRegExpG(s) {
345-
return s.replace(new RegExp("\'","g"), "\\$&"); // NOT OK -- should be flagged, but currently checking only for literals
345+
return s.replace(new RegExp("\'","g"), "\\$&"); // NOT OK
346346
}
347347

348348
function bad4NewRegExp(s) {
349-
return s.replace(new RegExp("\'"), "\\$&"); // NOT OK -- should be flagged, but currently checking only for literals
349+
return s.replace(new RegExp("\'"), "\\$&"); // NOT OK
350350
}
351351

352352
function bad4NewRegExpUnknown(s) {
353-
return s.replace(new RegExp("\'", unknownFlags()), "\\$&"); // NOT OK -- should be flagged, but currently checking only for literals
353+
return s.replace(new RegExp("\'", unknownFlags()), "\\$&"); // NOT OK
354354
}
355355

356356
function newlinesNewReGexp(s) {
@@ -359,9 +359,9 @@ function newlinesNewReGexp(s) {
359359
x.replace(new RegExp("\n", "g"), "").replace(x, y); // OK
360360
x.replace(x, y).replace(new RegExp("\n", "g"), ""); // OK
361361

362-
x.replace(new RegExp("\n"), "").replace(x, y); // NOT OK -- should be flagged, but currently checking only for literals
363-
x.replace(x, y).replace(new RegExp("\n"), ""); // NOT OK -- should be flagged, but currently checking only for literals
362+
x.replace(new RegExp("\n"), "").replace(x, y); // NOT OK
363+
x.replace(x, y).replace(new RegExp("\n"), ""); // NOT OK
364364

365-
x.replace(new RegExp("\n", unknownFlags()), "").replace(x, y); // OK
366-
x.replace(x, y).replace(new RegExp("\n", unknownFlags()), ""); // OK
365+
x.replace(new RegExp("\n", unknownFlags()), "").replace(x, y); // OK -- Should not be flagged but now it is
366+
x.replace(x, y).replace(new RegExp("\n", unknownFlags()), ""); // OK -- Should not be flagged but now it is
367367
}

0 commit comments

Comments
 (0)