Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 74b30d6

Browse files
committed
C#: Model EntityFramework
1 parent 7010ca8 commit 74b30d6

17 files changed

Lines changed: 490 additions & 45 deletions

File tree

csharp/ql/src/semmle/code/csharp/dataflow/DataFlow.qll

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ module DataFlow {
99
private import semmle.code.csharp.dataflow.CallContext
1010
private import semmle.code.csharp.dataflow.DelegateDataFlow
1111
private import semmle.code.csharp.dataflow.LibraryTypeDataFlow
12+
private import semmle.code.csharp.frameworks.EntityFramework
1213
private import Internal::Cached
1314
private import dotnet
1415
private import cil
@@ -117,6 +118,15 @@ module DataFlow {
117118

118119
predicate localFlowStep = Internal::LocalFlow::step/2;
119120

121+
/**
122+
* A dataflow node that jumps between callables. This can be extended in framework code
123+
* to add additional dataflow steps.
124+
*/
125+
abstract class NonLocalJumpNode extends Node {
126+
/** Gets a successor node that is potentially in another callable. */
127+
abstract Node getAJumpSuccessor();
128+
}
129+
120130
/**
121131
* A data flow node augmented with a call context and a configuration. Only
122132
* nodes that are reachable from a source, and which can reach a sink, are
@@ -1398,12 +1408,26 @@ module DataFlow {
13981408
*/
13991409
cached
14001410
predicate jumpStep(ExprNode pred, ExprNode succ) {
1401-
exists(FieldLike fl, FieldLikeRead flr | fl.isStatic() |
1402-
fl.getAnAssignedValue() = pred.getExpr() and
1411+
pred.(NonLocalJumpNode).getAJumpSuccessor() = succ
1412+
}
1413+
1414+
/** A dataflow node that has field-like dataflow. */
1415+
private class FieldLikeJumpNode extends NonLocalJumpNode, ExprNode {
1416+
FieldLike fl;
1417+
1418+
FieldLikeRead flr;
1419+
1420+
ExprNode succ;
1421+
1422+
FieldLikeJumpNode() {
1423+
fl.isStatic() and
1424+
fl.getAnAssignedValue() = this.getExpr() and
14031425
fl.getAnAccess() = flr and
14041426
flr = succ.getExpr() and
14051427
hasNonlocalValue(flr)
1406-
)
1428+
}
1429+
1430+
override ExprNode getAJumpSuccessor() { result = succ }
14071431
}
14081432

14091433
/**
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
/**
2+
* Classes modelling EntityFramework and EntityFrameworkCore.
3+
*/
4+
5+
import csharp
6+
private import semmle.code.csharp.frameworks.system.data.Entity
7+
private import semmle.code.csharp.frameworks.system.collections.Generic
8+
private import semmle.code.csharp.frameworks.Sql
9+
private import semmle.code.csharp.dataflow.LibraryTypeDataFlow
10+
11+
module DataAnnotations {
12+
class NotMappedAttribute extends Attribute {
13+
NotMappedAttribute() {
14+
this
15+
.getType()
16+
.hasQualifiedName("System.ComponentModel.DataAnnotations.Schema.NotMappedAttribute")
17+
}
18+
}
19+
}
20+
21+
module EntityFramework {
22+
/** An EF6 or EFCore namespace. */
23+
class EFNamespace extends Namespace {
24+
EFNamespace() {
25+
this.getQualifiedName() = "Microsoft.EntityFrameworkCore"
26+
or
27+
this.getQualifiedName() = "System.Data.Entity"
28+
}
29+
}
30+
31+
/** A taint source where the data has come from a mapped property stored in the database. */
32+
class StoredFlowSource extends DataFlow::Node {
33+
StoredFlowSource() {
34+
this.asExpr() = any(PropertyRead read | read.getTarget() instanceof MappedProperty)
35+
}
36+
}
37+
38+
private class EFClass extends Class {
39+
EFClass() { this.getDeclaringNamespace() instanceof EFNamespace }
40+
}
41+
42+
/** The class `Microsoft.EntityFrameworkCore.DbContext` or `System.Data.Entity.DbContext`. */
43+
class DbContext extends EFClass {
44+
DbContext() { this.getName() = "DbContext" }
45+
46+
Method getAFindMethod() {
47+
result = this.getAMethod("Find")
48+
or
49+
result = this.getAMethod("FindAsync")
50+
}
51+
52+
Method getAnUpdateMethod() { result = this.getAMethod("Update") }
53+
}
54+
55+
/** The class `Microsoft.EntityFrameworkCore.DbSet<>` or `System.Data.Entity.DbSet<>`. */
56+
class DbSet extends EFClass, UnboundGenericClass { DbSet() { this.getName() = "DbSet<>" } }
57+
58+
/** The class `Microsoft.EntityFrameworkCore.DbQuery<>` or `System.Data.Entity.DbQuery<>`. */
59+
class DbQuery extends EFClass, UnboundGenericClass { DbQuery() { this.hasName("DbQuery<>") } }
60+
61+
/** A generic type or method that takes a mapped type as its type argument. */
62+
private predicate usesMappedType(UnboundGeneric g) {
63+
g instanceof DbSet
64+
or
65+
g instanceof DbQuery
66+
or
67+
exists(DbContext db |
68+
g = db.getAnUpdateMethod()
69+
or
70+
g = db.getAFindMethod()
71+
)
72+
}
73+
74+
/** A type that is mapped to database table, or used as a query. */
75+
class MappedType extends ValueOrRefType {
76+
MappedType() {
77+
not this instanceof ObjectType and
78+
not this instanceof StringType and
79+
not this instanceof ValueType and
80+
(
81+
exists(UnboundGeneric g | usesMappedType(g) |
82+
this = g.getAConstructedGeneric().getATypeArgument()
83+
)
84+
or
85+
this.getASubType() instanceof MappedType
86+
)
87+
}
88+
}
89+
90+
/** A property that is potentially stored and retrieved from a database. */
91+
class MappedProperty extends Property {
92+
MappedProperty() {
93+
this = any(MappedType t).getAMember() and
94+
this.isPublic() and
95+
not this.getAnAttribute() instanceof DataAnnotations::NotMappedAttribute
96+
}
97+
}
98+
99+
/** The struct `Microsoft.EntityFrameworkCore.RawSqlString`. */
100+
class RawSqlStringStruct extends Struct, LibraryTypeDataFlow {
101+
RawSqlStringStruct() { this.getQualifiedName() = "Microsoft.EntityFrameworkCore.RawSqlString" }
102+
103+
override predicate callableFlow(
104+
CallableFlowSource source, CallableFlowSink sink, SourceDeclarationCallable c,
105+
boolean preservesValue
106+
) {
107+
c = this.getAConstructor() and
108+
source.(CallableFlowSourceArg).getArgumentIndex() = 0 and
109+
sink instanceof CallableFlowSinkReturn and
110+
preservesValue = true
111+
or
112+
c = this.getAConversionTo() and
113+
source.(CallableFlowSourceArg).getArgumentIndex() = 0 and
114+
sink instanceof CallableFlowSinkReturn and
115+
preservesValue = true
116+
}
117+
118+
ConversionOperator getAConversionTo() {
119+
result = this.getAMember() and
120+
result.getTargetType() instanceof RawSqlStringStruct and
121+
result.getSourceType() instanceof StringType
122+
}
123+
}
124+
125+
/**
126+
* A parameter that accepts raw SQL. Parameters of type `System.FormattableString`
127+
* are not included as they are not vulnerable to SQL injection.
128+
*/
129+
private class SqlParameter extends Parameter {
130+
SqlParameter() {
131+
this.getType() instanceof StringType and
132+
(
133+
exists(Callable c | this = c.getParameter(0) | c.getName().matches("%Sql"))
134+
or
135+
this.getName() = "sql"
136+
) and
137+
this.getCallable().getDeclaringType().getDeclaringNamespace().getParentNamespace*() instanceof
138+
EFNamespace
139+
or
140+
this.getType() instanceof RawSqlStringStruct
141+
or
142+
this = any(RawSqlStringStruct s).getAConstructor().getAParameter()
143+
or
144+
this = any(RawSqlStringStruct s).getAConversionTo().getAParameter()
145+
}
146+
}
147+
148+
/** A call to a method in EntityFrameworkCore that executes SQL. */
149+
class EntityFrameworkCoreSqlSink extends SqlExpr, Call {
150+
SqlParameter sqlParam;
151+
152+
EntityFrameworkCoreSqlSink() { this.getTarget().getAParameter() = sqlParam }
153+
154+
override Expr getSql() { result = this.getArgumentForParameter(sqlParam) }
155+
}
156+
157+
/** A call to `System.Data.Entity.DbSet.SqlQuery`. */
158+
class SystemDataEntityDbSetSqlExpr extends SqlExpr, MethodCall {
159+
SystemDataEntityDbSetSqlExpr() {
160+
this.getTarget() = any(SystemDataEntity::DbSet dbSet).getSqlQueryMethod()
161+
}
162+
163+
override Expr getSql() { result = this.getArgumentForName("sql") }
164+
}
165+
166+
/** A call to a method in `System.Data.Entity.Database` that executes SQL. */
167+
class SystemDataEntityDatabaseSqlExpr extends SqlExpr, MethodCall {
168+
SystemDataEntityDatabaseSqlExpr() {
169+
exists(SystemDataEntity::Database db |
170+
this.getTarget() = db.getSqlQueryMethod() or
171+
this.getTarget() = db.getExecuteSqlCommandMethod() or
172+
this.getTarget() = db.getExecuteSqlCommandAsyncMethod()
173+
)
174+
}
175+
176+
override Expr getSql() { result = this.getArgumentForName("sql") }
177+
}
178+
179+
/**
180+
* A dataflow node whereby data flows from a property write to a property read
181+
* via some database. The assumption is that all writes can flow to all reads.
182+
*/
183+
class MappedPropertyJumpNode extends DataFlow::NonLocalJumpNode {
184+
MappedProperty property;
185+
186+
MappedPropertyJumpNode() { this.asExpr() = property.getAnAssignedValue() }
187+
188+
override DataFlow::Node getAJumpSuccessor() {
189+
result.asExpr().(PropertyRead).getTarget() = property
190+
}
191+
}
192+
}

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

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
import csharp
44
private import semmle.code.csharp.frameworks.system.Data
5-
private import semmle.code.csharp.frameworks.system.data.Entity
65
private import semmle.code.csharp.frameworks.system.data.SqlClient
6+
private import semmle.code.csharp.frameworks.EntityFramework
77

88
/** An expression containing a SQL command. */
99
abstract class SqlExpr extends Expr {
@@ -22,7 +22,7 @@ class CommandTextAssignmentSqlExpr extends SqlExpr, AssignExpr {
2222
)
2323
}
2424

25-
override Expr getSql() { result = getRValue() }
25+
override Expr getSql() { result = this.getRValue() }
2626
}
2727

2828
/** A construction of an `IDbCommand` object. */
@@ -34,7 +34,7 @@ class IDbCommandConstructionSqlExpr extends SqlExpr, ObjectCreation {
3434
)
3535
}
3636

