Skip to content

Commit 34b29bf

Browse files
committed
js/xss-through-dom: filter away reads of .src that end in a URL sink
1 parent c891c53 commit 34b29bf

File tree

4 files changed

+40
-3
lines changed

4 files changed

+40
-3
lines changed

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,18 +89,25 @@ module XssThroughDom {
8989
* A source for text from the DOM from a DOM property read or call to `getAttribute()`.
9090
*/
9191
class DomTextSource extends Source {
92+
string prop;
93+
9294
DomTextSource() {
9395
exists(DataFlow::PropRead read | read = this |
9496
read.getBase().getALocalSource() = DOM::domValueRef() and
95-
read.mayHavePropertyName(unsafeDomPropertyName())
97+
prop = unsafeDomPropertyName() and
98+
read.mayHavePropertyName(prop)
9699
)
97100
or
98101
exists(DataFlow::MethodCallNode mcn | mcn = this |
99102
mcn.getReceiver().getALocalSource() = DOM::domValueRef() and
100103
mcn.getMethodName() = "getAttribute" and
101-
mcn.getArgument(0).mayHaveStringValue(unsafeAttributeName())
104+
prop = unsafeAttributeName() and
105+
mcn.getArgument(0).mayHaveStringValue(prop)
102106
)
103107
}
108+
109+
/** Gets the name of the property read. */
110+
string getPropertyName() { result = prop }
104111
}
105112

106113
/** DEPRECATED: Alias for DomTextSource */

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,13 @@ class Configuration extends TaintTracking::Configuration {
3535
override predicate isSanitizerEdge(DataFlow::Node pred, DataFlow::Node succ) {
3636
DomBasedXss::isOptionallySanitizedEdge(pred, succ)
3737
}
38+
39+
override predicate hasFlowPath(DataFlow::SourcePathNode src, DataFlow::SinkPathNode sink) {
40+
super.hasFlowPath(src, sink) and
41+
// filtering away readings of `src` that end in a URL sink.
42+
not (
43+
sink.getNode() instanceof DomBasedXss::WriteURLSink and
44+
src.getNode().(DomTextSource).getPropertyName() = "src"
45+
)
46+
}
3847
}

javascript/ql/test/query-tests/Security/CWE-079/XssThroughDom/XssThroughDom.expected

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,13 @@ nodes
122122
| xss-through-dom.js:109:31:109:70 | "<a src ... oo</a>" |
123123
| xss-through-dom.js:109:45:109:55 | this.el.src |
124124
| xss-through-dom.js:109:45:109:55 | this.el.src |
125+
| xss-through-dom.js:114:11:114:52 | src |
126+
| xss-through-dom.js:114:17:114:52 | documen ... k").src |
127+
| xss-through-dom.js:114:17:114:52 | documen ... k").src |
128+
| xss-through-dom.js:115:16:115:18 | src |
129+
| xss-through-dom.js:115:16:115:18 | src |
130+
| xss-through-dom.js:117:26:117:28 | src |
131+
| xss-through-dom.js:117:26:117:28 | src |
125132
edges
126133
| forms.js:8:23:8:28 | values | forms.js:9:31:9:36 | values |
127134
| forms.js:8:23:8:28 | values | forms.js:9:31:9:36 | values |
@@ -194,6 +201,12 @@ edges
194201
| xss-through-dom.js:109:45:109:55 | this.el.src | xss-through-dom.js:109:31:109:70 | "<a src ... oo</a>" |
195202
| xss-through-dom.js:109:45:109:55 | this.el.src | xss-through-dom.js:109:31:109:70 | "<a src ... oo</a>" |
196203
| xss-through-dom.js:109:45:109:55 | this.el.src | xss-through-dom.js:109:31:109:70 | "<a src ... oo</a>" |
204+
| xss-through-dom.js:114:11:114:52 | src | xss-through-dom.js:115:16:115:18 | src |
205+
| xss-through-dom.js:114:11:114:52 | src | xss-through-dom.js:115:16:115:18 | src |
206+
| xss-through-dom.js:114:11:114:52 | src | xss-through-dom.js:117:26:117:28 | src |
207+
| xss-through-dom.js:114:11:114:52 | src | xss-through-dom.js:117:26:117:28 | src |
208+
| xss-through-dom.js:114:17:114:52 | documen ... k").src | xss-through-dom.js:114:11:114:52 | src |
209+
| xss-through-dom.js:114:17:114:52 | documen ... k").src | xss-through-dom.js:114:11:114:52 | src |
197210
#select
198211
| forms.js:9:31:9:40 | values.foo | forms.js:8:23:8:28 | values | forms.js:9:31:9:40 | values.foo | $@ is reinterpreted as HTML without escaping meta-characters. | forms.js:8:23:8:28 | values | DOM text |
199212
| forms.js:12:31:12:40 | values.bar | forms.js:11:24:11:29 | values | forms.js:12:31:12:40 | values.bar | $@ is reinterpreted as HTML without escaping meta-characters. | forms.js:11:24:11:29 | values | DOM text |
@@ -228,3 +241,4 @@ edges
228241
| xss-through-dom.js:93:16:93:46 | $("#foo ... ].value | xss-through-dom.js:93:16:93:46 | $("#foo ... ].value | xss-through-dom.js:93:16:93:46 | $("#foo ... ].value | $@ is reinterpreted as HTML without escaping meta-characters. | xss-through-dom.js:93:16:93:46 | $("#foo ... ].value | DOM text |
229242
| xss-through-dom.js:96:17:96:47 | $("#foo ... ].value | xss-through-dom.js:96:17:96:47 | $("#foo ... ].value | xss-through-dom.js:96:17:96:47 | $("#foo ... ].value | $@ is reinterpreted as HTML without escaping meta-characters. | xss-through-dom.js:96:17:96:47 | $("#foo ... ].value | DOM text |
230243
| xss-through-dom.js:109:31:109:70 | "<a src ... oo</a>" | xss-through-dom.js:109:45:109:55 | this.el.src | xss-through-dom.js:109:31:109:70 | "<a src ... oo</a>" | $@ is reinterpreted as HTML without escaping meta-characters. | xss-through-dom.js:109:45:109:55 | this.el.src | DOM text |
244+
| xss-through-dom.js:115:16:115:18 | src | xss-through-dom.js:114:17:114:52 | documen ... k").src | xss-through-dom.js:115:16:115:18 | src | $@ is reinterpreted as HTML without escaping meta-characters. | xss-through-dom.js:114:17:114:52 | documen ... k").src | DOM text |

javascript/ql/test/query-tests/Security/CWE-079/XssThroughDom/xss-through-dom.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,4 +108,11 @@ class Sub extends Super {
108108
super();
109109
$("#id").get(0).innerHTML = "<a src=\"" + this.el.src + "\">foo</a>"; // NOT OK. Attack: `<mytag id="id" src="x:&quot;&gt;&lt;img src=1 onerror=&quot;alert(1)&quot;&gt;" />`
110110
}
111-
}
111+
}
112+
113+
(function () {
114+
const src = document.getElementById("#link").src;
115+
$("#id").html(src); // NOT OK.
116+
117+
$("#id").attr("src", src); // OK
118+
})();

0 commit comments

Comments
 (0)