support Next.js page request/response objects

This commit is contained in:
Erik Krogh Kristensen
2021-02-16 11:42:03 +01:00
parent a5cf024c9f
commit 1fdbbb682d
6 changed files with 133 additions and 11 deletions

View File

@@ -7,7 +7,8 @@ import javascript
/**
* Provides classes and predicates modelling [Next.js](https://www.npmjs.com/package/next).
*/
private module NextJS {
module NextJS {
// TODO: Private.
/**
* Gets a `package.json` that depends on the `Next.js` library.
*/
@@ -59,6 +60,41 @@ private module NextJS {
override string getSourceType() { result = "Next request parameter" }
}
/**
* Gets the `getStaticProps` function in a Next.js page.
* This function is executed at build time, or when a page with a new URL is requested for the first time (if `fallback` is not false).
*/
DataFlow::FunctionNode getStaticPropsFunction(Module pageModule) {
pageModule = getAPagesModule() and
result = pageModule.getAnExportedValue("getStaticProps").getAFunctionValue()
}
/**
* Gets the `getServerSideProps` function in a Next.js page.
* This function is executed on the server every time a request for the page is made.
* The function receives a context parameter, which includes HTTP request/response objects.
*/
DataFlow::FunctionNode getServerSidePropsFunction(Module pageModule) {
pageModule = getAPagesModule() and
result = pageModule.getAnExportedValue("getServerSideProps").getAFunctionValue()
}
/**
* Gets the `getInitialProps` function in a Next.js page.
* This function is executed on the server every time a request for the page is made.
* The function receives a context parameter, which includes HTTP request/response objects.
*/
DataFlow::FunctionNode getInitialProps(Module pageModule) {
pageModule = getAPagesModule() and
result =
pageModule
.getAnExportedValue("default")
.getAFunctionValue()
.getAPropertyWrite("getInitialProps")
.getRhs()
.getAFunctionValue()
}
/**
* A step modelling the flow from the server-computed `getStaticProps` to the server/client rendering of the page.
*/
@@ -73,17 +109,48 @@ private module NextJS {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
(
pred =
pageModule
.getAnExportedValue(["getStaticProps", "getServerSideProps"])
.getAFunctionValue()
[getStaticPropsFunction(pageModule), getServerSidePropsFunction(pageModule)]
.getAReturn()
.getALocalSource()
.getAPropertyWrite("props")
.getRhs()
or
pred = this.getAPropertyWrite("getInitialProps").getRhs().getAFunctionValue().getAReturn()
pred = getInitialProps(pageModule).getAReturn()
) and
succ = this.getParameter(0)
}
}
/**
* A Next.js function that is exected on the server for every request, seen as a routehandler.
*/
class NextHttpRouteHandler extends HTTP::Servers::StandardRouteHandler, DataFlow::FunctionNode {
Module pageModule;
NextHttpRouteHandler() {
this = getServerSidePropsFunction(pageModule) or this = getInitialProps(pageModule)
}
}
/**
* A NodeJS HTTP request object in a Next.js page.
*/
class NextHttpRequestSource extends NodeJSLib::RequestSource {
NextHttpRouteHandler rh;
NextHttpRequestSource() { this = rh.getParameter(0).getAPropertyRead("req") }
override HTTP::RouteHandler getRouteHandler() { result = rh }
}
/**
* A NodeJS HTTP response object in a Next.js page.
*/
class NextHttpResponseSource extends NodeJSLib::ResponseSource {
NextHttpRouteHandler rh;
NextHttpResponseSource() { this = rh.getParameter(0).getAPropertyRead("res") }
override HTTP::RouteHandler getRouteHandler() { result = rh }
}
}

View File

