Sitelet https://web.archive.org/web/20260519062522/https://github.com/github/codeql/pull/4513
Skip to content

Python: model fabric#4513

Merged
codeql-ci merged 7 commits into
github:mainfrom
RasmusWL:python-model-fabric
Oct 21, 2020
Merged

Python: model fabric#4513
codeql-ci merged 7 commits into
github:mainfrom
RasmusWL:python-model-fabric

Conversation

@RasmusWL
Copy link
Copy Markdown
Member

I intentionally left out the part of modeling fabric.api.execute (from version 1.x), since I didn't see an obvious way to integrate this as an additional taint step in our current setup.

@yoff proposed that we might be able to add this in the same way as we do for special methods (such as __add__). However, since these are currently as a part of the algebraic datatype TDataFlowCall in DataFlowPrivate, that doesn't really make it easy to make such a change entirely in the code modeling fabric (that is Fabric.qll).

The current implementation is able to do this as a simple additional taint step by inspecting the first argument of fabric.api.execute and the callable it points to -- and it automatically handles the distinction between passing in functions and bound-methods.

I'm hoping we can enable such easy extension as well, but I currently don't see a clear path forwards, so will leave that for future work.

Modeling subclasses of fabric.group.Group

The most interesting part of this PR is the part where we want to model subclasses of fabric.group.Group, and not the class itself. This required some thought for how to do so (you probably need to have read through the changes in 98691fe before you're able to understand what I'm talking about here)

After initially using the following code 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 code 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)

For v1 tests, just extended with explicit calls that use keyword arguments.

For v2 tests, rewrote pretty much everything to what it 100% explicit what we support
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`)
@RasmusWL RasmusWL requested a review from a team as a code owner October 19, 2020 16:44
Copy link
Copy Markdown
Contributor

@yoff yoff left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally looks good, just one minor question.
Also, I was momentarily confused by GroupSubclassInstanceSource not being connected to InstanceSource (because the latter is for Connection rather than Group).

Comment thread python/ql/src/experimental/semmle/python/frameworks/Fabric.qll Outdated
}

/**
* A call to `run`, `sudo` on an instance of a subclass of `fabric.group.Group`.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is sudo tracked here?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

too much copy paste 😐 there is no sudo method on a Group, so I'll just remove that bit 👍

* 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
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this include "sudo"?

@RasmusWL
Copy link
Copy Markdown
Member Author

Also, I was momentarily confused by GroupSubclassInstanceSource not being connected to InstanceSource (because the latter is for Connection rather than Group).

Aha. In my mind, we're at a pretty OK point in regards to modeling classes in an extensible way with ClassName::classRef(), ClassName::InstanceSource (abstract class of data-flow nodes), and ClassName::instance() -- that does mean that predicate names have fairly generic names, and that all that separates them is the module they are defined in. If you think that's not too good of a solution, please do speak up now 😊

@RasmusWL RasmusWL requested a review from yoff October 20, 2020 15:34
Copy link
Copy Markdown
Contributor

@yoff yoff left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good now.
Just to comment on the execute function: The special methods framework would need a slight generalisation, I think, in that it currently always invoke a class method. It might be worth it, if we foresee having to model many library functions that result in some function being called...

@codeql-ci codeql-ci merged commit eaed93f into github:main Oct 21, 2020
@RasmusWL RasmusWL deleted the python-model-fabric branch October 21, 2020 08:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants