Skip to content

Commit 0e7e3e6

Browse files
committed
support Next.js pages that export React components
1 parent 1fdbbb6 commit 0e7e3e6

File tree

3 files changed

+75
-18
lines changed

3 files changed

+75
-18
lines changed

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

Lines changed: 58 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -86,17 +86,46 @@ module NextJS {
8686
*/
8787
DataFlow::FunctionNode getInitialProps(Module pageModule) {
8888
pageModule = getAPagesModule() and
89-
result =
90-
pageModule
91-
.getAnExportedValue("default")
92-
.getAFunctionValue()
93-
.getAPropertyWrite("getInitialProps")
94-
.getRhs()
95-
.getAFunctionValue()
89+
(
90+
result =
91+
pageModule
92+
.getAnExportedValue("default")
93+
.getAFunctionValue()
94+
.getAPropertyWrite("getInitialProps")
95+
.getRhs()
96+
.getAFunctionValue()
97+
or
98+
result =
99+
pageModule
100+
.getAnExportedValue("default")
101+
.getALocalSource()
102+
.getAstNode()
103+
.(ReactComponent)
104+
.getStaticMethod("getInitialProps")
105+
.flow()
106+
)
107+
}
108+
109+
/**
110+
* Gets a reference to a `props` object computed by the Next.js server.
111+
* This `props` object is both used both by the server and client to render the page.
112+
*/
113+
DataFlow::Node getAPropsSource(Module pageModule) {
114+
pageModule = getAPagesModule() and
115+
(
116+
result =
117+
[getStaticPropsFunction(pageModule), getServerSidePropsFunction(pageModule)]
118+
.getAReturn()
119+
.getALocalSource()
120+
.getAPropertyWrite("props")
121+
.getRhs()
122+
or
123+
result = getInitialProps(pageModule).getAReturn()
124+
)
96125
}
97126

98127
/**
99-
* A step modelling the flow from the server-computed `getStaticProps` to the server/client rendering of the page.
128+
* A step modelling the flow from the server-computed props object to the default exported function that renders the page.
100129
*/
101130
class NextJSStaticPropsStep extends DataFlow::AdditionalFlowStep, DataFlow::FunctionNode {
102131
Module pageModule;
@@ -107,20 +136,31 @@ module NextJS {
107136
}
108137

109138
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
110-
(
111-
pred =
112-
[getStaticPropsFunction(pageModule), getServerSidePropsFunction(pageModule)]
113-
.getAReturn()
114-
.getALocalSource()
115-
.getAPropertyWrite("props")
116-
.getRhs()
117-
or
118-
pred = getInitialProps(pageModule).getAReturn()
119-
) and
139+
pred = getAPropsSource(pageModule) and
120140
succ = this.getParameter(0)
121141
}
122142
}
123143

144+
/**
145+
* A step modelling the flow from the server-computed props object to the default exported React component that renders the page.
146+
*/
147+
class NextJSStaticReactComponentPropsStep extends DataFlow::AdditionalFlowStep,
148+
DataFlow::ValueNode {
149+
Module pageModule;
150+
ReactComponent component;
151+
152+
NextJSStaticReactComponentPropsStep() {
153+
pageModule = getAPagesModule() and
154+
this.getAstNode() = component and
155+
this = pageModule.getAnExportedValue("default").getALocalSource()
156+
}
157+
158+
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
159+
pred = getAPropsSource(pageModule) and
160+
succ = component.getADirectPropsAccess()
161+
}
162+
}
163+
124164
/**
125165
* A Next.js function that is exected on the server for every request, seen as a routehandler.
126166
*/
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import React from 'react'
2+
3+
class Page extends React.Component {
4+
static async getInitialProps(ctx) {
5+
const url = ctx.req.url;
6+
return { stars: json.stargazers_count, taint: source(1) }
7+
}
8+
9+
render() {
10+
sink(this.props.taint);
11+
return <div>Next stars: {this.props.stars}</div>
12+
}
13+
}
14+
15+
export default Page
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
remoteFlow
22
| pages/[my-fallback-id].jsx:9:40:9:45 | params |
3+
| pages/secondpage.jsx:5:17:5:27 | ctx.req.url |
34
dataFlow
45
| pages/[my-fallback-id].jsx:13:20:13:27 | source() | pages/[my-fallback-id].jsx:19:10:19:14 | taint |
56
| pages/[my-fallback-id].jsx:26:21:26:29 | source(2) | pages/[my-fallback-id].jsx:20:10:20:14 | stars |
67
| pages/[my-fallback-id].jsx:32:19:32:27 | source(3) | pages/[my-fallback-id].jsx:21:10:21:13 | more |
8+
| pages/secondpage.jsx:6:51:6:59 | source(1) | pages/secondpage.jsx:10:10:10:25 | this.props.taint |

0 commit comments

Comments
 (0)