@@ -107,13 +107,18 @@ module NodeJSLib {
}
/**
* A Node.js response source, that is, the response parameter of a
* A Node.js response source.
*/
abstract class ResponseSource extends HTTP::Servers::ResponseSource { }
/**
* A standard Node.js response source, that is, the response parameter of a
* route handler.
*/
private class ResponseSource extends HTTP::Servers::ResponseSource {
private class StandardResponseSource extends ResponseSource {
RouteHandler rh;
ResponseSource() { this = DataFlow::parameterNode(rh.getResponseParameter()) }
StandardResponseSource() { this = DataFlow::parameterNode(rh.getResponseParameter()) }
/**
* Gets the route handler that provides this response.
@@ -122,13 +127,18 @@ module NodeJSLib {
}
/**
* A Node.js request source, that is, the request parameter of a
* A Node.js request source.
*/
abstract class RequestSource extends HTTP::Servers::RequestSource { }
/**
* A standard Node.js request source, that is, the request parameter of a
* route handler.
*/
private class RequestSource extends HTTP::Servers::RequestSource {
private class StandardRequestSource extends RequestSource {
RouteHandler rh;
RequestSource() { this = DataFlow::parameterNode(rh.getRequestParameter()) }
StandardRequestSource() { this = DataFlow::parameterNode(rh.getRequestParameter()) }
/**
* Gets the route handler that handles this request.

View File

@@ -103,6 +103,12 @@ nodes
| formatting.js:7:14:7:53 | require ... , evil) |
| formatting.js:7:14:7:53 | require ... , evil) |
| formatting.js:7:49:7:52 | evil |
| pages/Next.jsx:8:13:8:19 | req.url |
| pages/Next.jsx:8:13:8:19 | req.url |
| pages/Next.jsx:8:13:8:19 | req.url |
| pages/Next.jsx:15:13:15:19 | req.url |
| pages/Next.jsx:15:13:15:19 | req.url |
| pages/Next.jsx:15:13:15:19 | req.url |
| partial.js:9:25:9:25 | x |
| partial.js:10:14:10:14 | x |
| partial.js:10:14:10:18 | x + y |
@@ -237,6 +243,8 @@ edges
| formatting.js:6:43:6:46 | evil | formatting.js:6:14:6:47 | util.fo ... , evil) |
| formatting.js:7:49:7:52 | evil | formatting.js:7:14:7:53 | require ... , evil) |
| formatting.js:7:49:7:52 | evil | formatting.js:7:14:7:53 | require ... , evil) |
| pages/Next.jsx:8:13:8:19 | req.url | pages/Next.jsx:8:13:8:19 | req.url |
| pages/Next.jsx:15:13:15:19 | req.url | pages/Next.jsx:15:13:15:19 | req.url |
| partial.js:9:25:9:25 | x | partial.js:10:14:10:14 | x |
| partial.js:10:14:10:14 | x | partial.js:10:14:10:18 | x + y |
| partial.js:10:14:10:14 | x | partial.js:10:14:10:18 | x + y |
@@ -303,6 +311,8 @@ edges
| etherpad.js:11:12:11:19 | response | etherpad.js:9:16:9:30 | req.query.jsonp | etherpad.js:11:12:11:19 | response | Cross-site scripting vulnerability due to $@. | etherpad.js:9:16:9:30 | req.query.jsonp | user-provided value |
| formatting.js:6:14:6:47 | util.fo ... , evil) | formatting.js:4:16:4:29 | req.query.evil | formatting.js:6:14:6:47 | util.fo ... , evil) | Cross-site scripting vulnerability due to $@. | formatting.js:4:16:4:29 | req.query.evil | user-provided value |
| formatting.js:7:14:7:53 | require ... , evil) | formatting.js:4:16:4:29 | req.query.evil | formatting.js:7:14:7:53 | require ... , evil) | Cross-site scripting vulnerability due to $@. | formatting.js:4:16:4:29 | req.query.evil | user-provided value |
| pages/Next.jsx:8:13:8:19 | req.url | pages/Next.jsx:8:13:8:19 | req.url | pages/Next.jsx:8:13:8:19 | req.url | Cross-site scripting vulnerability due to $@. | pages/Next.jsx:8:13:8:19 | req.url | user-provided value |
| pages/Next.jsx:15:13:15:19 | req.url | pages/Next.jsx:15:13:15:19 | req.url | pages/Next.jsx:15:13:15:19 | req.url | Cross-site scripting vulnerability due to $@. | pages/Next.jsx:15:13:15:19 | req.url | user-provided value |
| partial.js:10:14:10:18 | x + y | partial.js:13:42:13:48 | req.url | partial.js:10:14:10:18 | x + y | Cross-site scripting vulnerability due to $@. | partial.js:13:42:13:48 | req.url | user-provided value |
| partial.js:19:14:19:18 | x + y | partial.js:22:51:22:57 | req.url | partial.js:19:14:19:18 | x + y | Cross-site scripting vulnerability due to $@. | partial.js:22:51:22:57 | req.url | user-provided value |
| partial.js:28:14:28:18 | x + y | partial.js:31:47:31:53 | req.url | partial.js:28:14:28:18 | x + y | Cross-site scripting vulnerability due to $@. | partial.js:31:47:31:53 | req.url | user-provided value |

View File

@@ -21,6 +21,8 @@
| ReflectedXssGood3.js:139:12:139:27 | escapeHtml3(url) | Cross-site scripting vulnerability due to $@. | ReflectedXssGood3.js:135:15:135:27 | req.params.id | user-provided value |
| formatting.js:6:14:6:47 | util.fo ... , evil) | Cross-site scripting vulnerability due to $@. | formatting.js:4:16:4:29 | req.query.evil | user-provided value |
| formatting.js:7:14:7:53 | require ... , evil) | Cross-site scripting vulnerability due to $@. | formatting.js:4:16:4:29 | req.query.evil | user-provided value |
| pages/Next.jsx:8:13:8:19 | req.url | Cross-site scripting vulnerability due to $@. | pages/Next.jsx:8:13:8:19 | req.url | user-provided value |
| pages/Next.jsx:15:13:15:19 | req.url | Cross-site scripting vulnerability due to $@. | pages/Next.jsx:15:13:15:19 | req.url | user-provided value |
| partial.js:10:14:10:18 | x + y | Cross-site scripting vulnerability due to $@. | partial.js:13:42:13:48 | req.url | user-provided value |
| partial.js:19:14:19:18 | x + y | Cross-site scripting vulnerability due to $@. | partial.js:22:51:22:57 | req.url | user-provided value |
| partial.js:28:14:28:18 | x + y | Cross-site scripting vulnerability due to $@. | partial.js:31:47:31:53 | req.url | user-provided value |

View File

@@ -0,0 +1,14 @@
{
"name": "my-app",
"version": "0.1.0",
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
},
"dependencies": {
"next": "^10.0.0",
"react": "17.0.1",
"react-dom": "17.0.1"
}
}

View File

@@ -0,0 +1,19 @@
export default function Post() {
return <span />;
}
Post.getInitialProps = async (ctx) => {
const req = ctx.req;
const res = ctx.res;
res.end(req.url);
return {}
}
export async function getServerSideProps(ctx) {
const req = ctx.req;
const res = ctx.res;
res.end(req.url);
return {
props: {}
}
}