Skip to content

Commit 3e889c3

Browse files
committed
updated document formatting
1 parent 858c0e6 commit 3e889c3

File tree

1 file changed

+131
-118
lines changed

1 file changed

+131
-118
lines changed

csharp/ql/src/semmle/code/csharp/frameworks/ServiceStack.qll

Lines changed: 131 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -9,151 +9,164 @@ import csharp
99

1010
/** A class representing a Service */
1111
class 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 */
2121
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+
}
2526
}
2627

2728
/** Top-level Response DTO types */
2829
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+
}
3637
}
3738

3839
/** Flow sources for the ServiceStack framework */
3940
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+
}
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 */
8285
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+
)
9395
}
9496
}
95-
97+
}
98+
9699
/** SQLi support for the ServiceStack framework */
97100
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
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 */
143155
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+
)
158170
}
171+
}
159172
}

0 commit comments

Comments
 (0)