Skip to content

Python: more additional taint steps#4149

Merged
yoff merged 24 commits into
github:mainfrom
RasmusWL:python-more-additional-taint-steps
Sep 1, 2020
Merged

Python: more additional taint steps#4149
yoff merged 24 commits into
github:mainfrom
RasmusWL:python-more-additional-taint-steps

Conversation

@RasmusWL
Copy link
Copy Markdown
Member

The taint steps defined here are fairly rough, and are only using syntactical matching. The idea is that this should be good enough as a first step, and then we can focus on more interesting things such as using TypeTracking 😉

This implementation is very generous in handing out taint. As in #4124 we don't make any attempt to limit taint propagation based on types, so since we want <tainted dict>.get(<key>) to propagate taint, we also propagate taint for <any tainted obj>.get(). I'm not too sure whether this turns out to be a problem or not.

The one thing I need want to fix is the very syntactical approach to matching library calls. This is blocked on having a proper replacement for Value::named("json.loads").getACall() (so we can also recognize from json import loads; loads(tainted_string)).

I took the bits from ql/test/library-tests/taint/ that seemed easy to port. I
left out namedtuple for now, but it is part of internal tracking ticket, so
won't be forgotten.
This makes it easier to add a new test-case, and makes it easier to work with
the existing files. It does have a downside on making it a bit more annoying
looking at TestTaint.expected, and possible longer runtime, but I think it's
still worth it.
deepcopy was already handled somehow, don't really know how :D
since the files was called `collection`, that conflicted with import system :|
Apprently it just works 😕 :magic:
@RasmusWL RasmusWL requested a review from a team as a code owner August 27, 2020 09:39
We got into problems since using `string.py` would shadow the string module from
the standard library. By some reason I adopted a pattern of `_` as suffix, but
let us just use the standard pattern of `test_` prefix like a normal testing
framework like pytest does.
@RasmusWL RasmusWL marked this pull request as draft August 31, 2020 12:12
@RasmusWL
Copy link
Copy Markdown
Member Author

should update now that #4161 has been merged

@RasmusWL
Copy link
Copy Markdown
Member Author

@yoff no changes after merging in #4161 -- looking over the PR, it looks like there was no test of flow from a PostUpdateNote. So we need some more work on PostUpdateNote, but it's not blocking this PR as I see it.

@RasmusWL RasmusWL marked this pull request as ready for review August 31, 2020 13:09
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.

It is currently a little inconsistent if there are comments in the test code when the result is an expected failure.

Comment thread python/ql/src/experimental/dataflow/internal/TaintTrackingPrivate.qll Outdated
Comment thread python/ql/src/experimental/dataflow/internal/TaintTrackingPrivate.qll Outdated
)

# For Python2, need to convert to unicode for StringIO to work
tainted_filelike = StringIO(unicode(json.dumps(ts)))
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.

Is StringIO implemented?

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.

StringIO is not modeled currently.

def contrived_2():
# A contrived example. Don't know why anyone would ever actually do this.

# We currently only handle taint nested 2 levels.
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.

This test passes, should it fail by the comment? (does the code represent 2 or 3 nested levels?)

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.

this is 3 levels [[[

@RasmusWL
Copy link
Copy Markdown
Member Author

RasmusWL commented Sep 1, 2020

@yoff thanks for thorough review. I see that I forgot to update a lot of the comments for the tests based on the new implementataion 😬 but I fixed all the cases you pointed out now 👍

@RasmusWL RasmusWL requested a review from yoff September 1, 2020 10:03
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.

All changes look good, thanks for also adding the extra test 👍

Copy link
Copy Markdown
Contributor

@tausbn tausbn left a comment

Choose a reason for hiding this comment

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

Generally this looks good to me, but I have a few comments about how the code is structured.

or
// constructor call
exists(CallNode call | call = nodeTo.asCfgNode() |
call.getFunction().(NameNode).getId() in ["list", "set", "frozenset", "dict", "defaultdict",
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.

I wonder if this code would be cleaner if all the call.getFunction().(NameNode).getId() = ... and call.getFunction().(AttrNode).getObject(name) stuff was factored out into helper predicates (likely_builtin and likely_method, perhaps?). As it is right now, it gets sort of overwhelming to see the same construction over and over.

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.

Thea idea was that all these matches on call.getFunction().(NameNode).getId() are temporary until we have a proper replacement for Value::named(). It's not something I'm too concerned about fixing up. If it's something you feel very strongly about, I can give it a try 👍

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.

Nah, I'm in the lower half of the sliding scale, given the context on this one. If we start adding a bunch more cases like these (pre-replacement), I definitely think we should factor it out, if nothing else to make the replacement easier to implement.

)
or
// list.append, set.add
exists(CallNode call, string name |
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.

Since all of these disjuncts introduce a CallNode I feel a strong urge to suggest that the exists be pulled out and combined across all disjuncts...

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.

Personally I don't find it too problematic, so how strong is your strong urge? 😊

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.

Not that strong. 🙂

@yoff yoff merged commit caa680c into github:main Sep 1, 2020
@RasmusWL RasmusWL deleted the python-more-additional-taint-steps branch September 1, 2020 15:02
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