Skip to content

Commit 611a412

Browse files
authored
Merge pull request #8990 from bananabr/selection
JS: Selection API DOM text source
2 parents 9a45949 + bf6663a commit 611a412

File tree

4 files changed

+67
-0
lines changed

4 files changed

+67
-0
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* Added the `Selection` api as a DOM text source in the `js/xss-through-dom` query.

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,4 +216,32 @@ module XssThroughDom {
216216
}
217217
}
218218
}
219+
220+
/**
221+
* Gets a reference to a value obtained by calling `window.getSelection()`.
222+
* https://developer.mozilla.org/en-US/docs/Web/API/Selection
223+
*/
224+
DataFlow::SourceNode getSelectionCall(DataFlow::TypeTracker t) {
225+
t.start() and
226+
exists(DataFlow::CallNode call |
227+
call = DataFlow::globalVarRef("getSelection").getACall()
228+
or
229+
call = DOM::documentRef().getAMemberCall("getSelection")
230+
|
231+
result = call
232+
)
233+
or
234+
exists(DataFlow::TypeTracker t2 | result = getSelectionCall(t2).track(t2, t))
235+
}
236+
237+
/**
238+
* A source for text from the DOM from calling `toString()` on a `Selection` object.
239+
* The `toString()` method returns the currently selected text in the DOM.
240+
* https://developer.mozilla.org/en-US/docs/Web/API/Selection
241+
*/
242+
class SelectionSource extends Source {
243+
SelectionSource() {
244+
this = getSelectionCall(DataFlow::TypeTracker::end()).getAMethodCall("toString")
245+
}
246+
}
219247
}

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,17 @@ nodes
139139
| xss-through-dom.js:122:53:122:67 | ev.target.files |
140140
| xss-through-dom.js:122:53:122:67 | ev.target.files |
141141
| xss-through-dom.js:122:53:122:70 | ev.target.files[0] |
142+
| xss-through-dom.js:130:6:130:68 | linkText |
143+
| xss-through-dom.js:130:17:130:37 | wSelect ... tring() |
144+
| xss-through-dom.js:130:17:130:37 | wSelect ... tring() |
145+
| xss-through-dom.js:130:17:130:62 | wSelect ... tring() |
146+
| xss-through-dom.js:130:17:130:68 | wSelect ... ) \|\| '' |
147+
| xss-through-dom.js:130:42:130:62 | dSelect ... tring() |
148+
| xss-through-dom.js:130:42:130:62 | dSelect ... tring() |
149+
| xss-through-dom.js:131:19:131:26 | linkText |
150+
| xss-through-dom.js:131:19:131:26 | linkText |
151+
| xss-through-dom.js:132:16:132:23 | linkText |
152+
| xss-through-dom.js:132:16:132:23 | linkText |
142153
edges
143154
| forms.js:8:23:8:28 | values | forms.js:9:31:9:36 | values |
144155
| forms.js:8:23:8:28 | values | forms.js:9:31:9:36 | values |
@@ -225,6 +236,16 @@ edges
225236
| xss-through-dom.js:122:53:122:67 | ev.target.files | xss-through-dom.js:122:53:122:70 | ev.target.files[0] |
226237
| xss-through-dom.js:122:53:122:70 | ev.target.files[0] | xss-through-dom.js:122:33:122:71 | URL.cre ... les[0]) |
227238
| xss-through-dom.js:122:53:122:70 | ev.target.files[0] | xss-through-dom.js:122:33:122:71 | URL.cre ... les[0]) |
239+
| xss-through-dom.js:130:6:130:68 | linkText | xss-through-dom.js:131:19:131:26 | linkText |
240+
| xss-through-dom.js:130:6:130:68 | linkText | xss-through-dom.js:131:19:131:26 | linkText |
241+
| xss-through-dom.js:130:6:130:68 | linkText | xss-through-dom.js:132:16:132:23 | linkText |
242+
| xss-through-dom.js:130:6:130:68 | linkText | xss-through-dom.js:132:16:132:23 | linkText |
243+
| xss-through-dom.js:130:17:130:37 | wSelect ... tring() | xss-through-dom.js:130:17:130:62 | wSelect ... tring() |
244+
| xss-through-dom.js:130:17:130:37 | wSelect ... tring() | xss-through-dom.js:130:17:130:62 | wSelect ... tring() |
245+
| xss-through-dom.js:130:17:130:62 | wSelect ... tring() | xss-through-dom.js:130:17:130:68 | wSelect ... ) \|\| '' |
246+
| xss-through-dom.js:130:17:130:68 | wSelect ... ) \|\| '' | xss-through-dom.js:130:6:130:68 | linkText |
247+
| xss-through-dom.js:130:42:130:62 | dSelect ... tring() | xss-through-dom.js:130:17:130:62 | wSelect ... tring() |
248+
| xss-through-dom.js:130:42:130:62 | dSelect ... tring() | xss-through-dom.js:130:17:130:62 | wSelect ... tring() |
228249
#select
229250
| 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 |
230251
| 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 |
@@ -262,3 +283,7 @@ edges
262283
| 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 |
263284
| xss-through-dom.js:120:23:120:45 | ev.targ ... 0].name | xss-through-dom.js:120:23:120:37 | ev.target.files | xss-through-dom.js:120:23:120:45 | ev.targ ... 0].name | $@ is reinterpreted as HTML without escaping meta-characters. | xss-through-dom.js:120:23:120:37 | ev.target.files | DOM text |
264285
| xss-through-dom.js:122:33:122:71 | URL.cre ... les[0]) | xss-through-dom.js:122:53:122:67 | ev.target.files | xss-through-dom.js:122:33:122:71 | URL.cre ... les[0]) | $@ is reinterpreted as HTML without escaping meta-characters. | xss-through-dom.js:122:53:122:67 | ev.target.files | DOM text |
286+
| xss-through-dom.js:131:19:131:26 | linkText | xss-through-dom.js:130:17:130:37 | wSelect ... tring() | xss-through-dom.js:131:19:131:26 | linkText | $@ is reinterpreted as HTML without escaping meta-characters. | xss-through-dom.js:130:17:130:37 | wSelect ... tring() | DOM text |
287+
| xss-through-dom.js:131:19:131:26 | linkText | xss-through-dom.js:130:42:130:62 | dSelect ... tring() | xss-through-dom.js:131:19:131:26 | linkText | $@ is reinterpreted as HTML without escaping meta-characters. | xss-through-dom.js:130:42:130:62 | dSelect ... tring() | DOM text |
288+
| xss-through-dom.js:132:16:132:23 | linkText | xss-through-dom.js:130:17:130:37 | wSelect ... tring() | xss-through-dom.js:132:16:132:23 | linkText | $@ is reinterpreted as HTML without escaping meta-characters. | xss-through-dom.js:130:17:130:37 | wSelect ... tring() | DOM text |
289+
| xss-through-dom.js:132:16:132:23 | linkText | xss-through-dom.js:130:42:130:62 | dSelect ... tring() | xss-through-dom.js:132:16:132:23 | linkText | $@ is reinterpreted as HTML without escaping meta-characters. | xss-through-dom.js:130:42:130:62 | dSelect ... tring() | DOM text |

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,3 +122,13 @@ class Sub extends Super {
122122
$("img#id").attr("src", URL.createObjectURL(ev.target.files[0])); // NOT OK
123123
}
124124
})();
125+
126+
(function () {
127+
let elem = document.createElement('a');
128+
const wSelection = getSelection();
129+
const dSelection = document.getSelection();
130+
let linkText = wSelection.toString() || dSelection.toString() || '';
131+
elem.innerHTML = linkText; // NOT OK
132+
$("#id").html(linkText); // NOT OK
133+
elem.innerText = linkText; // OK
134+
})();

0 commit comments

Comments
 (0)