Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 35ea746

Browse files
authored
Merge pull request #1172 from asger-semmle/hostname-prefix-sanitizer
Approved by xiemaisi
2 parents 8604057 + 6478d93 commit 35ea746

6 files changed

Lines changed: 46 additions & 17 deletions

File tree

change-notes/1.21/analysis-javascript.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818
| **Query** | **Expected impact** | **Change** |
1919
|--------------------------------|------------------------------|---------------------------------------------------------------------------|
2020
| Expression has no effect | Fewer false-positive results | This rule now treats uses of `Object.defineProperty` more conservatively. |
21-
| Useless assignment to property | Fewer false-positive results | This rule now ignore reads of additional getters. |
21+
| Useless assignment to property | Fewer false-positive results | This rule now ignores reads of additional getters. |
2222
| Arbitrary file write during zip extraction ("Zip Slip") | More results | This rule now considers more libraries, including tar as well as zip. |
23+
| Client-side URL redirect | Fewer false-positive results | This rule now treats URLs as safe in more cases where the hostname cannot be tampered with. |
24+
| Server-side URL redirect | Fewer false-positive results | This rule now treats URLs as safe in more cases where the hostname cannot be tampered with. |
2325

2426
## Changes to QL libraries

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,13 @@ predicate sanitizingPrefixEdge(DataFlow::Node source, DataFlow::Node sink) {
4343
* - `?` (any suffix becomes part of query)
4444
* - `#` (any suffix becomes part of fragment)
4545
* - `/` or `\`, immediately prefixed by a character other than `:`, `/`, or `\` (any suffix becomes part of the path)
46+
* - a leading `/` or `\` followed by a character other than `/` or `\` (any suffix becomes part of the path)
4647
*
47-
* In the latter case, the additional prefix check is necessary to avoid a `/` that could be interpreted as
48+
* In the latter two cases, the additional check is necessary to avoid a `/` that could be interpreted as
4849
* the `//` separating the (optional) scheme from the hostname.
4950
*/
5051
private predicate hasHostnameSanitizingSubstring(DataFlow::Node nd) {
51-
nd.getStringValue().regexpMatch(".*([?#]|[^?#:/\\\\][/\\\\]).*")
52+
nd.getStringValue().regexpMatch(".*([?#]|[^?#:/\\\\][/\\\\]).*|[/\\\\][^/\\\\].*")
5253
or
5354
hasHostnameSanitizingSubstring(StringConcatenation::getAnOperand(nd))
5455
or

javascript/ql/test/query-tests/Security/CWE-601/ClientSideUrlRedirect/ClientSideUrlRedirect.expected

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,18 @@ nodes
2222
| tst9.js:2:21:2:37 | document.location |
2323
| tst9.js:2:21:2:42 | documen ... on.hash |
2424
| tst9.js:2:21:2:55 | documen ... ring(1) |
25+
| tst10.js:5:17:5:46 | '/' + d ... .search |
26+
| tst10.js:5:23:5:39 | document.location |
27+
| tst10.js:5:23:5:46 | documen ... .search |
28+
| tst10.js:8:17:8:47 | '//' + ... .search |
29+
| tst10.js:8:24:8:40 | document.location |
30+
| tst10.js:8:24:8:47 | documen ... .search |
31+
| tst10.js:11:17:11:50 | '//foo' ... .search |
32+
| tst10.js:11:27:11:43 | document.location |
33+
| tst10.js:11:27:11:50 | documen ... .search |
34+
| tst10.js:14:17:14:56 | 'https: ... .search |
35+
| tst10.js:14:33:14:49 | document.location |
36+
| tst10.js:14:33:14:56 | documen ... .search |
2537
| tst.js:2:19:2:69 | /.*redi ... n.href) |
2638
| tst.js:2:19:2:72 | /.*redi ... ref)[1] |
2739
| tst.js:2:47:2:63 | document.location |
@@ -46,6 +58,14 @@ edges
4658
| tst9.js:2:21:2:37 | document.location | tst9.js:2:21:2:42 | documen ... on.hash |
4759
| tst9.js:2:21:2:42 | documen ... on.hash | tst9.js:2:21:2:55 | documen ... ring(1) |
4860
| tst9.js:2:21:2:55 | documen ... ring(1) | tst9.js:2:21:2:37 | document.location |
61+
| tst10.js:5:23:5:39 | document.location | tst10.js:5:23:5:46 | documen ... .search |
62+
| tst10.js:5:23:5:46 | documen ... .search | tst10.js:5:17:5:46 | '/' + d ... .search |
63+
| tst10.js:8:24:8:40 | document.location | tst10.js:8:24:8:47 | documen ... .search |
64+
| tst10.js:8:24:8:47 | documen ... .search | tst10.js:8:17:8:47 | '//' + ... .search |
65+
| tst10.js:11:27:11:43 | document.location | tst10.js:11:27:11:50 | documen ... .search |
66+
| tst10.js:11:27:11:50 | documen ... .search | tst10.js:11:17:11:50 | '//foo' ... .search |
67+
| tst10.js:14:33:14:49 | document.location | tst10.js:14:33:14:56 | documen ... .search |
68+
| tst10.js:14:33:14:56 | documen ... .search | tst10.js:14:17:14:56 | 'https: ... .search |
4969
| tst.js:2:19:2:69 | /.*redi ... n.href) | tst.js:2:19:2:72 | /.*redi ... ref)[1] |
5070
| tst.js:2:47:2:63 | document.location | tst.js:2:47:2:68 | documen ... on.href |
5171
| tst.js:2:47:2:68 | documen ... on.href | tst.js:2:19:2:69 | /.*redi ... n.href) |
@@ -59,4 +79,8 @@ edges
5979
| tst7.js:5:27:5:50 | documen ... .search | tst7.js:5:27:5:43 | document.location | tst7.js:5:27:5:50 | documen ... .search | Untrusted URL redirection due to $@. | tst7.js:5:27:5:43 | document.location | user-provided value |
6080
| tst9.js:2:21:2:55 | documen ... ring(1) | tst9.js:2:21:2:37 | document.location | tst9.js:2:21:2:55 | documen ... ring(1) | Untrusted URL redirection due to $@. | tst9.js:2:21:2:37 | document.location | user-provided value |
6181
| tst9.js:2:21:2:55 | documen ... ring(1) | tst9.js:2:21:2:37 | document.location | tst9.js:2:21:2:55 | documen ... ring(1) | Untrusted URL redirection due to $@. | tst9.js:2:21:2:37 | document.location | user-provided value |
82+
| tst10.js:5:17:5:46 | '/' + d ... .search | tst10.js:5:23:5:39 | document.location | tst10.js:5:17:5:46 | '/' + d ... .search | Untrusted URL redirection due to $@. | tst10.js:5:23:5:39 | document.location | user-provided value |
83+
| tst10.js:8:17:8:47 | '//' + ... .search | tst10.js:8:24:8:40 | document.location | tst10.js:8:17:8:47 | '//' + ... .search | Untrusted URL redirection due to $@. | tst10.js:8:24:8:40 | document.location | user-provided value |
84+
| tst10.js:11:17:11:50 | '//foo' ... .search | tst10.js:11:27:11:43 | document.location | tst10.js:11:17:11:50 | '//foo' ... .search | Untrusted URL redirection due to $@. | tst10.js:11:27:11:43 | document.location | user-provided value |
85+
| tst10.js:14:17:14:56 | 'https: ... .search | tst10.js:14:33:14:49 | document.location | tst10.js:14:17:14:56 | 'https: ... .search | Untrusted URL redirection due to $@. | tst10.js:14:33:14:49 | document.location | user-provided value |
6286
| tst.js:2:19:2:72 | /.*redi ... ref)[1] | tst.js:2:47:2:63 | document.location | tst.js:2:19:2:72 | /.*redi ... ref)[1] | Untrusted URL redirection due to $@. | tst.js:2:47:2:63 | document.location | user-provided value |
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// OK - cannot affect hostname
2+
location.href = '/foo' + document.location.search;
3+
4+
// NOT OK
5+
location.href = '/' + document.location.search;
6+
7+
// NOT OK
8+
location.href = '//' + document.location.search;
9+
10+
// NOT OK
11+
location.href = '//foo' + document.location.search;
12+
13+
// NOT OK
14+
location.href = 'https://foo' + document.location.search;

javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirect.expected

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,6 @@ nodes
2222
| express.js:135:23:135:37 | req.params.user |
2323
| express.js:136:16:136:36 | 'u' + r ... ms.user |
2424
| express.js:136:22:136:36 | req.params.user |
25-
| express.js:138:16:138:45 | '/' + ( ... s.user) |
26-
| express.js:138:22:138:45 | ('/u' + ... s.user) |
27-
| express.js:138:23:138:44 | '/u' + ... ms.user |
28-
| express.js:138:30:138:44 | req.params.user |
29-
| express.js:139:16:139:37 | '/u' + ... ms.user |
30-
| express.js:139:23:139:37 | req.params.user |
3125
| node.js:6:7:6:52 | target |
3226
| node.js:6:16:6:39 | url.par ... , true) |
3327
| node.js:6:16:6:45 | url.par ... ).query |
@@ -66,10 +60,6 @@ edges
6660
| express.js:134:22:134:36 | req.params.user | express.js:134:16:134:36 | '/' + r ... ms.user |
6761
| express.js:135:23:135:37 | req.params.user | express.js:135:16:135:37 | '//' + ... ms.user |
6862
| express.js:136:22:136:36 | req.params.user | express.js:136:16:136:36 | 'u' + r ... ms.user |
69-
| express.js:138:22:138:45 | ('/u' + ... s.user) | express.js:138:16:138:45 | '/' + ( ... s.user) |
70-
| express.js:138:23:138:44 | '/u' + ... ms.user | express.js:138:22:138:45 | ('/u' + ... s.user) |
71-
| express.js:138:30:138:44 | req.params.user | express.js:138:23:138:44 | '/u' + ... ms.user |
72-
| express.js:139:23:139:37 | req.params.user | express.js:139:16:139:37 | '/u' + ... ms.user |
7363
| node.js:6:7:6:52 | target | node.js:7:34:7:39 | target |
7464
| node.js:6:16:6:39 | url.par ... , true) | node.js:6:16:6:45 | url.par ... ).query |
7565
| node.js:6:16:6:45 | url.par ... ).query | node.js:6:16:6:52 | url.par ... .target |
@@ -105,8 +95,6 @@ edges
10595
| express.js:134:16:134:36 | '/' + r ... ms.user | express.js:134:22:134:36 | req.params.user | express.js:134:16:134:36 | '/' + r ... ms.user | Untrusted URL redirection due to $@. | express.js:134:22:134:36 | req.params.user | user-provided value |
10696
| express.js:135:16:135:37 | '//' + ... ms.user | express.js:135:23:135:37 | req.params.user | express.js:135:16:135:37 | '//' + ... ms.user | Untrusted URL redirection due to $@. | express.js:135:23:135:37 | req.params.user | user-provided value |
10797
| express.js:136:16:136:36 | 'u' + r ... ms.user | express.js:136:22:136:36 | req.params.user | express.js:136:16:136:36 | 'u' + r ... ms.user | Untrusted URL redirection due to $@. | express.js:136:22:136:36 | req.params.user | user-provided value |
108-
| express.js:138:16:138:45 | '/' + ( ... s.user) | express.js:138:30:138:44 | req.params.user | express.js:138:16:138:45 | '/' + ( ... s.user) | Untrusted URL redirection due to $@. | express.js:138:30:138:44 | req.params.user | user-provided value |
109-
| express.js:139:16:139:37 | '/u' + ... ms.user | express.js:139:23:139:37 | req.params.user | express.js:139:16:139:37 | '/u' + ... ms.user | Untrusted URL redirection due to $@. | express.js:139:23:139:37 | req.params.user | user-provided value |
11098
| node.js:7:34:7:39 | target | node.js:6:26:6:32 | req.url | node.js:7:34:7:39 | target | Untrusted URL redirection due to $@. | node.js:6:26:6:32 | req.url | user-provided value |
11199
| node.js:15:34:15:45 | '/' + target | node.js:11:26:11:32 | req.url | node.js:15:34:15:45 | '/' + target | Untrusted URL redirection due to $@. | node.js:11:26:11:32 | req.url | user-provided value |
112100
| node.js:32:34:32:55 | target ... =" + me | node.js:29:26:29:32 | req.url | node.js:32:34:32:55 | target ... =" + me | Untrusted URL redirection due to $@. | node.js:29:26:29:32 | req.url | user-provided value |

javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/express.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,6 @@ app.get('/redirect/:user', function(req, res) {
135135
res.redirect('//' + req.params.user); // BAD - could go to //evil.com
136136
res.redirect('u' + req.params.user); // BAD - could go to u.evil.com
137137

138-
res.redirect('/' + ('/u' + req.params.user)); // BAD - could go to //u.evil.com
139-
res.redirect('/u' + req.params.user); // GOOD - but flagged anyway
138+
res.redirect('/' + ('/u' + req.params.user)); // BAD - could go to //u.evil.com, but not flagged
139+
res.redirect('/u' + req.params.user); // GOOD
140140
});

0 commit comments

Comments
 (0)