add parameter values from Next as a RemoteFlowSource

This commit is contained in:
Erik Krogh Kristensen
2021-02-15 16:02:18 +01:00
parent 41a0c0b55e
commit 9d7bb57d8a
6 changed files with 114 additions and 0 deletions

View File

@@ -96,6 +96,7 @@ import semmle.javascript.frameworks.Logging
import semmle.javascript.frameworks.HttpFrameworks
import semmle.javascript.frameworks.HttpProxy
import semmle.javascript.frameworks.Markdown
import semmle.javascript.frameworks.Next
import semmle.javascript.frameworks.NoSQL
import semmle.javascript.frameworks.PkgCloud
import semmle.javascript.frameworks.PropertyProjection

View File

@@ -0,0 +1,56 @@
/**
* Provides classes and predicates for reasoning about [Next.js](https://www.npmjs.com/package/next).
*/
import javascript
/**
* Provides classes and predicates modelling [Next.js](https://www.npmjs.com/package/next).
*/
private module NextJS {
/**
* Gets a `package.json` that depends on the `Next.js` library.
*/
PackageJSON getANextPackage() { result.getDependencies().getADependency("next", _) }
/**
* Gets a "pages" folder in a `Next.js` application.
* JavaScript files inside these folders are mapped to routes.
*/
Folder getAPagesFolder() {
result = getANextPackage().getFile().getParentContainer().getFolder("pages")
or
result = getAPagesFolder().getAFolder()
}
/**
* Gets a module inside a "pages" folder where `fallback` from `getStaticPaths` is not set to false.
* In such a module the `getStaticProps` method can be called with user-defined parameters.
* If `fallback` is set to false, then only values defined by `getStaticPaths` are allowed.
*/
Module getAModuleWithFallbackPaths() {
result.getFile().getParentContainer() = getAPagesFolder() and
exists(DataFlow::FunctionNode staticPaths, Expr fallback |
staticPaths = result.getAnExportedValue("getStaticPaths").getAFunctionValue() and
fallback =
staticPaths.getAReturn().getALocalSource().getAPropertyWrite("fallback").getRhs().asExpr() and
not fallback.(BooleanLiteral).getValue() = "false"
)
}
/**
* User defined path parameter in `Next.js`.
*/
class NextParams extends RemoteFlowSource {
NextParams() {
this =
getAModuleWithFallbackPaths()
.getAnExportedValue("getStaticProps")
.getAFunctionValue()
.getParameter(0)
.getAPropertyRead("params")
}
override string getSourceType() { result = "Next request parameter" }
}
}

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,21 @@
export async function getStaticPaths() {
return {
paths: [],
fallback: true
}
}
export async function getStaticProps({ params }) {
return {
props: {
id: params.id,
taint: source()
}
}
}
export default function Post({ taint }) {
sink(taint);
return <span />;
}

View File

@@ -0,0 +1,3 @@
remoteFlow
| pages/[my-fallback-id].jsx:9:40:9:45 | params |
dataFlow

View File

@@ -0,0 +1,19 @@
import javascript
query RemoteFlowSource remoteFlow() { any() }
class Config extends DataFlow::Configuration {
Config() { this = "Config" }
override predicate isSource(DataFlow::Node source) {
source.(DataFlow::CallNode).getCalleeName() = "source"
}
override predicate isSink(DataFlow::Node sink) {
exists(DataFlow::CallNode call | call.getCalleeName() = "sink" | call.getAnArgument() = sink)
}
}
query predicate dataFlow(DataFlow::Node pred, DataFlow::Node succ) {
any(Config c).hasFlow(pred, succ)
}