-
Notifications
You must be signed in to change notification settings - Fork 1.8k
JS: model fastify #3527
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
JS: model fastify #3527
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
a76c70d
JS: model fastify
esbena 74fc33e
JS: make the qldoc check happy
esbena 894033d
JS: de-boilerplate the fastify model: address expr/dataflow comments
esbena c400b45
JS: make the Fastify model support `isUserControlledObject`
esbena e588e59
JS: fixup
esbena b31f83a
JS: fixup expected output
esbena File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
291 changes: 291 additions & 0 deletions
291
javascript/ql/src/semmle/javascript/frameworks/Fastify.qll
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,291 @@ | ||
/** | ||
* Provides classes for working with [Fastify](https://www.fastify.io/) applications. | ||
*/ | ||
|
||
import javascript | ||
import semmle.javascript.frameworks.HTTP | ||
|
||
/** | ||
* Provides classes for working with [Fastify](https://www.fastify.io/) applications. | ||
*/ | ||
module Fastify { | ||
/** | ||
* An expression that creates a new Fastify server. | ||
*/ | ||
abstract class ServerDefinition extends HTTP::Servers::StandardServerDefinition { } | ||
|
||
/** | ||
* A standard way to create a Fastify server. | ||
*/ | ||
class StandardServerDefinition extends ServerDefinition { | ||
StandardServerDefinition() { | ||
this = DataFlow::moduleImport("fastify").getAnInvocation().asExpr() | ||
} | ||
} | ||
|
||
/** | ||
* A function used as a Fastify route handler. | ||
* | ||
* By default, only handlers installed by a Fastify route setup are recognized, | ||
* but support for other kinds of route handlers can be added by implementing | ||
* additional subclasses of this class. | ||
*/ | ||
abstract class RouteHandler extends HTTP::Servers::StandardRouteHandler, DataFlow::ValueNode { | ||
/** | ||
* Gets the parameter of the route handler that contains the request object. | ||
*/ | ||
abstract DataFlow::ParameterNode getRequestParameter(); | ||
|
||
/** | ||
* Gets the parameter of the route handler that contains the reply object. | ||
*/ | ||
abstract DataFlow::ParameterNode getReplyParameter(); | ||
} | ||
|
||
/** | ||
* A Fastify route handler installed by a route setup. | ||
*/ | ||
class StandardRouteHandler extends RouteHandler, DataFlow::FunctionNode { | ||
StandardRouteHandler() { this = any(RouteSetup setup).getARouteHandler() } | ||
|
||
override DataFlow::ParameterNode getRequestParameter() { result = this.getParameter(0) } | ||
|
||
override DataFlow::ParameterNode getReplyParameter() { result = this.getParameter(1) } | ||
} | ||
|
||
/** | ||
* A Fastify reply source, that is, the `reply` parameter of a | ||
* route handler. | ||
*/ | ||
private class ReplySource extends HTTP::Servers::ResponseSource { | ||
RouteHandler rh; | ||
|
||
ReplySource() { this = rh.getReplyParameter() } | ||
|
||
/** | ||
* Gets the route handler that provides this response. | ||
*/ | ||
override RouteHandler getRouteHandler() { result = rh } | ||
} | ||
|
||
/** | ||
* A Fastify request source, that is, the request parameter of a | ||
* route handler. | ||
*/ | ||
private class RequestSource extends HTTP::Servers::RequestSource { | ||
RouteHandler rh; | ||
|
||
RequestSource() { this = rh.getRequestParameter() } | ||
|
||
/** | ||
* Gets the route handler that handles this request. | ||
*/ | ||
override RouteHandler getRouteHandler() { result = rh } | ||
} | ||
|
||
/** | ||
* A call to a Fastify method that sets up a route. | ||
*/ | ||
class RouteSetup extends MethodCallExpr, HTTP::Servers::StandardRouteSetup { | ||
ServerDefinition server; | ||
string methodName; | ||
|
||
RouteSetup() { | ||
this.getMethodName() = methodName and | ||
methodName = ["route", "get", "head", "post", "put", "delete", "options", "patch"] and | ||
server.flowsTo(this.getReceiver()) | ||
} | ||
|
||
override DataFlow::SourceNode getARouteHandler() { | ||
result = getARouteHandler(DataFlow::TypeBackTracker::end()) | ||
} | ||
|
||
private DataFlow::SourceNode getARouteHandler(DataFlow::TypeBackTracker t) { | ||
t.start() and | ||
result = this.getARouteHandlerExpr().getALocalSource() | ||
or | ||
exists(DataFlow::TypeBackTracker t2 | result = this.getARouteHandler(t2).backtrack(t2, t)) | ||
} | ||
|
||
override Expr getServer() { result = server } | ||
|
||
/** Gets an argument that represents a route handler being registered. */ | ||
private DataFlow::Node getARouteHandlerExpr() { | ||
if methodName = "route" | ||
then | ||
result = | ||
this | ||
.flow() | ||
.(DataFlow::MethodCallNode) | ||
.getOptionArgument(0, | ||
["onRequest", "preParsing", "preValidation", "preHandler", "preSerialization", | ||
"onSend", "onResponse", "handler"]) | ||
else result = getLastArgument().flow() | ||
} | ||
} | ||
|
||
/** | ||
* An access to a user-controlled Fastify request input. | ||
*/ | ||
private class RequestInputAccess extends HTTP::RequestInputAccess { | ||
RouteHandler rh; | ||
string kind; | ||
|
||
RequestInputAccess() { | ||
exists(string name | this = rh.getARequestSource().ref().getAPropertyRead(name) | | ||
kind = "parameter" and | ||
name = ["params", "query"] | ||
or | ||
kind = "body" and | ||
name = "body" | ||
) | ||
} | ||
|
||
override RouteHandler getRouteHandler() { result = rh } | ||
|
||
override string getKind() { result = kind } | ||
|
||
override predicate isUserControlledObject() { | ||
kind = "body" and | ||
( | ||
usesFastifyPlugin(rh, | ||
DataFlow::moduleImport(["fastify-xml-body-parser", "fastify-formbody"])) | ||
or | ||
usesMiddleware(rh, | ||
any(ExpressLibraries::BodyParser bodyParser | bodyParser.producesUserControlledObjects())) | ||
) | ||
or | ||
kind = "parameter" and | ||
usesFastifyPlugin(rh, DataFlow::moduleImport("fastify-qs")) | ||
} | ||
} | ||
|
||
/** | ||
* Holds if `rh` uses `plugin`. | ||
*/ | ||
private predicate usesFastifyPlugin(RouteHandler rh, DataFlow::SourceNode plugin) { | ||
exists(RouteSetup setup | | ||
plugin | ||
.flowsTo(setup | ||
.getServer() | ||
.flow() | ||
.(DataFlow::SourceNode) | ||
.getAMethodCall("register") | ||
.getArgument(0)) and // only matches the plugins that apply to all routes | ||
rh = setup.getARouteHandler() | ||
) | ||
} | ||
|
||
/** | ||
* Holds if `rh` uses `plugin`. | ||
*/ | ||
private predicate usesMiddleware(RouteHandler rh, DataFlow::SourceNode middleware) { | ||
exists(RouteSetup setup | | ||
middleware | ||
.flowsTo(setup | ||
.getServer() | ||
.flow() | ||
.(DataFlow::SourceNode) | ||
.getAMethodCall("use") | ||
.getArgument(0)) and // only matches the middlewares that apply to all routes | ||
rh = setup.getARouteHandler() | ||
) | ||
} | ||
|
||
/** | ||
* An access to a header on a Fastify request. | ||
*/ | ||
private class RequestHeaderAccess extends HTTP::RequestHeaderAccess { | ||
RouteHandler rh; | ||
|
||
RequestHeaderAccess() { | ||
this = rh.getARequestSource().ref().getAPropertyRead("headers").getAPropertyRead() | ||
} | ||
|
||
override string getAHeaderName() { | ||
result = this.(DataFlow::PropRead).getPropertyName().toLowerCase() | ||
} | ||
|
||
override RouteHandler getRouteHandler() { result = rh } | ||
|
||
override string getKind() { result = "header" } | ||
} | ||
|
||
/** | ||
* An argument passed to the `send` or `end` method of an HTTP response object. | ||
*/ | ||
private class ResponseSendArgument extends HTTP::ResponseSendArgument { | ||
RouteHandler rh; | ||
|
||
ResponseSendArgument() { | ||
this = rh.getAResponseSource().ref().getAMethodCall("send").getArgument(0).asExpr() | ||
or | ||
this = rh.(DataFlow::FunctionNode).getAReturn().asExpr() | ||
} | ||
|
||
override RouteHandler getRouteHandler() { result = rh } | ||
} | ||
|
||
/** | ||
* An invocation of the `redirect` method of an HTTP response object. | ||
*/ | ||
private class RedirectInvocation extends HTTP::RedirectInvocation, MethodCallExpr { | ||
RouteHandler rh; | ||
|
||
RedirectInvocation() { | ||
this = rh.getAResponseSource().ref().getAMethodCall("redirect").asExpr() | ||
} | ||
|
||
override Expr getUrlArgument() { result = this.getLastArgument() } | ||
|
||
override RouteHandler getRouteHandler() { result = rh } | ||
} | ||
|
||
/** | ||
* An invocation that sets a single header of the HTTP response. | ||
*/ | ||
private class SetOneHeader extends HTTP::Servers::StandardHeaderDefinition, | ||
DataFlow::MethodCallNode { | ||
RouteHandler rh; | ||
|
||
SetOneHeader() { | ||
this = rh.getAResponseSource().ref().getAMethodCall("header") and | ||
this.getNumArgument() = 2 | ||
} | ||
|
||
override RouteHandler getRouteHandler() { result = rh } | ||
} | ||
|
||
/** | ||
* An invocation that sets any number of headers of the HTTP response. | ||
*/ | ||
class SetMultipleHeaders extends HTTP::ExplicitHeaderDefinition, DataFlow::MethodCallNode { | ||
RouteHandler rh; | ||
|
||
SetMultipleHeaders() { | ||
this = rh.getAResponseSource().ref().getAMethodCall("headers") and | ||
this.getNumArgument() = 1 | ||
} | ||
|
||
/** | ||
* Gets a reference to the multiple headers object that is to be set. | ||
*/ | ||
private DataFlow::SourceNode getAHeaderSource() { result.flowsTo(this.getArgument(0)) } | ||
|
||
override predicate definesExplicitly(string headerName, Expr headerValue) { | ||
exists(string header | | ||
getAHeaderSource().hasPropertyWrite(header, headerValue.flow()) and | ||
headerName = header.toLowerCase() | ||
) | ||
} | ||
|
||
override RouteHandler getRouteHandler() { result = rh } | ||
|
||
override Expr getNameExpr() { | ||
exists(DataFlow::PropWrite write | | ||
this.getAHeaderSource().flowsTo(write.getBase()) and | ||
result = write.getPropertyNameExpr() | ||
) | ||
} | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
5 changes: 5 additions & 0 deletions
5
javascript/ql/test/library-tests/frameworks/fastify/HeaderAccess.qll
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import javascript | ||
|
||
query predicate test_HeaderAccess(HTTP::RequestHeaderAccess access, string res) { | ||
res = access.getAHeaderName() | ||
} |
5 changes: 5 additions & 0 deletions
5
javascript/ql/test/library-tests/frameworks/fastify/HeaderDefinition.qll
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import javascript | ||
|
||
query predicate test_HeaderDefinition(HTTP::HeaderDefinition hd, Fastify::RouteHandler rh) { | ||
rh = hd.getRouteHandler() | ||
} |
5 changes: 5 additions & 0 deletions
5
javascript/ql/test/library-tests/frameworks/fastify/HeaderDefinition_defines.qll
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import javascript | ||
|
||
query predicate test_HeaderDefinition_defines(HTTP::HeaderDefinition hd, string name, string value) { | ||
hd.defines(name, value) and hd.getRouteHandler() instanceof Fastify::RouteHandler | ||
} |
5 changes: 5 additions & 0 deletions
5
javascript/ql/test/library-tests/frameworks/fastify/HeaderDefinition_getAHeaderName.qll
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import javascript | ||
|
||
query predicate test_HeaderDefinition_getAHeaderName(HTTP::HeaderDefinition hd, string res) { | ||
hd.getRouteHandler() instanceof Fastify::RouteHandler and res = hd.getAHeaderName() | ||
} |
5 changes: 5 additions & 0 deletions
5
javascript/ql/test/library-tests/frameworks/fastify/RedirectInvocation.qll
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import javascript | ||
|
||
query predicate test_RedirectInvocation(HTTP::RedirectInvocation invk, Fastify::RouteHandler rh) { | ||
invk.getRouteHandler() = rh | ||
} |
11 changes: 11 additions & 0 deletions
11
javascript/ql/test/library-tests/frameworks/fastify/RequestInputAccess.qll
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import javascript | ||
|
||
query predicate test_RequestInputAccess( | ||
HTTP::RequestInputAccess ria, string res, Fastify::RouteHandler rh, boolean isUserControlledObject | ||
) { | ||
ria.getRouteHandler() = rh and | ||
res = ria.getKind() and | ||
if ria.isUserControlledObject() | ||
then isUserControlledObject = true | ||
else isUserControlledObject = false | ||
} |
5 changes: 5 additions & 0 deletions
5
javascript/ql/test/library-tests/frameworks/fastify/ResponseSendArgument.qll
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import javascript | ||
|
||
query predicate test_ResponseSendArgument(HTTP::ResponseSendArgument arg, Fastify::RouteHandler rh) { | ||
arg.getRouteHandler() = rh | ||
} |
3 changes: 3 additions & 0 deletions
3
javascript/ql/test/library-tests/frameworks/fastify/RouteHandler.qll
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import javascript | ||
|
||
query predicate test_RouteHandler(Fastify::RouteHandler rh, Expr res) { res = rh.getServer() } |
5 changes: 5 additions & 0 deletions
5
javascript/ql/test/library-tests/frameworks/fastify/RouteHandler_getARequestExpr.qll
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import semmle.javascript.frameworks.Express | ||
|
||
query predicate test_RouteHandler_getARequestExpr(Fastify::RouteHandler rh, HTTP::RequestExpr res) { | ||
res = rh.getARequestExpr() | ||
} |
7 changes: 7 additions & 0 deletions
7
javascript/ql/test/library-tests/frameworks/fastify/RouteHandler_getAResponseHeader.qll
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import semmle.javascript.frameworks.Express | ||
|
||
query predicate test_RouteHandler_getAResponseHeader( | ||
Fastify::RouteHandler rh, string name, HTTP::HeaderDefinition res | ||
) { | ||
res = rh.getAResponseHeader(name) | ||
} |
3 changes: 3 additions & 0 deletions
3
javascript/ql/test/library-tests/frameworks/fastify/RouteSetup.qll
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import javascript | ||
|
||
query predicate test_RouteSetup(Fastify::RouteSetup rs) { any() } |
5 changes: 5 additions & 0 deletions
5
javascript/ql/test/library-tests/frameworks/fastify/RouteSetup_getARouteHandler.qll
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import javascript | ||
|
||
query predicate test_RouteSetup_getARouteHandler(Fastify::RouteSetup r, DataFlow::SourceNode res) { | ||
res = r.getARouteHandler() | ||
} |
3 changes: 3 additions & 0 deletions
3
javascript/ql/test/library-tests/frameworks/fastify/RouteSetup_getServer.qll
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import javascript | ||
|
||
query predicate test_RouteSetup_getServer(Fastify::RouteSetup rs, Expr res) { res = rs.getServer() } |
3 changes: 3 additions & 0 deletions
3
javascript/ql/test/library-tests/frameworks/fastify/ServerDefinition.qll
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import javascript | ||
|
||
query predicate test_ServerDefinition(Fastify::ServerDefinition s) { any() } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.