4
4
*/
5
5
6
6
import csharp
7
+ private import codeql.util.Unit
7
8
private import semmle.code.csharp.controlflow.Guards
8
9
private import semmle.code.csharp.security.dataflow.flowsinks.FlowSinks
9
10
private import semmle.code.csharp.security.dataflow.flowsources.FlowSources
@@ -24,23 +25,67 @@ abstract class Sink extends ApiSinkExprNode { }
24
25
/**
25
26
* A sanitizer for uncontrolled data in path expression vulnerabilities.
26
27
*/
27
- abstract class Sanitizer extends DataFlow:: ExprNode { }
28
+ abstract class Sanitizer extends DataFlow:: ExprNode {
29
+ /** Holds if this is a sanitizer when the flow state is `state`. */
30
+ predicate isBarrier ( TaintedPathConfig:: FlowState state ) { any ( ) }
31
+ }
32
+
33
+ /** A path normalization step. */
34
+ private class PathNormalizationStep extends Unit {
35
+ /**
36
+ * Holds if the flow step from `n1` to `n2` transforms the path into an
37
+ * absolute path.
38
+ *
39
+ * For example, the argument-to-return-value step through a call
40
+ * to `System.IO.Path.GetFullPath` is a normalization step.
41
+ */
42
+ abstract predicate isAdditionalFlowStep ( DataFlow:: Node n1 , DataFlow:: Node n2 ) ;
43
+ }
44
+
45
+ private class GetFullPathStep extends PathNormalizationStep {
46
+ override predicate isAdditionalFlowStep ( DataFlow:: Node n1 , DataFlow:: Node n2 ) {
47
+ exists ( Call call |
48
+ call .getARuntimeTarget ( ) .hasFullyQualifiedName ( "System.IO.Path" , "GetFullPath" ) and
49
+ n1 .asExpr ( ) = call .getArgument ( 0 ) and
50
+ n2 .asExpr ( ) = call
51
+ )
52
+ }
53
+ }
28
54
29
55
/**
30
56
* A taint-tracking configuration for uncontrolled data in path expression vulnerabilities.
31
57
*/
32
- private module TaintedPathConfig implements DataFlow:: ConfigSig {
33
- predicate isSource ( DataFlow:: Node source ) { source instanceof Source }
58
+ private module TaintedPathConfig implements DataFlow:: StateConfigSig {
59
+ newtype FlowState =
60
+ additional NotNormalized ( ) or
61
+ additional Normalized ( )
62
+
63
+ predicate isSource ( DataFlow:: Node source , FlowState state ) {
64
+ source instanceof Source and state = NotNormalized ( )
65
+ }
34
66
35
- predicate isSink ( DataFlow:: Node sink ) { sink instanceof Sink }
67
+ predicate isSink ( DataFlow:: Node sink , FlowState state ) {
68
+ sink instanceof Sink and
69
+ exists ( state )
70
+ }
71
+
72
+ predicate isAdditionalFlowStep ( DataFlow:: Node n1 , FlowState s1 , DataFlow:: Node n2 , FlowState s2 ) {
73
+ any ( PathNormalizationStep step ) .isAdditionalFlowStep ( n1 , n2 ) and
74
+ s1 = NotNormalized ( ) and
75
+ s2 = Normalized ( )
76
+ }
36
77
37
- predicate isBarrier ( DataFlow:: Node node ) { node instanceof Sanitizer }
78
+ predicate isBarrier ( DataFlow:: Node node , FlowState state ) { node .( Sanitizer ) .isBarrier ( state ) }
79
+
80
+ predicate isBarrierOut ( DataFlow:: Node node , FlowState state ) {
81
+ isAdditionalFlowStep ( node , state , _, _)
82
+ }
38
83
}
39
84
40
85
/**
41
86
* A taint-tracking module for uncontrolled data in path expression vulnerabilities.
42
87
*/
43
- module TaintedPath = TaintTracking:: Global < TaintedPathConfig > ;
88
+ module TaintedPath = TaintTracking:: GlobalWithState < TaintedPathConfig > ;
44
89
45
90
/**
46
91
* DEPRECATED: Use `ThreatModelSource` instead.
@@ -99,7 +144,7 @@ class StreamWriterTaintedPathSink extends Sink {
99
144
}
100
145
101
146
/**
102
- * A weak guard that is insufficient to prevent path tampering.
147
+ * A weak guard that may be insufficient to prevent path tampering.
103
148
*/
104
149
private class WeakGuard extends Guard {
105
150
WeakGuard ( ) {
@@ -118,6 +163,14 @@ private class WeakGuard extends Guard {
118
163
or
119
164
this .( LogicalOperation ) .getAnOperand ( ) instanceof WeakGuard
120
165
}
166
+
167
+ predicate isBarrier ( TaintedPathConfig:: FlowState state ) {
168
+ state = TaintedPathConfig:: Normalized ( ) and
169
+ exists ( Method m | this .( MethodCall ) .getTarget ( ) = m |
170
+ m .getName ( ) = "StartsWith" or
171
+ m .getName ( ) = "EndsWith"
172
+ )
173
+ }
121
174
}
122
175
123
176
/**
@@ -126,12 +179,17 @@ private class WeakGuard extends Guard {
126
179
* A weak check is one that is insufficient to prevent path tampering.
127
180
*/
128
181
class PathCheck extends Sanitizer {
182
+ Guard g ;
183
+
129
184
PathCheck ( ) {
130
- // This expression is structurally replicated in a dominating guard which is not a "weak" check
131
- exists ( Guard g , AbstractValues:: BooleanValue v |
132
- g = this .( GuardedDataFlowNode ) .getAGuard ( _, v ) and
133
- not g instanceof WeakGuard
134
- )
185
+ // This expression is structurally replicated in a dominating guard
186
+ exists ( AbstractValues:: BooleanValue v | g = this .( GuardedDataFlowNode ) .getAGuard ( _, v ) )
187
+ }
188
+
189
+ override predicate isBarrier ( TaintedPathConfig:: FlowState state ) {
190
+ g .( WeakGuard ) .isBarrier ( state )
191
+ or
192
+ not g instanceof WeakGuard
135
193
}
136
194
}
137
195
0 commit comments