Skip to content

Commit 3c1456b

Browse files
authored
Merge pull request github#12913 from michaelnebel/csharp/inappropriateencoding
C#: Re-factor the InappropriateEncoding query to use the new API.
2 parents 8fcfc6f + f32b8ad commit 3c1456b

File tree

1 file changed

+74
-53
lines changed

1 file changed

+74
-53
lines changed

csharp/ql/src/Security Features/CWE-838/InappropriateEncoding.ql

Lines changed: 74 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -20,57 +20,46 @@ import semmle.code.csharp.security.dataflow.SqlInjectionQuery as SqlInjection
2020
import semmle.code.csharp.security.dataflow.flowsinks.Html
2121
import semmle.code.csharp.security.dataflow.UrlRedirectQuery as UrlRedirect
2222
import semmle.code.csharp.security.Sanitizers
23-
import semmle.code.csharp.dataflow.DataFlow2::DataFlow2
24-
import semmle.code.csharp.dataflow.DataFlow2::DataFlow2::PathGraph
25-
import semmle.code.csharp.dataflow.TaintTracking2
23+
import EncodingConfigurations::Flow::PathGraph
2624

27-
/**
28-
* A configuration for specifying expressions that must be
29-
* encoded, along with a set of potential valid encoded values.
30-
*/
31-
abstract class RequiresEncodingConfiguration extends TaintTracking2::Configuration {
32-
bindingset[this]
33-
RequiresEncodingConfiguration() { any() }
34-
35-
/** Gets a textual representation of this kind of encoding requirement. */
36-
abstract string getKind();
37-
38-
/** Holds if `e` is an expression whose value must be encoded. */
39-
abstract predicate requiresEncoding(Node n);
25+
signature module EncodingConfigSig {
26+
/** Holds if `n` is a node whose value must be encoded. */
27+
predicate requiresEncoding(DataFlow::Node n);
4028

4129
/** Holds if `e` is a possible valid encoded value. */
42-
predicate isPossibleEncodedValue(Expr e) { none() }
43-
44-
/**
45-
* Holds if `encodedValue` is a possibly ill-encoded value that reaches
46-
* `sink`, where `sink` is an expression of kind `kind` that is required
47-
* to be encoded.
48-
*/
49-
predicate hasWrongEncoding(PathNode encodedValue, PathNode sink, string kind) {
50-
this.hasFlowPath(encodedValue, sink) and
51-
kind = this.getKind()
52-
}
30+
predicate isPossibleEncodedValue(Expr e);
31+
}
5332

54-
override predicate isSource(Node source) {
33+
/**
34+
* A configuration for specifying expressions that must be encoded.
35+
*/
36+
module RequiresEncodingConfig<EncodingConfigSig EncodingConfig> implements DataFlow::ConfigSig {
37+
predicate isSource(DataFlow::Node source) {
5538
// all encoded values that do not match this configuration are
5639
// considered sources
5740
exists(Expr e | e = source.asExpr() |
5841
e instanceof EncodedValue and
59-
not this.isPossibleEncodedValue(e)
42+
not EncodingConfig::isPossibleEncodedValue(e)
6043
)
6144
}
6245

63-
override predicate isSink(Node sink) { this.requiresEncoding(sink) }
46+
predicate isSink(DataFlow::Node sink) { EncodingConfig::requiresEncoding(sink) }
6447

65-
override predicate isSanitizer(Node sanitizer) { this.isPossibleEncodedValue(sanitizer.asExpr()) }
48+
predicate isBarrier(DataFlow::Node sanitizer) {
49+
EncodingConfig::isPossibleEncodedValue(sanitizer.asExpr())
50+
}
6651

67-
override int fieldFlowBranchLimit() { result = 0 }
52+
int fieldFlowBranchLimit() { result = 0 }
6853
}
6954

70-
/** An encoded value, for example a call to `HttpServerUtility.HtmlEncode`. */
55+
/** An encoded value, for example through a call to `HttpServerUtility.HtmlEncode`. */
7156
class EncodedValue extends Expr {
7257
EncodedValue() {
73-
any(RequiresEncodingConfiguration c).isPossibleEncodedValue(this)
58+
EncodingConfigurations::SqlExprEncodingConfig::isPossibleEncodedValue(this)
59+
or
60+
EncodingConfigurations::HtmlExprEncodingConfig::isPossibleEncodedValue(this)
61+
or
62+
EncodingConfigurations::UrlExprEncodingConfig::isPossibleEncodedValue(this)
7463
or
7564
this = any(SystemWebHttpUtility c).getAJavaScriptStringEncodeMethod().getACall()
7665
or
@@ -86,18 +75,20 @@ class EncodedValue extends Expr {
8675
}
8776

8877
module EncodingConfigurations {
89-
/** An encoding configuration for SQL expressions. */
90-
class SqlExpr extends RequiresEncodingConfiguration {
91-
SqlExpr() { this = "SqlExpr" }
78+
module SqlExprEncodingConfig implements EncodingConfigSig {
79+
predicate requiresEncoding(DataFlow::Node n) { n instanceof SqlInjection::Sink }
9280

93-
override string getKind() { result = "SQL expression" }
81+
predicate isPossibleEncodedValue(Expr e) { none() }
82+
}
9483

95-
override predicate requiresEncoding(Node n) { n instanceof SqlInjection::Sink }
84+
/** An encoding configuration for SQL expressions. */
85+
module SqlExprConfig implements DataFlow::ConfigSig {
86+
import RequiresEncodingConfig<SqlExprEncodingConfig> as Super
9687

9788
// no override for `isPossibleEncodedValue` as SQL parameters should
9889
// be used instead of explicit encoding
99-
override predicate isSource(Node source) {
100-
super.isSource(source)
90+
predicate isSource(DataFlow::Node source) {
91+
Super::isSource(source)
10192
or
10293
// consider quote-replacing calls as additional sources for
10394
// SQL expressions (e.g., `s.Replace("\"", "\"\"")`)
@@ -107,32 +98,62 @@ module EncodingConfigurations {
10798
mc.getArgument(0).getValue().regexpMatch("\"|'|`")
10899
)
109100
}
101+
102+
predicate isSink = Super::isSink/1;
103+
104+
predicate isBarrier = Super::isBarrier/1;
105+
106+
int fieldFlowBranchLimit() { result = Super::fieldFlowBranchLimit() }
107+
}
108+
109+
module SqlExpr = TaintTracking::Global<SqlExprConfig>;
110+
111+
module HtmlExprEncodingConfig implements EncodingConfigSig {
112+
predicate requiresEncoding(DataFlow::Node n) { n instanceof HtmlSink }
113+
114+
predicate isPossibleEncodedValue(Expr e) { e instanceof HtmlSanitizedExpr }
110115
}
111116

112117
/** An encoding configuration for HTML expressions. */
113-
class HtmlExpr extends RequiresEncodingConfiguration {
114-
HtmlExpr() { this = "HtmlExpr" }
118+
module HtmlExprConfig = RequiresEncodingConfig<HtmlExprEncodingConfig>;
115119

116-
override string getKind() { result = "HTML expression" }
120+
module HtmlExpr = TaintTracking::Global<HtmlExprConfig>;
117121

118-
override predicate requiresEncoding(Node n) { n instanceof HtmlSink }
122+
module UrlExprEncodingConfig implements EncodingConfigSig {
123+
predicate requiresEncoding(DataFlow::Node n) { n instanceof UrlRedirect::Sink }
119124

120-
override predicate isPossibleEncodedValue(Expr e) { e instanceof HtmlSanitizedExpr }
125+
predicate isPossibleEncodedValue(Expr e) { e instanceof UrlSanitizedExpr }
121126
}
122127

123128
/** An encoding configuration for URL expressions. */
124-
class UrlExpr extends RequiresEncodingConfiguration {
125-
UrlExpr() { this = "UrlExpr" }
129+
module UrlExprConfig = RequiresEncodingConfig<UrlExprEncodingConfig>;
126130

127-
override string getKind() { result = "URL expression" }
131+
module UrlExpr = TaintTracking::Global<UrlExprConfig>;
128132

129-
override predicate requiresEncoding(Node n) { n instanceof UrlRedirect::Sink }
133+
module Flow =
134+
DataFlow::MergePathGraph3<SqlExpr::PathNode, HtmlExpr::PathNode, UrlExpr::PathNode,
135+
SqlExpr::PathGraph, HtmlExpr::PathGraph, UrlExpr::PathGraph>;
130136

131-
override predicate isPossibleEncodedValue(Expr e) { e instanceof UrlSanitizedExpr }
137+
/**
138+
* Holds if `encodedValue` is a possibly ill-encoded value that reaches
139+
* `sink`, where `sink` is an expression of kind `kind` that is required
140+
* to be encoded.
141+
*/
142+
predicate hasWrongEncoding(Flow::PathNode encodedValue, Flow::PathNode sink, string kind) {
143+
SqlExpr::flowPath(encodedValue.asPathNode1(), sink.asPathNode1()) and
144+
kind = "SQL expression"
145+
or
146+
HtmlExpr::flowPath(encodedValue.asPathNode2(), sink.asPathNode2()) and
147+
kind = "HTML expression"
148+
or
149+
UrlExpr::flowPath(encodedValue.asPathNode3(), sink.asPathNode3()) and
150+
kind = "URL expression"
132151
}
133152
}
134153

135-
from RequiresEncodingConfiguration c, PathNode encodedValue, PathNode sink, string kind
136-
where c.hasWrongEncoding(encodedValue, sink, kind)
154+
from
155+
EncodingConfigurations::Flow::PathNode encodedValue, EncodingConfigurations::Flow::PathNode sink,
156+
string kind
157+
where EncodingConfigurations::hasWrongEncoding(encodedValue, sink, kind)
137158
select sink.getNode(), encodedValue, sink, "This " + kind + " may include data from a $@.",
138159
encodedValue.getNode(), "possibly inappropriately encoded value"

0 commit comments

Comments
 (0)