66 * directed and labeled; they specify how the components represented by nodes relate to each other.
77 */
88
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
1011import semmle.python.dataflow.new.DataFlow
1112
1213/**
@@ -79,7 +80,7 @@ module API {
7980 /**
8081 * Gets a call to the function represented by this API component.
8182 */
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 ( ) }
8384
8485 /**
8586 * Gets a node representing member `m` of this API component.
@@ -289,6 +290,65 @@ module API {
289290 /** Gets a node corresponding to the built-in with the given name, if any. */
290291 Node builtin ( string n ) { result = moduleImport ( "builtins" ) .getMember ( n ) }
291292
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+
292352 /**
293353 * Provides the actual implementation of API graphs, cached for performance.
294354 *
@@ -376,13 +436,13 @@ module API {
376436 /** An abstract representative for imports of the module called `name`. */
377437 MkModuleImport ( string name ) {
378438 // 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
380440 (
381441 imports ( _, name )
382442 or
383443 // When we `import foo.bar.baz` we want to create API graph nodes also for the prefixes
384444 // `foo` and `foo.bar`:
385- name = any ( ImportExpr e | not e .isRelative ( ) ) .getAnImportedModuleName ( )
445+ name = any ( py :: ImportExpr e | not e .isRelative ( ) ) .getAnImportedModuleName ( )
386446 )
387447 or
388448 // The `builtins` module should always be implicitly available
@@ -418,7 +478,7 @@ module API {
418478 * Ignores relative imports, such as `from ..foo.bar import baz`.
419479 */
420480 private predicate imports ( DataFlow:: Node imp , string name ) {
421- exists ( ImportExprNode iexpr |
481+ exists ( py :: ImportExprNode iexpr |
422482 imp .asCfgNode ( ) = iexpr and
423483 not iexpr .getNode ( ) .isRelative ( ) and
424484 name = iexpr .getNode ( ) .getImportedModuleName ( )
@@ -441,7 +501,7 @@ module API {
441501 *
442502 * `moduleImport("foo").getMember("bar")`
443503 */
444- private TApiNode potential_import_star_base ( Scope s ) {
504+ private TApiNode potential_import_star_base ( py :: Scope s ) {
445505 exists ( DataFlow:: Node n |
446506 n .asCfgNode ( ) = ImportStar:: potentialImportStarBase ( s ) and
447507 use ( result , n )
@@ -464,17 +524,17 @@ module API {
464524 )
465525 or
466526 // 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 |
468528 dict = pred .asExpr ( ) and
469529 dict .getItem ( _) = item and
470- lbl = Label:: member ( item .getKey ( ) .( StrConst ) .getS ( ) ) and
530+ lbl = Label:: member ( item .getKey ( ) .( py :: StrConst ) .getS ( ) ) and
471531 rhs .asExpr ( ) = item .getValue ( )
472532 )
473533 or
474- exists ( CallableExpr fn | fn = pred .asExpr ( ) |
534+ exists ( py :: CallableExpr fn | fn = pred .asExpr ( ) |
475535 not fn .getInnerScope ( ) .isAsync ( ) and
476536 lbl = Label:: return ( ) and
477- exists ( Return ret |
537+ exists ( py :: Return ret |
478538 rhs .asExpr ( ) = ret .getValue ( ) and
479539 ret .getScope ( ) = fn .getInnerScope ( )
480540 )
@@ -517,7 +577,7 @@ module API {
517577 // Subclassing a node
518578 lbl = Label:: subclass ( ) and
519579 exists ( DataFlow:: Node superclass | pred .flowsTo ( superclass ) |
520- ref .asExpr ( ) .( ClassExpr ) .getABase ( ) = superclass .asExpr ( )
580+ ref .asExpr ( ) .( py :: ClassExpr ) .getABase ( ) = superclass .asExpr ( )
521581 )
522582 or
523583 // awaiting
@@ -528,7 +588,7 @@ module API {
528588 )
529589 )
530590 or
531- exists ( DataFlow:: Node def , CallableExpr fn |
591+ exists ( DataFlow:: Node def , py :: CallableExpr fn |
532592 rhs ( base , def ) and fn = trackDefNode ( def ) .asExpr ( )
533593 |
534594 exists ( int i |
@@ -547,7 +607,7 @@ module API {
547607 lbl = Label:: member ( any ( string name | ref = Builtins:: likelyBuiltin ( name ) ) )
548608 or
549609 // Unknown variables that may belong to a module imported with `import *`
550- exists ( Scope s |
610+ exists ( py :: Scope s |
551611 base = potential_import_star_base ( s ) and
552612 lbl =
553613 Label:: member ( any ( string name |
@@ -567,7 +627,7 @@ module API {
567627 )
568628 or
569629 // 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
571631 nd = MkModuleImport ( "builtins" ) and
572632 imports ( ref , "__builtin__" )
573633 or
@@ -710,18 +770,18 @@ module API {
710770 exists ( Builtins:: likelyBuiltin ( member ) ) or
711771 ImportStar:: namePossiblyDefinedInImportStar ( _, member , _) or
712772 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 ( )
714774 } or
715775 MkLabelUnknownMember ( ) or
716776 MkLabelParameter ( int i ) {
717777 exists ( any ( DataFlow:: CallCfgNode c ) .getArg ( i ) )
718778 or
719- exists ( any ( Function f ) .getArg ( i ) )
779+ exists ( any ( py :: Function f ) .getArg ( i ) )
720780 } or
721781 MkLabelNamedParameter ( string name ) {
722782 exists ( any ( DataFlow:: CallCfgNode c ) .getArgByName ( name ) )
723783 or
724- exists ( any ( Function f ) .getArgByName ( name ) )
784+ exists ( any ( py :: Function f ) .getArgByName ( name ) )
725785 } or
726786 MkLabelReturn ( ) or
727787 MkLabelSubclass ( ) or
0 commit comments