python-taint
Advanced tools
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
+2
-2
| Metadata-Version: 1.1 | ||
| Name: python-taint | ||
| Version: 0.40 | ||
| Version: 0.41 | ||
| Summary: Find security vulnerabilities in Python web applications using static analysis. | ||
@@ -9,3 +9,3 @@ Home-page: https://github.com/python-security/pyt | ||
| License: GPLv2 | ||
| Download-URL: https://github.com/python-security/pyt/archive/0.40.tar.gz | ||
| Download-URL: https://github.com/python-security/pyt/archive/0.41.tar.gz | ||
| Description: Check out PyT on `GitHub <https://github.com/python-security/pyt>`_! | ||
@@ -12,0 +12,0 @@ Keywords: security,vulnerability,web,flask,django,static-analysis,program-analysis |
@@ -34,2 +34,9 @@ from collections import namedtuple | ||
| ) | ||
| MUTATORS = ( # list.append(x) taints list if x is tainted | ||
| 'add', | ||
| 'append', | ||
| 'extend', | ||
| 'insert', | ||
| 'update', | ||
| ) | ||
@@ -36,0 +43,0 @@ |
@@ -24,2 +24,3 @@ import ast | ||
| BUILTINS, | ||
| MUTATORS, | ||
| return_connection_handler, | ||
@@ -63,2 +64,3 @@ SavedVariable | ||
| self.last_control_flow_nodes = list() | ||
| self._within_mutating_call = False | ||
@@ -583,2 +585,19 @@ # Are we already in a module? | ||
| 'ClassDef, cannot add the function ') | ||
| elif ( | ||
| not self._within_mutating_call and | ||
| last_attribute in MUTATORS | ||
| and isinstance(node.func, ast.Attribute) | ||
| ): | ||
| # Change list.append(x) ---> list += list.append(x) | ||
| # This does in fact propagate as we don't know that append returns None | ||
| fake_aug_assign = ast.AugAssign( | ||
| target=node.func.value, | ||
| op=ast.Add, | ||
| value=node, | ||
| ) | ||
| ast.copy_location(fake_aug_assign, node) | ||
| self._within_mutating_call = True # Don't do this recursively | ||
| result = self.visit(fake_aug_assign) | ||
| self._within_mutating_call = False | ||
| return result | ||
| elif last_attribute not in BUILTINS: | ||
@@ -585,0 +604,0 @@ # Mark the call as a blackbox because we don't have the definition |
@@ -67,3 +67,78 @@ import ast | ||
| class PytTransformer(AsyncTransformer, ChainedFunctionTransformer, ast.NodeTransformer): | ||
| class IfExpRewriter(ast.NodeTransformer): | ||
| """Splits IfExp ternary expressions containing complex tests into multiple statements | ||
| Will change | ||
| a if b(c) else d | ||
| into | ||
| a if __if_exp_0 else d | ||
| with Assign nodes in assignments [__if_exp_0 = b(c)] | ||
| """ | ||
| def __init__(self, starting_index=0): | ||
| self._temporary_variable_index = starting_index | ||
| self.assignments = [] | ||
| super().__init__() | ||
| def visit_IfExp(self, node): | ||
| if isinstance(node.test, (ast.Name, ast.Attribute)): | ||
| return self.generic_visit(node) | ||
| else: | ||
| temp_var_id = '__if_exp_{}'.format(self._temporary_variable_index) | ||
| self._temporary_variable_index += 1 | ||
| assignment_of_test = ast.Assign( | ||
| targets=[ast.Name(id=temp_var_id, ctx=ast.Store())], | ||
| value=self.visit(node.test), | ||
| ) | ||
| ast.copy_location(assignment_of_test, node) | ||
| self.assignments.append(assignment_of_test) | ||
| transformed_if_exp = ast.IfExp( | ||
| test=ast.Name(id=temp_var_id, ctx=ast.Load()), | ||
| body=self.visit(node.body), | ||
| orelse=self.visit(node.orelse), | ||
| ) | ||
| ast.copy_location(transformed_if_exp, node) | ||
| return transformed_if_exp | ||
| def visit_FunctionDef(self, node): | ||
| return node | ||
| class IfExpTransformer: | ||
| """Goes through module and function bodies, adding extra Assign nodes due to IfExp expressions.""" | ||
| def visit_body(self, nodes): | ||
| new_nodes = [] | ||
| count = 0 | ||
| for node in nodes: | ||
| rewriter = IfExpRewriter(count) | ||
| possibly_transformed_node = rewriter.visit(node) | ||
| if rewriter.assignments: | ||
| new_nodes.extend(rewriter.assignments) | ||
| count += len(rewriter.assignments) | ||
| new_nodes.append(possibly_transformed_node) | ||
| return new_nodes | ||
| def visit_FunctionDef(self, node): | ||
| transformed = ast.FunctionDef( | ||
| name=node.name, | ||
| args=node.args, | ||
| body=self.visit_body(node.body), | ||
| decorator_list=node.decorator_list, | ||
| returns=node.returns | ||
| ) | ||
| ast.copy_location(transformed, node) | ||
| return self.generic_visit(transformed) | ||
| def visit_Module(self, node): | ||
| transformed = ast.Module(self.visit_body(node.body)) | ||
| ast.copy_location(transformed, node) | ||
| return self.generic_visit(transformed) | ||
| class PytTransformer(AsyncTransformer, IfExpTransformer, ChainedFunctionTransformer, ast.NodeTransformer): | ||
| pass |
@@ -327,1 +327,10 @@ import ast | ||
| self.visit(node.value) | ||
| def visit_IfExp(self, node): | ||
| self.result += '(' | ||
| self.visit(node.test) | ||
| self.result += ') ? (' | ||
| self.visit(node.body) | ||
| self.result += ') : (' | ||
| self.visit(node.orelse) | ||
| self.result += ')' |
@@ -25,2 +25,7 @@ """Contains a class that finds all names. | ||
| def visit_IfExp(self, node): | ||
| # The test doesn't taint the assignment | ||
| self.visit(node.body) | ||
| self.visit(node.orelse) | ||
| @classmethod | ||
@@ -27,0 +32,0 @@ def result_for_node(cls, node): |
@@ -19,5 +19,5 @@ import json | ||
| self, trigger, *, | ||
| unlisted_args_propagate=True, unlisted_kwargs_propagate=True, | ||
| arg_list=None, kwarg_list=None, | ||
| sanitisers=None | ||
| unlisted_args_propagate=True, | ||
| arg_dict=None, | ||
| sanitisers=None, | ||
| ): | ||
@@ -27,22 +27,27 @@ self._trigger = trigger | ||
| self.arg_list_propagates = not unlisted_args_propagate | ||
| self.kwarg_list_propagates = not unlisted_kwargs_propagate | ||
| if trigger[-1] != '(': | ||
| if self.arg_list_propagates or self.kwarg_list_propagates or arg_list or kwarg_list: | ||
| if self.arg_list_propagates or arg_dict: | ||
| raise ValueError("Propagation options specified, but trigger word isn't a function call") | ||
| self.arg_list = set(arg_list or ()) | ||
| self.kwarg_list = set(kwarg_list or ()) | ||
| arg_dict = {} if arg_dict is None else arg_dict | ||
| self.arg_position_to_kwarg = { | ||
| position: name for name, position in arg_dict.items() if position is not None | ||
| } | ||
| self.kwarg_list = set(arg_dict.keys()) | ||
| def arg_propagates(self, index): | ||
| in_list = index in self.arg_list | ||
| return self.arg_list_propagates == in_list | ||
| kwarg = self.get_kwarg_from_position(index) | ||
| return self.kwarg_propagates(kwarg) | ||
| def kwarg_propagates(self, keyword): | ||
| in_list = keyword in self.kwarg_list | ||
| return self.kwarg_list_propagates == in_list | ||
| return self.arg_list_propagates == in_list | ||
| def get_kwarg_from_position(self, index): | ||
| return self.arg_position_to_kwarg.get(index) | ||
| @property | ||
| def all_arguments_propagate_taint(self): | ||
| if self.arg_list or self.kwarg_list: | ||
| if self.kwarg_list: | ||
| return False | ||
@@ -49,0 +54,0 @@ return True |
@@ -246,16 +246,13 @@ """Module for finding vulnerabilities based on a definitions file.""" | ||
| sink_args = [] | ||
| kwargs_present = set() | ||
| for i, vars in enumerate(sink_args_with_positions.args): | ||
| if sink.trigger.arg_propagates(i): | ||
| kwarg = sink.trigger.get_kwarg_from_position(i) | ||
| if kwarg: | ||
| kwargs_present.add(kwarg) | ||
| if sink.trigger.kwarg_propagates(kwarg): | ||
| sink_args.extend(vars) | ||
| if ( | ||
| # Either any unspecified arg propagates | ||
| not sink.trigger.arg_list_propagates or | ||
| # or there are some propagating args which weren't passed positionally | ||
| any(1 for position in sink.trigger.arg_list if position >= len(sink_args_with_positions.args)) | ||
| ): | ||
| sink_args.extend(sink_args_with_positions.unknown_args) | ||
| for keyword, vars in sink_args_with_positions.kwargs.items(): | ||
| kwargs_present.add(keyword) | ||
| if sink.trigger.kwarg_propagates(keyword): | ||
@@ -266,6 +263,7 @@ sink_args.extend(vars) | ||
| # Either any unspecified kwarg propagates | ||
| not sink.trigger.kwarg_list_propagates or | ||
| not sink.trigger.arg_list_propagates or | ||
| # or there are some propagating kwargs which have not been passed by keyword | ||
| sink.trigger.kwarg_list - set(sink_args_with_positions.kwargs.keys()) | ||
| sink.trigger.kwarg_list - kwargs_present | ||
| ): | ||
| sink_args.extend(sink_args_with_positions.unknown_args) | ||
| sink_args.extend(sink_args_with_positions.unknown_kwargs) | ||
@@ -272,0 +270,0 @@ |
| Metadata-Version: 1.1 | ||
| Name: python-taint | ||
| Version: 0.40 | ||
| Version: 0.41 | ||
| Summary: Find security vulnerabilities in Python web applications using static analysis. | ||
@@ -9,3 +9,3 @@ Home-page: https://github.com/python-security/pyt | ||
| License: GPLv2 | ||
| Download-URL: https://github.com/python-security/pyt/archive/0.40.tar.gz | ||
| Download-URL: https://github.com/python-security/pyt/archive/0.41.tar.gz | ||
| Description: Check out PyT on `GitHub <https://github.com/python-security/pyt>`_! | ||
@@ -12,0 +12,0 @@ Keywords: security,vulnerability,web,flask,django,static-analysis,program-analysis |
@@ -40,9 +40,24 @@ MANIFEST.in | ||
| pyt/vulnerabilities/vulnerability_helper.py | ||
| pyt/vulnerability_definitions/.all_trigger_words.pyt.un~ | ||
| pyt/vulnerability_definitions/.blackbox_mapping.json.un~ | ||
| pyt/vulnerability_definitions/.django_trigger_words.pyt.un~ | ||
| pyt/vulnerability_definitions/.flask_trigger_words.json.un~ | ||
| pyt/vulnerability_definitions/.flask_trigger_words.pyt.un~ | ||
| pyt/vulnerability_definitions/.test_positions.pyt.un~ | ||
| pyt/vulnerability_definitions/.test_triggers.pyt.un~ | ||
| pyt/vulnerability_definitions/README.rst | ||
| pyt/vulnerability_definitions/all_trigger_words.pyt | ||
| pyt/vulnerability_definitions/all_trigger_words.pyt~ | ||
| pyt/vulnerability_definitions/blackbox_mapping.json | ||
| pyt/vulnerability_definitions/blackbox_mapping.json~ | ||
| pyt/vulnerability_definitions/django_trigger_words.pyt | ||
| pyt/vulnerability_definitions/django_trigger_words.pyt~ | ||
| pyt/vulnerability_definitions/flask_trigger_words.json~ | ||
| pyt/vulnerability_definitions/flask_trigger_words.pyt | ||
| pyt/vulnerability_definitions/flask_trigger_words.pyt~ | ||
| pyt/vulnerability_definitions/test_positions.pyt | ||
| pyt/vulnerability_definitions/test_positions.pyt~ | ||
| pyt/vulnerability_definitions/test_sqli_triggers.pyt~ | ||
| pyt/vulnerability_definitions/test_triggers.pyt | ||
| pyt/vulnerability_definitions/test_triggers.pyt~ | ||
| pyt/web_frameworks/__init__.py | ||
@@ -49,0 +64,0 @@ pyt/web_frameworks/framework_adaptor.py |
+2
-2
@@ -5,5 +5,5 @@ [metadata] | ||
| [egg_info] | ||
| tag_build = | ||
| tag_date = 0 | ||
| tag_svn_revision = 0 | ||
| tag_date = 0 | ||
| tag_build = | ||
+1
-1
@@ -5,3 +5,3 @@ from setuptools import find_packages | ||
| VERSION = '0.40' | ||
| VERSION = '0.41' | ||
@@ -8,0 +8,0 @@ |
Sorry, the diff of this file is not supported yet
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
221026
25.06%70
27.27%4280
2.42%