Skip to content

Commit 6014614

Browse files
authored
Merge pull request #9103 from erik-krogh/nextParam
JS: add support for typed NextJS route-handlers
2 parents 85dc109 + 5e02a76 commit 6014614

File tree

5 files changed

+60
-9
lines changed

5 files changed

+60
-9
lines changed

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

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -158,13 +158,37 @@ module NextJS {
158158
NextHttpRouteHandler() { this = getServerSidePropsFunction(_) or this = getInitialProps(_) }
159159
}
160160

161+
/**
162+
* A function that handles both a request and response from Next.js, seen as a routehandler.
163+
*/
164+
class NextReqResHandler extends HTTP::Servers::StandardRouteHandler, DataFlow::FunctionNode {
165+
DataFlow::ParameterNode req;
166+
DataFlow::ParameterNode res;
167+
168+
NextReqResHandler() {
169+
res = this.getAParameter() and
170+
req = this.getAParameter() and
171+
req.hasUnderlyingType("next", "NextApiRequest") and
172+
res.hasUnderlyingType("next", "NextApiResponse")
173+
}
174+
175+
/** Gets the request parameter */
176+
DataFlow::ParameterNode getRequest() { result = req }
177+
178+
/** Gets the response parameter */
179+
DataFlow::ParameterNode getResponse() { result = res }
180+
}
181+
161182
/**
162183
* A NodeJS HTTP request object in a Next.js page.
163184
*/
164185
class NextHttpRequestSource extends NodeJSLib::RequestSource {
165-
NextHttpRouteHandler rh;
186+
HTTP::RouteHandler rh;
166187

167-
NextHttpRequestSource() { this = rh.getParameter(0).getAPropertyRead("req") }
188+
NextHttpRequestSource() {
189+
this = rh.(NextHttpRouteHandler).getParameter(0).getAPropertyRead("req") or
190+
this = rh.(NextReqResHandler).getRequest()
191+
}
168192

169193
override HTTP::RouteHandler getRouteHandler() { result = rh }
170194
}
@@ -173,9 +197,12 @@ module NextJS {
173197
* A NodeJS HTTP response object in a Next.js page.
174198
*/
175199
class NextHttpResponseSource extends NodeJSLib::ResponseSource {
176-
NextHttpRouteHandler rh;
200+
HTTP::RouteHandler rh;
177201

178-
NextHttpResponseSource() { this = rh.getParameter(0).getAPropertyRead("res") }
202+
NextHttpResponseSource() {
203+
this = rh.(NextHttpRouteHandler).getParameter(0).getAPropertyRead("res") or
204+
this = rh.(NextReqResHandler).getResponse()
205+
}
179206

180207
override HTTP::RouteHandler getRouteHandler() { result = rh }
181208
}
@@ -204,9 +231,9 @@ module NextJS {
204231
}
205232

206233
override Parameter getRouteHandlerParameter(string kind) {
207-
kind = "request" and result = getFunction().getParameter(0)
234+
kind = "request" and result = this.getFunction().getParameter(0)
208235
or
209-
kind = "response" and result = getFunction().getParameter(1)
236+
kind = "response" and result = this.getFunction().getParameter(1)
210237
}
211238
}
212239

