1
1
/**
2
- * @name Cors header being set from remote source
3
- * @description Cors header is being set from remote source , allowing to control the origin .
2
+ * @name CORS is derived from untrusted input
3
+ * @description CORS header is derived from untrusted input , allowing a remote user to control which origins are trusted .
4
4
* @kind path-problem
5
5
* @problem.severity error
6
6
* @precision high
@@ -13,6 +13,7 @@ import java
13
13
import semmle.code.java.dataflow.FlowSources
14
14
import semmle.code.java.frameworks.Servlets
15
15
import semmle.code.java.dataflow.TaintTracking
16
+ import semmle.code.java.dataflow.TaintTracking2
16
17
import DataFlow:: PathGraph
17
18
18
19
/**
@@ -25,10 +26,10 @@ private predicate setsAllowCredentials(MethodAccess header) {
25
26
) and
26
27
header .getArgument ( 0 ) .( CompileTimeConstantExpr ) .getStringValue ( ) .toLowerCase ( ) =
27
28
"access-control-allow-credentials" and
28
- header .getArgument ( 1 ) .( CompileTimeConstantExpr ) .getStringValue ( ) = "true"
29
+ header .getArgument ( 1 ) .( CompileTimeConstantExpr ) .getStringValue ( ) . toLowerCase ( ) = "true"
29
30
}
30
31
31
- class CorsProbableCheckAccess extends MethodAccess {
32
+ private class CorsProbableCheckAccess extends MethodAccess {
32
33
CorsProbableCheckAccess ( ) {
33
34
getMethod ( ) .hasName ( "contains" ) and
34
35
getMethod ( ) .getDeclaringType ( ) .getASourceSupertype * ( ) instanceof CollectionType
@@ -45,46 +46,41 @@ private Expr getAccessControlAllowOriginHeaderName() {
45
46
result .( CompileTimeConstantExpr ) .getStringValue ( ) .toLowerCase ( ) = "access-control-allow-origin"
46
47
}
47
48
49
+ /**
50
+ * This taintflow2 configuration checks if there is a flow from source node towards CorsProbableCheckAccess methods.
51
+ */
52
+ class CorsSourceReachesCheckConfig extends TaintTracking2:: Configuration {
53
+ CorsSourceReachesCheckConfig ( ) { this = "CorsOriginConfig" }
54
+
55
+ override predicate isSource ( DataFlow:: Node source ) { any ( CorsOriginConfig c ) .hasFlow ( source , _) }
56
+
57
+ override predicate isSink ( DataFlow:: Node sink ) {
58
+ sink .asExpr ( ) = any ( CorsProbableCheckAccess check ) .getAnArgument ( )
59
+ }
60
+ }
61
+
48
62
class CorsOriginConfig extends TaintTracking:: Configuration {
49
63
CorsOriginConfig ( ) { this = "CorsOriginConfig" }
50
64
51
65
override predicate isSource ( DataFlow:: Node source ) { source instanceof RemoteFlowSource }
52
66
53
67
override predicate isSink ( DataFlow:: Node sink ) {
54
- exists ( MethodAccess corsheader , MethodAccess allowcredentialsheader |
68
+ exists ( MethodAccess corsHeader , MethodAccess allowCredentialsHeader |
55
69
(
56
- corsheader .getMethod ( ) instanceof ResponseSetHeaderMethod or
57
- corsheader .getMethod ( ) instanceof ResponseAddHeaderMethod
70
+ corsHeader .getMethod ( ) instanceof ResponseSetHeaderMethod or
71
+ corsHeader .getMethod ( ) instanceof ResponseAddHeaderMethod
58
72
) and
59
- getAccessControlAllowOriginHeaderName ( ) = corsheader .getArgument ( 0 ) and
60
- setsAllowCredentials ( allowcredentialsheader ) and
61
- corsheader .getEnclosingCallable ( ) = allowcredentialsheader .getEnclosingCallable ( ) and
62
- sink .asExpr ( ) = corsheader .getArgument ( 1 )
63
- )
64
- }
65
-
66
- /**
67
- * this sanitizer is oversimplistic:
68
- * - it only considers local dataflows
69
- * - it will consider any method calling `Collection.contains` or `String.equals` as a sanitizer
70
- * no matter if that check is taken into account and its result reaches the
71
- * return statement of the wrapper.
72
- */
73
- override predicate isSanitizer ( DataFlow:: Node node ) {
74
- exists ( CorsProbableCheckAccess check |
75
- TaintTracking:: localTaint ( node , DataFlow:: exprNode ( check .getAnArgument ( ) ) )
76
- or
77
- exists ( MethodAccess wrapperAccess , Method wrapper , int i |
78
- TaintTracking:: localTaint ( node , DataFlow:: exprNode ( wrapperAccess .getArgument ( i ) ) ) and
79
- wrapperAccess .getMethod ( ) = wrapper and
80
- TaintTracking:: localTaint ( DataFlow:: parameterNode ( wrapper .getParameter ( i ) ) ,
81
- DataFlow:: exprNode ( check .getAnArgument ( ) ) )
82
- )
73
+ getAccessControlAllowOriginHeaderName ( ) = corsHeader .getArgument ( 0 ) and
74
+ setsAllowCredentials ( allowCredentialsHeader ) and
75
+ corsHeader .getEnclosingCallable ( ) = allowCredentialsHeader .getEnclosingCallable ( ) and
76
+ sink .asExpr ( ) = corsHeader .getArgument ( 1 )
83
77
)
84
78
}
85
79
}
86
80
87
- from DataFlow:: PathNode source , DataFlow:: PathNode sink , CorsOriginConfig conf
88
- where conf .hasFlowPath ( source , sink )
89
- select sink .getNode ( ) , source , sink , "Cors header is being set using user controlled value $@." ,
81
+ from
82
+ DataFlow:: PathNode source , DataFlow:: PathNode sink , CorsOriginConfig conf ,
83
+ CorsSourceReachesCheckConfig sanconf
84
+ where conf .hasFlowPath ( source , sink ) and not sanconf .hasFlow ( source .getNode ( ) , _)
85
+ select sink .getNode ( ) , source , sink , "CORS header is being set using user controlled value $@." ,
90
86
source .getNode ( ) , "user-provided value"
0 commit comments