Skip to content

Commit 0aebc90

Browse files
committed
don't lowercase the endpointExample, and correctly handle root states
1 parent bcf4c57 commit 0aebc90

File tree

3 files changed

+22
-11
lines changed

3 files changed

+22
-11
lines changed

javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.ql

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import semmle.javascript.security.regexp.NfaUtils as NfaUtils
2424

2525
/** Holds if `s` is a relevant regexp term were we want to compute a string that matches the term (for `getCaseSensitiveBypassExample`). */
2626
predicate isCand(NfaUtils::State s) {
27-
s.getRepr().isRootTerm() and
27+
s.getRepr() instanceof NfaUtils::RegExpRoot and
2828
exists(DataFlow::RegExpCreationNode creation |
2929
isCaseSensitiveMiddleware(_, creation, _) and
3030
s.getRepr().getRootTerm() = creation.getRoot()
@@ -87,13 +87,17 @@ predicate isGuardedCaseInsensitiveEndpoint(
8787
*/
8888
string getAnEndpointExample(Routing::RouteSetup endpoint) {
8989
exists(string raw |
90-
raw = endpoint.getRelativePath().toLowerCase() and
90+
raw = endpoint.getRelativePath() and
9191
result = raw.regexpReplaceAll(":\\w+\\b|\\*", ["a", "1"])
9292
)
9393
}
9494

9595
import semmle.javascript.security.regexp.RegexpMatching as RegexpMatching
9696

97+
NfaUtils::RegExpRoot getARoot(DataFlow::RegExpCreationNode creator) {
98+
result.getRootTerm() = creator.getRoot()
99+
}
100+
97101
/**
98102
* Holds if the regexp matcher should test whether `root` matches `str`.
99103
* The result is used to test whether a case-sensitive bypass exists.
@@ -109,26 +113,25 @@ predicate isMatchingCandidate(
109113
isGuardedCaseInsensitiveEndpoint(endPoint, middleware)
110114
|
111115
root = regexp.getRoot() and
112-
exists(getCaseSensitiveBypassExample(root)) and
116+
exists(getCaseSensitiveBypassExample(getARoot(regexp))) and
113117
ignorePrefix = true and
114118
testWithGroups = false and
115-
str = [getCaseSensitiveBypassExample(root), getAnEndpointExample(endPoint)]
119+
str = [getCaseSensitiveBypassExample(getARoot(regexp)), getAnEndpointExample(endPoint)]
116120
)
117121
}
118122

119123
import RegexpMatching::RegexpMatching<isMatchingCandidate/4> as Matcher
120124

121125
from
122126
DataFlow::RegExpCreationNode regexp, Routing::RouteSetup middleware, Routing::RouteSetup endpoint,
123-
DataFlow::Node arg, string byPassExample
127+
DataFlow::Node arg, string byPassExample, string endpointExample
124128
where
125129
isCaseSensitiveMiddleware(middleware, regexp, arg) and
126-
byPassExample = getCaseSensitiveBypassExample(regexp.getRoot()) and
130+
byPassExample = getCaseSensitiveBypassExample(getARoot(regexp)) and
127131
isGuardedCaseInsensitiveEndpoint(endpoint, middleware) and
128-
exists(string endpointExample | endpointExample = getAnEndpointExample(endpoint) |
129-
Matcher::matches(regexp.getRoot(), endpointExample) and
130-
not Matcher::matches(regexp.getRoot(), byPassExample)
131-
)
132+
endpointExample = getAnEndpointExample(endpoint) and
133+
Matcher::matches(regexp.getRoot(), endpointExample) and
134+
not Matcher::matches(regexp.getRoot(), byPassExample)
132135
select arg,
133136
"This route uses a case-sensitive path $@, but is guarding a case-insensitive path $@. A path such as '"
134137
+ byPassExample + "' will bypass the middleware.", regexp, "pattern", endpoint, "here"

javascript/ql/test/query-tests/Security/CWE-178/CaseSensitiveMiddlewarePath.expected

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,5 @@
44
| tst.js:64:5:64:28 | new Reg ... (.*)?') | This route uses a case-sensitive path $@, but is guarding a case-insensitive path $@. A path such as '/BAR' will bypass the middleware. | tst.js:64:5:64:28 | new Reg ... (.*)?') | pattern | tst.js:73:1:74:2 | app.get ... ware\\n}) | here |
55
| tst.js:76:9:76:20 | /\\/baz\\/bla/ | This route uses a case-sensitive path $@, but is guarding a case-insensitive path $@. A path such as '/BAZ/BLA' will bypass the middleware. | tst.js:76:9:76:20 | /\\/baz\\/bla/ | pattern | tst.js:77:1:79:2 | app.get ... });\\n}) | here |
66
| tst.js:86:9:86:30 | /\\/[Bb] ... 3\\/[a]/ | This route uses a case-sensitive path $@, but is guarding a case-insensitive path $@. A path such as '/BAZ3/A' will bypass the middleware. | tst.js:86:9:86:30 | /\\/[Bb] ... 3\\/[a]/ | pattern | tst.js:87:1:89:2 | app.get ... });\\n}) | here |
7+
| tst.js:91:9:91:40 | /\\/summ ... ntGame/ | This route uses a case-sensitive path $@, but is guarding a case-insensitive path $@. A path such as '/CURRENTGAME' will bypass the middleware. | tst.js:91:9:91:40 | /\\/summ ... ntGame/ | pattern | tst.js:93:1:95:2 | app.get ... O");\\n}) | here |
8+
| tst.js:91:9:91:40 | /\\/summ ... ntGame/ | This route uses a case-sensitive path $@, but is guarding a case-insensitive path $@. A path such as '/SUMMONERBYNAME' will bypass the middleware. | tst.js:91:9:91:40 | /\\/summ ... ntGame/ | pattern | tst.js:93:1:95:2 | app.get ... O");\\n}) | here |

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,10 @@ app.get('/baz2/a', (req, resp) => {
8686
app.use(/\/[Bb][Aa][Zz]3\/[a]/, unknown()); // NOT OK - case sensitive
8787
app.get('/baz3/a', (req, resp) => {
8888
resp.send({ test: 123 });
89-
});
89+
});
90+
91+
app.use(/\/summonerByName|\/currentGame/,apiLimit1, apiLimit2);
92+
93+
app.get('/currentGame', function (req, res) {
94+
res.send("FOO");
95+
});

0 commit comments

Comments
 (0)