javascript/ql/lib/semmle/javascript/frameworks/NodeJSLib.qll

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -168,9 +168,9 @@ module NodeJSLib {
168168
string kind;
169169

170170
RequestInputAccess() {
171-
// `req.url`
172-
kind = "url" and
173-
this.asExpr().(PropAccess).accesses(request, "url")
171+
// `req.url` / `req.body`
172+
kind = ["url", "body"] and
173+
this.asExpr().(PropAccess).accesses(request, kind)
174174
or
175175
exists(PropAccess headers |
176176
// `req.headers.cookie`

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ nodes
6262
| koa.js:14:16:14:18 | url |
6363
| koa.js:20:16:20:18 | url |
6464
| koa.js:20:16:20:18 | url |
65+
| next.ts:11:31:11:38 | req.body |
66+
| next.ts:11:31:11:38 | req.body |
67+
| next.ts:11:31:11:50 | req.body.callbackUrl |
68+
| next.ts:11:31:11:50 | req.body.callbackUrl |
6569
| node.js:5:7:5:52 | target |
6670
| node.js:5:16:5:39 | url.par ... , true) |
6771
| node.js:5:16:5:45 | url.par ... ).query |
@@ -147,6 +151,10 @@ edges
147151
| koa.js:6:12:6:27 | ctx.query.target | koa.js:6:6:6:27 | url |
148152
| koa.js:8:18:8:20 | url | koa.js:8:15:8:26 | `${url}${x}` |
149153
| koa.js:8:18:8:20 | url | koa.js:8:15:8:26 | `${url}${x}` |
154+
| next.ts:11:31:11:38 | req.body | next.ts:11:31:11:50 | req.body.callbackUrl |
155+
| next.ts:11:31:11:38 | req.body | next.ts:11:31:11:50 | req.body.callbackUrl |
156+
| next.ts:11:31:11:38 | req.body | next.ts:11:31:11:50 | req.body.callbackUrl |
157+
| next.ts:11:31:11:38 | req.body | next.ts:11:31:11:50 | req.body.callbackUrl |
150158
| node.js:5:7:5:52 | target | node.js:6:34:6:39 | target |
151159
| node.js:5:7:5:52 | target | node.js:6:34:6:39 | target |
152160
| node.js:5:16:5:39 | url.par ... , true) | node.js:5:16:5:45 | url.par ... ).query |
@@ -195,6 +203,7 @@ edges
195203
| koa.js:8:15:8:26 | `${url}${x}` | koa.js:6:12:6:27 | ctx.query.target | koa.js:8:15:8:26 | `${url}${x}` | Untrusted URL redirection due to $@. | koa.js:6:12:6:27 | ctx.query.target | user-provided value |
196204
| koa.js:14:16:14:18 | url | koa.js:6:12:6:27 | ctx.query.target | koa.js:14:16:14:18 | url | Untrusted URL redirection due to $@. | koa.js:6:12:6:27 | ctx.query.target | user-provided value |
197205
| koa.js:20:16:20:18 | url | koa.js:6:12:6:27 | ctx.query.target | koa.js:20:16:20:18 | url | Untrusted URL redirection due to $@. | koa.js:6:12:6:27 | ctx.query.target | user-provided value |
206+
| next.ts:11:31:11:50 | req.body.callbackUrl | next.ts:11:31:11:38 | req.body | next.ts:11:31:11:50 | req.body.callbackUrl | Untrusted URL redirection due to $@. | next.ts:11:31:11:38 | req.body | user-provided value |
198207
| node.js:6:34:6:39 | target | node.js:5:26:5:32 | req.url | node.js:6:34:6:39 | target | Untrusted URL redirection due to $@. | node.js:5:26:5:32 | req.url | user-provided value |
199208
| node.js:14:34:14:45 | '/' + target | node.js:10:26:10:32 | req.url | node.js:14:34:14:45 | '/' + target | Untrusted URL redirection due to $@. | node.js:10:26:10:32 | req.url | user-provided value |
200209
| node.js:31:34:31:55 | target ... =" + me | node.js:28:26:28:32 | req.url | node.js:31:34:31:55 | target ... =" + me | Untrusted URL redirection due to $@. | node.js:28:26:28:32 | req.url | user-provided value |
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
import type {
3+
NextApiRequest,
4+
NextApiResponse,
5+
} from "next"
6+
7+
export async function handler(
8+
req: NextApiRequest,
9+
res: NextApiResponse
10+
) {
11+
res.setHeader("Location", req.body.callbackUrl); // NOT OK
12+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"include": ["."]
3+
}

0 commit comments

Comments
 (0)