Skip to content

Commit e931433

Browse files
committed
refactor the js/xss query to use three flowlabels and one configuration
1 parent 4991f3f commit e931433

File tree

7 files changed

+1359
-106
lines changed

7 files changed

+1359
-106
lines changed

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,29 @@ module DomBasedXss {
1212
class RemoteFlowSourceAsSource extends Source {
1313
RemoteFlowSourceAsSource() { this instanceof RemoteFlowSource }
1414
}
15+
16+
/**
17+
* A flow-label representing tainted values where the prefix is attacker controlled.
18+
*/
19+
class PrefixString extends DataFlow::FlowLabel {
20+
PrefixString() { this = "PrefixString" }
21+
}
22+
23+
/** Gets the flow-label representing tainted values where the prefix is attacker controlled. */
24+
PrefixString prefixLabel() { any() }
25+
26+
/**
27+
* A sanitizer that blocks the `PrefixString` label when the start of the string is being tested as being of a particular prefix.
28+
*/
29+
class PrefixStringSanitizer extends SanitizerGuard instanceof StringOps::StartsWith {
30+
override predicate sanitizes(boolean outcome, Expr e) { none() }
31+
32+
override predicate blocks(boolean outcome, Expr e, DataFlow::FlowLabel label) {
33+
super.blocks(outcome, e, label)
34+
or
35+
e = super.getBaseString().asExpr() and
36+
label = prefixLabel() and
37+
outcome = super.getPolarity()
38+
}
39+
}
1540
}

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

Lines changed: 70 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -7,65 +7,61 @@ import javascript
77
private import semmle.javascript.security.TaintedUrlSuffix
88
import DomBasedXssCustomizations::DomBasedXss
99

10-
/**
11-
* DEPRECATED. Use `HtmlInjectionConfiguration` or `JQueryHtmlOrSelectorInjectionConfiguration`.
12-
*/
13-
deprecated class Configuration = HtmlInjectionConfiguration;
14-
1510
/**
1611
* DEPRECATED. Use `Vue::VHtmlSourceWrite` instead.
1712
*/
1813
deprecated class VHtmlSourceWrite = Vue::VHtmlSourceWrite;
1914

20-
/**
21-
* A taint-tracking configuration for reasoning about XSS.
22-
*/
23-
class HtmlInjectionConfiguration extends TaintTracking::Configuration {
24-
HtmlInjectionConfiguration() { this = "HtmlInjection" }
25-
26-
override predicate isSource(DataFlow::Node source) { source instanceof Source }
27-
28-
override predicate isSink(DataFlow::Node sink) {
29-
sink instanceof Sink and
30-
not sink instanceof JQueryHtmlOrSelectorSink // Handled by JQueryHtmlOrSelectorInjectionConfiguration below
31-
}
15+
/** DEPRECATED. Use `Configuration`. */
16+
deprecated class HtmlInjectionConfiguration = Configuration;
3217

33-
override predicate isSanitizer(DataFlow::Node node) {
34-
super.isSanitizer(node)
35-
or
36-
node instanceof Sanitizer
37-
}
38-
39-
override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode guard) {
40-
guard instanceof SanitizerGuard
41-
}
18+
/** DEPRECATED. Use `Configuration`. */
19+
deprecated class JQueryHtmlOrSelectorInjectionConfiguration = Configuration;
4220

