@@ -2,7 +2,9 @@ import python
2
2
import semmle.python.dataflow.new.DataFlow
3
3
import semmle.python.ApiGraphs
4
4
import TestUtilities.InlineExpectationsTest
5
+ import semmle.python.dataflow.new.internal.ImportResolution
5
6
7
+ /** A string that appears on the right hand side of an assignment. */
6
8
private class SourceString extends DataFlow:: Node {
7
9
string contents ;
8
10
@@ -14,6 +16,45 @@ private class SourceString extends DataFlow::Node {
14
16
string getContents ( ) { result = contents }
15
17
}
16
18
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
+
17
58
private class ImportConfiguration extends DataFlow:: Configuration {
18
59
ImportConfiguration ( ) { this = "ImportConfiguration" }
19
60
@@ -30,12 +71,29 @@ class ResolutionTest extends InlineExpectationsTest {
30
71
override string getARelevantTag ( ) { result = "prints" }
31
72
32
73
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
+ )
39
92
)
40
93
}
41
94
}
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