Skip to content

Commit a3bd072

Browse files
author
Esben Sparre Andreasen
committed
JS: add Hapi::RouteHandlerCandidate
1 parent fd48927 commit a3bd072

File tree

10 files changed

+79
-1
lines changed

10 files changed

+79
-1
lines changed

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

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,8 @@ module Hapi {
210210
}
211211

212212
override DataFlow::SourceNode getARouteHandler() {
213-
result.(DataFlow::SourceNode).flowsTo(handler.flow())
213+
result.(DataFlow::SourceNode).flowsTo(handler.flow()) or
214+
result.(DataFlow::TrackedNode).flowsTo(handler.flow())
214215
}
215216

216217
Expr getRouteHandlerExpr() {
@@ -221,4 +222,57 @@ module Hapi {
221222
result = server
222223
}
223224
}
225+
226+
/**
227+
* A function that looks like a Hapi route handler.
228+
*
229+
* For example, this could be the function `function(request, h){...}`.
230+
*/
231+
class RouteHandlerCandidate extends HTTP::RouteHandlerCandidate, DataFlow::FunctionNode {
232+
233+
override Function astNode;
234+
235+
RouteHandlerCandidate() {
236+
exists (string request, string responseToolkit |
237+
(request = "request" or request = "req") and
238+
responseToolkit = "h" and
239+
// heuristic: parameter names match the Hapi documentation
240+
astNode.getNumParameter() = 2 and
241+
astNode.getParameter(0).getName() = request and
242+
astNode.getParameter(1).getName() = responseToolkit |
243+
not (
244+
// heuristic: is not invoked (Hapi invokes this at a call site we cannot reason precisely about)
245+
exists(DataFlow::InvokeNode cs | cs.getACallee() = astNode)
246+
)
247+
)
248+
}
249+
}
250+
251+
/**
252+
* Tracking for `RouteHandlerCandidate`.
253+
*/
254+
private class TrackedRouteHandlerCandidate extends DataFlow::TrackedNode {
255+
256+
TrackedRouteHandlerCandidate() {
257+
this instanceof RouteHandlerCandidate
258+
}
259+
260+
}
261+
262+
/**
263+
* A function that looks like a Hapi route handler and flows to a route setup.
264+
*/
265+
private class TrackedRouteHandlerCandidateWithSetup extends RouteHandler, HTTP::Servers::StandardRouteHandler, DataFlow::ValueNode {
266+
267+
override Function astNode;
268+
269+
TrackedRouteHandlerCandidateWithSetup() {
270+
exists(TrackedRouteHandlerCandidate tracked |
271+
tracked.flowsTo(any(RouteSetup s).getRouteHandlerExpr().flow()) and
272+
this = tracked
273+
)
274+
}
275+
276+
}
277+
224278
}

javascript/ql/src/semmle/javascript/heuristics/AdditionalRouteHandlers.qll

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,17 @@ private class PromotedNodeJSLibCandidate extends NodeJSLib::RouteHandler, HTTP::
1818

1919
}
2020

21+
/**
22+
* Adds `Hapi::RouteHandlerCandidate` to the extent of `Hapi::RouteHandler`.
23+
*/
24+
private class PromotedHapiCandidate extends Hapi::RouteHandler, HTTP::Servers::StandardRouteHandler {
25+
26+
PromotedHapiCandidate() {
27+
this instanceof Hapi::RouteHandlerCandidate
28+
}
29+
30+
}
31+
2132
/**
2233
* Adds `ConnectExpressShared::RouteHandlerCandidate` to the extent of `Express::RouteHandler`.
2334
*/

javascript/ql/test/library-tests/frameworks/HTTP-heuristics/RouteHandlerCandidate.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
| src/exported-middleware-attacher.js:2:13:2:32 | function(req, res){} |
1616
| src/handler-in-property.js:5:14:5:33 | function(req, res){} |
1717
| src/handler-in-property.js:12:18:12:37 | function(req, res){} |
18+
| src/hapi.js:1:1:1:30 | functio ... t, h){} |
1819
| src/iterated-handlers.js:4:2:4:22 | functio ... res){} |
1920
| src/middleware-attacher-getter.js:4:17:4:36 | function(req, res){} |
2021
| src/middleware-attacher-getter.js:19:19:19:38 | function(req, res){} |

