mirror of
https://github.com/github/codeql.git
synced 2026-02-16 15:03:41 +01:00
261 lines
8.8 KiB
Plaintext
261 lines
8.8 KiB
Plaintext
/**
|
|
* Provides classes and predicates for reasoning about [Next.js](https://www.npmjs.com/package/next).
|
|
*/
|
|
|
|
import javascript
|
|
|
|
/**
|
|
* Provides classes and predicates modeling [Next.js](https://www.npmjs.com/package/next).
|
|
*/
|
|
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 corrosponding to a `Next.js` page.
|
|
*/
|
|
Module getAPagesModule() { result.getFile().getParentContainer() = getAPagesFolder() }
|
|
|
|
/**
|
|
* 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 = getAPagesModule() and
|
|
exists(DataFlow::FunctionNode staticPaths, DataFlow::Node fallback |
|
|
staticPaths = result.getAnExportedValue("getStaticPaths").getAFunctionValue() and
|
|
fallback = staticPaths.getAReturn().getALocalSource().getAPropertyWrite("fallback").getRhs() and
|
|
not fallback.mayHaveBooleanValue(false)
|
|
)
|
|
}
|
|
|
|
/**
|
|
* A user defined path or query parameter in `Next.js`.
|
|
*/
|
|
class NextParams extends RemoteFlowSource {
|
|
NextParams() {
|
|
this =
|
|
getAModuleWithFallbackPaths()
|
|
.getAnExportedValue("getStaticProps")
|
|
.getAFunctionValue()
|
|
.getParameter(0)
|
|
.getAPropertyRead("params")
|
|
or
|
|
this = getServerSidePropsFunction(_).getParameter(0).getAPropertyRead(["params", "query"])
|
|
or
|
|
this = nextRouter().getAPropertyRead("query")
|
|
}
|
|
|
|
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()
|
|
or
|
|
result =
|
|
pageModule
|
|
.getAnExportedValue("default")
|
|
.getALocalSource()
|
|
.getAstNode()
|
|
.(ReactComponent)
|
|
.getStaticMethod("getInitialProps")
|
|
.flow()
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Gets a reference to a `props` object computed by the Next.js server.
|
|
* This `props` object is both used both by the server and client to render the page.
|
|
*/
|
|
DataFlow::Node getAPropsSource(Module pageModule) {
|
|
pageModule = getAPagesModule() and
|
|
(
|
|
result =
|
|
[getStaticPropsFunction(pageModule), getServerSidePropsFunction(pageModule)]
|
|
.getAReturn()
|
|
.getALocalSource()
|
|
.getAPropertyWrite("props")
|
|
.getRhs()
|
|
or
|
|
result = getInitialProps(pageModule).getAReturn()
|
|
)
|
|
}
|
|
|
|
/**
|
|
* A step modeling the flow from the server-computed props object to the default exported function that renders the page.
|
|
*/
|
|
class NextJSStaticPropsStep extends DataFlow::SharedFlowStep {
|
|
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
|
exists(Module pageModule, DataFlow::FunctionNode function |
|
|
pageModule = getAPagesModule() and
|
|
function = pageModule.getAnExportedValue("default").getAFunctionValue() and
|
|
pred = getAPropsSource(pageModule) and
|
|
succ = function.getParameter(0)
|
|
)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A step modeling the flow from the server-computed props object to the default exported React component that renders the page.
|
|
*/
|
|
class NextJSStaticReactComponentPropsStep extends DataFlow::SharedFlowStep {
|
|
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
|
exists(Module pageModule, ReactComponent component |
|
|
pageModule = getAPagesModule() and
|
|
pageModule.getAnExportedValue("default").getALocalSource() = DataFlow::valueNode(component) and
|
|
pred = getAPropsSource(pageModule) and
|
|
succ = component.getADirectPropsAccess()
|
|
)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 {
|
|
NextHttpRouteHandler() { this = getServerSidePropsFunction(_) or this = getInitialProps(_) }
|
|
}
|
|
|
|
/**
|
|
* A function that handles both a request and response from Next.js, seen as a routehandler.
|
|
*/
|
|
class NextReqResHandler extends Http::Servers::StandardRouteHandler, DataFlow::FunctionNode {
|
|
DataFlow::ParameterNode req;
|
|
DataFlow::ParameterNode res;
|
|
|
|
NextReqResHandler() {
|
|
res = this.getAParameter() and
|
|
req = this.getAParameter() and
|
|
req.hasUnderlyingType("next", "NextApiRequest") and
|
|
res.hasUnderlyingType("next", "NextApiResponse")
|
|
}
|
|
|
|
/** Gets the request parameter */
|
|
DataFlow::ParameterNode getRequest() { result = req }
|
|
|
|
/** Gets the response parameter */
|
|
DataFlow::ParameterNode getResponse() { result = res }
|
|
}
|
|
|
|
/**
|
|
* A NodeJS HTTP request object in a Next.js page.
|
|
*/
|
|
class NextHttpRequestSource extends NodeJSLib::RequestSource {
|
|
Http::RouteHandler rh;
|
|
|
|
NextHttpRequestSource() {
|
|
this = rh.(NextHttpRouteHandler).getParameter(0).getAPropertyRead("req") or
|
|
this = rh.(NextReqResHandler).getRequest()
|
|
}
|
|
|
|
override Http::RouteHandler getRouteHandler() { result = rh }
|
|
}
|
|
|
|
/**
|
|
* A NodeJS HTTP response object in a Next.js page.
|
|
*/
|
|
class NextHttpResponseSource extends NodeJSLib::ResponseSource {
|
|
Http::RouteHandler rh;
|
|
|
|
NextHttpResponseSource() {
|
|
this = rh.(NextHttpRouteHandler).getParameter(0).getAPropertyRead("res") or
|
|
this = rh.(NextReqResHandler).getResponse()
|
|
}
|
|
|
|
override Http::RouteHandler getRouteHandler() { result = rh }
|
|
}
|
|
|
|
/**
|
|
* Gets a folder that contains API endpoints for a Next.js application.
|
|
* These API endpoints act as Express-like route-handlers.
|
|
*/
|
|
Folder apiFolder() {
|
|
result = getANextPackage().getFile().getParentContainer().getFolder("pages").getFolder("api")
|
|
or
|
|
result = apiFolder().getAFolder()
|
|
}
|
|
|
|
/**
|
|
* A Next.js route handler for an API endpoint.
|
|
* The response (res) includes a set of Express.js-like methods,
|
|
* and we therefore model the routehandler as an Express.js routehandler.
|
|
*/
|
|
class NextApiRouteHandler extends DataFlow::FunctionNode, Express::RouteHandler,
|
|
Http::Servers::StandardRouteHandler {
|
|
NextApiRouteHandler() {
|
|
exists(Module mod | mod.getFile().getParentContainer() = apiFolder() |
|
|
this = mod.getAnExportedValue("default").getAFunctionValue()
|
|
)
|
|
}
|
|
|
|
override DataFlow::ParameterNode getRouteHandlerParameter(string kind) {
|
|
kind = "request" and result = this.getParameter(0)
|
|
or
|
|
kind = "response" and result = this.getParameter(1)
|
|
}
|
|
}
|
|
|
|
/** DEPRECATED: Alias for NextApiRouteHandler */
|
|
deprecated class NextAPIRouteHandler = NextApiRouteHandler;
|
|
|
|
/**
|
|
* Gets a reference to a [Next.js router](https://nextjs.org/docs/api-reference/next/router).
|
|
*/
|
|
DataFlow::SourceNode nextRouter() {
|
|
result = DataFlow::moduleMember("next/router", "useRouter").getACall()
|
|
or
|
|
result =
|
|
API::moduleImport("next/router")
|
|
.getMember("withRouter")
|
|
.getParameter(0)
|
|
.getParameter(0)
|
|
.getMember("router")
|
|
.asSource()
|
|
}
|
|
}
|