37-
override Expr getSql() { result = getArgument(0) }
37+
override Expr getSql() { result = this.getArgument(0) }
3838
}
3939

4040
/** A construction of an `SqlDataAdapter` object. */
@@ -47,7 +47,7 @@ class SqlDataAdapterConstructionSqlExpr extends SqlExpr, ObjectCreation {
4747
)
4848
}
4949

50-
override Expr getSql() { result = getArgument(0) }
50+
override Expr getSql() { result = this.getArgument(0) }
5151
}
5252

5353
/** A `MySql.Data.MySqlClient.MySqlHelper` method. */
@@ -83,25 +83,3 @@ class MicrosoftSqlHelperMethodCallSqlExpr extends SqlExpr, MethodCall {
8383
)
8484
}
8585
}
86-
87-
/** A call to `System.Data.Entity.DbSet.SqlQuery`. */
88-
class SystemDataEntityDbSetSqlExpr extends SqlExpr, MethodCall {
89-
SystemDataEntityDbSetSqlExpr() {
90-
this.getTarget() = any(SystemDataEntity::DbSet dbSet).getSqlQueryMethod()
91-
}
92-
93-
override Expr getSql() { result = getArgumentForName("sql") }
94-
}
95-
96-
/** A call to a method in `System.Data.Entity.Database` that executes SQL. */
97-
class SystemDataEntityDatabaseSqlExpr extends SqlExpr, MethodCall {
98-
SystemDataEntityDatabaseSqlExpr() {
99-
exists(SystemDataEntity::Database db |
100-
this.getTarget() = db.getSqlQueryMethod() or
101-
this.getTarget() = db.getExecuteSqlCommandMethod() or
102-
this.getTarget() = db.getExecuteSqlCommandAsyncMethod()
103-
)
104-
}
105-
106-
override Expr getSql() { result = getArgumentForName("sql") }
107-
}

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

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,12 @@
55
import csharp
66
private import semmle.code.csharp.frameworks.system.data.Common
77
private import semmle.code.csharp.frameworks.system.data.Entity
8+
private import semmle.code.csharp.frameworks.EntityFramework
89
private import semmle.code.csharp.frameworks.Sql
910

