Skip to content

Commit 97032f8

Browse files
committed
add ClientSideUrlRedirect sink for Next.js routers
1 parent a79c30a commit 97032f8

File tree

4 files changed

+56
-2
lines changed

4 files changed

+56
-2
lines changed

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import javascript
88
* Provides classes and predicates modelling [Next.js](https://www.npmjs.com/package/next).
99
*/
1010
module NextJS {
11-
// TODO: Private.
1211
/**
1312
* Gets a `package.json` that depends on the `Next.js` library.
1413
*/
@@ -223,4 +222,19 @@ module NextJS {
223222
kind = "response" and result = getFunction().getParameter(1)
224223
}
225224
}
225+
226+
/**
227+
* Gets a reference to a [Next.js router](https://nextjs.org/docs/api-reference/next/router).
228+
*/
229+
DataFlow::SourceNode nextRouter() {
230+
result = DataFlow::moduleMember("next/router", "useRouter").getACall()
231+
or
232+
result =
233+
API::moduleImport("next/router")
234+
.getMember("withRouter")
235+
.getParameter(0)
236+
.getParameter(0)
237+
.getMember("router")
238+
.getAnImmediateUse()
239+
}
226240
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,4 +177,11 @@ module ClientSideUrlRedirect {
177177
)
178178
}
179179
}
180+
181+
/**
182+
* A call to change the current url with a Next.js router.
183+
*/
184+
class NextRoutePushUrlSink extends ScriptUrlSink {
185+
NextRoutePushUrlSink() { this = NextJS::nextRouter().getAMemberCall("push").getArgument(0) }
186+
}
180187
}

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,14 @@ nodes
1111
| react.js:21:24:21:40 | document.location |
1212
| react.js:21:24:21:45 | documen ... on.hash |
1313
| react.js:21:24:21:45 | documen ... on.hash |
14+
| react.js:28:43:28:59 | document.location |
15+
| react.js:28:43:28:59 | document.location |
16+
| react.js:28:43:28:64 | documen ... on.hash |
17+
| react.js:28:43:28:64 | documen ... on.hash |
18+
| react.js:34:43:34:59 | document.location |
19+
| react.js:34:43:34:59 | document.location |
20+
| react.js:34:43:34:64 | documen ... on.hash |
21+
| react.js:34:43:34:64 | documen ... on.hash |
1422
| sanitizer.js:2:9:2:25 | url |
1523
| sanitizer.js:2:15:2:25 | window.name |
1624
| sanitizer.js:2:15:2:25 | window.name |
@@ -205,6 +213,14 @@ edges
205213
| react.js:21:24:21:40 | document.location | react.js:21:24:21:45 | documen ... on.hash |
206214
| react.js:21:24:21:40 | document.location | react.js:21:24:21:45 | documen ... on.hash |
207215
| react.js:21:24:21:40 | document.location | react.js:21:24:21:45 | documen ... on.hash |
216+
| react.js:28:43:28:59 | document.location | react.js:28:43:28:64 | documen ... on.hash |
217+
| react.js:28:43:28:59 | document.location | react.js:28:43:28:64 | documen ... on.hash |
218+
| react.js:28:43:28:59 | document.location | react.js:28:43:28:64 | documen ... on.hash |
219+
| react.js:28:43:28:59 | document.location | react.js:28:43:28:64 | documen ... on.hash |
220+
| react.js:34:43:34:59 | document.location | react.js:34:43:34:64 | documen ... on.hash |
221+
| react.js:34:43:34:59 | document.location | react.js:34:43:34:64 | documen ... on.hash |
222+
| react.js:34:43:34:59 | document.location | react.js:34:43:34:64 | documen ... on.hash |
223+
| react.js:34:43:34:59 | document.location | react.js:34:43:34:64 | documen ... on.hash |
208224
| sanitizer.js:2:9:2:25 | url | sanitizer.js:4:27:4:29 | url |
209225
| sanitizer.js:2:9:2:25 | url | sanitizer.js:4:27:4:29 | url |
210226
| sanitizer.js:2:9:2:25 | url | sanitizer.js:16:27:16:29 | url |
@@ -376,6 +392,8 @@ edges
376392
| electron.js:7:20:7:29 | getTaint() | electron.js:4:12:4:22 | window.name | electron.js:7:20:7:29 | getTaint() | Untrusted URL redirection due to $@. | electron.js:4:12:4:22 | window.name | user-provided value |
377393
| react.js:10:60:10:81 | documen ... on.hash | react.js:10:60:10:76 | document.location | react.js:10:60:10:81 | documen ... on.hash | Untrusted URL redirection due to $@. | react.js:10:60:10:76 | document.location | user-provided value |
378394
| react.js:21:24:21:45 | documen ... on.hash | react.js:21:24:21:40 | document.location | react.js:21:24:21:45 | documen ... on.hash | Untrusted URL redirection due to $@. | react.js:21:24:21:40 | document.location | user-provided value |
395+
| react.js:28:43:28:64 | documen ... on.hash | react.js:28:43:28:59 | document.location | react.js:28:43:28:64 | documen ... on.hash | Untrusted URL redirection due to $@. | react.js:28:43:28:59 | document.location | user-provided value |
396+
| react.js:34:43:34:64 | documen ... on.hash | react.js:34:43:34:59 | document.location | react.js:34:43:34:64 | documen ... on.hash | Untrusted URL redirection due to $@. | react.js:34:43:34:59 | document.location | user-provided value |
379397
| sanitizer.js:4:27:4:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:4:27:4:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value |
380398
| sanitizer.js:16:27:16:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:16:27:16:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value |
381399
| sanitizer.js:19:27:19:29 | url | sanitizer.js:2:15:2:25 | window.name | sanitizer.js:19:27:19:29 | url | Untrusted URL redirection due to $@. | sanitizer.js:2:15:2:25 | window.name | user-provided value |

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,19 @@ export default Application
1919
import Link from 'next/link'
2020
export function NextLink() {
2121
return <Link href={document.location.hash}><a>this page!</a></Link>;
22-
}
22+
}
23+
24+
import { useRouter } from 'next/router'
25+
26+
export function nextRouter() {
27+
const router = useRouter();
28+
return <span onClick={() => router.push(document.location.hash)}>Click to XSS 1</span>
29+
}
30+
31+
import { withRouter } from 'next/router'
32+
33+
function Page({ router }) {
34+
return <span onClick={() => router.push(document.location.hash)}>Click to XSS 2</span>
35+
}
36+
37+
export const pageWithRouter = withRouter(Page);

0 commit comments

Comments
 (0)