6
6
* directed and labeled; they specify how the components represented by nodes relate to each other.
7
7
*/
8
8
9
- private import python
9
+ // Importing python under the `py` namespace to avoid importing `CallNode` from `Flow.qll` and thereby having a naming conflict with `API::CallNode`.
10
+ private import python as py
10
11
import semmle.python.dataflow.new.DataFlow
11
12
12
13
/**
@@ -79,7 +80,7 @@ module API {
79
80
/**
80
81
* Gets a call to the function represented by this API component.
81
82
*/
82
- DataFlow :: CallCfgNode getACall ( ) { result = this .getReturn ( ) .getAnImmediateUse ( ) } // TODO: Make a API::CallNode. After I figure out named parameters
83
+ CallNode getACall ( ) { result = this .getReturn ( ) .getAnImmediateUse ( ) }
83
84
84
85
/**
85
86
* Gets a node representing member `m` of this API component.
@@ -289,6 +290,65 @@ module API {
289
290
/** Gets a node corresponding to the built-in with the given name, if any. */
290
291
Node builtin ( string n ) { result = moduleImport ( "builtins" ) .getMember ( n ) }
291
292
293
+ /**
294
+ * An `CallCfgNode` that is connected to the API graph.
295
+ *
296
+ * Can be used to reason about calls to an external API in which the correlation between
297
+ * parameters and/or return values must be retained.
298
+ *
299
+ * The member predicates `getParameter`, `getNamedParameter`, `getReturn`, and `getInstance` mimic
300
+ * the corresponding predicates from `API::Node`. These are guaranteed to exist and be unique to this call.
301
+ */
302
+ class CallNode extends DataFlow:: CallCfgNode {
303
+ API:: Node callee ;
304
+
305
+ CallNode ( ) { this = callee .getReturn ( ) .getAnImmediateUse ( ) }
306
+
307
+ /** Gets the API node for the `i`th parameter of this invocation. */
308
+ pragma [ nomagic]
309
+ Node getParameter ( int i ) {
310
+ result = callee .getParameter ( i ) and
311
+ result = this .getAParameterCandidate ( i )
312
+ }
313
+
314
+ /**
315
+ * Gets an API node where a RHS of the node is the `i`th argument to this call.
316
+ */
317
+ pragma [ noinline]
318
+ private Node getAParameterCandidate ( int i ) { result .getARhs ( ) = this .getArg ( i ) }
319
+
320
+ /** Gets the API node for a parameter of this invocation. */
321
+ Node getAParameter ( ) { result = this .getParameter ( _) }
322
+
323
+ /** Gets the API node for the last parameter of this invocation. */
324
+ Node getLastParameter ( ) { result = this .getParameter ( max ( int i | exists ( this .getArg ( i ) ) ) ) }
325
+
326
+ /** Gets the API node for the parameter named `name` of this invocation. */
327
+ Node getNamedParameter ( string name ) {
328
+ result = callee .getNamedParameter ( name ) and
329
+ result = this .getANamedParameterCandidate ( name )
330
+ }
331
+
332
+ /** Gets the API node for the parameter that has index `i` or is named `name`. */
333
+ bindingset [ i, name]
334
+ Node getParameter ( int i , string name ) {
335
+ result = this .getParameter ( i )
336
+ or
337
+ result = this .getNamedParameter ( name )
338
+ }
339
+
340
+ pragma [ noinline]
341
+ private Node getANamedParameterCandidate ( string name ) {
342
+ result .getARhs ( ) = this .getArgByName ( name )
343
+ }
344
+
345
+ /** Gets the API node for the return value of this call. */
346
+ Node getReturn ( ) {
347
+ result = callee .getReturn ( ) and
348
+ result .getAnImmediateUse ( ) = this
349
+ }
350
+ }
351
+
292
352
/**
293
353
* Provides the actual implementation of API graphs, cached for performance.
294
354
*
@@ -376,13 +436,13 @@ module API {
376
436
/** An abstract representative for imports of the module called `name`. */
377
437
MkModuleImport ( string name ) {
378
438
// Ignore the following module name for Python 2, as we alias `__builtin__` to `builtins` elsewhere
379
- ( name != "__builtin__" or major_version ( ) = 3 ) and
439
+ ( name != "__builtin__" or py :: major_version ( ) = 3 ) and
380
440
(
381
441
imports ( _, name )
382
442
or
383
443
// When we `import foo.bar.baz` we want to create API graph nodes also for the prefixes
384
444
// `foo` and `foo.bar`:
385
- name = any ( ImportExpr e | not e .isRelative ( ) ) .getAnImportedModuleName ( )
445
+ name = any ( py :: ImportExpr e | not e .isRelative ( ) ) .getAnImportedModuleName ( )
386
446
)
387
447
or
388
448
// The `builtins` module should always be implicitly available
@@ -418,7 +478,7 @@ module API {
418
478
* Ignores relative imports, such as `from ..foo.bar import baz`.
419
479
*/
420
480
private predicate imports ( DataFlow:: Node imp , string name ) {
421
- exists ( ImportExprNode iexpr |
481
+ exists ( py :: ImportExprNode iexpr |
422
482
imp .asCfgNode ( ) = iexpr and
423
483
not iexpr .getNode ( ) .isRelative ( ) and
424
484
name = iexpr .getNode ( ) .getImportedModuleName ( )
@@ -441,7 +501,7 @@ module API {
441
501
*
442
502
* `moduleImport("foo").getMember("bar")`
443
503
*/
444
- private TApiNode potential_import_star_base ( Scope s ) {
504
+ private TApiNode potential_import_star_base ( py :: Scope s ) {
445
505
exists ( DataFlow:: Node n |
446
506
n .asCfgNode ( ) = ImportStar:: potentialImportStarBase ( s ) and
447
507
use ( result , n )
@@ -464,17 +524,17 @@ module API {
464
524
)
465
525
or
466
526
// TODO: I had expected `DataFlow::AttrWrite` to contain the attribute writes from a dict, that's how JS works.
467
- exists ( Dict dict , KeyValuePair item |
527
+ exists ( py :: Dict dict , py :: KeyValuePair item |
468
528
dict = pred .asExpr ( ) and
469
529
dict .getItem ( _) = item and
470
- lbl = Label:: member ( item .getKey ( ) .( StrConst ) .getS ( ) ) and
530
+ lbl = Label:: member ( item .getKey ( ) .( py :: StrConst ) .getS ( ) ) and
471
531
rhs .asExpr ( ) = item .getValue ( )
472
532
)
473
533
or
474
- exists ( CallableExpr fn | fn = pred .asExpr ( ) |
534
+ exists ( py :: CallableExpr fn | fn = pred .asExpr ( ) |
475
535
not fn .getInnerScope ( ) .isAsync ( ) and
476
536
lbl = Label:: return ( ) and
477
- exists ( Return ret |
537
+ exists ( py :: Return ret |
478
538
rhs .asExpr ( ) = ret .getValue ( ) and
479
539
ret .getScope ( ) = fn .getInnerScope ( )
480
540
)
@@ -517,7 +577,7 @@ module API {
517
577
// Subclassing a node
518
578
lbl = Label:: subclass ( ) and
519
579
exists ( DataFlow:: Node superclass | pred .flowsTo ( superclass ) |
520
- ref .asExpr ( ) .( ClassExpr ) .getABase ( ) = superclass .asExpr ( )
580
+ ref .asExpr ( ) .( py :: ClassExpr ) .getABase ( ) = superclass .asExpr ( )
521
581
)
522
582
or
523
583
// awaiting
@@ -528,7 +588,7 @@ module API {
528
588
)
529
589
)
530
590
or
531
- exists ( DataFlow:: Node def , CallableExpr fn |
591
+ exists ( DataFlow:: Node def , py :: CallableExpr fn |
532
592
rhs ( base , def ) and fn = trackDefNode ( def ) .asExpr ( )
533
593
|
534
594
exists ( int i |
@@ -547,7 +607,7 @@ module API {
547
607
lbl = Label:: member ( any ( string name | ref = Builtins:: likelyBuiltin ( name ) ) )
548
608
or
549
609
// Unknown variables that may belong to a module imported with `import *`
550
- exists ( Scope s |
610
+ exists ( py :: Scope s |
551
611
base = potential_import_star_base ( s ) and
552
612
lbl =
553
613
Label:: member ( any ( string name |
@@ -567,7 +627,7 @@ module API {
567
627
)
568
628
or
569
629
// Ensure the Python 2 `__builtin__` module gets the name of the Python 3 `builtins` module.
570
- major_version ( ) = 2 and
630
+ py :: major_version ( ) = 2 and
571
631
nd = MkModuleImport ( "builtins" ) and
572
632
imports ( ref , "__builtin__" )
573
633
or
@@ -710,18 +770,18 @@ module API {
710
770
exists ( Builtins:: likelyBuiltin ( member ) ) or
711
771
ImportStar:: namePossiblyDefinedInImportStar ( _, member , _) or
712
772
Impl:: prefix_member ( _, member , _) or
713
- member = any ( Dict d ) .getAnItem ( ) .( KeyValuePair ) .getKey ( ) .( StrConst ) .getS ( )
773
+ member = any ( py :: Dict d ) .getAnItem ( ) .( py :: KeyValuePair ) .getKey ( ) .( py :: StrConst ) .getS ( )
714
774
} or
715
775
MkLabelUnknownMember ( ) or
716
776
MkLabelParameter ( int i ) {
717
777
exists ( any ( DataFlow:: CallCfgNode c ) .getArg ( i ) )
718
778
or
719
- exists ( any ( Function f ) .getArg ( i ) )
779
+ exists ( any ( py :: Function f ) .getArg ( i ) )
720
780
} or
721
781
MkLabelNamedParameter ( string name ) {
722
782
exists ( any ( DataFlow:: CallCfgNode c ) .getArgByName ( name ) )
723
783
or
724
- exists ( any ( Function f ) .getArgByName ( name ) )
784
+ exists ( any ( py :: Function f ) .getArgByName ( name ) )
725
785
} or
726
786
MkLabelReturn ( ) or
727
787
MkLabelSubclass ( ) or
0 commit comments