1011
/** A data flow source of stored user input. */
1112
abstract class StoredFlowSource extends DataFlow::Node { }
1213

13-
/** An access of an Entity Framework `Entity` property that may hold stored data. */
14-
class EntityPropertyStoredFlowSource extends StoredFlowSource {
15-
EntityPropertyStoredFlowSource() {
16-
this.asExpr().(PropertyAccess).getTarget() = any(SystemDataEntity::Entity e).getAProperty()
17-
}
18-
}
19-
2014
/**
2115
* An expression that has a type of `DbRawSqlQuery`, representing the result of an Entity Framework
2216
* SqlQuery.
@@ -37,22 +31,23 @@ class DbDataReaderStoredFlowSource extends StoredFlowSource {
3731
}
3832
}
3933

40-
/**
41-
* An expression that accesses a method of `DbDataReader` or a sub-class.
42-
*/
34+
/** An expression that accesses a method of `DbDataReader` or a sub-class. */
4335
class DbDataReaderMethodStoredFlowSource extends StoredFlowSource {
4436
DbDataReaderMethodStoredFlowSource() {
4537
this.asExpr().(MethodCall).getTarget().getDeclaringType() = any(SystemDataCommon::DbDataReader dataReader
4638
).getASubType*()
4739
}
4840
}
4941