43-
override predicate isSanitizerEdge(DataFlow::Node pred, DataFlow::Node succ) {
44-
isOptionallySanitizedEdge(pred, succ)
21+
/**
22+
* A sink that is not a URL write or a JQuery selector,
23+
* assumed to be a value that is interpreted as HTML.
24+
*/
25+
class HTMLSink extends DataFlow::Node instanceof Sink {
26+
HTMLSink() {
27+
not this instanceof WriteURLSink and
28+
not this instanceof JQueryHtmlOrSelectorSink
4529
}
4630
}
4731

4832
/**
49-
* A taint-tracking configuration for reasoning about injection into the jQuery `$` function
50-
* or similar, where the interpretation of the input string depends on its first character.
33+
* A taint-tracking configuration for reasoning about XSS.
34+
* Both ordinary HTML sinks, URL sinks, and JQuery selector based sinks.
35+
* - HTML sinks are sinks for any tainted value
36+
* - URL sinks are only sinks when the scheme is user controlled
37+
* - JQuery selector sinks are sinks when the tainted value can start with `<`.
5138
*
52-
* Values are only considered tainted if they can start with the `<` character.
39+
* The above is achieved using three flow labels:
40+
* - TaintedUrlSuffix: a URL where the attacker only controls a suffix.
41+
* - Taint: a tainted value where the attacker controls part of the value.
42+
* - PrefixLabel: a tainted value where the attacker controls the prefix
5343
*/
54-
class JQueryHtmlOrSelectorInjectionConfiguration extends TaintTracking::Configuration {
55-
JQueryHtmlOrSelectorInjectionConfiguration() { this = "JQueryHtmlOrSelectorInjection" }
44+
class Configuration extends TaintTracking::Configuration {
45+
Configuration() { this = "HtmlInjection" }
5646

5747
override predicate isSource(DataFlow::Node source, DataFlow::FlowLabel label) {
58-
// Reuse any source not derived from location
5948
source instanceof Source and
60-
not source = [DOM::locationRef(), DOM::locationRef().getAPropertyRead()] and
61-
label.isTaint()
49+
(label.isTaint() or label = prefixLabel()) and
50+
not source = TaintedUrlSuffix::source()
6251
or
63-
source = [DOM::locationSource(), DOM::locationRef().getAPropertyRead(["hash", "search"])] and
52+
source = TaintedUrlSuffix::source() and
6453
label = TaintedUrlSuffix::label()
6554
}
6655

6756
override predicate isSink(DataFlow::Node sink, DataFlow::FlowLabel label) {
68-
sink instanceof JQueryHtmlOrSelectorSink and label.isTaint()
57+
sink instanceof HTMLSink and
58+
label = [TaintedUrlSuffix::label(), prefixLabel(), DataFlow::FlowLabel::taint()]
59+
or
60+
sink instanceof JQueryHtmlOrSelectorSink and
61+
label = [DataFlow::FlowLabel::taint(), prefixLabel()]
62+
or
63+
sink instanceof WriteURLSink and
64+
label = prefixLabel()
6965
}
7066

7167
override predicate isSanitizer(DataFlow::Node node) {
@@ -78,6 +74,32 @@ class JQueryHtmlOrSelectorInjectionConfiguration extends TaintTracking::Configur
7874
guard instanceof SanitizerGuard
7975
}
8076

77+
override predicate isLabeledBarrier(DataFlow::Node node, DataFlow::FlowLabel lbl) {
78+
super.isLabeledBarrier(node, lbl)
79+
or
80+
// copy all taint barriers to the TaintedUrlSuffix/PrefixLabel label. This copies both the ordinary sanitizers and the sanitizer-guards.
81+
super.isLabeledBarrier(node, DataFlow::FlowLabel::taint()) and
82+
lbl = [TaintedUrlSuffix::label(), prefixLabel()]
83+
or
84+
// any non-first string-concatenation leaf is a barrier for the prefix label.
85+
exists(StringOps::ConcatenationRoot root |
86+
node = root.getALeaf() and
87+
not node = root.getFirstLeaf() and
88+
lbl = prefixLabel()
89+
)
90+
or
91+
// we assume that `.join()` calls have a prefix, and thus block the prefix label.
92+
node = any(DataFlow::MethodCallNode call | call.getMethodName() = "join") and
93+
lbl = prefixLabel()
94+
}
95+
96+
override predicate isSanitizerEdge(
97+
DataFlow::Node pred, DataFlow::Node succ, DataFlow::FlowLabel label
98+
) {
99+
isOptionallySanitizedEdge(pred, succ) and
100+
label = [DataFlow::FlowLabel::taint(), prefixLabel(), TaintedUrlSuffix::label()]
101+
}
102+
81103
override predicate isAdditionalFlowStep(
82104
DataFlow::Node src, DataFlow::Node trg, DataFlow::FlowLabel inlbl, DataFlow::FlowLabel outlbl
83105
) {
@@ -89,5 +111,15 @@ class JQueryHtmlOrSelectorInjectionConfiguration extends TaintTracking::Configur
89111
inlbl = TaintedUrlSuffix::label() and
90112
outlbl.isTaint()
91113
)
114+
or
115+
// inherit all ordinary taint steps for prefixLabel
116+
inlbl = prefixLabel() and
117+
outlbl = prefixLabel() and
118+
TaintTracking::sharedTaintStep(src, trg)
119+
or
120+
// steps out of taintedSuffixlabel to taint-label are also a steps to prefixLabel.
121+
TaintedUrlSuffix::step(src, trg, TaintedUrlSuffix::label(), DataFlow::FlowLabel::taint()) and
122+
inlbl = TaintedUrlSuffix::label() and
123+
outlbl = prefixLabel()
92124
}
93125
}

javascript/ql/src/Security/CWE-079/Xss.ql

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,7 @@ import semmle.javascript.security.dataflow.DomBasedXssQuery
1717
import DataFlow::PathGraph
1818

1919
from DataFlow::Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
20-
where
21-
(
22-
cfg instanceof HtmlInjectionConfiguration or
23-
cfg instanceof JQueryHtmlOrSelectorInjectionConfiguration
24-
) and
25-
cfg.hasFlowPath(source, sink)
20+
where cfg.hasFlowPath(source, sink)
2621
select sink.getNode(), source, sink,
2722
sink.getNode().(Sink).getVulnerabilityKind() + " vulnerability due to $@.", source.getNode(),
2823
"user-provided value"

0 commit comments

Comments
 (0)