@@ -7,65 +7,61 @@ import javascript
77private import semmle.javascript.security.TaintedUrlSuffix
88import 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 */
1813deprecated 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- }
32-
33- override predicate isSanitizer(DataFlow::Node node) {
34- super.isSanitizer(node)
35- or
36- node instanceof Sanitizer
37- }
15+ /** DEPRECATED. Use `Configuration`. */
16+ deprecated class HtmlInjectionConfiguration = Configuration;
3817
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,26 @@ 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()
124+ }
125+ }
126+
127+ /**
128+ * A sanitizer that blocks the `PrefixString` label when the start of the string is being tested as being of a particular prefix.
129+ */
130+ class PrefixStringSanitizer extends SanitizerGuard, TaintTracking::LabeledSanitizerGuardNode instanceof StringOps::StartsWith {
131+ override predicate sanitizes(boolean outcome, Expr e, DataFlow::FlowLabel label) {
132+ e = super.getBaseString().asExpr() and
133+ label = prefixLabel() and
134+ outcome = super.getPolarity()
92135 }
93136}
0 commit comments