Files
codeql/python/ql/test/library-tests/frameworks/modeling-example
Taus 55d822cc56 Python: Add TypeTrackingNode
Splits `ModuleVariableNode` away from `LocalSourceNode`, instead
creating a class `TypeTrackingNode` that encapsulates both of these.

This means we no longer have module variable nodes as part of
`LocalSourceNode` (which is good, since they have no "local" aspect to
them), and hence we can have `LocalSourceNode` inherit directly from
`ExprNode` (which makes the API a bit nicer).

Unfortunately these are breaking changes, so we can't actually fulfil
the above two desiderata until the `track` and `backtrack` methods on
`LocalSourceNode` have been fully deprecated. For this reason, we
preserve the present implementation of `LocalSourceNode`, and instead
lay the foundation for switching over in the future, by deprecating
`track` and `backtrack` on `LocalSourceNode`.
2021-07-02 18:00:33 +00:00
..
2021-07-02 18:00:33 +00:00

This test illustrates that you need to be very careful when adding additional taint-steps or dataflow steps using TypeTracker.

The basic setup is that we're modeling the behavior of a (fictitious) external library class MyClass, and (fictitious) source of such an instance (the source function).

class MyClass:
    def __init__(self, value):
        self.value = value

    def get_value(self):
        return self.value

We want to extend our analysis to obj.get_value() is also tainted if obj is a tainted instance of MyClass.

The actual type-tracking is done in SharedCode.qll, but it's the way we use it that matters.

In NaiveModel.ql we add an additional taint step from an instance of MyClass to calls of the bound method get_value (that we have tracked). It provides us with the correct results, but the path explanations are not very useful, since we are now able to cross functions in one step.

In ProperModel.ql we split the additional taint step in two:

  1. from tracked obj that is instance of MyClass, to obj.get_value but only exactly where the attribute is accessed (by an AttrNode). This is important, since if we allowed <any tracked qualifier>.get_value we would again be able to cross functions in one step.
  2. from tracked get_value bound method to calls of it, but only exactly where the call is (by a CallNode). for same reason as above.

Try running the queries in VS Code to see the difference

Possible improvements

Using AttrNode directly in the code here means there is no easy way to add getattr support too all such predicates. Not really sure how to handle this in a generalized way though :|