mirror of
https://github.com/github/codeql.git
synced 2026-04-29 10:45:15 +02:00
Python: FastAPI: Model extra-taint for pydantic models
It feels a bit strange to add it to `frameworks.rst` since we only support a little bit of it, but if I don't do it now, we will most likely forget to do it later on (since it has already been added to `frameworks.qll`).
This commit is contained in:
@@ -24,6 +24,7 @@ private import semmle.python.frameworks.Mysql
|
||||
private import semmle.python.frameworks.MySQLdb
|
||||
private import semmle.python.frameworks.Peewee
|
||||
private import semmle.python.frameworks.Psycopg2
|
||||
private import semmle.python.frameworks.Pydantic
|
||||
private import semmle.python.frameworks.PyMySQL
|
||||
private import semmle.python.frameworks.Rsa
|
||||
private import semmle.python.frameworks.Simplejson
|
||||
|
||||
@@ -9,6 +9,7 @@ private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import semmle.python.dataflow.new.TaintTracking
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
private import semmle.python.frameworks.Pydantic
|
||||
|
||||
/**
|
||||
* Provides models for the `fastapi` PyPI package.
|
||||
@@ -78,6 +79,18 @@ private module FastApi {
|
||||
DataFlow::Node getResponseClassArg() { result = this.getArgByName("response_class") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A parameter to a request handler that has a type-annotation with a class that is a
|
||||
* Pydantic model.
|
||||
*/
|
||||
private class PydanticModelRequestHandlerParam extends Pydantic::BaseModel::InstanceSource,
|
||||
DataFlow::ParameterNode {
|
||||
PydanticModelRequestHandlerParam() {
|
||||
this.getParameter().getAnnotation() = Pydantic::BaseModel::subclassRef().getAUse().asExpr() and
|
||||
any(FastApiRouteSetup rs).getARequestHandler().getArgByName(_) = this.getParameter()
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Response modeling
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
101
python/ql/lib/semmle/python/frameworks/Pydantic.qll
Normal file
101
python/ql/lib/semmle/python/frameworks/Pydantic.qll
Normal file
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* Provides classes modeling security-relevant aspects of the `pydantic` PyPI package.
|
||||
*
|
||||
* See
|
||||
* - https://pypi.org/project/pydantic/
|
||||
* - https://pydantic-docs.helpmanual.io/
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.TaintTracking
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Provides models for `pydantic` PyPI package.
|
||||
*
|
||||
* See
|
||||
* - https://pypi.org/project/pydantic/
|
||||
* - https://pydantic-docs.helpmanual.io/
|
||||
*/
|
||||
module Pydantic {
|
||||
/**
|
||||
* Provides models for `pydantic.BaseModel` subclasses (a pydantic model).
|
||||
*
|
||||
* See https://pydantic-docs.helpmanual.io/usage/models/.
|
||||
*/
|
||||
module BaseModel {
|
||||
/** Gets a reference to a `pydantic.BaseModel` subclass (a pydantic model). */
|
||||
API::Node subclassRef() {
|
||||
result = API::moduleImport("pydantic").getMember("BaseModel").getASubclass+()
|
||||
}
|
||||
|
||||
/**
|
||||
* A source of instances of `pydantic.BaseModel` subclasses, 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 `BaseModel::instance()` to get references to instances of `pydantic.BaseModel`.
|
||||
*/
|
||||
abstract class InstanceSource extends DataFlow::LocalSourceNode { }
|
||||
|
||||
/** Gets a reference to an instance of a `pydantic.BaseModel` subclass. */
|
||||
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result instanceof InstanceSource
|
||||
or
|
||||
t.start() and
|
||||
instanceStepToPydanticModel(_, result)
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of a `pydantic.BaseModel` subclass. */
|
||||
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
|
||||
/**
|
||||
* A step from an instance of a `pydantic.BaseModel` subclass, that might result in
|
||||
* an instance of a `pydantic.BaseModel` subclass.
|
||||
*
|
||||
* NOTE: We currently overapproximate, and treat all attributes as containing another
|
||||
* pydantic model. For the code below, we _could_ limit this to `main_foo` and
|
||||
* members of `other_foos`.
|
||||
*
|
||||
* ```py
|
||||
* class MyComplexModel(BaseModel):
|
||||
* field: str
|
||||
* main_foo: Foo
|
||||
* other_foos: List[Foo]
|
||||
* ```
|
||||
*/
|
||||
private predicate instanceStepToPydanticModel(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
// attributes (such as `model.foo`)
|
||||
nodeFrom = instance() and
|
||||
nodeTo.(DataFlow::AttrRead).getObject() = nodeFrom
|
||||
or
|
||||
// subscripts on attributes (such as `model.foo[0]`)
|
||||
nodeFrom.(DataFlow::AttrRead).getObject() = instance() and
|
||||
nodeTo.asCfgNode().(SubscriptNode).getObject() = nodeFrom.asCfgNode()
|
||||
}
|
||||
|
||||
/**
|
||||
* Extra taint propagation for `pydantic.BaseModel` subclasses. (note that these could also be `pydantic.BaseModel` subclasses)
|
||||
*/
|
||||
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
// attributes (such as `model.foo`)
|
||||
nodeFrom = instance() and
|
||||
nodeTo.(DataFlow::AttrRead).getObject() = nodeFrom
|
||||
or
|
||||
// subscripts on attributes (such as `model.foo[0]`)
|
||||
nodeFrom.(DataFlow::AttrRead).getObject() = instance() and
|
||||
nodeTo.asCfgNode().(SubscriptNode).getObject() = nodeFrom.asCfgNode()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user