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:
Rasmus Wriedt Larsen
2020-10-19 18:00:04 +02:00
parent f10456e35f
commit 98691fe8ec
2 changed files with 180 additions and 6 deletions

View File

@@ -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() }
}
}
}
}
}