-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathInappropriateEncoding.ql
More file actions
158 lines (134 loc) · 4.67 KB
/
InappropriateEncoding.ql
File metadata and controls
158 lines (134 loc) · 4.67 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
/**
* @name Inappropriate encoding
* @description Using an inappropriate encoding may give unintended results and may
* pose a security risk.
* @kind problem
* @problem.severity error
* @precision low
* @id cs/inappropriate-encoding
* @tags security
* external/cwe/cwe-838
*/
import csharp
import DataFlow
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.SqlInjection
import semmle.code.csharp.security.dataflow.XSS
import semmle.code.csharp.security.dataflow.UrlRedirect
import semmle.code.csharp.security.Sanitizers
/**
* A configuration for specifying expressions that must be
* encoded, along with a set of potential valid encoded values.
*/
abstract class RequiresEncodingConfiguration extends TaintTracking::Configuration {
bindingset[this]
RequiresEncodingConfiguration() { any() }
/** Gets a textual representation of this kind of encoding requirement. */
abstract string getKind();
/** Holds if `e` is an expression whose value must be encoded. */
abstract predicate requiresEncoding(Node n);
/** Holds if `e` is a possible valid encoded value. */
predicate isPossibleEncodedValue(Expr e) { none() }
/**
* 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(Expr encodedValue, Expr sink, string kind) {
hasFlow(exprNode(encodedValue), exprNode(sink)) and
kind = this.getKind()
}
override predicate isSource(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 this.isPossibleEncodedValue(e)
)
}
override predicate isSink(Node sink) {
this.requiresEncoding(sink)
}
override predicate isSanitizer(Node sanitizer) {
this.isPossibleEncodedValue(sanitizer.asExpr())
}
}
/** An encoded value, for example a call to `HttpServerUtility.HtmlEncode`. */
class EncodedValue extends Expr {
EncodedValue() {
any(RequiresEncodingConfiguration c).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 {
/** An encoding configuration for SQL expressions. */
class SqlExpr extends RequiresEncodingConfiguration {
SqlExpr() {
this = "SqlExpr"
}
override string getKind() {
result = "SQL expression"
}
override predicate requiresEncoding(Node n) {
n instanceof SqlInjection::Sink
}
// no override for `isPossibleEncodedValue` as SQL parameters should
// be used instead of explicit encoding
override predicate isSource(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("\"|'|`")
)
}
}
/** An encoding configuration for HTML expressions. */
class HtmlExpr extends RequiresEncodingConfiguration {
HtmlExpr() {
this = "HtmlExpr"
}
override string getKind() {
result = "HTML expression"
}
override predicate requiresEncoding(Node n) {
n instanceof XSS::HtmlSink
}
override predicate isPossibleEncodedValue(Expr e) {
e instanceof HtmlSanitizedExpr
}
}
/** An encoding configuration for URL expressions. */
class UrlExpr extends RequiresEncodingConfiguration {
UrlExpr() {
this = "UrlExpr"
}
override string getKind() {
result = "URL expression"
}
override predicate requiresEncoding(Node n) {
n instanceof UrlRedirect::Sink
}
override predicate isPossibleEncodedValue(Expr e) {
e instanceof UrlSanitizedExpr
}
}
}
from RequiresEncodingConfiguration c, Expr encodedValue, Expr sink, string kind
where c.hasWrongEncoding(encodedValue, sink, kind)
select sink, "This " + kind + " may include data from a $@.", encodedValue, "possibly inappropriately encoded value"