Skip to content

Commit 402ed04

Browse files
authored
Merge pull request github#4844 from johnlugton/servicestack
Add provisional support for ServiceStack framework to feature branch
2 parents 7bfc285 + 059d6b0 commit 402ed04

File tree

4 files changed

+148
-0
lines changed

4 files changed

+148
-0
lines changed
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/**
2+
* General modelling of ServiceStack framework including separate modules for:
3+
* - flow sources
4+
* - SQLi sinks
5+
* - XSS sinks
6+
*/
7+
8+
import csharp
9+
10+
/** A class representing a Service */
11+
class ServiceClass extends Class {
12+
ServiceClass() { this.getBaseClass+().getQualifiedName()="ServiceStack.Service" }
13+
14+
/** Get a method that handles incoming requests */
15+
Method getARequestMethod() {
16+
result = this.getAMethod(["Post", "Get", "Put", "Delete", "Any", "Option", "Head"])
17+
}
18+
}
19+
20+
/** Top-level Request DTO types */
21+
class RequestDTO extends Class {
22+
RequestDTO() {
23+
this.getABaseInterface().getQualifiedName() = ["ServiceStack.IReturn", "ServieStack.IReturnVoid"]
24+
}
25+
}
26+
27+
/** Top-level Response DTO types */
28+
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+
}
36+
}
37+
38+
/** Flow sources for the ServiceStack framework */
39+
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+
}
56+
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+
}
79+
}
80+
}
81+
82+
/** SQLi support for the ServiceStack framework */
83+
module SQL {
84+
private import semmle.code.csharp.security.dataflow.SqlInjection::SqlInjection
85+
86+
/** SQLi sinks for ServiceStack */
87+
class ServiceStackSink extends Sink {
88+
ServiceStackSink() {
89+
exists(MethodCall mc, Method m, int p |
90+
(mc.getTarget() = m.getAnOverrider*() or mc.getTarget() = m.getAnImplementor*()) and
91+
sqlSinkParam(m, p) and
92+
mc.getArgument(p) = this.asExpr())
93+
}
94+
}
95+
96+
private predicate sqlSinkParam(Method m, int p) {
97+
exists(RefType cls | cls = m.getDeclaringType() |
98+
(
99+
// if using the typed query builder api, only need to worry about Unsafe variants
100+
cls.getQualifiedName() = ["ServiceStack.OrmLite.SqlExpression", "ServiceStack.OrmLite.IUntypedSqlExpression"] and
101+
m.getName().matches("Unsafe%") and
102+
p = 0
103+
) or (
104+
// Read api - all string typed 1st params are potential sql sinks. They should be templates, not directly user controlled.
105+
cls.getQualifiedName() = ["ServiceStack.OrmLite.OrmLiteReadApi", "ServiceStack.OrmLite.OrmLiteReadExpressionsApi", "ServiceStack.OrmLite.OrmLiteReadApiAsync", "ServiceStack.OrmLite.OrmLiteReadExpressionsApiAsync"] and
106+
m.getParameter(p).getType() instanceof StringType and
107+
p = 1
108+
) or (
109+
// Write API - only 2 methods that take string
110+
cls.getQualifiedName() = ["ServiceStack.OrmLite.OrmLiteWriteApi", "ServiceStack.OrmLite.OrmLiteWriteApiAsync"] and
111+
m.getName() = ["ExecuteSql", "ExecuteSqlAsync"] and
112+
p = 1
113+
) or (
114+
// NoSQL sinks in redis client. TODO should these be separate query?
115+
cls.getQualifiedName() = "ServiceStack.Redis.IRedisClient" and
116+
(m.getName() = ["Custom", "LoadLuaScript"] or (m.getName().matches("%Lua%") and not m.getName().matches("%Sha%"))) and
117+
p = 0
118+
)
119+
// TODO
120+
// ServiceStack.OrmLite.OrmLiteUtils.SqlColumn - what about other similar classes?
121+
// couldn't find CustomSelect
122+
// need to handle "PreCreateTable", "PostCreateTable", "PreDropTable", "PostDropTable"
123+
124+
)
125+
}
126+
}
127+
128+
/** XSS support for ServiceStack framework */
129+
module XSS {
130+
private import semmle.code.csharp.security.dataflow.XSS::XSS
131+
132+
/** XSS sinks for ServiceStack */
133+
class XssSink extends Sink {
134+
XssSink() {
135+
exists(ServiceClass service, ReturnStmt r |
136+
this.asExpr() = r.getExpr() and
137+
r.getEnclosingCallable() = service.getARequestMethod()
138+
) or
139+
exists(ObjectCreation oc |
140+
oc.getType().hasQualifiedName("ServiceStack.HttpResult") and
141+
this.asExpr() = oc.getArgument(0)
142+
)
143+
}
144+
}
145+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ private import semmle.code.csharp.frameworks.system.Data
55
private import semmle.code.csharp.frameworks.system.data.SqlClient
66
private import semmle.code.csharp.frameworks.EntityFramework
77
private import semmle.code.csharp.frameworks.NHibernate
8+
private import semmle.code.csharp.frameworks.ServiceStack::SQL
89

910
/** An expression containing a SQL command. */
1011
abstract class SqlExpr extends Expr {

csharp/ql/src/semmle/code/csharp/security/dataflow/XSS.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ module XSS {
1010
import semmle.code.csharp.frameworks.system.Net
1111
import semmle.code.csharp.frameworks.system.Web
1212
import semmle.code.csharp.frameworks.system.web.UI
13+
import semmle.code.csharp.frameworks.ServiceStack::XSS
1314
import semmle.code.csharp.security.Sanitizers
1415
import semmle.code.csharp.security.dataflow.flowsinks.Html
1516
import semmle.code.csharp.security.dataflow.flowsinks.Remote

csharp/ql/src/semmle/code/csharp/security/dataflow/flowsources/Remote.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ private import semmle.code.csharp.frameworks.system.web.ui.WebControls
1212
private import semmle.code.csharp.frameworks.WCF
1313
private import semmle.code.csharp.frameworks.microsoft.Owin
1414
private import semmle.code.csharp.frameworks.microsoft.AspNetCore
15+
import semmle.code.csharp.frameworks.ServiceStack::Sources
1516

1617
/** A data flow source of remote user input. */
1718
abstract class RemoteFlowSource extends DataFlow::Node {

0 commit comments

Comments
 (0)