50-
/**
51-
* An expression that accesses a property of `DbDataReader` or a sub-class.
52-
*/
42+
/** An expression that accesses a property of `DbDataReader` or a sub-class. */
5343
class DbDataReaderPropertyStoredFlowSource extends StoredFlowSource {
5444
DbDataReaderPropertyStoredFlowSource() {
5545
this.asExpr().(PropertyAccess).getTarget().getDeclaringType() = any(SystemDataCommon::DbDataReader dataReader
5646
).getASubType*()
5747
}
5848
}
49+
50+
/** A read of a mapped property. */
51+
class EntityFrameworkMappedProperty extends StoredFlowSource {
52+
EntityFrameworkMappedProperty() { this instanceof EntityFramework::StoredFlowSource }
53+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
| EntityFramework.cs:52:18:52:24 | access to property Name | EntityFramework.cs:47:34:47:42 | "tainted" |
2+
| EntityFramework.cs:53:18:53:34 | access to property Name | EntityFramework.cs:47:34:47:42 | "tainted" |
3+
| EntityFrameworkCore.cs:50:18:50:28 | access to local variable taintSource | EntityFrameworkCore.cs:47:31:47:39 | "tainted" |
4+
| EntityFrameworkCore.cs:51:18:51:46 | (...) ... | EntityFrameworkCore.cs:47:31:47:39 | "tainted" |
5+
| EntityFrameworkCore.cs:52:18:52:42 | (...) ... | EntityFrameworkCore.cs:47:31:47:39 | "tainted" |
6+
| EntityFrameworkCore.cs:60:18:60:24 | access to property Name | EntityFrameworkCore.cs:47:31:47:39 | "tainted" |
7+
| EntityFrameworkCore.cs:61:18:61:34 | access to property Name | EntityFrameworkCore.cs:47:31:47:39 | "tainted" |
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import csharp
2+
import semmle.code.csharp.dataflow.TaintTracking
3+
4+
class MyConfiguration extends TaintTracking::Configuration
5+
{
6+
MyConfiguration() { this = "EntityFramework dataflow" }
7+
8+
override predicate isSource(DataFlow::Node node) {
9+
node.asExpr().getValue() = "tainted"
10+
}
11+
12+
override predicate isSink(DataFlow::Node node) {
13+
node.asExpr() = any(MethodCall c | c.getTarget().hasName("Sink")).getAnArgument()
14+
}
15+
}
16+
17+
from MyConfiguration config, DataFlow::Node source, DataFlow::Node sink
18+
where config.hasFlow(source, sink)
19+
select sink, source

0 commit comments

Comments
 (0)