@@ -9,151 +9,164 @@ import csharp
99
1010/** A class representing a Service */
1111class ServiceClass extends Class {
12- ServiceClass ( ) { this .getBaseClass + ( ) .getQualifiedName ( ) = "ServiceStack.Service" }
12+ ServiceClass ( ) { this .getBaseClass + ( ) .getQualifiedName ( ) = "ServiceStack.Service" }
1313
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+ }
1818}
1919
2020/** Top-level Request DTO types */
2121class 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+ }
2526}
2627
2728/** Top-level Response DTO types */
2829class 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+ }
3637}
3738
3839/** Flow sources for the ServiceStack framework */
3940module 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+ }
5657
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+ )
7978 }
79+
80+ override string getSourceType ( ) { result = "ServiceStack request DTO field" }
81+ }
8082}
83+
8184/** Flow Sinks for the ServiceStack framework */
8285module 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+ )
9395 }
9496 }
95-
97+ }
98+
9699/** SQLi support for the ServiceStack framework */
97100module 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
109102
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+ )
139111 }
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+ }
140152}
141153
142154/** XSS support for ServiceStack framework */
143155module 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+ )
158170 }
171+ }
159172}
0 commit comments