Skip to content

Commit b25b19f

Browse files
authored
Merge pull request #6584 from erik-krogh/clipBoard
Approved by esbena
2 parents 136d043 + 3b6c8c5 commit b25b19f

File tree

10 files changed

+148
-153
lines changed

10 files changed

+148
-153
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
lgtm,codescanning
2+
* The security queries now recognize clipboard data as a source, enabling the queries to flag additional alerts.

javascript/ql/lib/javascript.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ import semmle.javascript.frameworks.ComposedFunctions
7878
import semmle.javascript.frameworks.Classnames
7979
import semmle.javascript.frameworks.ClassValidator
8080
import semmle.javascript.frameworks.ClientRequests
81+
import semmle.javascript.frameworks.Clipboard
8182
import semmle.javascript.frameworks.ClosureLibrary
8283
import semmle.javascript.frameworks.CookieLibraries
8384
import semmle.javascript.frameworks.Credentials

javascript/ql/lib/semmle/javascript/DOM.qll

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,8 @@ module DOM {
450450
result = domValueRef(DataFlow::TypeTracker::end())
451451
or
452452
result.hasUnderlyingType("Element")
453+
or
454+
result.hasUnderlyingType(any(string s | s.matches("HTML%Element")))
453455
}
454456

455457
module LocationSource {
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/**
2+
* Provides predicates for reasoning about clipboard data.
3+
*/
4+
5+
import javascript
6+
7+
/**
8+
* Gets a jQuery "paste" event.
9+
* E.g. `e` in `$("#foo").on("paste", function(e) { ... })`.
10+
*/
11+
private DataFlow::SourceNode jQueryPasteEvent(DataFlow::TypeTracker t) {
12+
t.start() and
13+
exists(DataFlow::CallNode call |
14+
call = JQuery::objectRef().getAMethodCall(["bind", "on", "live", "one", "delegate"]) and
15+
call.getArgument(0).mayHaveStringValue("paste")
16+
|
17+
result = call.getCallback(call.getNumArgument() - 1).getParameter(0)
18+
)
19+
or
20+
exists(DataFlow::TypeTracker t2 | result = jQueryPasteEvent(t2).track(t2, t))
21+
}
22+
23+
/**
24+
* Gets a DOM "paste" event.
25+
* E.g. `e` in `document.addEventListener("paste", e => { ... })`.
26+
*/
27+
private DataFlow::SourceNode pasteEvent(DataFlow::TypeTracker t) {
28+
t.start() and
29+
exists(DataFlow::CallNode call | call = DOM::domValueRef().getAMemberCall("addEventListener") |
30+
call.getArgument(0).mayHaveStringValue("paste") and
31+
result = call.getCallback(1).getParameter(0)
32+
)
33+
or
34+
t.start() and
35+
result = jQueryPasteEvent(DataFlow::TypeTracker::end()).getAPropertyRead("originalEvent")
36+
or
37+
exists(DataFlow::TypeTracker t2 | result = pasteEvent(t2).track(t2, t))
38+
}
39+
40+
/**
41+
* Gets a reference to the clipboardData DataTransfer object.
42+
* https://developer.mozilla.org/en-US/docs/Web/API/ClipboardEvent/clipboardData
43+
*/
44+
private DataFlow::SourceNode clipboardDataTransferSource(DataFlow::TypeTracker t) {
45+
t.start() and
46+
exists(DataFlow::PropRead read | read = result |
47+
read.getPropertyName() = "clipboardData" and
48+
read.getBase().getALocalSource() = pasteEvent(DataFlow::TypeTracker::end())
49+
)
50+
or
51+
exists(DataFlow::TypeTracker t2 | result = clipboardDataTransferSource(t2).track(t2, t))
52+
}
53+
54+
/**
55+
* A reference to data from the clipboard. Seen as a source for DOM-based XSS.
56+
*/
57+
private class ClipboardSource extends RemoteFlowSource {
58+
ClipboardSource() {
59+
this = clipboardDataTransferSource(DataFlow::TypeTracker::end()).getAMethodCall("getData")
60+
}
61+
62+
override string getSourceType() { result = "Clipboard data" }
63+
}

javascript/ql/src/experimental/Security/CWE-079/ClipboardXss.qhelp

Lines changed: 0 additions & 57 deletions
This file was deleted.

javascript/ql/src/experimental/Security/CWE-079/ClipboardXss.ql

Lines changed: 0 additions & 78 deletions
This file was deleted.

javascript/ql/src/experimental/Security/CWE-079/examples/clipboard-xss-sample.js

Lines changed: 0 additions & 18 deletions
This file was deleted.

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,20 @@ nodes
9191
| classnames.js:15:47:15:63 | clsx(window.name) |
9292
| classnames.js:15:52:15:62 | window.name |
9393
| classnames.js:15:52:15:62 | window.name |
94+
| clipboard.ts:8:11:8:51 | html |
95+
| clipboard.ts:8:18:8:51 | clipboa ... /html') |
96+
| clipboard.ts:8:18:8:51 | clipboa ... /html') |
97+
| clipboard.ts:15:25:15:28 | html |
98+
| clipboard.ts:15:25:15:28 | html |
99+
| clipboard.ts:24:23:24:58 | e.clipb ... /html') |
100+
| clipboard.ts:24:23:24:58 | e.clipb ... /html') |
101+
| clipboard.ts:24:23:24:58 | e.clipb ... /html') |
102+
| clipboard.ts:29:19:29:54 | e.clipb ... /html') |
103+
| clipboard.ts:29:19:29:54 | e.clipb ... /html') |
104+
| clipboard.ts:29:19:29:54 | e.clipb ... /html') |
105+
| clipboard.ts:33:19:33:68 | e.origi ... /html') |
106+
| clipboard.ts:33:19:33:68 | e.origi ... /html') |
107+
| clipboard.ts:33:19:33:68 | e.origi ... /html') |
94108
| d3.js:4:12:4:22 | window.name |
95109
| d3.js:4:12:4:22 | window.name |
96110
| d3.js:11:15:11:24 | getTaint() |
@@ -857,6 +871,13 @@ edges
857871
| classnames.js:15:47:15:63 | clsx(window.name) | classnames.js:15:31:15:78 | `<span ... <span>` |
858872
| classnames.js:15:52:15:62 | window.name | classnames.js:15:47:15:63 | clsx(window.name) |
859873
| classnames.js:15:52:15:62 | window.name | classnames.js:15:47:15:63 | clsx(window.name) |
874+
| clipboard.ts:8:11:8:51 | html | clipboard.ts:15:25:15:28 | html |
875+
| clipboard.ts:8:11:8:51 | html | clipboard.ts:15:25:15:28 | html |
876+
| clipboard.ts:8:18:8:51 | clipboa ... /html') | clipboard.ts:8:11:8:51 | html |
877+
| clipboard.ts:8:18:8:51 | clipboa ... /html') | clipboard.ts:8:11:8:51 | html |
878+
| clipboard.ts:24:23:24:58 | e.clipb ... /html') | clipboard.ts:24:23:24:58 | e.clipb ... /html') |
879+
| clipboard.ts:29:19:29:54 | e.clipb ... /html') | clipboard.ts:29:19:29:54 | e.clipb ... /html') |
880+
| clipboard.ts:33:19:33:68 | e.origi ... /html') | clipboard.ts:33:19:33:68 | e.origi ... /html') |
860881
| d3.js:4:12:4:22 | window.name | d3.js:11:15:11:24 | getTaint() |
861882
| d3.js:4:12:4:22 | window.name | d3.js:11:15:11:24 | getTaint() |
862883
| d3.js:4:12:4:22 | window.name | d3.js:11:15:11:24 | getTaint() |
@@ -1514,6 +1535,10 @@ edges
15141535
| classnames.js:11:31:11:79 | `<span ... <span>` | classnames.js:10:45:10:55 | window.name | classnames.js:11:31:11:79 | `<span ... <span>` | Cross-site scripting vulnerability due to $@. | classnames.js:10:45:10:55 | window.name | user-provided value |
15151536
| classnames.js:13:31:13:83 | `<span ... <span>` | classnames.js:13:57:13:67 | window.name | classnames.js:13:31:13:83 | `<span ... <span>` | Cross-site scripting vulnerability due to $@. | classnames.js:13:57:13:67 | window.name | user-provided value |
15161537
| classnames.js:15:31:15:78 | `<span ... <span>` | classnames.js:15:52:15:62 | window.name | classnames.js:15:31:15:78 | `<span ... <span>` | Cross-site scripting vulnerability due to $@. | classnames.js:15:52:15:62 | window.name | user-provided value |
1538+
| clipboard.ts:15:25:15:28 | html | clipboard.ts:8:18:8:51 | clipboa ... /html') | clipboard.ts:15:25:15:28 | html | Cross-site scripting vulnerability due to $@. | clipboard.ts:8:18:8:51 | clipboa ... /html') | user-provided value |
1539+
| clipboard.ts:24:23:24:58 | e.clipb ... /html') | clipboard.ts:24:23:24:58 | e.clipb ... /html') | clipboard.ts:24:23:24:58 | e.clipb ... /html') | Cross-site scripting vulnerability due to $@. | clipboard.ts:24:23:24:58 | e.clipb ... /html') | user-provided value |
1540+
| 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 |
1541+
| 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 |
15171542
| 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 |
15181543
| 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 |
15191544
| 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 |

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,20 @@ nodes
9191
| classnames.js:15:47:15:63 | clsx(window.name) |
9292
| classnames.js:15:52:15:62 | window.name |
9393
| classnames.js:15:52:15:62 | window.name |
94+
| clipboard.ts:8:11:8:51 | html |
95+
| clipboard.ts:8:18:8:51 | clipboa ... /html') |
96+
| clipboard.ts:8:18:8:51 | clipboa ... /html') |
97+
| clipboard.ts:15:25:15:28 | html |
98+
| clipboard.ts:15:25:15:28 | html |
99+
| clipboard.ts:24:23:24:58 | e.clipb ... /html') |
100+
| clipboard.ts:24:23:24:58 | e.clipb ... /html') |
101+
| clipboard.ts:24:23:24:58 | e.clipb ... /html') |
102+
| clipboard.ts:29:19:29:54 | e.clipb ... /html') |
103+
| clipboard.ts:29:19:29:54 | e.clipb ... /html') |
104+
| clipboard.ts:29:19:29:54 | e.clipb ... /html') |
105+
| clipboard.ts:33:19:33:68 | e.origi ... /html') |
106+
| clipboard.ts:33:19:33:68 | e.origi ... /html') |
107+
| clipboard.ts:33:19:33:68 | e.origi ... /html') |
94108
| d3.js:4:12:4:22 | window.name |
95109
| d3.js:4:12:4:22 | window.name |
96110
| d3.js:11:15:11:24 | getTaint() |
@@ -875,6 +889,13 @@ edges
875889
| classnames.js:15:47:15:63 | clsx(window.name) | classnames.js:15:31:15:78 | `<span ... <span>` |
876890
| classnames.js:15:52:15:62 | window.name | classnames.js:15:47:15:63 | clsx(window.name) |
877891
| classnames.js:15:52:15:62 | window.name | classnames.js:15:47:15:63 | clsx(window.name) |
892+
| clipboard.ts:8:11:8:51 | html | clipboard.ts:15:25:15:28 | html |
893+
| clipboard.ts:8:11:8:51 | html | clipboard.ts:15:25:15:28 | html |
894+
| clipboard.ts:8:18:8:51 | clipboa ... /html') | clipboard.ts:8:11:8:51 | html |
895+
| clipboard.ts:8:18:8:51 | clipboa ... /html') | clipboard.ts:8:11:8:51 | html |
896+
| clipboard.ts:24:23:24:58 | e.clipb ... /html') | clipboard.ts:24:23:24:58 | e.clipb ... /html') |
897+
| clipboard.ts:29:19:29:54 | e.clipb ... /html') | clipboard.ts:29:19:29:54 | e.clipb ... /html') |
898+
| clipboard.ts:33:19:33:68 | e.origi ... /html') | clipboard.ts:33:19:33:68 | e.origi ... /html') |
878899
| d3.js:4:12:4:22 | window.name | d3.js:11:15:11:24 | getTaint() |
879900
| d3.js:4:12:4:22 | window.name | d3.js:11:15:11:24 | getTaint() |
880901
| d3.js:4:12:4:22 | window.name | d3.js:11:15:11:24 | getTaint() |
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
$("#foo").on("paste", paste);
2+
3+
function paste(e) {
4+
const { clipboardData } = e.originalEvent;
5+
if (!clipboardData) return;
6+
7+
const text = clipboardData.getData('text/plain');
8+
const html = clipboardData.getData('text/html');
9+
if (!text && !html) return;
10+
11+
e.preventDefault();
12+
13+
const div = document.createElement('div');
14+
if (html) {
15+
div.innerHTML = html; // NOT OK
16+
} else {
17+
div.textContent = text;
18+
}
19+
document.body.append(div);
20+
}
21+
22+
export function install(el: HTMLElement): void {
23+
el.addEventListener('paste', (e) => {
24+
$("#id").html(e.clipboardData.getData('text/html')); // NOT OK
25+
})
26+
}
27+
28+
document.addEventListener('paste', (e) => {
29+
$("#id").html(e.clipboardData.getData('text/html')); // NOT OK
30+
});
31+
32+
$("#foo").bind('paste', (e) => {
33+
$("#id").html(e.originalEvent.clipboardData.getData('text/html')); // NOT OK
34+
});

0 commit comments

Comments
 (0)