-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathInappropriateEncoding.ql
More file actions
159 lines (132 loc) · 5.66 KB
/
InappropriateEncoding.ql
File metadata and controls
159 lines (132 loc) · 5.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
/**
* @name Inappropriate encoding
* @description Using an inappropriate encoding may give unintended results and may
* pose a security risk.
* @kind path-problem
* @problem.severity error
* @security-severity 7.8
* @precision low
* @id cs/inappropriate-encoding
* @tags security
* external/cwe/cwe-838
*/
import csharp
import semmle.code.csharp.frameworks.System
import semmle.code.csharp.frameworks.system.Net
import semmle.code.csharp.frameworks.system.Web
import semmle.code.csharp.frameworks.system.web.UI
import semmle.code.csharp.security.dataflow.SqlInjectionQuery as SqlInjection
import semmle.code.csharp.security.dataflow.flowsinks.Html
import semmle.code.csharp.security.dataflow.UrlRedirectQuery as UrlRedirect
import semmle.code.csharp.security.Sanitizers
import EncodingConfigurations::Flow::PathGraph
signature module EncodingConfigSig {
/** Holds if `n` is a node whose value must be encoded. */
predicate requiresEncoding(DataFlow::Node n);
/** Holds if `e` is a possible valid encoded value. */
predicate isPossibleEncodedValue(Expr e);
}
/**
* A configuration for specifying expressions that must be encoded.
*/
module RequiresEncodingConfig<EncodingConfigSig EncodingConfig> implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
// all encoded values that do not match this configuration are
// considered sources
exists(Expr e | e = source.asExpr() |
e instanceof EncodedValue and
not EncodingConfig::isPossibleEncodedValue(e)
)
}
predicate isSink(DataFlow::Node sink) { EncodingConfig::requiresEncoding(sink) }
predicate isBarrier(DataFlow::Node sanitizer) {
EncodingConfig::isPossibleEncodedValue(sanitizer.asExpr())
}
int fieldFlowBranchLimit() { result = 0 }
}
/** An encoded value, for example through a call to `HttpServerUtility.HtmlEncode`. */
class EncodedValue extends Expr {
EncodedValue() {
EncodingConfigurations::SqlExprEncodingConfig::isPossibleEncodedValue(this)
or
EncodingConfigurations::HtmlExprEncodingConfig::isPossibleEncodedValue(this)
or
EncodingConfigurations::UrlExprEncodingConfig::isPossibleEncodedValue(this)
or
this = any(SystemWebHttpUtility c).getAJavaScriptStringEncodeMethod().getACall()
or
// Also try to identify user-defined encoders, which are likely wrong
exists(Method m, string name, string regexp |
this.(MethodCall).getTarget() = m and
m.fromSource() and
m.getName().toLowerCase() = name and
name.toLowerCase().regexpMatch(regexp) and
regexp = ".*(encode|saniti(s|z)e|cleanse|escape).*"
)
}
}
module EncodingConfigurations {
module SqlExprEncodingConfig implements EncodingConfigSig {
predicate requiresEncoding(DataFlow::Node n) { n instanceof SqlInjection::Sink }
predicate isPossibleEncodedValue(Expr e) { none() }
}
/** An encoding configuration for SQL expressions. */
module SqlExprConfig implements DataFlow::ConfigSig {
import RequiresEncodingConfig<SqlExprEncodingConfig> as Super
// no override for `isPossibleEncodedValue` as SQL parameters should
// be used instead of explicit encoding
predicate isSource(DataFlow::Node source) {
Super::isSource(source)
or
// consider quote-replacing calls as additional sources for
// SQL expressions (e.g., `s.Replace("\"", "\"\"")`)
source.asExpr() =
any(MethodCall mc |
mc.getTarget() = any(SystemStringClass c).getReplaceMethod() and
mc.getArgument(0).getValue().regexpMatch("\"|'|`")
)
}
predicate isSink = Super::isSink/1;
predicate isBarrier = Super::isBarrier/1;
int fieldFlowBranchLimit() { result = Super::fieldFlowBranchLimit() }
}
module SqlExpr = TaintTracking::Global<SqlExprConfig>;
module HtmlExprEncodingConfig implements EncodingConfigSig {
predicate requiresEncoding(DataFlow::Node n) { n instanceof HtmlSink }
predicate isPossibleEncodedValue(Expr e) { e instanceof HtmlSanitizedExpr }
}
/** An encoding configuration for HTML expressions. */
module HtmlExprConfig = RequiresEncodingConfig<HtmlExprEncodingConfig>;
module HtmlExpr = TaintTracking::Global<HtmlExprConfig>;
module UrlExprEncodingConfig implements EncodingConfigSig {
predicate requiresEncoding(DataFlow::Node n) { n instanceof UrlRedirect::Sink }
predicate isPossibleEncodedValue(Expr e) { e instanceof UrlSanitizedExpr }
}
/** An encoding configuration for URL expressions. */
module UrlExprConfig = RequiresEncodingConfig<UrlExprEncodingConfig>;
module UrlExpr = TaintTracking::Global<UrlExprConfig>;
module Flow =
DataFlow::MergePathGraph3<SqlExpr::PathNode, HtmlExpr::PathNode, UrlExpr::PathNode,
SqlExpr::PathGraph, HtmlExpr::PathGraph, UrlExpr::PathGraph>;
/**
* Holds if `encodedValue` is a possibly ill-encoded value that reaches
* `sink`, where `sink` is an expression of kind `kind` that is required
* to be encoded.
*/
predicate hasWrongEncoding(Flow::PathNode encodedValue, Flow::PathNode sink, string kind) {
SqlExpr::flowPath(encodedValue.asPathNode1(), sink.asPathNode1()) and
kind = "SQL expression"
or
HtmlExpr::flowPath(encodedValue.asPathNode2(), sink.asPathNode2()) and
kind = "HTML expression"
or
UrlExpr::flowPath(encodedValue.asPathNode3(), sink.asPathNode3()) and
kind = "URL expression"
}
}
from
EncodingConfigurations::Flow::PathNode encodedValue, EncodingConfigurations::Flow::PathNode sink,
string kind
where EncodingConfigurations::hasWrongEncoding(encodedValue, sink, kind)
select sink.getNode(), encodedValue, sink, "This " + kind + " may include data from a $@.",
encodedValue.getNode(), "possibly inappropriately encoded value"