@@ -253,6 +253,12 @@ class GraphqlFieldDefinitionMethodCall extends GraphqlSchemaObjectClassMethodCal
253
253
254
254
/** Gets the name of this GraphQL field. */
255
255
string getFieldName ( ) { result = this .getArgument ( 0 ) .getConstantValue ( ) .getStringlikeValue ( ) }
256
+
257
+ GraphqlType getFieldType ( ) { result = this .getArgument ( 1 ) }
258
+
259
+ GraphqlFieldArgumentDefinitionMethodCall getArgumentCall ( ) {
260
+ result .getEnclosingCallable ( ) = this .getBlock ( )
261
+ }
256
262
}
257
263
258
264
/**
@@ -289,6 +295,38 @@ private class GraphqlFieldArgumentDefinitionMethodCall extends GraphqlSchemaObje
289
295
290
296
/** Gets the name of the argument (i.e. the first argument to this `argument` method call) */
291
297
string getArgumentName ( ) { result = this .getArgument ( 0 ) .getConstantValue ( ) .getStringlikeValue ( ) }
298
+
299
+ /** Gets the type of this argument */
300
+ GraphqlType getArgumentType ( ) { result = this .getArgument ( 1 ) }
301
+ }
302
+
303
+ private DataFlow:: LocalSourceNode graphQlEnum ( ) {
304
+ result =
305
+ API:: getTopLevelMember ( "GraphQL" )
306
+ .getMember ( "Schema" )
307
+ .getMember ( "Enum" )
308
+ .getADescendentModule ( )
309
+ .getAnImmediateReference ( )
310
+ }
311
+
312
+ private class GraphqlType extends ConstantAccess {
313
+ Module getModule ( ) { result .getAnImmediateReference ( ) = this }
314
+
315
+ GraphqlType getAField ( ) { result = this .getField ( _) }
316
+
317
+ GraphqlType getField ( string name ) {
318
+ result =
319
+ any ( GraphqlFieldDefinitionMethodCall field |
320
+ field .getFieldName ( ) = name and
321
+ this .getModule ( ) .getADeclaration ( ) = field .getReceiverClass ( )
322
+ ) .getFieldType ( )
323
+ }
324
+
325
+ predicate isEnum ( ) { graphQlEnum ( ) .asExpr ( ) .getExpr ( ) = this }
326
+
327
+ predicate isUserControlled ( ) { this .getName ( ) = [ "String" , "ID" , "JSON" ] }
328
+
329
+ predicate isScalar ( ) { not exists ( this .getAField ( ) ) and not this .isEnum ( ) }
292
330
}
293
331
294
332
/**
@@ -363,16 +401,9 @@ class GraphqlFieldResolutionMethod extends Method, Http::Server::RequestHandler:
363
401
override Parameter getARoutedParameter ( ) {
364
402
result = this .getAParameter ( ) and
365
403
exists ( GraphqlFieldArgumentDefinitionMethodCall argDefn |
366
- argDefn .getEnclosingCallable ( ) = this .getDefinition ( ) .getBlock ( ) and
367
- (
368
- result .( KeywordParameter ) .hasName ( argDefn .getArgumentName ( ) )
369
- or
370
- // TODO this will cause false positives because now *anything* in the **args
371
- // param will be flagged as RoutedParameter/RemoteFlowSource, but really
372
- // only the hash keys corresponding to the defined arguments are user input
373
- // others could be things defined in the `:extras` keyword argument to the `argument`
374
- result instanceof HashSplatParameter // often you see `def field(**args)`
375
- )
404
+ argDefn = this .getDefinition ( ) .getArgumentCall ( )
405
+ |
406
+ result .( KeywordParameter ) .hasName ( argDefn .getArgumentName ( ) )
376
407
)
377
408
}
378
409
@@ -383,3 +414,43 @@ class GraphqlFieldResolutionMethod extends Method, Http::Server::RequestHandler:
383
414
/** Gets the class containing this method. */
384
415
GraphqlSchemaObjectClass getGraphqlClass ( ) { result = schemaObjectClass }
385
416
}
417
+
418
+ private DataFlow:: CallNode hashAccess ( DataFlow:: Node recv , string key ) {
419
+ result .asExpr ( ) instanceof ExprNodes:: ElementReferenceCfgNode and
420
+ result .getArgument ( 0 ) .getConstantValue ( ) .isStringlikeValue ( key ) and
421
+ result .getReceiver ( ) = recv
422
+ }
423
+
424
+ private DataFlow:: CallNode parameterAccess (
425
+ GraphqlFieldResolutionMethod method , GraphqlFieldArgumentDefinitionMethodCall def ,
426
+ HashSplatParameter param , string key , GraphqlType type
427
+ ) {
428
+ param = method .getARoutedParameter ( ) and
429
+ def = method .getDefinition ( ) .getArgumentCall ( ) and
430
+ (
431
+ // Direct access to the params hash
432
+ def .getArgumentType ( ) = type and
433
+ def .getArgumentName ( ) = key and
434
+ exists ( DataFlow:: Node paramRead |
435
+ paramRead .asExpr ( ) .getExpr ( ) = param .getVariable ( ) .getAnAccess ( ) .( VariableReadAccess ) and
436
+ result = hashAccess ( paramRead , key )
437
+ )
438
+ or
439
+ // Nested access
440
+ exists ( GraphqlType type2 |
441
+ parameterAccess ( method , _, param , _, type2 )
442
+ .( DataFlow:: LocalSourceNode )
443
+ .flowsTo ( result .getReceiver ( ) ) and
444
+ result = hashAccess ( _, key ) and
445
+ type2 .getField ( key ) = type
446
+ )
447
+ )
448
+ }
449
+
450
+ private class GraphqlParameterAccess extends RemoteFlowSource:: Range {
451
+ GraphqlParameterAccess ( ) {
452
+ exists ( GraphqlType type | this = parameterAccess ( _, _, _, _, type ) and type .isScalar ( ) )
453
+ }
454
+
455
+ override string getSourceType ( ) { result = "GraphQL" }
456
+ }
0 commit comments