Add support for vulnerable CORS middlewares

This commit is contained in:
Kevin Stubbings
2024-08-26 21:30:48 -07:00
parent e81fdc951a
commit 8bf8893307
11 changed files with 337 additions and 0 deletions

View File

@@ -1411,6 +1411,56 @@ module Http {
override DataFlow::Node getValueArg() { none() }
}
/**
* A data-flow node that enables or disables CORS
* in a global manner.
*
* Extend this class to refine existing API models. If you want to model new APIs,
* extend `CorsMiddleware::Range` instead.
*/
class CorsMiddleware extends DataFlow::Node instanceof CorsMiddleware::Range {
/**
* Gets the string corresponding to the middleware
*/
string middleware_name() { result = super.middleware_name() }
/**
* Gets the boolean value corresponding to if CORS credentials is enabled
* (`true`) or disabled (`false`) by this node.
*/
DataFlow::Node allowed_origins() { result = super.allowed_origins() }
DataFlow::Node allowed_credentials() { result = super.allowed_credentials() }
}
/** Provides a class for modeling new CORS middleware APIs. */
module CorsMiddleware {
/**
* A data-flow node that enables or disables Cross-site request forgery protection
* in a global manner.
*
* Extend this class to model new APIs. If you want to refine existing API models,
* extend `CorsMiddleware` instead.
*/
abstract class Range extends DataFlow::Node {
/**
* Gets the string corresponding to the middleware
*/
abstract string middleware_name();
/**
* Gets the boolean value corresponding to if CORS credentials is enabled
* (`true`) or disabled (`false`) by this node.
*/
abstract DataFlow::Node allowed_credentials();
/**
* Gets the strings corresponding to the origins allowed by the cors policy
*/
abstract DataFlow::Node allowed_origins();
}
}
/**
* A data-flow node that enables or disables Cross-site request forgery protection
* in a global manner.

View File

@@ -30,6 +30,32 @@ module FastApi {
API::Node instance() { result = cls().getReturn() }
}
/**
* A call to `app.add_middleware` adding a generic middleware.
*/
private class AddMiddlewareCall extends DataFlow::CallCfgNode {
AddMiddlewareCall() { this = App::instance().getMember("add_middleware").getACall() }
string middleware_name() { result = this.getArg(0).asExpr().(Name).toString() }
}
/**
* A call to `app.add_middleware` adding CORSMiddleware.
*/
class AddCorsMiddlewareCall extends Http::Server::CorsMiddleware::Range, AddMiddlewareCall {
override string middleware_name() { result = this.getArg(0).asExpr().(Name).toString() }
override DataFlow::Node allowed_origins() { result = this.getArgByName("allow_origins") }
override DataFlow::Node allowed_credentials() {
result = this.getArgByName("allow_credentials")
}
DataFlow::Node allowed_methods() { result = this.getArgByName("allow_methods") }
DataFlow::Node allowed_headers() { result = this.getArgByName("allow_headers") }
}
/**
* Provides models for the `fastapi.APIRouter` class
*

View File

@@ -25,6 +25,87 @@ private import semmle.python.frameworks.data.ModelsAsData
* - https://www.starlette.io/
*/
module Starlette {
/**
* Provides models for the `starlette.app` class
*
* See https://www.starlette.io/websockets/.
*/
module App {
API::Node cls() { result = API::moduleImport("starlette").getMember("app") }
/** Gets a reference to a FastAPI application (an instance of `fastapi.FastAPI`). */
API::Node instance() { result = cls().getReturn() }
}
/**
* A call to any of the execute methods on a `app.add_middleware`.
*/
class AddMiddlewareCall extends DataFlow::CallCfgNode {
AddMiddlewareCall() {
this = [App::instance().getMember("add_middleware").getACall(), Middleware::instance()]
}
string middleware_name() { result = this.getArg(0).asExpr().(Name).toString() }
}
/**
* A call to any of the execute methods on a `app.add_middleware` with CORSMiddleware.
*/
class AddCorsMiddlewareCall extends AddMiddlewareCall, Http::Server::CorsMiddleware::Range {
override string middleware_name() { result = this.getArg(0).asExpr().(Name).toString() }
override DataFlow::Node allowed_origins() { result = this.getArgByName("allow_origins") }
override DataFlow::Node allowed_credentials() {
result = this.getArgByName("allow_credentials")
}
DataFlow::Node allowed_methods() { result = this.getArgByName("allow_methods") }
DataFlow::Node allowed_headers() { result = this.getArgByName("allow_headers") }
}
/**
* Provides models for the `starlette.middleware.Middleware` class
*
* See https://www.starlette.io/.
*/
module Middleware {
/** Gets a reference to the `starlette.middleware.Middleware` class. */
API::Node classRef() {
result = API::moduleImport("starlette").getMember("middleware").getMember("Middleware")
or
result = ModelOutput::getATypeNode("starlette.middleware.Middleware~Subclass").getASubclass*()
}
/**
* A source of instances of `starlette.middleware.Middleware`, extend this class to model new instances.
*
* This can include instantiations of the class, return values from function
* calls, or a special parameter that will be set when functions are called by an external
* library.
*
* Use the predicate `Middleware::instance()` to get references to instances of `starlette.middleware.middleware`.
*/
abstract class InstanceSource extends DataFlow::LocalSourceNode { }
/** A direct instantiation of `starlette.middleware.Middleware`. */
class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode {
ClassInstantiation() { this = classRef().getACall() }
}
/** Gets a reference to an instance of `starlette.middleware.Middleware`. */
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
t.start() and
result instanceof InstanceSource
or
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
}
/** Gets a reference to an instance of `starlette.middleware.Middleware`. */
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
}
/**
* Provides models for the `starlette.websockets.WebSocket` class
*