Skip to content

Commit 9d7bb57

Browse files
committed
add parameter values from Next as a RemoteFlowSource
1 parent 41a0c0b commit 9d7bb57

File tree

6 files changed

+114
-0
lines changed

6 files changed

+114
-0
lines changed

javascript/ql/src/javascript.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ import semmle.javascript.frameworks.Logging
9696
import semmle.javascript.frameworks.HttpFrameworks
9797
import semmle.javascript.frameworks.HttpProxy
9898
import semmle.javascript.frameworks.Markdown
99+
import semmle.javascript.frameworks.Next
99100
import semmle.javascript.frameworks.NoSQL
100101
import semmle.javascript.frameworks.PkgCloud
101102
import semmle.javascript.frameworks.PropertyProjection
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/**
2+
* Provides classes and predicates for reasoning about [Next.js](https://www.npmjs.com/package/next).
3+
*/
4+
5+
import javascript
6+
7+
/**
8+
* Provides classes and predicates modelling [Next.js](https://www.npmjs.com/package/next).
9+
*/
10+
private module NextJS {
11+
/**
12+
* Gets a `package.json` that depends on the `Next.js` library.
13+
*/
14+
PackageJSON getANextPackage() { result.getDependencies().getADependency("next", _) }
15+
16+
/**
17+
* Gets a "pages" folder in a `Next.js` application.
18+
* JavaScript files inside these folders are mapped to routes.
19+
*/
20+
Folder getAPagesFolder() {
21+
result = getANextPackage().getFile().getParentContainer().getFolder("pages")
22+
or
23+
result = getAPagesFolder().getAFolder()
24+
}
25+
26+
/**
27+
* Gets a module inside a "pages" folder where `fallback` from `getStaticPaths` is not set to false.
28+
* In such a module the `getStaticProps` method can be called with user-defined parameters.
29+
* If `fallback` is set to false, then only values defined by `getStaticPaths` are allowed.
30+
*/
31+
Module getAModuleWithFallbackPaths() {
32+
result.getFile().getParentContainer() = getAPagesFolder() and
33+
exists(DataFlow::FunctionNode staticPaths, Expr fallback |
34+
staticPaths = result.getAnExportedValue("getStaticPaths").getAFunctionValue() and
35+
fallback =
36+
staticPaths.getAReturn().getALocalSource().getAPropertyWrite("fallback").getRhs().asExpr() and
37+
not fallback.(BooleanLiteral).getValue() = "false"
38+
)
39+
}
40+
41+
/**
42+
* User defined path parameter in `Next.js`.
43+
*/
44+
class NextParams extends RemoteFlowSource {
45+
NextParams() {
46+
this =
47+
getAModuleWithFallbackPaths()
48+
.getAnExportedValue("getStaticProps")
49+
.getAFunctionValue()
50+
.getParameter(0)
51+
.getAPropertyRead("params")
52+
}
53+
54+
override string getSourceType() { result = "Next request parameter" }
55+
}
56+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"name": "my-app",
3+
"version": "0.1.0",
4+
"scripts": {
5+
"dev": "next dev",
6+
"build": "next build",
7+
"start": "next start"
8+
},
9+
"dependencies": {
10+
"next": "^10.0.0",
11+
"react": "17.0.1",
12+
"react-dom": "17.0.1"
13+
}
14+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
export async function getStaticPaths() {
2+
return {
3+
paths: [],
4+
fallback: true
5+
}
6+
}
7+
8+
9+
export async function getStaticProps({ params }) {
10+
return {
11+
props: {
12+
id: params.id,
13+
taint: source()
14+
}
15+
}
16+
}
17+
18+
export default function Post({ taint }) {
19+
sink(taint);
20+
return <span />;
21+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
remoteFlow
2+
| pages/[my-fallback-id].jsx:9:40:9:45 | params |
3+
dataFlow
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import javascript
2+
3+
query RemoteFlowSource remoteFlow() { any() }
4+
5+
class Config extends DataFlow::Configuration {
6+
Config() { this = "Config" }
7+
8+
override predicate isSource(DataFlow::Node source) {
9+
source.(DataFlow::CallNode).getCalleeName() = "source"
10+
}
11+
12+
override predicate isSink(DataFlow::Node sink) {
13+
exists(DataFlow::CallNode call | call.getCalleeName() = "sink" | call.getAnArgument() = sink)
14+
}
15+
}
16+
17+
query predicate dataFlow(DataFlow::Node pred, DataFlow::Node succ) {
18+
any(Config c).hasFlow(pred, succ)
19+
}

0 commit comments

Comments
 (0)