@@ -9,151 +9,164 @@ import csharp
9
9
10
10
/** A class representing a Service */
11
11
class ServiceClass extends Class {
12
- ServiceClass ( ) { this .getBaseClass + ( ) .getQualifiedName ( ) = "ServiceStack.Service" }
12
+ ServiceClass ( ) { this .getBaseClass + ( ) .getQualifiedName ( ) = "ServiceStack.Service" }
13
13
14
- /** Get a method that handles incoming requests */
15
- Method getARequestMethod ( ) {
16
- result = this .getAMethod ( [ "Post" , "Get" , "Put" , "Delete" , "Any" , "Option" , "Head" ] )
17
- }
14
+ /** Get a method that handles incoming requests */
15
+ Method getARequestMethod ( ) {
16
+ result = this .getAMethod ( [ "Post" , "Get" , "Put" , "Delete" , "Any" , "Option" , "Head" ] )
17
+ }
18
18
}
19
19
20
20
/** Top-level Request DTO types */
21
21
class RequestDTO extends Class {
22
- RequestDTO ( ) {
23
- this .getABaseInterface ( ) .getQualifiedName ( ) = [ "ServiceStack.IReturn" , "ServieStack.IReturnVoid" ]
24
- }
22
+ RequestDTO ( ) {
23
+ this .getABaseInterface ( ) .getQualifiedName ( ) =
24
+ [ "ServiceStack.IReturn" , "ServieStack.IReturnVoid" ]
25
+ }
25
26
}
26
27
27
28
/** Top-level Response DTO types */
28
29
class ResponseDTO extends Class {
29
- ResponseDTO ( ) {
30
- exists ( RequestDTO req , ConstructedGeneric respInterface |
31
- req .getABaseInterface ( ) = respInterface and
32
- respInterface .getUndecoratedName ( ) = "IReturn" and
33
- respInterface .getATypeArgument ( ) = this
34
- )
35
- }
30
+ ResponseDTO ( ) {
31
+ exists ( RequestDTO req , ConstructedGeneric respInterface |
32
+ req .getABaseInterface ( ) = respInterface and
33
+ respInterface .getUndecoratedName ( ) = "IReturn" and
34
+ respInterface .getATypeArgument ( ) = this
35
+ )
36
+ }
36
37
}
37
38
38
39
/** Flow sources for the ServiceStack framework */
39
40
module Sources {
40
- private import semmle.code.csharp.security.dataflow.flowsources.Remote
41
- private import semmle.code.csharp.commons.Collections
42
-
43
- /** Types involved in a RequestDTO. Recurse through props and collection types */
44
- private predicate involvedInRequest ( RefType c ) {
45
- c instanceof RequestDTO or
46
- exists ( RefType parent , RefType propType | involvedInRequest ( parent ) |
47
- ( propType = parent . getAProperty ( ) . getType ( ) or propType = parent . getAField ( ) . getType ( ) ) and
48
- if propType instanceof CollectionType then (
49
- c = propType . ( ConstructedGeneric ) . getATypeArgument ( ) or
50
- c = propType . ( ArrayType ) . getElementType ( )
51
- ) else (
52
- c = propType
53
- )
54
- )
55
- }
41
+ private import semmle.code.csharp.security.dataflow.flowsources.Remote
42
+ private import semmle.code.csharp.commons.Collections
43
+
44
+ /** Types involved in a RequestDTO. Recurse through props and collection types */
45
+ private predicate involvedInRequest ( RefType c ) {
46
+ c instanceof RequestDTO
47
+ or
48
+ exists ( RefType parent , RefType propType | involvedInRequest ( parent ) |
49
+ ( propType = parent . getAProperty ( ) . getType ( ) or propType = parent . getAField ( ) . getType ( ) ) and
50
+ if propType instanceof CollectionType
51
+ then
52
+ c = propType . ( ConstructedGeneric ) . getATypeArgument ( ) or
53
+ c = propType . ( ArrayType ) . getElementType ( )
54
+ else c = propType
55
+ )
56
+ }
56
57
57
- /**
58
- * Remote flow sources for ServiceStack
59
- *
60
- * Assumes all nested fields/properties on request DTOs are tainted, which is
61
- * an overapproximation and may lead to FPs depending on how Service Stack app
62
- * is configured.
63
- */
64
- class ServiceStackSource extends RemoteFlowSource {
65
- ServiceStackSource ( ) {
66
- // Parameters are sources. In practice only interesting when they are string/primitive typed.
67
- exists ( ServiceClass service |
68
- service .getARequestMethod ( ) .getAParameter ( ) = this .asParameter ( ) ) or
69
- // Field/property accesses on RequestDTOs and request involved types
70
- // involved types aren't necessarily only from requests so may lead to FPs...
71
- exists ( RefType reqType | involvedInRequest ( reqType ) |
72
- reqType .getAProperty ( ) .getAnAccess ( ) = this .asExpr ( ) or
73
- reqType .getAField ( ) .getAnAccess ( ) = this .asExpr ( ) )
74
- }
75
-
76
- override string getSourceType ( ) {
77
- result = "ServiceStack request DTO field"
78
- }
58
+ /**
59
+ * Remote flow sources for ServiceStack
60
+ *
61
+ * Assumes all nested fields/properties on request DTOs are tainted, which is
62
+ * an overapproximation and may lead to FPs depending on how Service Stack app
63
+ * is configured.
64
+ */
65
+ class ServiceStackSource extends RemoteFlowSource {
66
+ ServiceStackSource ( ) {
67
+ // Parameters are sources. In practice only interesting when they are string/primitive typed.
68
+ exists ( ServiceClass service |
69
+ service .getARequestMethod ( ) .getAParameter ( ) = this .asParameter ( )
70
+ )
71
+ or
72
+ // Field/property accesses on RequestDTOs and request involved types
73
+ // involved types aren't necessarily only from requests so may lead to FPs...
74
+ exists ( RefType reqType | involvedInRequest ( reqType ) |
75
+ reqType .getAProperty ( ) .getAnAccess ( ) = this .asExpr ( ) or
76
+ reqType .getAField ( ) .getAnAccess ( ) = this .asExpr ( )
77
+ )
79
78
}
79
+
80
+ override string getSourceType ( ) { result = "ServiceStack request DTO field" }
81
+ }
80
82
}
83
+
81
84
/** Flow Sinks for the ServiceStack framework */
82
85
module Sinks {
83
- private import semmle.code.csharp.security.dataflow.flowsinks.Remote
84
-
85
- /** RemoteFlow sinks for service stack */
86
- class ServiceStackRemoteRequestParameter extends RemoteFlowSink {
87
- ServiceStackRemoteRequestParameter ( ) {
88
- exists ( MethodCall mc |
89
- mc .getTarget ( ) .hasQualifiedName ( "ServiceStack.IRestClient.Get" ) and
90
- mc .getArgument ( 0 ) = this .asExpr ( )
91
- )
92
- }
86
+ private import semmle.code.csharp.security.dataflow.flowsinks.Remote
87
+
88
+ /** RemoteFlow sinks for service stack */
89
+ class ServiceStackRemoteRequestParameter extends RemoteFlowSink {
90
+ ServiceStackRemoteRequestParameter ( ) {
91
+ exists ( MethodCall mc |
92
+ mc .getTarget ( ) .hasQualifiedName ( "ServiceStack.IRestClient.Get" ) and
93
+ mc .getArgument ( 0 ) = this .asExpr ( )
94
+ )
93
95
}
94
96
}
95
-
97
+ }
98
+
96
99
/** SQLi support for the ServiceStack framework */
97
100
module SQL {
98
- private import semmle.code.csharp.security.dataflow.SqlInjection:: SqlInjection
99
-
100
- /** SQLi sinks for ServiceStack */
101
- class ServiceStackSink extends Sink {
102
- ServiceStackSink ( ) {
103
- exists ( MethodCall mc , Method m , int p |
104
- ( mc .getTarget ( ) = m .getAnOverrider * ( ) or mc .getTarget ( ) = m .getAnImplementor * ( ) ) and
105
- sqlSinkParam ( m , p ) and
106
- mc .getArgument ( p ) = this .asExpr ( ) )
107
- }
108
- }
101
+ private import semmle.code.csharp.security.dataflow.SqlInjection:: SqlInjection
109
102
110
- private predicate sqlSinkParam ( Method m , int p ) {
111
- exists ( RefType cls | cls = m .getDeclaringType ( ) |
112
- (
113
- // if using the typed query builder api, only need to worry about Unsafe variants
114
- cls .getQualifiedName ( ) = [ "ServiceStack.OrmLite.SqlExpression" , "ServiceStack.OrmLite.IUntypedSqlExpression" ] and
115
- m .getName ( ) .matches ( "Unsafe%" ) and
116
- p = 0
117
- ) or (
118
- // Read api - all string typed 1st params are potential sql sinks. They should be templates, not directly user controlled.
119
- cls .getQualifiedName ( ) = [ "ServiceStack.OrmLite.OrmLiteReadApi" , "ServiceStack.OrmLite.OrmLiteReadExpressionsApi" , "ServiceStack.OrmLite.OrmLiteReadApiAsync" , "ServiceStack.OrmLite.OrmLiteReadExpressionsApiAsync" ] and
120
- m .getParameter ( p ) .getType ( ) instanceof StringType and
121
- p = 1
122
- ) or (
123
- // Write API - only 2 methods that take string
124
- cls .getQualifiedName ( ) = [ "ServiceStack.OrmLite.OrmLiteWriteApi" , "ServiceStack.OrmLite.OrmLiteWriteApiAsync" ] and
125
- m .getName ( ) = [ "ExecuteSql" , "ExecuteSqlAsync" ] and
126
- p = 1
127
- ) or (
128
- // NoSQL sinks in redis client. TODO should these be separate query?
129
- cls .getQualifiedName ( ) = "ServiceStack.Redis.IRedisClient" and
130
- ( m .getName ( ) = [ "Custom" , "LoadLuaScript" ] or ( m .getName ( ) .matches ( "%Lua%" ) and not m .getName ( ) .matches ( "%Sha%" ) ) ) and
131
- p = 0
132
- )
133
- // TODO
134
- // ServiceStack.OrmLite.OrmLiteUtils.SqlColumn - what about other similar classes?
135
- // couldn't find CustomSelect
136
- // need to handle "PreCreateTable", "PostCreateTable", "PreDropTable", "PostDropTable"
137
-
138
- )
103
+ /** SQLi sinks for ServiceStack */
104
+ class ServiceStackSink extends Sink {
105
+ ServiceStackSink ( ) {
106
+ exists ( MethodCall mc , Method m , int p |
107
+ ( mc .getTarget ( ) = m .getAnOverrider * ( ) or mc .getTarget ( ) = m .getAnImplementor * ( ) ) and
108
+ sqlSinkParam ( m , p ) and
109
+ mc .getArgument ( p ) = this .asExpr ( )
110
+ )
139
111
}
112
+ }
113
+
114
+ private predicate sqlSinkParam ( Method m , int p ) {
115
+ exists ( RefType cls | cls = m .getDeclaringType ( ) |
116
+ // if using the typed query builder api, only need to worry about Unsafe variants
117
+ cls .getQualifiedName ( ) =
118
+ [ "ServiceStack.OrmLite.SqlExpression" , "ServiceStack.OrmLite.IUntypedSqlExpression" ] and
119
+ m .getName ( ) .matches ( "Unsafe%" ) and
120
+ p = 0
121
+ or
122
+ // Read api - all string typed 1st params are potential sql sinks. They should be templates, not directly user controlled.
123
+ cls .getQualifiedName ( ) =
124
+ [
125
+ "ServiceStack.OrmLite.OrmLiteReadApi" , "ServiceStack.OrmLite.OrmLiteReadExpressionsApi" ,
126
+ "ServiceStack.OrmLite.OrmLiteReadApiAsync" ,
127
+ "ServiceStack.OrmLite.OrmLiteReadExpressionsApiAsync"
128
+ ] and
129
+ m .getParameter ( p ) .getType ( ) instanceof StringType and
130
+ p = 1
131
+ or
132
+ // Write API - only 2 methods that take string
133
+ cls .getQualifiedName ( ) =
134
+ [ "ServiceStack.OrmLite.OrmLiteWriteApi" , "ServiceStack.OrmLite.OrmLiteWriteApiAsync" ] and
135
+ m .getName ( ) = [ "ExecuteSql" , "ExecuteSqlAsync" ] and
136
+ p = 1
137
+ or
138
+ // NoSQL sinks in redis client. TODO should these be separate query?
139
+ cls .getQualifiedName ( ) = "ServiceStack.Redis.IRedisClient" and
140
+ (
141
+ m .getName ( ) = [ "Custom" , "LoadLuaScript" ]
142
+ or
143
+ m .getName ( ) .matches ( "%Lua%" ) and not m .getName ( ) .matches ( "%Sha%" )
144
+ ) and
145
+ p = 0
146
+ // TODO
147
+ // ServiceStack.OrmLite.OrmLiteUtils.SqlColumn - what about other similar classes?
148
+ // couldn't find CustomSelect
149
+ // need to handle "PreCreateTable", "PostCreateTable", "PreDropTable", "PostDropTable"
150
+ )
151
+ }
140
152
}
141
153
142
154
/** XSS support for ServiceStack framework */
143
155
module XSS {
144
- private import semmle.code.csharp.security.dataflow.XSS:: XSS
145
-
146
- /** XSS sinks for ServiceStack */
147
- class XssSink extends Sink {
148
- XssSink ( ) {
149
- exists ( ServiceClass service , ReturnStmt r |
150
- this .asExpr ( ) = r .getExpr ( ) and
151
- r .getEnclosingCallable ( ) = service .getARequestMethod ( )
152
- ) or
153
- exists ( ObjectCreation oc |
154
- oc . getType ( ) . hasQualifiedName ( "ServiceStack.HttpResult" ) and
155
- this . asExpr ( ) = oc . getArgument ( 0 )
156
- )
157
- }
156
+ private import semmle.code.csharp.security.dataflow.XSS:: XSS
157
+
158
+ /** XSS sinks for ServiceStack */
159
+ class XssSink extends Sink {
160
+ XssSink ( ) {
161
+ exists ( ServiceClass service , ReturnStmt r |
162
+ this .asExpr ( ) = r .getExpr ( ) and
163
+ r .getEnclosingCallable ( ) = service .getARequestMethod ( )
164
+ )
165
+ or
166
+ exists ( ObjectCreation oc |
167
+ oc . getType ( ) . hasQualifiedName ( "ServiceStack.HttpResult" ) and
168
+ this . asExpr ( ) = oc . getArgument ( 0 )
169
+ )
158
170
}
171
+ }
159
172
}
0 commit comments