mirror of
https://github.com/github/codeql.git
synced 2025-12-20 10:46:30 +01:00
Merge branch 'master' of github.com:github/codeql into OptionalSanitizer
This commit is contained in:
14
javascript/ql/src/external/CodeDuplication.qll
vendored
14
javascript/ql/src/external/CodeDuplication.qll
vendored
@@ -261,6 +261,11 @@ predicate similarContainers(StmtContainer sc, StmtContainer other, float percent
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: do not use.
|
||||
*
|
||||
* Holds if `line` in `f` is similar to a line somewhere else.
|
||||
*/
|
||||
predicate similarLines(File f, int line) {
|
||||
exists(SimilarBlock b | b.sourceFile() = f and line in [b.sourceStartLine() .. b.sourceEndLine()])
|
||||
}
|
||||
@@ -275,6 +280,7 @@ private predicate similarLinesPerEquivalenceClass(int equivClass, int lines, Fil
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `coveredLines` lines of `f` are similar to lines in `otherFile`. */
|
||||
pragma[noopt]
|
||||
private predicate similarLinesCovered(File f, int coveredLines, File otherFile) {
|
||||
exists(int numLines | numLines = f.getNumberOfLines() |
|
||||
@@ -296,6 +302,11 @@ private predicate similarLinesCovered(File f, int coveredLines, File otherFile)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: do not use.
|
||||
*
|
||||
* Holds if `line` in `f` is duplicated by a line somewhere else.
|
||||
*/
|
||||
predicate duplicateLines(File f, int line) {
|
||||
exists(DuplicateBlock b |
|
||||
b.sourceFile() = f and line in [b.sourceStartLine() .. b.sourceEndLine()]
|
||||
@@ -312,6 +323,7 @@ private predicate duplicateLinesPerEquivalenceClass(int equivClass, int lines, F
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `coveredLines` lines of `f` are duplicates of lines in `otherFile`. */
|
||||
pragma[noopt]
|
||||
private predicate duplicateLinesCovered(File f, int coveredLines, File otherFile) {
|
||||
exists(int numLines | numLines = f.getNumberOfLines() |
|
||||
@@ -333,6 +345,7 @@ private predicate duplicateLinesCovered(File f, int coveredLines, File otherFile
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if most of `f` (`percent`%) is similar to `other`. */
|
||||
predicate similarFiles(File f, File other, int percent) {
|
||||
exists(int covered, int total |
|
||||
similarLinesCovered(f, covered, other) and
|
||||
@@ -343,6 +356,7 @@ predicate similarFiles(File f, File other, int percent) {
|
||||
not duplicateFiles(f, other, _)
|
||||
}
|
||||
|
||||
/** Holds if most of `f` (`percent`%) is duplicated by `other`. */
|
||||
predicate duplicateFiles(File f, File other, int percent) {
|
||||
exists(int covered, int total |
|
||||
duplicateLinesCovered(f, covered, other) and
|
||||
|
||||
@@ -152,6 +152,18 @@ private class RequireVariable extends Variable {
|
||||
*/
|
||||
private predicate moduleInFile(Module m, File f) { m.getFile() = f }
|
||||
|
||||
/**
|
||||
* Holds if `nd` may refer to `require`, either directly or modulo local data flow.
|
||||
*/
|
||||
cached
|
||||
private predicate isRequire(DataFlow::Node nd) {
|
||||
nd.asExpr() = any(RequireVariable req).getAnAccess() and
|
||||
// `mjs` files explicitly disallow `require`
|
||||
not nd.getFile().getExtension() = "mjs"
|
||||
or
|
||||
isRequire(nd.getAPredecessor())
|
||||
}
|
||||
|
||||
/**
|
||||
* A `require` import.
|
||||
*
|
||||
@@ -162,12 +174,7 @@ private predicate moduleInFile(Module m, File f) { m.getFile() = f }
|
||||
* ```
|
||||
*/
|
||||
class Require extends CallExpr, Import {
|
||||
cached
|
||||
Require() {
|
||||
any(RequireVariable req).getAnAccess() = getCallee() and
|
||||
// `mjs` files explicitly disallow `require`
|
||||
not getFile().getExtension() = "mjs"
|
||||
}
|
||||
Require() { isRequire(getCallee().flow()) }
|
||||
|
||||
override PathExpr getImportedPath() { result = getArgument(0) }
|
||||
|
||||
@@ -257,8 +264,8 @@ private class RequirePath extends PathExprCandidate {
|
||||
RequirePath() {
|
||||
this = any(Require req).getArgument(0)
|
||||
or
|
||||
exists(RequireVariable req, MethodCallExpr reqres |
|
||||
reqres.getReceiver() = req.getAnAccess() and
|
||||
exists(MethodCallExpr reqres |
|
||||
isRequire(reqres.getReceiver().flow()) and
|
||||
reqres.getMethodName() = "resolve" and
|
||||
this = reqres.getArgument(0)
|
||||
)
|
||||
|
||||
@@ -116,7 +116,7 @@ class XMLFile extends XMLParent, File {
|
||||
XMLFile() { xmlEncoding(this, _) }
|
||||
|
||||
/** Gets a printable representation of this XML file. */
|
||||
override string toString() { result = XMLParent.super.toString() }
|
||||
override string toString() { result = getName() }
|
||||
|
||||
/** Gets the name of this XML file. */
|
||||
override string getName() { result = File.super.getAbsolutePath() }
|
||||
@@ -236,7 +236,7 @@ class XMLElement extends @xmlelement, XMLParent, XMLLocatable {
|
||||
string getAttributeValue(string name) { result = this.getAttribute(name).getValue() }
|
||||
|
||||
/** Gets a printable representation of this XML element. */
|
||||
override string toString() { result = XMLParent.super.toString() }
|
||||
override string toString() { result = getName() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -151,11 +151,14 @@ private module CachedSteps {
|
||||
) {
|
||||
calls(invk, f) and
|
||||
(
|
||||
exists(int i, Parameter p |
|
||||
f.getParameter(i) = p and
|
||||
not p.isRestParameter() and
|
||||
arg = invk.getArgument(i) and
|
||||
parm = DataFlow::parameterNode(p)
|
||||
exists(int i | arg = invk.getArgument(i) |
|
||||
exists(Parameter p |
|
||||
f.getParameter(i) = p and
|
||||
not p.isRestParameter() and
|
||||
parm = DataFlow::parameterNode(p)
|
||||
)
|
||||
or
|
||||
parm = reflectiveParameterAccess(f, i)
|
||||
)
|
||||
or
|
||||
arg = invk.(DataFlow::CallNode).getReceiver() and
|
||||
@@ -185,6 +188,22 @@ private module CachedSteps {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a data-flow node inside `f` that refers to the `arguments` object of `f`.
|
||||
*/
|
||||
private DataFlow::Node argumentsAccess(Function f) {
|
||||
result.getContainer().getEnclosingContainer*() = f and
|
||||
result.analyze().getAValue().(AbstractArguments).getFunction() = f
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a data-flow node that refers to the `i`th parameter of `f` through its `arguments`
|
||||
* object.
|
||||
*/
|
||||
private DataFlow::SourceNode reflectiveParameterAccess(Function f, int i) {
|
||||
result.(DataFlow::PropRead).accesses(argumentsAccess(f), any(string p | i = p.toInt()))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is a flow step from `pred` to `succ` through parameter passing
|
||||
* to a function call.
|
||||
|
||||
291
javascript/ql/src/semmle/javascript/frameworks/Fastify.qll
Normal file
291
javascript/ql/src/semmle/javascript/frameworks/Fastify.qll
Normal file
@@ -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()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,3 +4,4 @@ import semmle.javascript.frameworks.Koa
|
||||
import semmle.javascript.frameworks.NodeJSLib
|
||||
import semmle.javascript.frameworks.Restify
|
||||
import semmle.javascript.frameworks.Connect
|
||||
import semmle.javascript.frameworks.Fastify
|
||||
|
||||
Reference in New Issue
Block a user