@@ -2,7 +2,9 @@ import python
22import semmle.python.dataflow.new.DataFlow
33import semmle.python.ApiGraphs
44import TestUtilities.InlineExpectationsTest
5+ import semmle.python.dataflow.new.internal.ImportResolution
56
7+ /** A string that appears on the right hand side of an assignment. */
68private class SourceString extends DataFlow:: Node {
79 string contents ;
810
@@ -14,6 +16,45 @@ private class SourceString extends DataFlow::Node {
1416 string getContents ( ) { result = contents }
1517}
1618
19+ /** An argument that is checked using the `check` function. */
20+ private class CheckArgument extends DataFlow:: Node {
21+ CheckArgument ( ) { this = API:: moduleImport ( "trace" ) .getMember ( "check" ) .getACall ( ) .getArg ( 1 ) }
22+ }
23+
24+ /** A data-flow node that is a reference to a module. */
25+ private class ModuleRef extends DataFlow:: Node {
26+ Module mod ;
27+
28+ ModuleRef ( ) {
29+ this = ImportResolution:: getModuleReference ( mod ) and
30+ not mod .getName ( ) in [ "__future__" , "trace" ]
31+ }
32+
33+ string getName ( ) { result = mod .getName ( ) }
34+ }
35+
36+ /**
37+ * A data-flow node that is guarded by a version check. Only supports checks of the form `if
38+ *sys.version_info[0] == ...` where the right hand side is either `2` or `3`.
39+ */
40+ private class VersionGuardedNode extends DataFlow:: Node {
41+ int version ;
42+
43+ VersionGuardedNode ( ) {
44+ version in [ 2 , 3 ] and
45+ exists ( If parent , CompareNode c | parent .getBody ( ) .contains ( this .asExpr ( ) ) |
46+ c .operands ( API:: moduleImport ( "sys" )
47+ .getMember ( "version_info" )
48+ .getASubscript ( )
49+ .asSource ( )
50+ .asCfgNode ( ) , any ( Eq eq ) ,
51+ any ( IntegerLiteral lit | lit .getValue ( ) = version ) .getAFlowNode ( ) )
52+ )
53+ }
54+
55+ int getVersion ( ) { result = version }
56+ }
57+
1758private class ImportConfiguration extends DataFlow:: Configuration {
1859 ImportConfiguration ( ) { this = "ImportConfiguration" }
1960
@@ -30,12 +71,29 @@ class ResolutionTest extends InlineExpectationsTest {
3071 override string getARelevantTag ( ) { result = "prints" }
3172
3273 override predicate hasActualResult ( Location location , string element , string tag , string value ) {
33- exists ( DataFlow:: PathNode source , DataFlow:: PathNode sink , ImportConfiguration config |
34- config .hasFlowPath ( source , sink ) and
35- tag = "prints" and
36- location = sink .getNode ( ) .getLocation ( ) and
37- value = source .getNode ( ) .( SourceString ) .getContents ( ) and
38- element = sink .getNode ( ) .toString ( )
74+ (
75+ exists ( DataFlow:: PathNode source , DataFlow:: PathNode sink , ImportConfiguration config |
76+ config .hasFlowPath ( source , sink ) and
77+ correct_version ( sink .getNode ( ) ) and
78+ tag = "prints" and
79+ location = sink .getNode ( ) .getLocation ( ) and
80+ value = source .getNode ( ) .( SourceString ) .getContents ( ) and
81+ element = sink .getNode ( ) .toString ( )
82+ )
83+ or
84+ exists ( ModuleRef ref |
85+ correct_version ( ref ) and
86+ ref instanceof CheckArgument and
87+ tag = "prints" and
88+ location = ref .getLocation ( ) and
89+ value = "\"<module " + ref .getName ( ) + ">\"" and
90+ element = ref .toString ( )
91+ )
3992 )
4093 }
4194}
95+
96+ private predicate correct_version ( DataFlow:: Node n ) {
97+ not n instanceof VersionGuardedNode or
98+ n .( VersionGuardedNode ) .getVersion ( ) = major_version ( )
99+ }
0 commit comments