Skip to content

Commit 7f9b855

Browse files
committed
Add Next.js router push as XSS sink
1 parent 5ee9711 commit 7f9b855

File tree

4 files changed

+87
-0
lines changed

4 files changed

+87
-0
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,8 @@ module ClientSideUrlRedirect {
231231
NextRoutePushUrlSink() {
232232
this = NextJS::nextRouter().getAMemberCall(["push", "replace"]).getArgument(0)
233233
}
234+
235+
override predicate isXssSink() { any() }
234236
}
235237

236238
private class SinkFromModel extends Sink {

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,20 @@ nodes
560560
| react-use-context.js:16:26:16:36 | window.name |
561561
| react-use-context.js:16:26:16:36 | window.name |
562562
| react-use-context.js:16:26:16:36 | window.name |
563+
| react-use-router.js:5:9:5:28 | router |
564+
| react-use-router.js:5:18:5:28 | useRouter() |
565+
| react-use-router.js:9:21:9:26 | router |
566+
| react-use-router.js:9:21:9:32 | router.query |
567+
| react-use-router.js:9:21:9:32 | router.query |
568+
| react-use-router.js:9:21:9:39 | router.query.foobar |
569+
| react-use-router.js:9:21:9:39 | router.query.foobar |
570+
| react-use-router.js:20:15:20:24 | router |
571+
| react-use-router.js:20:17:20:22 | router |
572+
| react-use-router.js:21:43:21:48 | router |
573+
| react-use-router.js:21:43:21:54 | router.query |
574+
| react-use-router.js:21:43:21:54 | router.query |
575+
| react-use-router.js:21:43:21:61 | router.query.foobar |
576+
| react-use-router.js:21:43:21:61 | router.query.foobar |
563577
| react-use-state.js:4:9:4:49 | state |
564578
| react-use-state.js:4:9:4:49 | state |
565579
| react-use-state.js:4:10:4:14 | state |
@@ -1700,6 +1714,22 @@ edges
17001714
| react-native.js:7:17:7:33 | req.param("code") | react-native.js:7:7:7:33 | tainted |
17011715
| react-use-context.js:10:22:10:32 | window.name | react-use-context.js:10:22:10:32 | window.name |
17021716
| react-use-context.js:16:26:16:36 | window.name | react-use-context.js:16:26:16:36 | window.name |
1717+
| react-use-router.js:5:9:5:28 | router | react-use-router.js:9:21:9:26 | router |
1718+
| react-use-router.js:5:18:5:28 | useRouter() | react-use-router.js:5:9:5:28 | router |
1719+
| react-use-router.js:9:21:9:26 | router | react-use-router.js:9:21:9:32 | router.query |
1720+
| react-use-router.js:9:21:9:32 | router.query | react-use-router.js:9:21:9:39 | router.query.foobar |
1721+
| react-use-router.js:9:21:9:32 | router.query | react-use-router.js:9:21:9:39 | router.query.foobar |
1722+
| react-use-router.js:9:21:9:32 | router.query | react-use-router.js:9:21:9:39 | router.query.foobar |
1723+
| react-use-router.js:9:21:9:32 | router.query | react-use-router.js:9:21:9:39 | router.query.foobar |
1724+
| react-use-router.js:9:21:9:39 | router.query.foobar | react-use-router.js:5:18:5:28 | useRouter() |
1725+
| react-use-router.js:20:15:20:24 | router | react-use-router.js:21:43:21:48 | router |
1726+
| react-use-router.js:20:17:20:22 | router | react-use-router.js:20:15:20:24 | router |
1727+
| react-use-router.js:21:43:21:48 | router | react-use-router.js:21:43:21:54 | router.query |
1728+
| react-use-router.js:21:43:21:54 | router.query | react-use-router.js:21:43:21:61 | router.query.foobar |
1729+
| react-use-router.js:21:43:21:54 | router.query | react-use-router.js:21:43:21:61 | router.query.foobar |
1730+
| react-use-router.js:21:43:21:54 | router.query | react-use-router.js:21:43:21:61 | router.query.foobar |
1731+
| react-use-router.js:21:43:21:54 | router.query | react-use-router.js:21:43:21:61 | router.query.foobar |
1732+
| react-use-router.js:21:43:21:61 | router.query.foobar | react-use-router.js:20:17:20:22 | router |
17031733
| react-use-state.js:4:9:4:49 | state | react-use-state.js:5:51:5:55 | state |
17041734
| react-use-state.js:4:9:4:49 | state | react-use-state.js:5:51:5:55 | state |
17051735
| react-use-state.js:4:9:4:49 | state | react-use-state.js:5:51:5:55 | state |
@@ -2386,6 +2416,8 @@ edges
23862416
| react-native.js:9:27:9:33 | tainted | react-native.js:7:17:7:33 | req.param("code") | react-native.js:9:27:9:33 | tainted | Cross-site scripting vulnerability due to $@. | react-native.js:7:17:7:33 | req.param("code") | user-provided value |
23872417
| react-use-context.js:10:22:10:32 | window.name | react-use-context.js:10:22:10:32 | window.name | react-use-context.js:10:22:10:32 | window.name | Cross-site scripting vulnerability due to $@. | react-use-context.js:10:22:10:32 | window.name | user-provided value |
23882418
| react-use-context.js:16:26:16:36 | window.name | react-use-context.js:16:26:16:36 | window.name | react-use-context.js:16:26:16:36 | window.name | Cross-site scripting vulnerability due to $@. | react-use-context.js:16:26:16:36 | window.name | user-provided value |
2419+
| react-use-router.js:9:21:9:39 | router.query.foobar | react-use-router.js:9:21:9:32 | router.query | react-use-router.js:9:21:9:39 | router.query.foobar | Cross-site scripting vulnerability due to $@. | react-use-router.js:9:21:9:32 | router.query | user-provided value |
2420+
| react-use-router.js:21:43:21:61 | router.query.foobar | react-use-router.js:21:43:21:54 | router.query | react-use-router.js:21:43:21:61 | router.query.foobar | Cross-site scripting vulnerability due to $@. | react-use-router.js:21:43:21:54 | router.query | user-provided value |
23892421
| react-use-state.js:5:51:5:55 | state | react-use-state.js:4:38:4:48 | window.name | react-use-state.js:5:51:5:55 | state | Cross-site scripting vulnerability due to $@. | react-use-state.js:4:38:4:48 | window.name | user-provided value |
23902422
| react-use-state.js:11:51:11:55 | state | react-use-state.js:10:14:10:24 | window.name | react-use-state.js:11:51:11:55 | state | Cross-site scripting vulnerability due to $@. | react-use-state.js:10:14:10:24 | window.name | user-provided value |
23912423
| react-use-state.js:17:51:17:55 | state | react-use-state.js:16:20:16:30 | window.name | react-use-state.js:17:51:17:55 | state | Cross-site scripting vulnerability due to $@. | react-use-state.js:16:20:16:30 | window.name | user-provided value |

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,20 @@ nodes
572572
| react-use-context.js:16:26:16:36 | window.name |
573573
| react-use-context.js:16:26:16:36 | window.name |
574574
| react-use-context.js:16:26:16:36 | window.name |
575+
| react-use-router.js:5:9:5:28 | router |
576+
| react-use-router.js:5:18:5:28 | useRouter() |
577+
| react-use-router.js:9:21:9:26 | router |
578+
| react-use-router.js:9:21:9:32 | router.query |
579+
| react-use-router.js:9:21:9:32 | router.query |
580+
| react-use-router.js:9:21:9:39 | router.query.foobar |
581+
| react-use-router.js:9:21:9:39 | router.query.foobar |
582+
| react-use-router.js:20:15:20:24 | router |
583+
| react-use-router.js:20:17:20:22 | router |
584+
| react-use-router.js:21:43:21:48 | router |
585+
| react-use-router.js:21:43:21:54 | router.query |
586+
| react-use-router.js:21:43:21:54 | router.query |
587+
| react-use-router.js:21:43:21:61 | router.query.foobar |
588+
| react-use-router.js:21:43:21:61 | router.query.foobar |
575589
| react-use-state.js:4:9:4:49 | state |
576590
| react-use-state.js:4:9:4:49 | state |
577591
| react-use-state.js:4:10:4:14 | state |
@@ -1762,6 +1776,22 @@ edges
17621776
| react-native.js:7:17:7:33 | req.param("code") | react-native.js:7:7:7:33 | tainted |
17631777
| react-use-context.js:10:22:10:32 | window.name | react-use-context.js:10:22:10:32 | window.name |
17641778
| react-use-context.js:16:26:16:36 | window.name | react-use-context.js:16:26:16:36 | window.name |
1779+
| react-use-router.js:5:9:5:28 | router | react-use-router.js:9:21:9:26 | router |
1780+
| react-use-router.js:5:18:5:28 | useRouter() | react-use-router.js:5:9:5:28 | router |
1781+
| react-use-router.js:9:21:9:26 | router | react-use-router.js:9:21:9:32 | router.query |
1782+
| react-use-router.js:9:21:9:32 | router.query | react-use-router.js:9:21:9:39 | router.query.foobar |
1783+
| react-use-router.js:9:21:9:32 | router.query | react-use-router.js:9:21:9:39 | router.query.foobar |
1784+
| react-use-router.js:9:21:9:32 | router.query | react-use-router.js:9:21:9:39 | router.query.foobar |
1785+
| react-use-router.js:9:21:9:32 | router.query | react-use-router.js:9:21:9:39 | router.query.foobar |
1786+
| react-use-router.js:9:21:9:39 | router.query.foobar | react-use-router.js:5:18:5:28 | useRouter() |
1787+
| react-use-router.js:20:15:20:24 | router | react-use-router.js:21:43:21:48 | router |
1788+
| react-use-router.js:20:17:20:22 | router | react-use-router.js:20:15:20:24 | router |
1789+
| react-use-router.js:21:43:21:48 | router | react-use-router.js:21:43:21:54 | router.query |
1790+
| react-use-router.js:21:43:21:54 | router.query | react-use-router.js:21:43:21:61 | router.query.foobar |
1791+
| react-use-router.js:21:43:21:54 | router.query | react-use-router.js:21:43:21:61 | router.query.foobar |
1792+
| react-use-router.js:21:43:21:54 | router.query | react-use-router.js:21:43:21:61 | router.query.foobar |
1793+
| react-use-router.js:21:43:21:54 | router.query | react-use-router.js:21:43:21:61 | router.query.foobar |
1794+
| react-use-router.js:21:43:21:61 | router.query.foobar | react-use-router.js:20:17:20:22 | router |
17651795
| react-use-state.js:4:9:4:49 | state | react-use-state.js:5:51:5:55 | state |
17661796
| react-use-state.js:4:9:4:49 | state | react-use-state.js:5:51:5:55 | state |
17671797
| react-use-state.js:4:9:4:49 | state | react-use-state.js:5:51:5:55 | state |
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
2+
import { useRouter } from 'next/router'
3+
4+
export function nextRouter() {
5+
const router = useRouter();
6+
return (
7+
<div>
8+
<span onClick={() => {
9+
router.push(router.query.foobar) // NOT OK
10+
}}>Click to XSS 1</span>
11+
<span onClick={() => {
12+
router.push('/?foobar=' + router.query.foobar) // OK
13+
}}>Safe Link</span>
14+
</div>
15+
)
16+
}
17+
18+
import { withRouter } from 'next/router'
19+
20+
function Page({ router }) {
21+
return <span onClick={() => router.push(router.query.foobar)}>Click to XSS 2</span> // NOT OK
22+
}
23+
export const pageWithRouter = withRouter(Page);

0 commit comments

Comments
 (0)