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

Skip to content

Commit 173e1d0

Browse files
committed
move the DomBasedXss sources/sinks into the Customizations file
1 parent 9631b68 commit 173e1d0

14 files changed

Lines changed: 335 additions & 328 deletions

File tree

javascript/ql/lib/semmle/javascript/frameworks/Angular2.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*/
44

55
private import javascript
6-
private import semmle.javascript.security.dataflow.Xss
6+
private import semmle.javascript.security.dataflow.DomBasedXssCustomizations
77
private import semmle.javascript.security.dataflow.CodeInjectionCustomizations
88
private import semmle.javascript.security.dataflow.ClientSideUrlRedirectCustomizations
99
private import semmle.javascript.DynamicPropertyAccess

javascript/ql/lib/semmle/javascript/frameworks/Cheerio.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*/
44

55
import javascript
6-
private import semmle.javascript.security.dataflow.Xss as Xss
6+
private import semmle.javascript.security.dataflow.DomBasedXssCustomizations
77

88
module Cheerio {
99
/** Gets a reference to the `cheerio` function, possibly with a loaded DOM. */
@@ -95,7 +95,7 @@ module Cheerio {
9595
/**
9696
* An XSS sink through `cheerio`.
9797
*/
98-
class XssSink extends Xss::DomBasedXss::Sink {
98+
class XssSink extends DomBasedXss::Sink {
9999
XssSink() {
100100
exists(string name | this = cheerioObjectRef().getAMethodCall(name).getAnArgument() |
101101
JQuery::isMethodArgumentInterpretedAsHtml(name)

javascript/ql/lib/semmle/javascript/frameworks/D3.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/** Provides classes and predicates modeling aspects of the `d3` library. */
22

33
private import javascript
4-
private import semmle.javascript.security.dataflow.Xss
4+
private import semmle.javascript.security.dataflow.DomBasedXssCustomizations
55

66
/** Provides classes and predicates modeling aspects of the `d3` library. */
77
module D3 {

javascript/ql/lib/semmle/javascript/frameworks/TrustedTypes.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*/
44

55
private import javascript
6-
private import semmle.javascript.security.dataflow.Xss
6+
private import semmle.javascript.security.dataflow.DomBasedXssCustomizations
77
private import semmle.javascript.security.dataflow.ClientSideUrlRedirectCustomizations
88
private import semmle.javascript.security.dataflow.CodeInjectionCustomizations
99

javascript/ql/lib/semmle/javascript/heuristics/AdditionalSinks.qll

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ private import semmle.javascript.security.dataflow.RegExpInjectionCustomizations
1616
private import semmle.javascript.security.dataflow.ClientSideUrlRedirectCustomizations
1717
private import semmle.javascript.security.dataflow.ServerSideUrlRedirectCustomizations
1818
private import semmle.javascript.security.dataflow.InsecureRandomnessCustomizations
19+
private import semmle.javascript.security.dataflow.DomBasedXssCustomizations
1920
private import HeuristicSinks as Sinks
2021

2122
class HeuristicSink = Sinks::HeuristicSink;
@@ -30,7 +31,7 @@ private class HeuristicCommandInjectionSink extends HeuristicSink, CommandInject
3031
}
3132
}
3233

33-
private class HeuristicDomBasedXssSink extends HeuristicSink, Xss::DomBasedXss::Sink {
34+
private class HeuristicDomBasedXssSink extends HeuristicSink, DomBasedXss::Sink {
3435
HeuristicDomBasedXssSink() {
3536
isAssignedToOrConcatenatedWith(this, "(?i)(html|innerhtml)") or
3637
isArgTo(this, "(?i)(html|render)") or

javascript/ql/lib/semmle/javascript/security/dataflow/DomBasedXssCustomizations.qll

Lines changed: 314 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,322 @@
44
*/
55

66
import javascript
7+
private import semmle.javascript.dataflow.InferredTypes
78

89
module DomBasedXss {
9-
import Xss::DomBasedXss
10+
private import Xss::Shared as Shared
11+
12+
/** A data flow source for DOM-based XSS vulnerabilities. */
13+
abstract class Source extends Shared::Source { }
14+
15+
/** A data flow sink for DOM-based XSS vulnerabilities. */
16+
abstract class Sink extends Shared::Sink { }
17+
18+
/** A sanitizer for DOM-based XSS vulnerabilities. */
19+
abstract class Sanitizer extends Shared::Sanitizer { }
20+
21+
/** A sanitizer guard for DOM-based XSS vulnerabilities. */
22+
abstract class SanitizerGuard extends Shared::SanitizerGuard { }
23+
24+
/**
25+
* An expression whose value is interpreted as HTML
26+
* and may be inserted into the DOM through a library.
27+
*/
28+
class LibrarySink extends Sink {
29+
LibrarySink() {
30+
// call to a jQuery method that interprets its argument as HTML
31+
exists(JQuery::MethodCall call |
32+
call.interpretsArgumentAsHtml(this) and
33+
not call.interpretsArgumentAsSelector(this) // Handled by `JQuerySelectorSink`
34+
)
35+
or
36+
// call to an Angular method that interprets its argument as HTML
37+
any(AngularJS::AngularJSCall call).interpretsArgumentAsHtml(this.asExpr())
38+
or
39+
// call to a WinJS function that interprets its argument as HTML
40+
exists(DataFlow::MethodCallNode mcn, string m |
41+
m = "setInnerHTMLUnsafe" or m = "setOuterHTMLUnsafe"
42+
|
43+
mcn.getMethodName() = m and
44+
this = mcn.getArgument(1)
45+
)
46+
or
47+
this = any(Typeahead::TypeaheadSuggestionFunction f).getAReturn()
48+
or
49+
this = any(Handlebars::SafeString s).getAnArgument()
50+
or
51+
this = any(JQuery::MethodCall call | call.getMethodName() = "jGrowl").getArgument(0)
52+
or
53+
// A construction of a JSDOM object (server side DOM), where scripts are allowed.
54+
exists(DataFlow::NewNode instance |
55+
instance = API::moduleImport("jsdom").getMember("JSDOM").getInstance().getAnImmediateUse() and
56+
this = instance.getArgument(0) and
57+
instance.getOptionArgument(1, "runScripts").mayHaveStringValue("dangerously")
58+
)
59+
or
60+
MooTools::interpretsNodeAsHtml(this)
61+
}
62+
}
63+
64+
/**
65+
* Holds if `prefix` is a prefix of `htmlString`, which may be intepreted as
66+
* HTML by a jQuery method.
67+
*/
68+
predicate isPrefixOfJQueryHtmlString(DataFlow::Node htmlString, DataFlow::Node prefix) {
69+
prefix = getAPrefixOfJQuerySelectorString(htmlString)
70+
}
71+
72+
/**
73+
* Holds if `prefix` is a prefix of `htmlString`, which may be intepreted as
74+
* HTML by a jQuery method.
75+
*/
76+
private DataFlow::Node getAPrefixOfJQuerySelectorString(DataFlow::Node htmlString) {
77+
any(JQuery::MethodCall call).interpretsArgumentAsSelector(htmlString) and
78+
result = htmlString
79+
or
80+
exists(DataFlow::Node pred | pred = getAPrefixOfJQuerySelectorString(htmlString) |
81+
result = StringConcatenation::getFirstOperand(pred)
82+
or
83+
result = pred.getAPredecessor()
84+
)
85+
}
86+
87+
/**
88+
* An argument to the jQuery `$` function or similar, which is interpreted as either a selector
89+
* or as an HTML string depending on its first character.
90+
*/
91+
class JQueryHtmlOrSelectorArgument extends DataFlow::Node {
92+
JQueryHtmlOrSelectorArgument() {
93+
exists(JQuery::MethodCall call |
94+
call.interpretsArgumentAsHtml(this) and
95+
call.interpretsArgumentAsSelector(this) and
96+
pragma[only_bind_out](this.analyze()).getAType() = TTString()
97+
)
98+
}
99+
100+
/** Gets a string that flows to the prefix of this argument. */
101+
string getAPrefix() { result = getAPrefixOfJQuerySelectorString(this).getStringValue() }
102+
}
103+
104+
/**
105+
* An argument to the jQuery `$` function or similar, which may be interpreted as HTML.
106+
*
107+
* This is the same as `JQueryHtmlOrSelectorArgument`, excluding cases where the value
108+
* is prefixed by something other than `<`.
109+
*/
110+
class JQueryHtmlOrSelectorSink extends Sink, JQueryHtmlOrSelectorArgument {
111+
JQueryHtmlOrSelectorSink() {
112+
// If a prefix of the string is known, it must start with '<' or be an empty string
113+
forall(string strval | strval = this.getAPrefix() | strval.regexpMatch("(?s)\\s*<.*|"))
114+
}
115+
}
116+
117+
import ClientSideUrlRedirectCustomizations::ClientSideUrlRedirect as ClientSideUrlRedirect
118+
119+
/**
120+
* A write to a URL which may execute JavaScript code.
121+
*/
122+
class WriteURLSink extends Sink instanceof ClientSideUrlRedirect::Sink {
123+
WriteURLSink() { super.isXssSink() }
124+
}
125+
126+
/**
127+
* An expression whose value is interpreted as HTML or CSS
128+
* and may be inserted into the DOM.
129+
*/
130+
class DomSink extends Sink {
131+
DomSink() {
132+
// Call to a DOM function that inserts its argument into the DOM
133+
any(DomMethodCallExpr call).interpretsArgumentsAsHtml(this.asExpr())
134+
or
135+
// Assignment to a dangerous DOM property
136+
exists(DomPropWriteNode pw |
137+
pw.interpretsValueAsHtml() and
138+
this = DataFlow::valueNode(pw.getRhs())
139+
)
140+
or
141+
// `html` or `source.html` properties of React Native `WebView`
142+
exists(ReactNative::WebViewElement webView, DataFlow::SourceNode source |
143+
source = webView or
144+
source = webView.getAPropertyWrite("source").getRhs().getALocalSource()
145+
|
146+
this = source.getAPropertyWrite("html").getRhs()
147+
)
148+
}
149+
}
150+
151+
/**
152+
* An expression whose value is interpreted as HTML.
153+
*/
154+
class HtmlParserSink extends Sink {
155+
HtmlParserSink() {
156+
exists(DataFlow::GlobalVarRefNode domParser |
157+
domParser.getName() = "DOMParser" and
158+
this = domParser.getAnInstantiation().getAMethodCall("parseFromString").getArgument(0)
159+
)
160+
or
161+
exists(DataFlow::MethodCallNode ccf |
162+
isDomValue(ccf.getReceiver().asExpr()) and
163+
ccf.getMethodName() = "createContextualFragment" and
164+
this = ccf.getArgument(0)
165+
)
166+
}
167+
}
168+
169+
/**
170+
* A React `dangerouslySetInnerHTML` attribute, viewed as an XSS sink.
171+
*
172+
* Any write to the `__html` property of an object assigned to this attribute
173+
* is considered an XSS sink.
174+
*/
175+
class DangerouslySetInnerHtmlSink extends Sink, DataFlow::ValueNode {
176+
DangerouslySetInnerHtmlSink() {
177+
exists(DataFlow::Node danger, DataFlow::SourceNode valueSrc |
178+
exists(JsxAttribute attr |
179+
attr.getName() = "dangerouslySetInnerHTML" and
180+
attr.getValue() = danger.asExpr()
181+
)
182+
or
183+
exists(ReactElementDefinition def, DataFlow::ObjectLiteralNode props |
184+
props.flowsTo(def.getProps()) and
185+
props.hasPropertyWrite("dangerouslySetInnerHTML", danger)
186+
)
187+
|
188+
valueSrc.flowsTo(danger) and
189+
valueSrc.hasPropertyWrite("__html", this)
190+
)
191+
}
192+
}
193+
194+
/**
195+
* A React tooltip where the `data-html` attribute is set to `true`.
196+
*/
197+
class TooltipSink extends Sink {
198+
TooltipSink() {
199+
exists(JsxElement el |
200+
el.getAttributeByName("data-html").getStringValue() = "true" or
201+
el.getAttributeByName("data-html").getValue().mayHaveBooleanValue(true)
202+
|
203+
this = el.getAttributeByName("data-tip").getValue().flow()
204+
)
205+
}
206+
}
207+
208+
/**
209+
* The HTML body of an email, viewed as an XSS sink.
210+
*/
211+
class EmailHtmlBodySink extends Sink {
212+
EmailHtmlBodySink() { this = any(EmailSender sender).getHtmlBody() }
213+
214+
override string getVulnerabilityKind() { result = "HTML injection" }
215+
}
216+
217+
/**
218+
* A write to the `template` option of a Vue instance, viewed as an XSS sink.
219+
*/
220+
class VueTemplateSink extends Sink {
221+
VueTemplateSink() {
222+
// Note: don't use Vue::Component#getTemplate as it includes an unwanted getALocalSource() step
223+
this = any(Vue::Component c).getOption("template")
224+
}
225+
}
226+
227+
/**
228+
* The tag name argument to the `createElement` parameter of the
229+
* `render` method of a Vue instance, viewed as an XSS sink.
230+
*/
231+
class VueCreateElementSink extends Sink {
232+
VueCreateElementSink() {
233+
exists(Vue::Component c, DataFlow::FunctionNode f |
234+
f.flowsTo(c.getRender()) and
235+
this = f.getParameter(0).getACall().getArgument(0)
236+
)
237+
}
238+
}
239+
240+
/**
241+
* A Vue `v-html` attribute, viewed as an XSS sink.
242+
*/
243+
class VHtmlSink extends Vue::VHtmlAttribute, Sink { }
244+
245+
/**
246+
* A raw interpolation tag in a template file, viewed as an XSS sink.
247+
*/
248+
class TemplateSink extends Sink {
249+
TemplateSink() {
250+
exists(Templating::TemplatePlaceholderTag tag |
251+
tag.isRawInterpolation() and
252+
this = tag.asDataFlowNode()
253+
)
254+
}
255+
}
256+
257+
/**
258+
* A value being piped into the `safe` pipe in a template file,
259+
* disabling subsequent HTML escaping.
260+
*/
261+
class SafePipe extends Sink {
262+
SafePipe() { this = Templating::getAPipeCall("safe").getArgument(0) }
263+
}
264+
265+
/**
266+
* A property read from a safe property is considered a sanitizer.
267+
*/
268+
class SafePropertyReadSanitizer extends Sanitizer, DataFlow::Node {
269+
SafePropertyReadSanitizer() {
270+
exists(PropAccess pacc | pacc = this.asExpr() | pacc.getPropertyName() = "length")
271+
}
272+
}
273+
274+
/**
275+
* A regexp replacement involving an HTML meta-character, viewed as a sanitizer for
276+
* XSS vulnerabilities.
277+
*
278+
* The XSS queries do not attempt to reason about correctness or completeness of sanitizers,
279+
* so any such replacement stops taint propagation.
280+
*/
281+
private class MetacharEscapeSanitizer extends Sanitizer, Shared::MetacharEscapeSanitizer { }
282+
283+
private class UriEncodingSanitizer extends Sanitizer, Shared::UriEncodingSanitizer { }
284+
285+
private class SerializeJavascriptSanitizer extends Sanitizer, Shared::SerializeJavascriptSanitizer {
286+
}
287+
288+
private class IsEscapedInSwitchSanitizer extends Sanitizer, Shared::IsEscapedInSwitchSanitizer { }
289+
290+
private class QuoteGuard extends SanitizerGuard, Shared::QuoteGuard { }
291+
292+
/**
293+
* Holds if there exists two dataflow edges to `succ`, where one edges is sanitized, and the other edge starts with `pred`.
294+
*/
295+
predicate isOptionallySanitizedEdge(DataFlow::Node pred, DataFlow::Node succ) {
296+
exists(HtmlSanitizerCall sanitizer |
297+
// sanitized = sanitize ? sanitizer(source) : source;
298+
exists(ConditionalExpr branch, Variable var, VarAccess access |
299+
branch = succ.asExpr() and access = var.getAnAccess()
300+
|
301+
branch.getABranch() = access and
302+
pred.getEnclosingExpr() = access and
303+
sanitizer = branch.getABranch().flow() and
304+
sanitizer.getAnArgument().getEnclosingExpr() = var.getAnAccess()
305+
)
306+
or
307+
// sanitized = source; if (sanitize) {sanitized = sanitizer(source)};
308+
exists(SsaPhiNode phi, SsaExplicitDefinition a, SsaDefinition b |
309+
a = phi.getAnInput().getDefinition() and
310+
b = phi.getAnInput().getDefinition() and
311+
count(phi.getAnInput()) = 2 and
312+
not a = b and
313+
sanitizer = DataFlow::valueNode(a.getDef().getSource()) and
314+
sanitizer.getAnArgument().asExpr().(VarAccess).getVariable() = b.getSourceVariable()
315+
|
316+
pred = DataFlow::ssaDefinitionNode(b) and
317+
succ = DataFlow::ssaDefinitionNode(phi)
318+
)
319+
)
320+
}
321+
322+
private class ContainsHtmlGuard extends SanitizerGuard, Shared::ContainsHtmlGuard { }
10323

11324
/** A source of remote user input, considered as a flow source for DOM-based XSS. */
12325
class RemoteFlowSourceAsSource extends Source {

javascript/ql/lib/semmle/javascript/security/dataflow/UnsafeJQueryPluginCustomizations.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import javascript
88
private import semmle.javascript.dataflow.InferredTypes
9-
import semmle.javascript.security.dataflow.Xss
9+
import semmle.javascript.security.dataflow.DomBasedXssCustomizations
1010

1111
module UnsafeJQueryPlugin {
1212
private import DataFlow::FlowLabel

0 commit comments

Comments
 (0)