mirror of
https://github.com/github/codeql.git
synced 2025-12-20 18:56:32 +01:00
Python: Model fabric Group execution (version 2.x)
This required some thought for how to model that we're interested in subclasses
of `fabric.group.Group`, and not so much that class itself. Some thoughts:
---
After initially using this in `module Group`
/** A reference to a subclass of `fabric.group.Group` */
abstract class SubclassRef extends DataFlow::Node { }
private class SubclassInstantiation extends SubclassInstanceSource, DataFlow::CfgNode {
override CallNode node;
SubclassInstantiation() { node.getFunction() = any(SubclassRef ref).asCfgNode() }
}
with this in `module SerialGroup` and `module ThreadingGroup`:
class ClassRef extends DataFlow::Node, fabric::group::Group::SubclassRef {
ClassRef() { this = classRef(DataFlow::TypeTracker::end()) }
}
I wasn't too much of fan of that approach. Since we probably need the `SubclassInstanceSource` anyway, and don't really have a specific use for `SubclassRef`, I just went with concrete (QL) subclasses of `SubclassInstanceSource` in each of the modules for the Python subclasses.
I really don't know what the best approach is, so I'm very open to suggestions. I think we'll really have to flesh this out for handling Django responses, since we're interested in the fact that some subclasses provide default values for the content-type, and keeping track of that is important for XSS (since there is no XSS if response is `text/plain`)
This commit is contained in:
@@ -394,5 +394,185 @@ private module FabricV2 {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// fabric.group
|
||||
// -------------------------------------------------------------------------
|
||||
/** Gets a reference to the `fabric.group` module. */
|
||||
DataFlow::Node group() { result = fabric_attr("group") }
|
||||
|
||||
/** Provides models for the `fabric.group` module */
|
||||
module group {
|
||||
/**
|
||||
* Gets a reference to the attribute `attr_name` of the `fabric.group` module.
|
||||
* WARNING: Only holds for a few predefined attributes.
|
||||
*/
|
||||
private DataFlow::Node group_attr(DataFlow::TypeTracker t, string attr_name) {
|
||||
attr_name in ["SerialGroup", "ThreadingGroup"] and
|
||||
(
|
||||
t.start() and
|
||||
result = DataFlow::importNode("fabric.group" + "." + attr_name)
|
||||
or
|
||||
t.startInAttr(attr_name) and
|
||||
result = group()
|
||||
)
|
||||
or
|
||||
// Due to bad performance when using normal setup with `group_attr(t2, attr_name).track(t2, t)`
|
||||
// we have inlined that code and forced a join
|
||||
exists(DataFlow::TypeTracker t2 |
|
||||
exists(DataFlow::StepSummary summary |
|
||||
group_attr_first_join(t2, attr_name, result, summary) and
|
||||
t = t2.append(summary)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate group_attr_first_join(
|
||||
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res,
|
||||
DataFlow::StepSummary summary
|
||||
) {
|
||||
DataFlow::StepSummary::step(group_attr(t2, attr_name), res, summary)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to the attribute `attr_name` of the `fabric.group` module.
|
||||
* WARNING: Only holds for a few predefined attributes.
|
||||
*/
|
||||
private DataFlow::Node group_attr(string attr_name) {
|
||||
result = group_attr(DataFlow::TypeTracker::end(), attr_name)
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides models for the `fabric.group.Group` class and its subclasses.
|
||||
*
|
||||
* `fabric.group.Group` is an abstract class, that has concrete implementations
|
||||
* `SerialGroup` and `ThreadingGroup`.
|
||||
*
|
||||
* See
|
||||
* - https://docs.fabfile.org/en/2.5/api/group.html#fabric.group.Group
|
||||
* - https://docs.fabfile.org/en/2.5/api/group.html#fabric.group.SerialGroup
|
||||
* - https://docs.fabfile.org/en/2.5/api/group.html#fabric.group.ThreadingGroup
|
||||
*/
|
||||
module Group {
|
||||
/**
|
||||
* A source of an instance of a subclass of `fabric.group.Group`
|
||||
*
|
||||
* This can include instantiation of a class, return value from function
|
||||
* calls, or a special parameter that will be set when functions are call by external
|
||||
* library.
|
||||
*
|
||||
* Use `Group::subclassInstance()` predicate to get references to an instance of a subclass of `fabric.group.Group`.
|
||||
*/
|
||||
abstract class SubclassInstanceSource extends DataFlow::Node { }
|
||||
|
||||
/** Gets a reference to an instance of a subclass of `fabric.group.Group`. */
|
||||
private DataFlow::Node subclassInstance(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result instanceof SubclassInstanceSource
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = subclassInstance(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of a subclass of `fabric.group.Group`. */
|
||||
DataFlow::Node subclassInstance() {
|
||||
result = subclassInstance(DataFlow::TypeTracker::end())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to the `run` method on an instance of a subclass of `fabric.group.Group`.
|
||||
*
|
||||
* See https://docs.fabfile.org/en/2.5/api/group.html#fabric.group.Group.run
|
||||
*/
|
||||
private DataFlow::Node subclassInstanceRunMethod(DataFlow::TypeTracker t) {
|
||||
t.startInAttr("run") and
|
||||
result = subclassInstance()
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = subclassInstanceRunMethod(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to the `run` method on an instance of a subclass of `fabric.group.Group`.
|
||||
*
|
||||
* See https://docs.fabfile.org/en/2.5/api/group.html#fabric.group.Group.run
|
||||
*/
|
||||
DataFlow::Node subclassInstanceRunMethod() {
|
||||
result = subclassInstanceRunMethod(DataFlow::TypeTracker::end())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `run`, `sudo` on an instance of a subclass of `fabric.group.Group`.
|
||||
*
|
||||
* See https://docs.fabfile.org/en/2.5/api/group.html#fabric.group.Group.run
|
||||
*/
|
||||
private class FabricGroupRunCall extends SystemCommandExecution::Range, DataFlow::CfgNode {
|
||||
override CallNode node;
|
||||
|
||||
FabricGroupRunCall() {
|
||||
node.getFunction() = fabric::group::Group::subclassInstanceRunMethod().asCfgNode()
|
||||
}
|
||||
|
||||
override DataFlow::Node getCommand() {
|
||||
result.asCfgNode() = [node.getArg(0), node.getArgByName("command")]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides models for the `fabric.group.SerialGroup` class
|
||||
*
|
||||
* See https://docs.fabfile.org/en/2.5/api/group.html#fabric.group.SerialGroup.
|
||||
*/
|
||||
module SerialGroup {
|
||||
/** Gets a reference to the `fabric.group.SerialGroup` class. */
|
||||
private DataFlow::Node classRef(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result = group_attr("SerialGroup")
|
||||
or
|
||||
// Handle `fabric.SerialGroup` alias
|
||||
t.start() and
|
||||
result = fabric_attr("SerialGroup")
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = classRef(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to the `fabric.group.SerialGroup` class. */
|
||||
private DataFlow::Node classRef() { result = classRef(DataFlow::TypeTracker::end()) }
|
||||
|
||||
private class ClassInstantiation extends Group::SubclassInstanceSource, DataFlow::CfgNode {
|
||||
override CallNode node;
|
||||
|
||||
ClassInstantiation() { node.getFunction() = classRef().asCfgNode() }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides models for the `fabric.group.ThreadingGroup` class
|
||||
*
|
||||
* See https://docs.fabfile.org/en/2.5/api/group.html#fabric.group.ThreadingGroup.
|
||||
*/
|
||||
module ThreadingGroup {
|
||||
/** Gets a reference to the `fabric.group.ThreadingGroup` class. */
|
||||
private DataFlow::Node classRef(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result = group_attr("ThreadingGroup")
|
||||
or
|
||||
// Handle `fabric.ThreadingGroup` alias
|
||||
t.start() and
|
||||
result = fabric_attr("ThreadingGroup")
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = classRef(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to the `fabric.group.ThreadingGroup` class. */
|
||||
DataFlow::Node classRef() { result = classRef(DataFlow::TypeTracker::end()) }
|
||||
|
||||
private class ClassInstantiation extends Group::SubclassInstanceSource, DataFlow::CfgNode {
|
||||
override CallNode node;
|
||||
|
||||
ClassInstantiation() { node.getFunction() = classRef().asCfgNode() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user