Skip to content

Commit aa8291e

Browse files
authored
Merge pull request github#12870 from michaelnebel/csharp/refactordataflow6
C#: Re-factor data flow and taint tracking configurations to use the new API.
2 parents 51b6da4 + b410791 commit aa8291e

File tree

9 files changed

+181
-74
lines changed

9 files changed

+181
-74
lines changed

csharp/ql/src/API Abuse/FormatInvalid.ql

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,38 +12,36 @@
1212

1313
import csharp
1414
import semmle.code.csharp.frameworks.Format
15-
import DataFlow::PathGraph
15+
import FormatInvalid::PathGraph
1616

17-
private class FormatConfiguration extends DataFlow::Configuration {
18-
FormatConfiguration() { this = "format" }
17+
module FormatInvalidConfig implements DataFlow::ConfigSig {
18+
predicate isSource(DataFlow::Node n) { n.asExpr() instanceof StringLiteral }
1919

20-
override predicate isSource(DataFlow::Node n) { n.asExpr() instanceof StringLiteral }
21-
22-
override predicate isSink(DataFlow::Node n) {
23-
exists(FormatCall c | n.asExpr() = c.getFormatExpr())
24-
}
20+
predicate isSink(DataFlow::Node n) { exists(FormatCall c | n.asExpr() = c.getFormatExpr()) }
2521
}
2622

23+
module FormatInvalid = DataFlow::Global<FormatInvalidConfig>;
24+
2725
private predicate invalidFormatString(
28-
InvalidFormatString src, DataFlow::PathNode source, DataFlow::PathNode sink, string msg,
26+
InvalidFormatString src, FormatInvalid::PathNode source, FormatInvalid::PathNode sink, string msg,
2927
FormatCall call, string callString
3028
) {
3129
source.getNode().asExpr() = src and
3230
sink.getNode().asExpr() = call.getFormatExpr() and
33-
any(FormatConfiguration conf).hasFlowPath(source, sink) and
31+
FormatInvalid::flowPath(source, sink) and
3432
call.hasInsertions() and
3533
msg = "Invalid format string used in $@ formatting call." and
3634
callString = "this"
3735
}
3836

3937
private predicate unusedArgument(
40-
FormatCall call, DataFlow::PathNode source, DataFlow::PathNode sink, string msg,
38+
FormatCall call, FormatInvalid::PathNode source, FormatInvalid::PathNode sink, string msg,
4139
ValidFormatString src, string srcString, Expr unusedExpr, string unusedString
4240
) {
4341
exists(int unused |
4442
source.getNode().asExpr() = src and
4543
sink.getNode().asExpr() = call.getFormatExpr() and
46-
any(FormatConfiguration conf).hasFlowPath(source, sink) and
44+
FormatInvalid::flowPath(source, sink) and
4745
unused = call.getASuppliedArgument() and
4846
not unused = src.getAnInsert() and
4947
not src.getValue() = "" and
@@ -55,13 +53,13 @@ private predicate unusedArgument(
5553
}
5654

5755
private predicate missingArgument(
58-
FormatCall call, DataFlow::PathNode source, DataFlow::PathNode sink, string msg,
56+
FormatCall call, FormatInvalid::PathNode source, FormatInvalid::PathNode sink, string msg,
5957
ValidFormatString src, string srcString
6058
) {
6159
exists(int used, int supplied |
6260
source.getNode().asExpr() = src and
6361
sink.getNode().asExpr() = call.getFormatExpr() and
64-
any(FormatConfiguration conf).hasFlowPath(source, sink) and
62+
FormatInvalid::flowPath(source, sink) and
6563
used = src.getAnInsert() and
6664
supplied = call.getSuppliedArguments() and
6765
used >= supplied and
@@ -71,8 +69,8 @@ private predicate missingArgument(
7169
}
7270

7371
from
74-
Element alert, DataFlow::PathNode source, DataFlow::PathNode sink, string msg, Element extra1,
75-
string extra1String, Element extra2, string extra2String
72+
Element alert, FormatInvalid::PathNode source, FormatInvalid::PathNode sink, string msg,
73+
Element extra1, string extra1String, Element extra2, string extra2String
7674
where
7775
invalidFormatString(alert, source, sink, msg, extra1, extra1String) and
7876
extra2 = extra1 and

csharp/ql/src/API Abuse/NoDisposeCallOnLocalIDisposable.ql

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,8 @@ private class ReturnNode extends DataFlow::ExprNode {
2424
}
2525
}
2626

27-
private class Conf extends DataFlow::Configuration {
28-
Conf() { this = "NoDisposeCallOnLocalIDisposable" }
29-
30-
override predicate isSource(DataFlow::Node node) {
27+
module DisposeCallOnLocalIDisposableConfig implements DataFlow::ConfigSig {
28+
predicate isSource(DataFlow::Node node) {
3129
node.asExpr() =
3230
any(LocalScopeDisposableCreation disposable |
3331
// Only care about library types - user types often have spurious IDisposable declarations
@@ -37,7 +35,7 @@ private class Conf extends DataFlow::Configuration {
3735
)
3836
}
3937

40-
override predicate isSink(DataFlow::Node node) {
38+
predicate isSink(DataFlow::Node node) {
4139
// Things that return may be disposed elsewhere
4240
node instanceof ReturnNode
4341
or
@@ -80,23 +78,27 @@ private class Conf extends DataFlow::Configuration {
8078
)
8179
}
8280

83-
override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
81+
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
8482
node2.asExpr() =
8583
any(LocalScopeDisposableCreation other | other.getAnArgument() = node1.asExpr())
8684
}
8785

88-
override predicate isBarrierOut(DataFlow::Node node) {
89-
this.isSink(node) and
86+
predicate isBarrierOut(DataFlow::Node node) {
87+
isSink(node) and
9088
not node instanceof ReturnNode
9189
}
9290
}
9391

92+
module DisposeCallOnLocalIDisposable = DataFlow::Global<DisposeCallOnLocalIDisposableConfig>;
93+
9494
/** Holds if `disposable` may not be disposed. */
9595
predicate mayNotBeDisposed(LocalScopeDisposableCreation disposable) {
96-
exists(Conf conf, DataFlow::ExprNode e |
96+
exists(DataFlow::ExprNode e |
9797
e.getExpr() = disposable and
98-
conf.isSource(e) and
99-
not exists(DataFlow::Node sink | conf.hasFlow(DataFlow::exprNode(disposable), sink) |
98+
DisposeCallOnLocalIDisposableConfig::isSource(e) and
99+
not exists(DataFlow::Node sink |
100+
DisposeCallOnLocalIDisposable::flow(DataFlow::exprNode(disposable), sink)
101+
|
100102
sink instanceof ReturnNode
101103
implies
102104
sink.getEnclosingCallable() = disposable.getEnclosingCallable()

csharp/ql/src/Security Features/InsecureRandomness.ql

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
import csharp
1616
import semmle.code.csharp.frameworks.Test
17-
import semmle.code.csharp.dataflow.DataFlow::DataFlow::PathGraph
17+
import Random::InsecureRandomness::PathGraph
1818

1919
module Random {
2020
import semmle.code.csharp.security.dataflow.flowsources.Remote
@@ -38,21 +38,24 @@ module Random {
3838
/**
3939
* A taint-tracking configuration for insecure randomness in security sensitive context.
4040
*/
41-
class TaintTrackingConfiguration extends TaintTracking::Configuration {
42-
TaintTrackingConfiguration() { this = "RandomDataFlowConfiguration" }
41+
module InsecureRandomnessConfig implements DataFlow::ConfigSig {
42+
predicate isSource(DataFlow::Node source) { source instanceof Source }
4343

44-
override predicate isSource(DataFlow::Node source) { source instanceof Source }
44+
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
4545

46-
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
46+
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
4747

48-
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
49-
50-
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
48+
predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
5149
// succ = array_or_indexer[pred] - use of random numbers in an index
5250
succ.asExpr().(ElementAccess).getAnIndex() = pred.asExpr()
5351
}
5452
}
5553

54+
/**
55+
* A taint-tracking module for insecure randomness in security sensitive context.
56+
*/
57+
module InsecureRandomness = TaintTracking::Global<InsecureRandomnessConfig>;
58+
5659
/** A source of cryptographically insecure random numbers. */
5760
class RandomSource extends Source {
5861
RandomSource() {
@@ -112,10 +115,8 @@ module Random {
112115
}
113116
}
114117

115-
from
116-
Random::TaintTrackingConfiguration randomTracking, DataFlow::PathNode source,
117-
DataFlow::PathNode sink
118-
where randomTracking.hasFlowPath(source, sink)
118+
from Random::InsecureRandomness::PathNode source, Random::InsecureRandomness::PathNode sink
119+
where Random::InsecureRandomness::flowPath(source, sink)
119120
select sink.getNode(), source, sink,
120121
"This uses a cryptographically insecure random number generated at $@ in a security context.",
121122
source.getNode(), source.getNode().toString()

csharp/ql/src/experimental/CWE-918/RequestForgery.ql

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@
1212

1313
import csharp
1414
import RequestForgery::RequestForgery
15-
import semmle.code.csharp.dataflow.DataFlow::DataFlow::PathGraph
15+
import RequestForgeryFlow::PathGraph
1616

17-
from RequestForgeryConfiguration c, DataFlow::PathNode source, DataFlow::PathNode sink
18-
where c.hasFlowPath(source, sink)
17+
from RequestForgeryFlow::PathNode source, RequestForgeryFlow::PathNode sink
18+
where RequestForgeryFlow::flowPath(source, sink)
1919
select sink.getNode(), source, sink, "The URL of this request depends on a $@.", source.getNode(),
2020
"user-provided value"

csharp/ql/src/experimental/CWE-918/RequestForgery.qll

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@ module RequestForgery {
2424
abstract private class Barrier extends DataFlow::Node { }
2525

2626
/**
27+
* DEPRECATED: Use `RequestForgeryFlow` instead.
28+
*
2729
* A data flow configuration for detecting server side request forgery vulnerabilities.
2830
*/
29-
class RequestForgeryConfiguration extends DataFlow::Configuration {
31+
deprecated class RequestForgeryConfiguration extends DataFlow::Configuration {
3032
RequestForgeryConfiguration() { this = "Server Side Request forgery" }
3133

3234
override predicate isSource(DataFlow::Node source) { source instanceof Source }
@@ -54,6 +56,40 @@ module RequestForgery {
5456
override predicate isBarrier(DataFlow::Node node) { node instanceof Barrier }
5557
}
5658

59+
/**
60+
* A data flow configuration for detecting server side request forgery vulnerabilities.
61+
*/
62+
private module RequestForgeryFlowConfig implements DataFlow::ConfigSig {
63+
predicate isSource(DataFlow::Node source) { source instanceof Source }
64+
65+
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
66+
67+
predicate isAdditionalFlowStep(DataFlow::Node prev, DataFlow::Node succ) {
68+
interpolatedStringFlowStep(prev, succ)
69+
or
70+
stringReplaceStep(prev, succ)
71+
or
72+
uriCreationStep(prev, succ)
73+
or
74+
formatConvertStep(prev, succ)
75+
or
76+
toStringStep(prev, succ)
77+
or
78+
stringConcatStep(prev, succ)
79+
or
80+
stringFormatStep(prev, succ)
81+
or
82+
pathCombineStep(prev, succ)
83+
}
84+
85+
predicate isBarrier(DataFlow::Node node) { node instanceof Barrier }
86+
}
87+
88+
/**
89+
* A data flow module for detecting server side request forgery vulnerabilities.
90+
*/
91+
module RequestForgeryFlow = DataFlow::Global<RequestForgeryFlowConfig>;
92+
5793
/**
5894
* A remote data flow source taken as a source
5995
* for Server Side Request Forgery(SSRF) Vulnerabilities.

csharp/ql/src/experimental/Security Features/CWE-1004/CookieWithoutHttpOnly.ql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ where
3636
iResponse.getAppendMethod() = mc.getTarget() and
3737
isCookieWithSensitiveName(mc.getArgument(0)) and
3838
// there is no callback `OnAppendCookie` that sets `HttpOnly` to true
39-
not exists(OnAppendCookieHttpOnlyTrackingConfig config | config.hasFlowTo(_)) and
39+
not OnAppendCookieHttpOnlyTracking::flowTo(_) and
4040
// Passed as third argument to `IResponseCookies.Append`
4141
exists(DataFlow::Node creation, DataFlow::Node append |
4242
CookieOptionsTracking::flow(creation, append) and
@@ -67,7 +67,7 @@ where
6767
// default is not configured or is not set to `Always`
6868
not getAValueForCookiePolicyProp("HttpOnly").getValue() = "1" and
6969
// there is no callback `OnAppendCookie` that sets `HttpOnly` to true
70-
not exists(OnAppendCookieHttpOnlyTrackingConfig config | config.hasFlowTo(_)) and
70+
not OnAppendCookieHttpOnlyTracking::flowTo(_) and
7171
iResponse.getAppendMethod() = mc.getTarget() and
7272
isCookieWithSensitiveName(mc.getArgument(0)) and
7373
(

csharp/ql/src/experimental/Security Features/CWE-614/CookieWithoutSecure.ql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ where
3030
getAValueForCookiePolicyProp("Secure").getValue() = "1"
3131
) and
3232
// there is no callback `OnAppendCookie` that sets `Secure` to true
33-
not exists(OnAppendCookieSecureTrackingConfig config | config.hasFlowTo(_)) and
33+
not OnAppendCookieSecureTracking::flowTo(_) and
3434
(
3535
// `Secure` property in `CookieOptions` passed to IResponseCookies.Append(...) wasn't set
3636
exists(ObjectCreation oc |
@@ -80,7 +80,7 @@ where
8080
or
8181
oc.getType() instanceof MicrosoftAspNetCoreHttpCookieOptions and
8282
// there is no callback `OnAppendCookie` that sets `Secure` to true
83-
not exists(OnAppendCookieSecureTrackingConfig config | config.hasFlowTo(_)) and
83+
not OnAppendCookieSecureTracking::flowTo(_) and
8484
// the cookie option is passed to `Append`
8585
exists(DataFlow::Node creation |
8686
CookieOptionsTracking::flow(creation, _) and

csharp/ql/src/experimental/Security Features/JsonWebTokenHandler/JsonWebTokenHandlerLib.qll

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -102,21 +102,6 @@ private class TokenValidationResultIsValidCall extends PropertyRead {
102102
}
103103
}
104104

105-
/**
106-
* Dataflow from the output of `Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.ValidateToken` call to access the `IsValid` or `Exception` property
107-
*/
108-
private class FlowsToTokenValidationResultIsValidCall extends DataFlow::Configuration {
109-
FlowsToTokenValidationResultIsValidCall() { this = "FlowsToTokenValidationResultIsValidCall" }
110-
111-
override predicate isSource(DataFlow::Node source) {
112-
source.asExpr() instanceof JsonWebTokenHandlerValidateTokenCall
113-
}
114-
115-
override predicate isSink(DataFlow::Node sink) {
116-
exists(TokenValidationResultIsValidCall call | sink.asExpr() = call.getQualifier())
117-
}
118-
}
119-
120105
/**
121106
* A security-sensitive property for `Microsoft.IdentityModel.Tokens.TokenValidationParameters`
122107
*/

0 commit comments

Comments
 (0)