44
55import javascript
66import semmle.javascript.frameworks.HTTP
7+ private import semmle.javascript.dataflow.internal.CallGraphs
78
89module Hapi {
910 /**
@@ -101,6 +102,19 @@ module Hapi {
101102 override RequestSource src ;
102103 }
103104
105+ private DataFlow:: SourceNode requestInputRef (
106+ RouteHandler rh , string property , DataFlow:: TypeTracker t
107+ ) {
108+ t .start ( ) and
109+ result = rh .getARequestSource ( ) .ref ( ) .getAPropertyRead ( property )
110+ or
111+ exists ( DataFlow:: TypeTracker t2 | result = requestInputRef ( rh , property , t2 ) .track ( t2 , t ) )
112+ }
113+
114+ private DataFlow:: SourceNode requestInputRef ( RouteHandler rh , string property ) {
115+ result = requestInputRef ( rh , property , DataFlow:: TypeTracker:: end ( ) )
116+ }
117+
104118 /**
105119 * An access to a user-controlled Hapi request input.
106120 */
@@ -115,18 +129,18 @@ module Hapi {
115129 // `request.rawPayload`
116130 this .( DataFlow:: PropRead ) .accesses ( request , "rawPayload" )
117131 or
118- exists ( DataFlow:: PropRead payload |
119- // `request.payload.name`
120- payload .accesses ( request , "payload" ) and
121- this .( DataFlow:: PropRead ) .accesses ( payload , _)
122- )
132+ // `request.payload` is an object, so prefer a property read if possible.
133+ if exists ( requestInputRef ( rh , "payload" ) .getAPropertyRead ( ) )
134+ then this = requestInputRef ( rh , "payload" ) .getAPropertyRead ( )
135+ else this = rh .getARequestSource ( ) .ref ( ) .getAPropertyRead ( "payload" )
123136 )
124137 or
125138 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 , _)
139+ exists ( string property | property = [ "query" , "params" ] |
140+ // These are objects, so prefer a property read if possible.
141+ if exists ( requestInputRef ( rh , property ) .getAPropertyRead ( ) )
142+ then this = requestInputRef ( rh , property ) .getAPropertyRead ( )
143+ else this = rh .getARequestSource ( ) .ref ( ) .getAPropertyRead ( property )
130144 )
131145 or
132146 exists ( DataFlow:: PropRead url |
@@ -199,30 +213,10 @@ module Hapi {
199213 */
200214 class RouteSetup extends DataFlow:: MethodCallNode , Http:: Servers:: StandardRouteSetup {
201215 ServerDefinition server ;
202- DataFlow:: Node handler ;
203216
204217 RouteSetup ( ) {
205218 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- )
219+ this .getMethodName ( ) = [ "route" , "ext" ]
226220 }
227221
228222 override DataFlow:: SourceNode getARouteHandler ( ) {
@@ -233,11 +227,45 @@ module Hapi {
233227 t .start ( ) and
234228 result = this .getRouteHandler ( ) .getALocalSource ( )
235229 or
236- exists ( DataFlow:: TypeBackTracker t2 | result = this .getARouteHandler ( t2 ) .backtrack ( t2 , t ) )
230+ this .getMethodName ( ) = "route" and
231+ t .isInProp ( "handler" ) and
232+ result = this .getArgument ( 0 ) .getALocalSource ( )
233+ or
234+ exists ( DataFlow:: TypeBackTracker t2 , DataFlow:: SourceNode succ |
235+ succ = this .getARouteHandler ( t2 )
236+ |
237+ result = succ .backtrack ( t2 , t )
238+ or
239+ Http:: routeHandlerStep ( result , succ ) and
240+ t = t2
241+ or
242+ DataFlow:: SharedFlowStep:: storeStep ( result .getALocalUse ( ) , succ ,
243+ DataFlow:: PseudoProperties:: arrayElement ( ) ) and
244+ t = t2 .continue ( )
245+ )
237246 }
238247
239248 pragma [ noinline]
240- private DataFlow:: Node getRouteHandler ( ) { result = handler }
249+ private DataFlow:: Node getRouteHandler ( ) {
250+ // server.route({ handler: fun })
251+ this .getMethodName ( ) = "route" and
252+ this .getOptionArgument ( 0 , "handler" ) = result
253+ or
254+ // server.ext('/', fun)
255+ this .getMethodName ( ) = "ext" and
256+ result = this .getArgument ( 1 )
257+ or
258+ // server.route([{ handler(request){}])
259+ this .getMethodName ( ) = "route" and
260+ result =
261+ this .getArgument ( 0 )
262+ .getALocalSource ( )
263+ .( DataFlow:: ArrayCreationNode )
264+ .getAnElement ( )
265+ .getALocalSource ( )
266+ .getAPropertySource ( "handler" )
267+ .getAFunctionValue ( )
268+ }
241269
242270 override DataFlow:: Node getServer ( ) { result = server }
243271 }
@@ -263,6 +291,56 @@ module Hapi {
263291 }
264292 }
265293
294+ private DataFlow:: SourceNode routeDefinitionRef (
295+ DataFlow:: ObjectLiteralNode definition , DataFlow:: TypeTracker t
296+ ) {
297+ t .start ( ) and
298+ result = definition
299+ or
300+ exists ( DataFlow:: TypeTracker t2 | result = routeDefinitionRef ( definition , t2 ) .track ( t2 , t ) )
301+ }
302+
303+ private predicate handlerRegistration (
304+ DataFlow:: FunctionNode handler , DataFlow:: ObjectLiteralNode definition
305+ ) {
306+ exists (
307+ DataFlow:: CallNode registration , DataFlow:: FunctionNode registrar ,
308+ DataFlow:: ParameterNode handlerParameter , DataFlow:: SourceNode handlerRef , int index
309+ |
310+ registration .getACallee ( ) = registrar .getFunction ( ) and
311+ handlerParameter = registrar .getParameter ( index ) and
312+ handlerParameter .flowsTo ( definition .getAPropertyWrite ( "handler" ) .getRhs ( ) ) and
313+ (
314+ handlerRef = handler
315+ or
316+ handlerRef = CallGraph:: callgraphStep ( handler , DataFlow:: TypeTracker:: end ( ) )
317+ ) and
318+ handlerRef .flowsTo ( registration .getArgument ( index ) )
319+ )
320+ }
321+
322+ /** Data flow through handlers stored in route definitions by registration helpers. */
323+ private class RegisteredHandlerCallStep extends DataFlow:: SharedFlowStep {
324+ DataFlow:: CallNode call ;
325+ DataFlow:: FunctionNode handler ;
326+
327+ RegisteredHandlerCallStep ( ) {
328+ exists ( DataFlow:: ObjectLiteralNode definition , DataFlow:: PropRead handlerRead |
329+ handlerRegistration ( handler , definition ) and
330+ handlerRead .getPropertyName ( ) = "handler" and
331+ routeDefinitionRef ( definition , DataFlow:: TypeTracker:: end ( ) ) .flowsTo ( handlerRead .getBase ( ) ) and
332+ call .getCalleeNode ( ) = handlerRead
333+ )
334+ }
335+
336+ override predicate step ( DataFlow:: Node pred , DataFlow:: Node succ ) {
337+ exists ( int index |
338+ pred = call .getArgument ( index ) and
339+ succ = handler .getParameter ( index )
340+ )
341+ }
342+ }
343+
266344 /**
267345 * A function that looks like a Hapi route handler and flows to a route setup.
268346 */
0 commit comments