javascript/ql/test/library-tests/frameworks/HTTP-heuristics/UnpromotedRouteHandlerCandidate.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
| src/bound-handler.js:4:1:4:28 | functio ... res){} | A `RouteHandlerCandidate` that did not get promoted to `RouteHandler`, and it is not used in a `RouteSetupCandidate`. |
22
| src/bound-handler.js:9:12:9:31 | function(req, res){} | A `RouteHandlerCandidate` that did not get promoted to `RouteHandler`, and it is not used in a `RouteSetupCandidate`. |
3+
| src/hapi.js:1:1:1:30 | functio ... t, h){} | A `RouteHandlerCandidate` that did not get promoted to `RouteHandler`, and it is not used in a `RouteSetupCandidate`. |
34
| src/iterated-handlers.js:4:2:4:22 | functio ... res){} | A `RouteHandlerCandidate` that did not get promoted to `RouteHandler`, and it is not used in a `RouteSetupCandidate`. |
45
| src/middleware-attacher-getter.js:29:32:29:51 | function(req, res){} | A `RouteHandlerCandidate` that did not get promoted to `RouteHandler`, and it is not used in a `RouteSetupCandidate`. |
56
| src/route-objects.js:7:19:7:38 | function(req, res){} | A `RouteHandlerCandidate` that did not get promoted to `RouteHandler`, and it is not used in a `RouteSetupCandidate`. |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
function handler(request, h){}

javascript/ql/test/library-tests/frameworks/hapi/RouteHandler.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
| src/hapi.js:13:14:15:5 | functio ... n\\n } | src/hapi.js:4:15:4:31 | new Hapi.Server() |
33
| src/hapi.js:17:30:18:1 | functio ... ndler\\n} | src/hapi.js:4:15:4:31 | new Hapi.Server() |
44
| src/hapi.js:20:1:27:1 | functio ... oken;\\n} | src/hapi.js:4:15:4:31 | new Hapi.Server() |
5+
| src/hapi.js:34:12:34:30 | function (req, h){} | src/hapi.js:4:15:4:31 | new Hapi.Server() |

javascript/ql/test/library-tests/frameworks/hapi/RouteSetup.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
| src/hapi.js:12:1:15:7 | server2 ... }}) |
33
| src/hapi.js:17:1:18:2 | server2 ... dler\\n}) |
44
| src/hapi.js:29:1:29:20 | server2.route(route) |
5+
| src/hapi.js:36:1:36:38 | server2 ... ler()}) |

javascript/ql/test/library-tests/frameworks/hapi/RouteSetup_getARouteHandler.expected

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,5 @@
22
| src/hapi.js:12:1:15:7 | server2 ... }}) | src/hapi.js:13:14:15:5 | functio ... n\\n } |
33
| src/hapi.js:17:1:18:2 | server2 ... dler\\n}) | src/hapi.js:17:30:18:1 | functio ... ndler\\n} |
44
| src/hapi.js:29:1:29:20 | server2.route(route) | src/hapi.js:20:1:27:1 | functio ... oken;\\n} |
5+
| src/hapi.js:36:1:36:38 | server2 ... ler()}) | src/hapi.js:34:12:34:30 | function (req, h){} |
6+
| src/hapi.js:36:1:36:38 | server2 ... ler()}) | src/hapi.js:36:25:36:36 | getHandler() |

javascript/ql/test/library-tests/frameworks/hapi/RouteSetup_getServer.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
| src/hapi.js:12:1:15:7 | server2 ... }}) | src/hapi.js:4:15:4:31 | new Hapi.Server() |
33
| src/hapi.js:17:1:18:2 | server2 ... dler\\n}) | src/hapi.js:4:15:4:31 | new Hapi.Server() |
44
| src/hapi.js:29:1:29:20 | server2.route(route) | src/hapi.js:4:15:4:31 | new Hapi.Server() |
5+
| src/hapi.js:36:1:36:38 | server2 ... ler()}) | src/hapi.js:4:15:4:31 | new Hapi.Server() |

javascript/ql/test/library-tests/frameworks/hapi/src/hapi.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,8 @@ var route = {handler: handler4};
2929
server2.route(route);
3030

3131
server2.cache({ segment: 'countries', expiresIn: 60*60*1000 });
32+
33+
function getHandler() {
34+
return function (req, h){}
35+
}
36+
server2.route({handler: getHandler()});

0 commit comments

Comments
 (0)