Skip to content

Commit a908e59

Browse files
authored
Merge pull request github#4574 from erik-krogh/jsdom
Approved by asgerf
2 parents a3894be + e124ba6 commit a908e59

File tree

7 files changed

+66
-1
lines changed

7 files changed

+66
-1
lines changed

javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -809,4 +809,19 @@ module ClientRequest {
809809

810810
override DataFlow::Node getADataNode() { none() }
811811
}
812+
813+
/**
814+
* A model of a URL request made using `jsdom.fromUrl()`.
815+
*/
816+
class JSDOMFromUrl extends ClientRequest::Range {
817+
JSDOMFromUrl() {
818+
this = API::moduleImport("jsdom").getMember("JSDOM").getMember("fromURL").getACall()
819+
}
820+
821+
override DataFlow::Node getUrl() { result = getArgument(0) }
822+
823+
override DataFlow::Node getHost() { none() }
824+
825+
override DataFlow::Node getADataNode() { none() }
826+
}
812827
}

javascript/ql/src/semmle/javascript/security/dataflow/Xss.qll

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ module DomBasedXss {
151151
* An expression whose value is interpreted as HTML
152152
* and may be inserted into the DOM through a library.
153153
*/
154-
class LibrarySink extends Sink, DataFlow::ValueNode {
154+
class LibrarySink extends Sink {
155155
LibrarySink() {
156156
// call to a jQuery method that interprets its argument as HTML
157157
exists(JQuery::MethodCall call |
@@ -175,6 +175,13 @@ module DomBasedXss {
175175
this = any(Handlebars::SafeString s).getAnArgument()
176176
or
177177
this = any(JQuery::MethodCall call | call.getMethodName() = "jGrowl").getArgument(0)
178+
or
179+
// A construction of a JSDOM object (server side DOM), where scripts are allowed.
180+
exists(DataFlow::NewNode instance |
181+
instance = API::moduleImport("jsdom").getMember("JSDOM").getInstance().getAnImmediateUse() and
182+
this = instance.getArgument(0) and
183+
instance.getOptionArgument(1, "runScripts").mayHaveStringValue("dangerously")
184+
)
178185
}
179186
}
180187

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ nodes
8989
| classnames.js:15:47:15:63 | clsx(window.name) |
9090
| classnames.js:15:52:15:62 | window.name |
9191
| classnames.js:15:52:15:62 | window.name |
92+
| express.js:7:15:7:33 | req.param("wobble") |
93+
| express.js:7:15:7:33 | req.param("wobble") |
94+
| express.js:7:15:7:33 | req.param("wobble") |
9295
| jquery.js:2:7:2:40 | tainted |
9396
| jquery.js:2:7:2:40 | tainted |
9497
| jquery.js:2:17:2:33 | document.___location |
@@ -685,6 +688,7 @@ edges
685688
| classnames.js:15:47:15:63 | clsx(window.name) | classnames.js:15:31:15:78 | `<span ... <span>` |
686689
| classnames.js:15:52:15:62 | window.name | classnames.js:15:47:15:63 | clsx(window.name) |
687690
| classnames.js:15:52:15:62 | window.name | classnames.js:15:47:15:63 | clsx(window.name) |
691+
| express.js:7:15:7:33 | req.param("wobble") | express.js:7:15:7:33 | req.param("wobble") |
688692
| jquery.js:2:7:2:40 | tainted | jquery.js:7:20:7:26 | tainted |
689693
| jquery.js:2:7:2:40 | tainted | jquery.js:8:28:8:34 | tainted |
690694
| jquery.js:2:17:2:33 | document.___location | jquery.js:2:17:2:40 | documen ... .search |
@@ -1175,6 +1179,7 @@ edges
11751179
| 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 |
11761180
| 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 |
11771181
| 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 |
1182+
| 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 |
11781183
| 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 |
11791184
| jquery.js:8:18:8:34 | "XSS: " + tainted | jquery.js:2:17:2:33 | document.___location | jquery.js:8:18:8:34 | "XSS: " + tainted | Cross-site scripting vulnerability due to $@. | jquery.js:2:17:2:33 | document.___location | user-provided value |
11801185
| jquery.js:10:5:10:40 | "<b>" + ... "</b>" | jquery.js:10:13:10:20 | ___location | jquery.js:10:5:10:40 | "<b>" + ... "</b>" | Cross-site scripting vulnerability due to $@. | jquery.js:10:13:10:20 | ___location | user-provided value |

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ nodes
8989
| classnames.js:15:47:15:63 | clsx(window.name) |
9090
| classnames.js:15:52:15:62 | window.name |
9191
| classnames.js:15:52:15:62 | window.name |
92+
| express.js:7:15:7:33 | req.param("wobble") |
93+
| express.js:7:15:7:33 | req.param("wobble") |
94+
| express.js:7:15:7:33 | req.param("wobble") |
9295
| jquery.js:2:7:2:40 | tainted |
9396
| jquery.js:2:7:2:40 | tainted |
9497
| jquery.js:2:17:2:33 | document.___location |
@@ -689,6 +692,7 @@ edges
689692
| classnames.js:15:47:15:63 | clsx(window.name) | classnames.js:15:31:15:78 | `<span ... <span>` |
690693
| classnames.js:15:52:15:62 | window.name | classnames.js:15:47:15:63 | clsx(window.name) |
691694
| classnames.js:15:52:15:62 | window.name | classnames.js:15:47:15:63 | clsx(window.name) |
695+
| express.js:7:15:7:33 | req.param("wobble") | express.js:7:15:7:33 | req.param("wobble") |
692696
| jquery.js:2:7:2:40 | tainted | jquery.js:7:20:7:26 | tainted |
693697
| jquery.js:2:7:2:40 | tainted | jquery.js:8:28:8:34 | tainted |
694698
| jquery.js:2:17:2:33 | document.___location | jquery.js:2:17:2:40 | documen ... .search |
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
var express = require('express');
2+
var app = express();
3+
4+
import { JSDOM } from "jsdom";
5+
app.get('/some/path', function (req, res) {
6+
// NOT OK
7+
new JSDOM(req.param("wobble"), { runScripts: "dangerously" });
8+
9+
// OK
10+
new JSDOM(req.param("wobble"), { runScripts: "outside-only" });
11+
});

javascript/ql/test/query-tests/Security/CWE-918/RequestForgery.expected

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,14 @@ nodes
4949
| tst.js:64:30:64:36 | tainted |
5050
| tst.js:68:30:68:36 | tainted |
5151
| tst.js:68:30:68:36 | tainted |
52+
| tst.js:74:9:74:52 | tainted |
53+
| tst.js:74:19:74:42 | url.par ... , true) |
54+
| tst.js:74:19:74:48 | url.par ... ).query |
55+
| tst.js:74:19:74:52 | url.par ... ery.url |
56+
| tst.js:74:29:74:35 | req.url |
57+
| tst.js:74:29:74:35 | req.url |
58+
| tst.js:76:19:76:25 | tainted |
59+
| tst.js:76:19:76:25 | tainted |
5260
edges
5361
| tst.js:14:9:14:52 | tainted | tst.js:18:13:18:19 | tainted |
5462
| tst.js:14:9:14:52 | tainted | tst.js:18:13:18:19 | tainted |
@@ -98,6 +106,13 @@ edges
98106
| tst.js:58:19:58:52 | url.par ... ery.url | tst.js:58:9:58:52 | tainted |
99107
| tst.js:58:29:58:35 | req.url | tst.js:58:19:58:42 | url.par ... , true) |
100108
| tst.js:58:29:58:35 | req.url | tst.js:58:19:58:42 | url.par ... , true) |
109+
| tst.js:74:9:74:52 | tainted | tst.js:76:19:76:25 | tainted |
110+
| tst.js:74:9:74:52 | tainted | tst.js:76:19:76:25 | tainted |
111+
| tst.js:74:19:74:42 | url.par ... , true) | tst.js:74:19:74:48 | url.par ... ).query |
112+
| tst.js:74:19:74:48 | url.par ... ).query | tst.js:74:19:74:52 | url.par ... ery.url |
113+
| tst.js:74:19:74:52 | url.par ... ery.url | tst.js:74:9:74:52 | tainted |
114+
| tst.js:74:29:74:35 | req.url | tst.js:74:19:74:42 | url.par ... , true) |
115+
| tst.js:74:29:74:35 | req.url | tst.js:74:19:74:42 | url.par ... , true) |
101116
#select
102117
| tst.js:18:5:18:20 | request(tainted) | tst.js:14:29:14:35 | req.url | tst.js:18:13:18:19 | tainted | The $@ of this request depends on $@. | tst.js:18:13:18:19 | tainted | URL | tst.js:14:29:14:35 | req.url | a user-provided value |
103118
| tst.js:20:5:20:24 | request.get(tainted) | tst.js:14:29:14:35 | req.url | tst.js:20:17:20:23 | tainted | The $@ of this request depends on $@. | tst.js:20:17:20:23 | tainted | URL | tst.js:14:29:14:35 | req.url | a user-provided value |
@@ -114,3 +129,4 @@ edges
114129
| tst.js:61:2:61:37 | client. ... inted}) | tst.js:58:29:58:35 | req.url | tst.js:61:29:61:35 | tainted | The $@ of this request depends on $@. | tst.js:61:29:61:35 | tainted | URL | tst.js:58:29:58:35 | req.url | a user-provided value |
115130
| tst.js:64:3:64:38 | client. ... inted}) | tst.js:58:29:58:35 | req.url | tst.js:64:30:64:36 | tainted | The $@ of this request depends on $@. | tst.js:64:30:64:36 | tainted | URL | tst.js:58:29:58:35 | req.url | a user-provided value |
116131
| tst.js:68:3:68:38 | client. ... inted}) | tst.js:58:29:58:35 | req.url | tst.js:68:30:68:36 | tainted | The $@ of this request depends on $@. | tst.js:68:30:68:36 | tainted | URL | tst.js:58:29:58:35 | req.url | a user-provided value |
132+
| tst.js:76:5:76:26 | JSDOM.f ... ainted) | tst.js:74:29:74:35 | req.url | tst.js:76:19:76:25 | tainted | The $@ of this request depends on $@. | tst.js:76:19:76:25 | tainted | URL | tst.js:74:29:74:35 | req.url | a user-provided value |

javascript/ql/test/query-tests/Security/CWE-918/tst.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,10 @@ var server = http.createServer(async function(req, res) {
6868
client.Page.navigate({url: tainted}); // NOT OK.
6969
});
7070
})
71+
72+
import {JSDOM} from "jsdom";
73+
var server = http.createServer(async function(req, res) {
74+
var tainted = url.parse(req.url, true).query.url;
75+
76+
JSDOM.fromURL(tainted); // NOT OK
77+
});

0 commit comments

Comments
 (0)