Skip to content

Commit ccee34d

Browse files
committed
Added support for matchAll in CWE-020 including new test cases
1 parent 7b870d3 commit ccee34d

File tree

5 files changed

+37
-3
lines changed

5 files changed

+37
-3
lines changed

javascript/ql/lib/semmle/javascript/Regexp.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -938,7 +938,7 @@ private predicate isMatchObjectProperty(string name) {
938938

939939
/** Holds if `call` is a call to `match` whose result is used in a way that is incompatible with Match objects. */
940940
private predicate isUsedAsNonMatchObject(DataFlow::MethodCallNode call) {
941-
call.getMethodName() = "match" and
941+
call.getMethodName() = ["match", "matchAll"] and
942942
call.getNumArgument() = 1 and
943943
(
944944
// Accessing a property that is absent on Match objects
@@ -996,7 +996,7 @@ predicate isInterpretedAsRegExp(DataFlow::Node source) {
996996
not isNativeStringMethod(func, methodName)
997997
)
998998
|
999-
methodName = "match" and
999+
methodName = ["match", "matchAll"] and
10001000
source = mce.getArgument(0) and
10011001
mce.getNumArgument() = 1 and
10021002
not isUsedAsNonMatchObject(mce)

javascript/ql/lib/semmle/javascript/StringOps.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -722,7 +722,7 @@ module StringOps {
722722
}
723723

724724
private class MatchCall extends DataFlow::MethodCallNode {
725-
MatchCall() { this.getMethodName() = "match" }
725+
MatchCall() { this.getMethodName() = ["match", "matchAll"] }
726726
}
727727

728728
private class ExecCall extends DataFlow::MethodCallNode {

javascript/ql/src/Security/CWE-020/MissingRegExpAnchor.ql

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ private module Impl implements
3636
name = "replace"
3737
or
3838
name = "match" and exists(mcn.getAPropertyRead())
39+
or
40+
name = "matchAll" and exists(mcn.getAPropertyRead())
3941
)
4042
)
4143
}

javascript/ql/test/query-tests/Security/CWE-020/MissingRegExpAnchor/MissingRegExpAnchor.expected

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,12 @@
5959
| tst-UnanchoredUrlRegExp.js:26:3:26:22 | "^https?://good.com" | This hostname pattern may match any domain name, as it is missing a '$' or '/' at the end. |
6060
| tst-UnanchoredUrlRegExp.js:35:2:35:32 | /https? ... 0-9]+)/ | When this is used as a regular expression on a URL, it may match anywhere, and arbitrary hosts may come before or after it. |
6161
| tst-UnanchoredUrlRegExp.js:77:11:77:32 | /vimeo\\ ... 0-9]+)/ | When this is used as a regular expression on a URL, it may match anywhere, and arbitrary hosts may come before or after it. |
62+
| tst-UnanchoredUrlRegExp.js:111:50:111:68 | "https?://good.com" | When this is used as a regular expression on a URL, it may match anywhere, and arbitrary hosts may come before or after it. |
63+
| tst-UnanchoredUrlRegExp.js:112:61:112:79 | "https?://good.com" | When this is used as a regular expression on a URL, it may match anywhere, and arbitrary hosts may come before or after it. |
64+
| tst-UnanchoredUrlRegExp.js:113:50:113:69 | "^https?://good.com" | This hostname pattern may match any domain name, as it is missing a '$' or '/' at the end. |
65+
| tst-UnanchoredUrlRegExp.js:114:50:114:72 | /^https ... d.com/g | This hostname pattern may match any domain name, as it is missing a '$' or '/' at the end. |
66+
| tst-UnanchoredUrlRegExp.js:115:50:115:94 | "(^http ... 2.com)" | This hostname pattern may match any domain name, as it is missing a '$' or '/' at the end. |
67+
| tst-UnanchoredUrlRegExp.js:116:50:116:93 | "(https ... e.com)" | This hostname pattern may match any domain name, as it is missing a '$' or '/' at the end. |
68+
| tst-UnanchoredUrlRegExp.js:117:50:117:59 | "good.com" | When this is used as a regular expression on a URL, it may match anywhere, and arbitrary hosts may come before or after it. |
69+
| tst-UnanchoredUrlRegExp.js:118:50:118:68 | "https?://good.com" | When this is used as a regular expression on a URL, it may match anywhere, and arbitrary hosts may come before or after it. |
70+
| tst-UnanchoredUrlRegExp.js:119:50:119:73 | "https? ... m:8080" | When this is used as a regular expression on a URL, it may match anywhere, and arbitrary hosts may come before or after it. |

javascript/ql/test/query-tests/Security/CWE-020/MissingRegExpAnchor/tst-UnanchoredUrlRegExp.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,4 +105,27 @@
105105

106106
/\.com|\.org/; // OK, has no domain name
107107
/example\.com|whatever/; // OK, the other disjunction doesn't match a hostname
108+
109+
// MatchAll test cases:
110+
// Vulnerable patterns
111+
if ("http://evil.com/?http://good.com".matchAll("https?://good.com")) {} // NOT OK
112+
if ("http://evil.com/?http://good.com".matchAll(new RegExp("https?://good.com"))) {} // NOT OK
113+
if ("http://evil.com/?http://good.com".matchAll("^https?://good.com")) {} // NOT OK - missing post-anchor
114+
if ("http://evil.com/?http://good.com".matchAll(/^https?:\/\/good.com/g)) {} // NOT OK - missing post-anchor
115+
if ("http://evil.com/?http://good.com".matchAll("(^https?://good1.com)|(^https?://good2.com)")) {} // NOT OK - missing post-anchor
116+
if ("http://evil.com/?http://good.com".matchAll("(https?://good.com)|(^https?://goodie.com)")) {} // NOT OK - missing post-anchor
117+
if ("http://evil.com/?http://good.com".matchAll("good.com")) {} // NOT OK - missing protocol
118+
if ("http://evil.com/?http://good.com".matchAll("https?://good.com")) {} // NOT OK
119+
if ("http://evil.com/?http://good.com".matchAll("https?://good.com:8080")) {} // NOT OK
120+
121+
// Non-vulnerable patterns
122+
if ("something".matchAll("other")) {} // OK
123+
if ("something".matchAll("x.commissary")) {} // OK
124+
if ("http://evil.com/?http://good.com".matchAll("^https?://good.com$")) {} // OK
125+
if ("http://evil.com/?http://good.com".matchAll(new RegExp("^https?://good.com$"))) {} // OK
126+
if ("http://evil.com/?http://good.com".matchAll("^https?://good.com/$")) {} // OK
127+
if ("http://evil.com/?http://good.com".matchAll(/^https?:\/\/good.com\/$/)) {} // OK
128+
if ("http://evil.com/?http://good.com".matchAll("(^https?://good1.com$)|(^https?://good2.com$)")) {} // OK
129+
if ("http://evil.com/?http://good.com".matchAll("(https?://good.com$)|(^https?://goodie.com$)")) {} // OK
130+
108131
});

0 commit comments

Comments
 (0)