Skip to content

Commit 57fac94

Browse files
author
bananabr
committed
included ClipboardEvent and DragEvent as XSS sources
1 parent aafa8dd commit 57fac94

File tree

7 files changed

+147
-2
lines changed

7 files changed

+147
-2
lines changed

javascript/ql/lib/change-notes/2022-04-11-drag-and-drop-data.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
category: minorAnalysis
33
---
44
* The security queries now recognize drag and drop data as a source, enabling the queries to flag additional alerts.
5+
* The security queries now recognize ClipboardEvent function parameters as a source, enabling the queries to flag additional alerts.

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ private DataFlow::SourceNode pasteEvent(DataFlow::TypeTracker t) {
3232
)
3333
or
3434
t.start() and
35+
exists(DataFlow::ParameterNode pn |
36+
// https://developer.mozilla.org/en-US/docs/Web/API/ClipboardEvent
37+
pn.hasUnderlyingType("ClipboardEvent") and result = pn
38+
)
39+
or
40+
t.start() and
3541
exists(DataFlow::PropWrite pw | pw = DOM::domValueRef().getAPropertyWrite() |
3642
pw.getPropertyName() = "onpaste" and
3743
result = pw.getRhs().getABoundFunctionValue(0).getParameter(0)

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ private DataFlow::SourceNode dropEvent(DataFlow::TypeTracker t) {
3232
)
3333
or
3434
t.start() and
35+
exists(DataFlow::ParameterNode pn |
36+
// https://developer.mozilla.org/en-US/docs/Web/API/DragEvent
37+
pn.hasUnderlyingType("DragEvent") and result = pn
38+
)
39+
or
40+
t.start() and
3541
exists(DataFlow::PropWrite pw | pw = DOM::domValueRef().getAPropertyWrite() |
3642
pw.getPropertyName() = "ondrop" and
3743
result = pw.getRhs().getABoundFunctionValue(0).getParameter(0)

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,14 @@ nodes
144144
| clipboard.ts:50:29:50:32 | html |
145145
| clipboard.ts:50:29:50:32 | html |
146146
| clipboard.ts:50:29:50:32 | html |
147+
| clipboard.ts:71:13:71:62 | droppedHtml |
148+
| clipboard.ts:71:13:71:62 | droppedHtml |
149+
| clipboard.ts:71:27:71:62 | e.clipb ... /html') |
150+
| clipboard.ts:71:27:71:62 | e.clipb ... /html') |
151+
| clipboard.ts:71:27:71:62 | e.clipb ... /html') |
152+
| clipboard.ts:73:29:73:39 | droppedHtml |
153+
| clipboard.ts:73:29:73:39 | droppedHtml |
154+
| clipboard.ts:73:29:73:39 | droppedHtml |
147155
| d3.js:4:12:4:22 | window.name |
148156
| d3.js:4:12:4:22 | window.name |
149157
| d3.js:4:12:4:22 | window.name |
@@ -331,6 +339,14 @@ nodes
331339
| dragAndDrop.ts:50:29:50:32 | html |
332340
| dragAndDrop.ts:50:29:50:32 | html |
333341
| dragAndDrop.ts:50:29:50:32 | html |
342+
| dragAndDrop.ts:71:13:71:61 | droppedHtml |
343+
| dragAndDrop.ts:71:13:71:61 | droppedHtml |
344+
| dragAndDrop.ts:71:27:71:61 | e.dataT ... /html') |
345+
| dragAndDrop.ts:71:27:71:61 | e.dataT ... /html') |
346+
| dragAndDrop.ts:71:27:71:61 | e.dataT ... /html') |
347+
| dragAndDrop.ts:73:29:73:39 | droppedHtml |
348+
| dragAndDrop.ts:73:29:73:39 | droppedHtml |
349+
| dragAndDrop.ts:73:29:73:39 | droppedHtml |
334350
| event-handler-receiver.js:2:31:2:83 | '<h2><a ... ></h2>' |
335351
| event-handler-receiver.js:2:31:2:83 | '<h2><a ... ></h2>' |
336352
| event-handler-receiver.js:2:31:2:83 | '<h2><a ... ></h2>' |
@@ -1174,6 +1190,14 @@ edges
11741190
| clipboard.ts:43:22:43:55 | clipboa ... /html') | clipboard.ts:43:15:43:55 | html |
11751191
| clipboard.ts:43:22:43:55 | clipboa ... /html') | clipboard.ts:43:15:43:55 | html |
11761192
| clipboard.ts:43:22:43:55 | clipboa ... /html') | clipboard.ts:43:15:43:55 | html |
1193+
| clipboard.ts:71:13:71:62 | droppedHtml | clipboard.ts:73:29:73:39 | droppedHtml |
1194+
| clipboard.ts:71:13:71:62 | droppedHtml | clipboard.ts:73:29:73:39 | droppedHtml |
1195+
| clipboard.ts:71:13:71:62 | droppedHtml | clipboard.ts:73:29:73:39 | droppedHtml |
1196+
| clipboard.ts:71:13:71:62 | droppedHtml | clipboard.ts:73:29:73:39 | droppedHtml |
1197+
| clipboard.ts:71:27:71:62 | e.clipb ... /html') | clipboard.ts:71:13:71:62 | droppedHtml |
1198+
| clipboard.ts:71:27:71:62 | e.clipb ... /html') | clipboard.ts:71:13:71:62 | droppedHtml |
1199+
| clipboard.ts:71:27:71:62 | e.clipb ... /html') | clipboard.ts:71:13:71:62 | droppedHtml |
1200+
| clipboard.ts:71:27:71:62 | e.clipb ... /html') | clipboard.ts:71:13:71:62 | droppedHtml |
11771201
| d3.js:4:12:4:22 | window.name | d3.js:11:15:11:24 | getTaint() |
11781202
| d3.js:4:12:4:22 | window.name | d3.js:11:15:11:24 | getTaint() |
11791203
| d3.js:4:12:4:22 | window.name | d3.js:11:15:11:24 | getTaint() |
@@ -1381,6 +1405,14 @@ edges
13811405
| dragAndDrop.ts:43:22:43:54 | dataTra ... /html') | dragAndDrop.ts:43:15:43:54 | html |
13821406
| dragAndDrop.ts:43:22:43:54 | dataTra ... /html') | dragAndDrop.ts:43:15:43:54 | html |
13831407
| dragAndDrop.ts:43:22:43:54 | dataTra ... /html') | dragAndDrop.ts:43:15:43:54 | html |
1408+
| dragAndDrop.ts:71:13:71:61 | droppedHtml | dragAndDrop.ts:73:29:73:39 | droppedHtml |
1409+
| dragAndDrop.ts:71:13:71:61 | droppedHtml | dragAndDrop.ts:73:29:73:39 | droppedHtml |
1410+
| dragAndDrop.ts:71:13:71:61 | droppedHtml | dragAndDrop.ts:73:29:73:39 | droppedHtml |
1411+
| dragAndDrop.ts:71:13:71:61 | droppedHtml | dragAndDrop.ts:73:29:73:39 | droppedHtml |
1412+
| dragAndDrop.ts:71:27:71:61 | e.dataT ... /html') | dragAndDrop.ts:71:13:71:61 | droppedHtml |
1413+
| dragAndDrop.ts:71:27:71:61 | e.dataT ... /html') | dragAndDrop.ts:71:13:71:61 | droppedHtml |
1414+
| dragAndDrop.ts:71:27:71:61 | e.dataT ... /html') | dragAndDrop.ts:71:13:71:61 | droppedHtml |
1415+
| dragAndDrop.ts:71:27:71:61 | e.dataT ... /html') | dragAndDrop.ts:71:13:71:61 | droppedHtml |
13841416
| event-handler-receiver.js:2:49:2:61 | location.href | event-handler-receiver.js:2:31:2:83 | '<h2><a ... ></h2>' |
13851417
| event-handler-receiver.js:2:49:2:61 | location.href | event-handler-receiver.js:2:31:2:83 | '<h2><a ... ></h2>' |
13861418
| event-handler-receiver.js:2:49:2:61 | location.href | event-handler-receiver.js:2:31:2:83 | '<h2><a ... ></h2>' |
@@ -2126,6 +2158,7 @@ edges
21262158
| clipboard.ts:29:19:29:54 | e.clipb ... /html') | clipboard.ts:29:19:29:54 | e.clipb ... /html') | clipboard.ts:29:19:29:54 | e.clipb ... /html') | Cross-site scripting vulnerability due to $@. | clipboard.ts:29:19:29:54 | e.clipb ... /html') | user-provided value |
21272159
| clipboard.ts:33:19:33:68 | e.origi ... /html') | clipboard.ts:33:19:33:68 | e.origi ... /html') | clipboard.ts:33:19:33:68 | e.origi ... /html') | Cross-site scripting vulnerability due to $@. | clipboard.ts:33:19:33:68 | e.origi ... /html') | user-provided value |
21282160
| clipboard.ts:50:29:50:32 | html | clipboard.ts:43:22:43:55 | clipboa ... /html') | clipboard.ts:50:29:50:32 | html | Cross-site scripting vulnerability due to $@. | clipboard.ts:43:22:43:55 | clipboa ... /html') | user-provided value |
2161+
| clipboard.ts:73:29:73:39 | droppedHtml | clipboard.ts:71:27:71:62 | e.clipb ... /html') | clipboard.ts:73:29:73:39 | droppedHtml | Cross-site scripting vulnerability due to $@. | clipboard.ts:71:27:71:62 | e.clipb ... /html') | user-provided value |
21292162
| d3.js:11:15:11:24 | getTaint() | d3.js:4:12:4:22 | window.name | d3.js:11:15:11:24 | getTaint() | Cross-site scripting vulnerability due to $@. | d3.js:4:12:4:22 | window.name | user-provided value |
21302163
| d3.js:12:20:12:29 | getTaint() | d3.js:4:12:4:22 | window.name | d3.js:12:20:12:29 | getTaint() | Cross-site scripting vulnerability due to $@. | d3.js:4:12:4:22 | window.name | user-provided value |
21312164
| d3.js:14:20:14:29 | getTaint() | d3.js:4:12:4:22 | window.name | d3.js:14:20:14:29 | getTaint() | Cross-site scripting vulnerability due to $@. | d3.js:4:12:4:22 | window.name | user-provided value |
@@ -2151,6 +2184,7 @@ edges
21512184
| dragAndDrop.ts:29:19:29:53 | e.dataT ... /html') | dragAndDrop.ts:29:19:29:53 | e.dataT ... /html') | dragAndDrop.ts:29:19:29:53 | e.dataT ... /html') | Cross-site scripting vulnerability due to $@. | dragAndDrop.ts:29:19:29:53 | e.dataT ... /html') | user-provided value |
21522185
| dragAndDrop.ts:33:19:33:67 | e.origi ... /html') | dragAndDrop.ts:33:19:33:67 | e.origi ... /html') | dragAndDrop.ts:33:19:33:67 | e.origi ... /html') | Cross-site scripting vulnerability due to $@. | dragAndDrop.ts:33:19:33:67 | e.origi ... /html') | user-provided value |
21532186
| dragAndDrop.ts:50:29:50:32 | html | dragAndDrop.ts:43:22:43:54 | dataTra ... /html') | dragAndDrop.ts:50:29:50:32 | html | Cross-site scripting vulnerability due to $@. | dragAndDrop.ts:43:22:43:54 | dataTra ... /html') | user-provided value |
2187+
| dragAndDrop.ts:73:29:73:39 | droppedHtml | dragAndDrop.ts:71:27:71:61 | e.dataT ... /html') | dragAndDrop.ts:73:29:73:39 | droppedHtml | Cross-site scripting vulnerability due to $@. | dragAndDrop.ts:71:27:71:61 | e.dataT ... /html') | user-provided value |
21542188
| event-handler-receiver.js:2:31:2:83 | '<h2><a ... ></h2>' | event-handler-receiver.js:2:49:2:61 | location.href | event-handler-receiver.js:2:31:2:83 | '<h2><a ... ></h2>' | Cross-site scripting vulnerability due to $@. | event-handler-receiver.js:2:49:2:61 | location.href | user-provided value |
21552189
| express.js:7:15:7:33 | req.param("wobble") | express.js:7:15:7:33 | req.param("wobble") | express.js:7:15:7:33 | req.param("wobble") | Cross-site scripting vulnerability due to $@. | express.js:7:15:7:33 | req.param("wobble") | user-provided value |
21562190
| jquery.js:7:5:7:34 | "<div i ... + "\\">" | jquery.js:2:17:2:40 | documen ... .search | jquery.js:7:5:7:34 | "<div i ... + "\\">" | Cross-site scripting vulnerability due to $@. | jquery.js:2:17:2:40 | documen ... .search | user-provided value |

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,14 @@ nodes
144144
| clipboard.ts:50:29:50:32 | html |
145145
| clipboard.ts:50:29:50:32 | html |
146146
| clipboard.ts:50:29:50:32 | html |
147+
| clipboard.ts:71:13:71:62 | droppedHtml |
148+
| clipboard.ts:71:13:71:62 | droppedHtml |
149+
| clipboard.ts:71:27:71:62 | e.clipb ... /html') |
150+
| clipboard.ts:71:27:71:62 | e.clipb ... /html') |
151+
| clipboard.ts:71:27:71:62 | e.clipb ... /html') |
152+
| clipboard.ts:73:29:73:39 | droppedHtml |
153+
| clipboard.ts:73:29:73:39 | droppedHtml |
154+
| clipboard.ts:73:29:73:39 | droppedHtml |
147155
| d3.js:4:12:4:22 | window.name |
148156
| d3.js:4:12:4:22 | window.name |
149157
| d3.js:4:12:4:22 | window.name |
@@ -331,6 +339,14 @@ nodes
331339
| dragAndDrop.ts:50:29:50:32 | html |
332340
| dragAndDrop.ts:50:29:50:32 | html |
333341
| dragAndDrop.ts:50:29:50:32 | html |
342+
| dragAndDrop.ts:71:13:71:61 | droppedHtml |
343+
| dragAndDrop.ts:71:13:71:61 | droppedHtml |
344+
| dragAndDrop.ts:71:27:71:61 | e.dataT ... /html') |
345+
| dragAndDrop.ts:71:27:71:61 | e.dataT ... /html') |
346+
| dragAndDrop.ts:71:27:71:61 | e.dataT ... /html') |
347+
| dragAndDrop.ts:73:29:73:39 | droppedHtml |
348+
| dragAndDrop.ts:73:29:73:39 | droppedHtml |
349+
| dragAndDrop.ts:73:29:73:39 | droppedHtml |
334350
| event-handler-receiver.js:2:31:2:83 | '<h2><a ... ></h2>' |
335351
| event-handler-receiver.js:2:31:2:83 | '<h2><a ... ></h2>' |
336352
| event-handler-receiver.js:2:31:2:83 | '<h2><a ... ></h2>' |
@@ -1224,6 +1240,14 @@ edges
12241240
| clipboard.ts:43:22:43:55 | clipboa ... /html') | clipboard.ts:43:15:43:55 | html |
12251241
| clipboard.ts:43:22:43:55 | clipboa ... /html') | clipboard.ts:43:15:43:55 | html |
12261242
| clipboard.ts:43:22:43:55 | clipboa ... /html') | clipboard.ts:43:15:43:55 | html |
1243+
| clipboard.ts:71:13:71:62 | droppedHtml | clipboard.ts:73:29:73:39 | droppedHtml |
1244+
| clipboard.ts:71:13:71:62 | droppedHtml | clipboard.ts:73:29:73:39 | droppedHtml |
1245+
| clipboard.ts:71:13:71:62 | droppedHtml | clipboard.ts:73:29:73:39 | droppedHtml |
1246+
| clipboard.ts:71:13:71:62 | droppedHtml | clipboard.ts:73:29:73:39 | droppedHtml |
1247+
| clipboard.ts:71:27:71:62 | e.clipb ... /html') | clipboard.ts:71:13:71:62 | droppedHtml |
1248+
| clipboard.ts:71:27:71:62 | e.clipb ... /html') | clipboard.ts:71:13:71:62 | droppedHtml |
1249+
| clipboard.ts:71:27:71:62 | e.clipb ... /html') | clipboard.ts:71:13:71:62 | droppedHtml |
1250+
| clipboard.ts:71:27:71:62 | e.clipb ... /html') | clipboard.ts:71:13:71:62 | droppedHtml |
12271251
| d3.js:4:12:4:22 | window.name | d3.js:11:15:11:24 | getTaint() |
12281252
| d3.js:4:12:4:22 | window.name | d3.js:11:15:11:24 | getTaint() |
12291253
| d3.js:4:12:4:22 | window.name | d3.js:11:15:11:24 | getTaint() |
@@ -1431,6 +1455,14 @@ edges
14311455
| dragAndDrop.ts:43:22:43:54 | dataTra ... /html') | dragAndDrop.ts:43:15:43:54 | html |
14321456
| dragAndDrop.ts:43:22:43:54 | dataTra ... /html') | dragAndDrop.ts:43:15:43:54 | html |
14331457
| dragAndDrop.ts:43:22:43:54 | dataTra ... /html') | dragAndDrop.ts:43:15:43:54 | html |
1458+
| dragAndDrop.ts:71:13:71:61 | droppedHtml | dragAndDrop.ts:73:29:73:39 | droppedHtml |
1459+
| dragAndDrop.ts:71:13:71:61 | droppedHtml | dragAndDrop.ts:73:29:73:39 | droppedHtml |
1460+
| dragAndDrop.ts:71:13:71:61 | droppedHtml | dragAndDrop.ts:73:29:73:39 | droppedHtml |
1461+
| dragAndDrop.ts:71:13:71:61 | droppedHtml | dragAndDrop.ts:73:29:73:39 | droppedHtml |
1462+
| dragAndDrop.ts:71:27:71:61 | e.dataT ... /html') | dragAndDrop.ts:71:13:71:61 | droppedHtml |
1463+
| dragAndDrop.ts:71:27:71:61 | e.dataT ... /html') | dragAndDrop.ts:71:13:71:61 | droppedHtml |
1464+
| dragAndDrop.ts:71:27:71:61 | e.dataT ... /html') | dragAndDrop.ts:71:13:71:61 | droppedHtml |
1465+
| dragAndDrop.ts:71:27:71:61 | e.dataT ... /html') | dragAndDrop.ts:71:13:71:61 | droppedHtml |
14341466
| event-handler-receiver.js:2:49:2:61 | location.href | event-handler-receiver.js:2:31:2:83 | '<h2><a ... ></h2>' |
14351467
| event-handler-receiver.js:2:49:2:61 | location.href | event-handler-receiver.js:2:31:2:83 | '<h2><a ... ></h2>' |
14361468
| event-handler-receiver.js:2:49:2:61 | location.href | event-handler-receiver.js:2:31:2:83 | '<h2><a ... ></h2>' |

javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/clipboard.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,37 @@ $("#foo").bind('paste', (e) => {
5353
}
5454
document.body.append(div);
5555
}
56-
})();
56+
})();
57+
58+
async function getClipboardData(e: ClipboardEvent): Promise<Array<File | string>> {
59+
// Using a set to filter out duplicates. For some reason, dropping URLs duplicates them 3 times (for me)
60+
const dropItems = new Set<File | string>();
61+
62+
// First get all files in the drop event
63+
if (e.clipboardData.files.length > 0) {
64+
// tslint:disable-next-line: prefer-for-of
65+
for (let i = 0; i < e.clipboardData.files.length; i++) {
66+
const file = e.clipboardData.files[i];
67+
}
68+
}
69+
70+
if (e.clipboardData.types.includes('text/html')) {
71+
const droppedHtml = e.clipboardData.getData('text/html');
72+
const container = document.createElement('html');
73+
container.innerHTML = droppedHtml;
74+
const imgs = container.getElementsByTagName('img');
75+
if (imgs.length === 1) {
76+
const src = imgs[0].src;
77+
dropItems.add(src);
78+
}
79+
} else if (e.clipboardData.types.includes('text/plain')) {
80+
const plainText = e.clipboardData.getData('text/plain');
81+
// Check if text is an URL
82+
if (/^https?:\/\//i.test(plainText)) {
83+
dropItems.add(plainText);
84+
}
85+
}
86+
87+
const imageItems = Array.from(dropItems);
88+
return imageItems;
89+
}

javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/dragAndDrop.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,37 @@ $("#foo").bind('drop', (e) => {
5353
}
5454
document.body.append(div);
5555
}
56-
})();
56+
})();
57+
58+
async function getDropData(e: DragEvent): Promise<Array<File | string>> {
59+
// Using a set to filter out duplicates. For some reason, dropping URLs duplicates them 3 times (for me)
60+
const dropItems = new Set<File | string>();
61+
62+
// First get all files in the drop event
63+
if (e.dataTransfer.files.length > 0) {
64+
// tslint:disable-next-line: prefer-for-of
65+
for (let i = 0; i < e.dataTransfer.files.length; i++) {
66+
const file = e.dataTransfer.files[i];
67+
}
68+
}
69+
70+
if (e.dataTransfer.types.includes('text/html')) {
71+
const droppedHtml = e.dataTransfer.getData('text/html');
72+
const container = document.createElement('html');
73+
container.innerHTML = droppedHtml;
74+
const imgs = container.getElementsByTagName('img');
75+
if (imgs.length === 1) {
76+
const src = imgs[0].src;
77+
dropItems.add(src);
78+
}
79+
} else if (e.dataTransfer.types.includes('text/plain')) {
80+
const plainText = e.dataTransfer.getData('text/plain');
81+
// Check if text is an URL
82+
if (/^https?:\/\//i.test(plainText)) {
83+
dropItems.add(plainText);
84+
}
85+
}
86+
87+
const imageItems = Array.from(dropItems);
88+
return imageItems;
89+
}

0 commit comments

Comments
 (0)