44
55import javascript
66import semmle.javascript.frameworks.HTTP
7+ private import semmle.javascript.dataflow.internal.CallGraphs
78
89module Hapi {
910 /**
@@ -116,17 +117,21 @@ module Hapi {
116117 this .( DataFlow:: PropRead ) .accesses ( request , "rawPayload" )
117118 or
118119 exists ( DataFlow:: PropRead payload |
119- // `request.payload.name`
120+ // `request.payload.name`, or `request.payload` when the object is forwarded.
120121 payload .accesses ( request , "payload" ) and
121- this .( DataFlow:: PropRead ) .accesses ( payload , _)
122+ if exists ( payload .getAPropertyRead ( ) )
123+ then this = payload .getAPropertyRead ( )
124+ else this = payload
122125 )
123126 )
124127 or
125128 kind = "parameter" and
126- exists ( DataFlow:: PropRead query |
127- // `request.query.name`
128- query .accesses ( request , [ "query" , "params" ] ) and
129- this .( DataFlow:: PropRead ) .accesses ( query , _)
129+ exists ( DataFlow:: PropRead parameter |
130+ // `request.query.name` / `request.params.name`, or the object when it is forwarded.
131+ parameter .accesses ( request , [ "query" , "params" ] ) and
132+ if exists ( parameter .getAPropertyRead ( ) )
133+ then this = parameter .getAPropertyRead ( )
134+ else this = parameter
130135 )
131136 or
132137 exists ( DataFlow:: PropRead url |
@@ -199,30 +204,10 @@ module Hapi {
199204 */
200205 class RouteSetup extends DataFlow:: MethodCallNode , Http:: Servers:: StandardRouteSetup {
201206 ServerDefinition server ;
202- DataFlow:: Node handler ;
203207
204208 RouteSetup ( ) {
205209 server .ref ( ) .getAMethodCall ( ) = this and
206- (
207- // server.route({ handler: fun })
208- this .getMethodName ( ) = "route" and
209- this .getOptionArgument ( 0 , "handler" ) = handler
210- or
211- // server.ext('/', fun)
212- this .getMethodName ( ) = "ext" and
213- handler = this .getArgument ( 1 )
214- or
215- // server.route([{ handler(request){}])
216- this .getMethodName ( ) = "route" and
217- handler =
218- this .getArgument ( 0 )
219- .getALocalSource ( )
220- .( DataFlow:: ArrayCreationNode )
221- .getAnElement ( )
222- .getALocalSource ( )
223- .getAPropertySource ( "handler" )
224- .getAFunctionValue ( )
225- )
210+ this .getMethodName ( ) = [ "route" , "ext" ]
226211 }
227212
228213 override DataFlow:: SourceNode getARouteHandler ( ) {
@@ -233,11 +218,45 @@ module Hapi {
233218 t .start ( ) and
234219 result = this .getRouteHandler ( ) .getALocalSource ( )
235220 or
236- exists ( DataFlow:: TypeBackTracker t2 | result = this .getARouteHandler ( t2 ) .backtrack ( t2 , t ) )
221+ this .getMethodName ( ) = "route" and
222+ t .isInProp ( "handler" ) and
223+ result = this .getArgument ( 0 ) .getALocalSource ( )
224+ or
225+ exists ( DataFlow:: TypeBackTracker t2 , DataFlow:: SourceNode succ |
226+ succ = this .getARouteHandler ( t2 )
227+ |
228+ result = succ .backtrack ( t2 , t )
229+ or
230+ Http:: routeHandlerStep ( result , succ ) and
231+ t = t2
232+ or
233+ DataFlow:: SharedFlowStep:: storeStep ( result .getALocalUse ( ) , succ ,
234+ DataFlow:: PseudoProperties:: arrayElement ( ) ) and
235+ t = t2 .continue ( )
236+ )
237237 }
238238
239239 pragma [ noinline]
240- private DataFlow:: Node getRouteHandler ( ) { result = handler }
240+ private DataFlow:: Node getRouteHandler ( ) {
241+ // server.route({ handler: fun })
242+ this .getMethodName ( ) = "route" and
243+ this .getOptionArgument ( 0 , "handler" ) = result
244+ or
245+ // server.ext('/', fun)
246+ this .getMethodName ( ) = "ext" and
247+ result = this .getArgument ( 1 )
248+ or
249+ // server.route([{ handler(request){}])
250+ this .getMethodName ( ) = "route" and
251+ result =
252+ this .getArgument ( 0 )
253+ .getALocalSource ( )
254+ .( DataFlow:: ArrayCreationNode )
255+ .getAnElement ( )
256+ .getALocalSource ( )
257+ .getAPropertySource ( "handler" )
258+ .getAFunctionValue ( )
259+ }
241260
242261 override DataFlow:: Node getServer ( ) { result = server }
243262 }
@@ -263,6 +282,56 @@ module Hapi {
263282 }
264283 }
265284
285+ private DataFlow:: SourceNode routeDefinitionRef (
286+ DataFlow:: ObjectLiteralNode definition , DataFlow:: TypeTracker t
287+ ) {
288+ t .start ( ) and
289+ result = definition
290+ or
291+ exists ( DataFlow:: TypeTracker t2 | result = routeDefinitionRef ( definition , t2 ) .track ( t2 , t ) )
292+ }
293+
294+ private predicate handlerRegistration (
295+ DataFlow:: FunctionNode handler , DataFlow:: ObjectLiteralNode definition
296+ ) {
297+ exists (
298+ DataFlow:: CallNode registration , DataFlow:: FunctionNode registrar ,
299+ DataFlow:: ParameterNode handlerParameter , DataFlow:: SourceNode handlerRef , int index
300+ |
301+ registration .getACallee ( ) = registrar .getFunction ( ) and
302+ handlerParameter = registrar .getParameter ( index ) and
303+ handlerParameter .flowsTo ( definition .getAPropertyWrite ( "handler" ) .getRhs ( ) ) and
304+ (
305+ handlerRef = handler
306+ or
307+ handlerRef = CallGraph:: callgraphStep ( handler , DataFlow:: TypeTracker:: end ( ) )
308+ ) and
309+ handlerRef .flowsTo ( registration .getArgument ( index ) )
310+ )
311+ }
312+
313+ /** Data flow through handlers stored in route definitions by registration helpers. */
314+ private class RegisteredHandlerCallStep extends DataFlow:: SharedFlowStep {
315+ DataFlow:: CallNode call ;
316+ DataFlow:: FunctionNode handler ;
317+
318+ RegisteredHandlerCallStep ( ) {
319+ exists ( DataFlow:: ObjectLiteralNode definition , DataFlow:: PropRead handlerRead |
320+ handlerRegistration ( handler , definition ) and
321+ handlerRead .getPropertyName ( ) = "handler" and
322+ routeDefinitionRef ( definition , DataFlow:: TypeTracker:: end ( ) ) .flowsTo ( handlerRead .getBase ( ) ) and
323+ call .getCalleeNode ( ) = handlerRead
324+ )
325+ }
326+
327+ override predicate step ( DataFlow:: Node pred , DataFlow:: Node succ ) {
328+ exists ( int index |
329+ pred = call .getArgument ( index ) and
330+ succ = handler .getParameter ( index )
331+ )
332+ }
333+ }
334+
266335 /**
267336 * A function that looks like a Hapi route handler and flows to a route setup.
268337 */
0 commit comments