diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/ClientSideUrlRedirectCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/ClientSideUrlRedirectCustomizations.qll index 98d85d887414..de19e549f9c3 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/ClientSideUrlRedirectCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/ClientSideUrlRedirectCustomizations.qll @@ -231,6 +231,8 @@ module ClientSideUrlRedirect { NextRoutePushUrlSink() { this = NextJS::nextRouter().getAMemberCall(["push", "replace"]).getArgument(0) } + + override predicate isXssSink() { any() } } private class SinkFromModel extends Sink { diff --git a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/Xss.expected b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/Xss.expected index d40a652b962f..ddf5bbb51b27 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/Xss.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/Xss.expected @@ -560,6 +560,20 @@ nodes | 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 | +| react-use-router.js:5:9:5:28 | router | +| react-use-router.js:5:18:5:28 | useRouter() | +| react-use-router.js:9:21:9:26 | router | +| react-use-router.js:9:21:9:32 | router.query | +| react-use-router.js:9:21:9:32 | router.query | +| react-use-router.js:9:21:9:39 | router.query.foobar | +| react-use-router.js:9:21:9:39 | router.query.foobar | +| react-use-router.js:20:15:20:24 | router | +| react-use-router.js:20:17:20:22 | router | +| react-use-router.js:21:43:21:48 | router | +| react-use-router.js:21:43:21:54 | router.query | +| react-use-router.js:21:43:21:54 | router.query | +| react-use-router.js:21:43:21:61 | router.query.foobar | +| react-use-router.js:21:43:21:61 | router.query.foobar | | react-use-state.js:4:9:4:49 | state | | react-use-state.js:4:9:4:49 | state | | react-use-state.js:4:10:4:14 | state | @@ -1700,6 +1714,22 @@ edges | react-native.js:7:17:7:33 | req.param("code") | react-native.js:7:7:7:33 | tainted | | react-use-context.js:10:22:10:32 | window.name | react-use-context.js:10:22:10:32 | window.name | | react-use-context.js:16:26:16:36 | window.name | react-use-context.js:16:26:16:36 | window.name | +| react-use-router.js:5:9:5:28 | router | react-use-router.js:9:21:9:26 | router | +| react-use-router.js:5:18:5:28 | useRouter() | react-use-router.js:5:9:5:28 | router | +| react-use-router.js:9:21:9:26 | router | react-use-router.js:9:21:9:32 | router.query | +| react-use-router.js:9:21:9:32 | router.query | 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 | +| react-use-router.js:9:21:9:32 | router.query | 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 | +| react-use-router.js:9:21:9:39 | router.query.foobar | react-use-router.js:5:18:5:28 | useRouter() | +| react-use-router.js:20:15:20:24 | router | react-use-router.js:21:43:21:48 | router | +| react-use-router.js:20:17:20:22 | router | react-use-router.js:20:15:20:24 | router | +| react-use-router.js:21:43:21:48 | router | react-use-router.js:21:43:21:54 | router.query | +| react-use-router.js:21:43:21:54 | router.query | 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 | +| react-use-router.js:21:43:21:54 | router.query | 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 | +| react-use-router.js:21:43:21:61 | router.query.foobar | react-use-router.js:20:17:20:22 | router | | react-use-state.js:4:9:4:49 | state | react-use-state.js:5:51:5:55 | state | | react-use-state.js:4:9:4:49 | state | react-use-state.js:5:51:5:55 | state | | react-use-state.js:4:9:4:49 | state | react-use-state.js:5:51:5:55 | state | @@ -2386,6 +2416,8 @@ edges | 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 | | 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 | | 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 | +| 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 | +| 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 | | 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 | | 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 | | 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 | diff --git a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/XssWithAdditionalSources.expected b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/XssWithAdditionalSources.expected index 5e5010d126e1..e9b676b4a272 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/XssWithAdditionalSources.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/XssWithAdditionalSources.expected @@ -572,6 +572,20 @@ nodes | 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 | +| react-use-router.js:5:9:5:28 | router | +| react-use-router.js:5:18:5:28 | useRouter() | +| react-use-router.js:9:21:9:26 | router | +| react-use-router.js:9:21:9:32 | router.query | +| react-use-router.js:9:21:9:32 | router.query | +| react-use-router.js:9:21:9:39 | router.query.foobar | +| react-use-router.js:9:21:9:39 | router.query.foobar | +| react-use-router.js:20:15:20:24 | router | +| react-use-router.js:20:17:20:22 | router | +| react-use-router.js:21:43:21:48 | router | +| react-use-router.js:21:43:21:54 | router.query | +| react-use-router.js:21:43:21:54 | router.query | +| react-use-router.js:21:43:21:61 | router.query.foobar | +| react-use-router.js:21:43:21:61 | router.query.foobar | | react-use-state.js:4:9:4:49 | state | | react-use-state.js:4:9:4:49 | state | | react-use-state.js:4:10:4:14 | state | @@ -1762,6 +1776,22 @@ edges | react-native.js:7:17:7:33 | req.param("code") | react-native.js:7:7:7:33 | tainted | | react-use-context.js:10:22:10:32 | window.name | react-use-context.js:10:22:10:32 | window.name | | react-use-context.js:16:26:16:36 | window.name | react-use-context.js:16:26:16:36 | window.name | +| react-use-router.js:5:9:5:28 | router | react-use-router.js:9:21:9:26 | router | +| react-use-router.js:5:18:5:28 | useRouter() | react-use-router.js:5:9:5:28 | router | +| react-use-router.js:9:21:9:26 | router | react-use-router.js:9:21:9:32 | router.query | +| react-use-router.js:9:21:9:32 | router.query | 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 | +| react-use-router.js:9:21:9:32 | router.query | 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 | +| react-use-router.js:9:21:9:39 | router.query.foobar | react-use-router.js:5:18:5:28 | useRouter() | +| react-use-router.js:20:15:20:24 | router | react-use-router.js:21:43:21:48 | router | +| react-use-router.js:20:17:20:22 | router | react-use-router.js:20:15:20:24 | router | +| react-use-router.js:21:43:21:48 | router | react-use-router.js:21:43:21:54 | router.query | +| react-use-router.js:21:43:21:54 | router.query | 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 | +| react-use-router.js:21:43:21:54 | router.query | 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 | +| react-use-router.js:21:43:21:61 | router.query.foobar | react-use-router.js:20:17:20:22 | router | | react-use-state.js:4:9:4:49 | state | react-use-state.js:5:51:5:55 | state | | react-use-state.js:4:9:4:49 | state | react-use-state.js:5:51:5:55 | state | | react-use-state.js:4:9:4:49 | state | react-use-state.js:5:51:5:55 | state | diff --git a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/react-use-router.js b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/react-use-router.js new file mode 100644 index 000000000000..674f812467ca --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/react-use-router.js @@ -0,0 +1,23 @@ + +import { useRouter } from 'next/router' + +export function nextRouter() { + const router = useRouter(); + return ( +
+ { + router.push(router.query.foobar) // NOT OK + }}>Click to XSS 1 + { + router.push('/?foobar=' + router.query.foobar) // OK + }}>Safe Link +
+ ) +} + +import { withRouter } from 'next/router' + +function Page({ router }) { + return router.push(router.query.foobar)}>Click to XSS 2 // NOT OK +} +export const pageWithRouter = withRouter(Page);