Skip to content

Commit 33329f9

Browse files
authored
Merge pull request #1874 from asger-semmle/express-types
Approved by esben-semmle, xiemaisi
2 parents 48b6b67 + 61c4d30 commit 33329f9

File tree

6 files changed

+89
-21
lines changed

6 files changed

+89
-21
lines changed

javascript/ql/src/semmle/javascript/frameworks/Express.qll

Lines changed: 54 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -37,27 +37,25 @@ module Express {
3737
*/
3838
private predicate isRouter(Expr e, RouterDefinition router) {
3939
router.flowsTo(e)
40+
}
41+
42+
/**
43+
* Holds if `e` may refer to a router object.
44+
*/
45+
private predicate isRouter(Expr e) {
46+
isRouter(e, _)
4047
or
41-
exists(DataFlow::MethodCallNode chain, DataFlow::Node base, string name |
42-
name = "route" or
43-
name = routeSetupMethodName()
44-
|
45-
chain.calls(base, name) and
46-
isRouter(base.asExpr(), router) and
47-
chain.flowsToExpr(e)
48-
)
48+
e.getType().hasUnderlyingType("express", "Router")
4949
}
5050

5151
/**
5252
* An expression that refers to a route.
5353
*/
5454
class RouteExpr extends MethodCallExpr {
55-
RouterDefinition router;
56-
57-
RouteExpr() { isRouter(this, router) }
55+
RouteExpr() { isRouter(this) }
5856

59-
/** Gets the router from which this route was created. */
60-
RouterDefinition getRouter() { result = router }
57+
/** Gets the router from which this route was created, if it is known. */
58+
RouterDefinition getRouter() { isRouter(this, result) }
6159
}
6260

6361
/**
@@ -77,18 +75,16 @@ module Express {
7775
* A call to an Express router method that sets up a route.
7876
*/
7977
class RouteSetup extends HTTP::Servers::StandardRouteSetup, MethodCallExpr {
80-
RouterDefinition router;
81-
8278
RouteSetup() {
83-
isRouter(getReceiver(), router) and
79+
isRouter(getReceiver()) and
8480
getMethodName() = routeSetupMethodName()
8581
}
8682

8783
/** Gets the path associated with the route. */
8884
string getPath() { getArgument(0).mayHaveStringValue(result) }
8985

9086
/** Gets the router on which handlers are being registered. */
91-
RouterDefinition getRouter() { result = router }
87+
RouterDefinition getRouter() { isRouter(getReceiver(), result) }
9288

9389
/** Holds if this is a call `use`, such as `app.use(handler)`. */
9490
predicate isUseCall() { getMethodName() = "use" }
@@ -340,14 +336,18 @@ module Express {
340336
)
341337
}
342338

339+
/** An Express response source. */
340+
abstract private class ResponseSource extends HTTP::Servers::ResponseSource {
341+
}
342+
343343
/**
344344
* An Express response source, that is, the response parameter of a
345345
* route handler, or a chained method call on a response.
346346
*/
347-
private class ResponseSource extends HTTP::Servers::ResponseSource {
347+
private class ExplicitResponseSource extends ResponseSource {
348348
RouteHandler rh;
349349

350-
ResponseSource() {
350+
ExplicitResponseSource() {
351351
this = DataFlow::parameterNode(rh.getResponseParameter())
352352
or
353353
isChainableResponseMethodCall(rh, this.asExpr())
@@ -359,21 +359,47 @@ module Express {
359359
override RouteHandler getRouteHandler() { result = rh }
360360
}
361361

362+
/**
363+
* An Express response source, based on static type information.
364+
*/
365+
private class TypedResponseSource extends ResponseSource {
366+
TypedResponseSource() {
367+
hasUnderlyingType("express", "Response")
368+
}
369+
370+
override RouteHandler getRouteHandler() { none() } // Not known.
371+
}
372+
373+
/** An Express request source. */
374+
abstract private class RequestSource extends HTTP::Servers::RequestSource {
375+
}
376+
362377
/**
363378
* An Express request source, that is, the request parameter of a
364379
* route handler.
365380
*/
366-
private class RequestSource extends HTTP::Servers::RequestSource {
381+
private class ExplicitRequestSource extends RequestSource {
367382
RouteHandler rh;
368383

369-
RequestSource() { this = DataFlow::parameterNode(rh.getRequestParameter()) }
384+
ExplicitRequestSource() { this = DataFlow::parameterNode(rh.getRequestParameter()) }
370385

371386
/**
372387
* Gets the route handler that handles this request.
373388
*/
374389
override RouteHandler getRouteHandler() { result = rh }
375390
}
376391

392+
/**
393+
* An Express request source, based on static type information.
394+
*/
395+
private class TypedRequestSource extends RequestSource {
396+
TypedRequestSource() {
397+
hasUnderlyingType("express", "Request")
398+
}
399+
400+
override RouteHandler getRouteHandler() { none() } // Not known.
401+
}
402+
377403
/**
378404
* An Express response expression.
379405
*/
@@ -678,6 +704,13 @@ module Express {
678704
t.start() and
679705
result = DataFlow::exprNode(this)
680706
or
707+
exists(string name |
708+
result = ref(t.continue()).getAMethodCall(name)
709+
|
710+
name = "route" or
711+
name = routeSetupMethodName()
712+
)
713+
or
681714
exists(DataFlow::TypeTracker t2 | result = ref(t2).track(t2, t))
682715
}
683716

javascript/ql/test/library-tests/frameworks/Express/RequestExpr.qll

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,7 @@ import javascript
33
query predicate test_RequestExpr(Express::RequestExpr e, HTTP::RouteHandler res) {
44
res = e.getRouteHandler()
55
}
6+
7+
query predicate test_RequestExprStandalone(Express::RequestExpr e) {
8+
not exists(e.getRouteHandler())
9+
}

javascript/ql/test/library-tests/frameworks/Express/tests.expected

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ test_RouteSetup
2626
| src/csurf-example.js:39:1:39:48 | app.get ... es) {}) | src/csurf-example.js:7:11:7:19 | express() | false |
2727
| src/csurf-example.js:40:1:40:49 | app.pos ... es) {}) | src/csurf-example.js:7:11:7:19 | express() | false |
2828
| src/express2.js:3:1:3:56 | router. ... res }) | src/express2.js:5:11:5:13 | e() | false |
29+
| src/express2.js:3:1:4:77 | router. ... sult }) | src/express2.js:5:11:5:13 | e() | false |
2930
| src/express3.js:4:1:7:2 | app.get ... l");\\n}) | src/express3.js:2:11:2:19 | express() | false |
3031
| src/express4.js:4:1:6:2 | app.get ... ery;\\n}) | src/express4.js:2:11:2:19 | express() | false |
3132
| src/express.js:4:1:9:2 | app.get ... es);\\n}) | src/express.js:2:11:2:19 | express() | false |
@@ -245,6 +246,7 @@ test_StandardRouteHandler
245246
| src/csurf-example.js:39:26:39:47 | functio ... res) {} | src/csurf-example.js:7:11:7:19 | express() | src/csurf-example.js:39:36:39:38 | req | src/csurf-example.js:39:41:39:43 | res |
246247
| src/csurf-example.js:40:27:40:48 | functio ... res) {} | src/csurf-example.js:7:11:7:19 | express() | src/csurf-example.js:40:37:40:39 | req | src/csurf-example.js:40:42:40:44 | res |
247248
| src/express2.js:3:25:3:55 | functio ... , res } | src/express2.js:5:11:5:13 | e() | src/express2.js:3:34:3:36 | req | src/express2.js:3:39:3:41 | res |
249+
| src/express2.js:4:32:4:76 | functio ... esult } | src/express2.js:5:11:5:13 | e() | src/express2.js:4:41:4:47 | request | src/express2.js:4:50:4:55 | result |
248250
| src/express3.js:4:23:7:1 | functio ... al");\\n} | src/express3.js:2:11:2:19 | express() | src/express3.js:4:32:4:34 | req | src/express3.js:4:37:4:39 | res |
249251
| src/express4.js:4:23:6:1 | functio ... uery;\\n} | src/express4.js:2:11:2:19 | express() | src/express4.js:4:32:4:34 | req | src/express4.js:4:37:4:39 | res |
250252
| src/express.js:4:23:9:1 | functio ... res);\\n} | src/express.js:2:11:2:19 | express() | src/express.js:4:32:4:34 | req | src/express.js:4:37:4:39 | res |
@@ -395,6 +397,7 @@ test_RouterDefinition_getARouteHandler
395397
| src/csurf-example.js:7:11:7:19 | express() | src/csurf-example.js:40:27:40:48 | functio ... res) {} |
396398
| src/csurf-example.js:30:16:30:35 | new express.Router() | src/csurf-example.js:32:30:34:3 | functio ... e')\\n } |
397399
| src/express2.js:2:14:2:23 | e.Router() | src/express2.js:3:25:3:55 | functio ... , res } |
400+
| src/express2.js:2:14:2:23 | e.Router() | src/express2.js:4:32:4:76 | functio ... esult } |
398401
| src/express3.js:2:11:2:19 | express() | src/express3.js:4:23:7:1 | functio ... al");\\n} |
399402
| src/express4.js:2:11:2:19 | express() | src/express4.js:4:23:6:1 | functio ... uery;\\n} |
400403
| src/express.js:2:11:2:19 | express() | src/express.js:4:23:9:1 | functio ... res);\\n} |
@@ -407,6 +410,7 @@ test_RouterDefinition_getARouteHandler
407410
| src/responseExprs.js:2:11:2:19 | express() | src/responseExprs.js:10:23:12:1 | functio ... res3;\\n} |
408411
| src/responseExprs.js:2:11:2:19 | express() | src/responseExprs.js:13:23:15:1 | functio ... res4;\\n} |
409412
| src/responseExprs.js:2:11:2:19 | express() | src/responseExprs.js:16:30:42:1 | functio ... }\\n} |
413+
| src/route.js:2:14:2:29 | express.Router() | src/route.js:5:12:5:38 | functio ... ext) {} |
410414
test_CookieMiddlewareInstance
411415
| src/cookie-parser.js:3:1:3:23 | session ... key-1") | src/cookie-parser.js:3:9:3:22 | "secret-key-1" |
412416
| src/cookie-parser.js:5:1:5:41 | session ... ey-3"]) | src/cookie-parser.js:5:10:5:23 | "secret-key-2" |
@@ -451,6 +455,7 @@ test_RouteSetup_getServer
451455
| src/csurf-example.js:39:1:39:48 | app.get ... es) {}) | src/csurf-example.js:7:11:7:19 | express() |
452456
| src/csurf-example.js:40:1:40:49 | app.pos ... es) {}) | src/csurf-example.js:7:11:7:19 | express() |
453457
| src/express2.js:3:1:3:56 | router. ... res }) | src/express2.js:5:11:5:13 | e() |
458+
| src/express2.js:3:1:4:77 | router. ... sult }) | src/express2.js:5:11:5:13 | e() |
454459
| src/express3.js:4:1:7:2 | app.get ... l");\\n}) | src/express3.js:2:11:2:19 | express() |
455460
| src/express4.js:4:1:6:2 | app.get ... ery;\\n}) | src/express4.js:2:11:2:19 | express() |
456461
| src/express.js:4:1:9:2 | app.get ... es);\\n}) | src/express.js:2:11:2:19 | express() |
@@ -1019,6 +1024,8 @@ test_RequestExpr
10191024
| src/express.js:50:3:50:5 | req | src/express.js:46:22:51:1 | functio ... ame];\\n} |
10201025
| src/inheritedFromNode.js:7:2:7:4 | req | src/inheritedFromNode.js:4:15:8:1 | functio ... .url;\\n} |
10211026
| src/responseExprs.js:17:5:17:7 | req | src/responseExprs.js:16:30:42:1 | functio ... }\\n} |
1027+
test_RequestExprStandalone
1028+
| typed_src/tst.ts:6:3:6:3 | x |
10221029
test_RouteHandlerExpr_getAsSubRouter
10231030
| src/csurf-example.js:13:17:13:19 | api | src/csurf-example.js:30:16:30:35 | new express.Router() |
10241031
| src/express2.js:6:9:6:14 | router | src/express2.js:2:14:2:23 | e.Router() |
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"include": ["typed_src"]
3+
}
4+
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
declare namespace ServeStaticCore {
2+
interface Request {
3+
body: any;
4+
}
5+
}
6+
7+
declare module 'express' {
8+
interface Request extends ServeStaticCore.Request {}
9+
}
10+
11+
declare module 'express-serve-static-core' {
12+
export = ServeStaticCore;
13+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/// <reference path="./shim.d.ts"/>
2+
3+
import * as express from 'express';
4+
5+
function test(x: express.Request) {
6+
x.body;
7+
}

0 commit comments

Comments
 (0)