From 4cd3ea596e2ef0c834e206938af6963bbd08130d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20G=C3=BCnal?= Date: Sun, 22 Apr 2018 13:51:49 +0300 Subject: [PATCH 01/22] nosec_lines for #108's issue Added args and nosec_lines --- pyt/__main__.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/pyt/__main__.py b/pyt/__main__.py index 4ceb67d1..fd2ba223 100644 --- a/pyt/__main__.py +++ b/pyt/__main__.py @@ -142,6 +142,9 @@ def parse_args(args): '(only JSON-formatted files are accepted)', type=str, default=False) + parser.add_argument('-in', '--ignore-nosec', + help='Ignoring nosec commands', + action='store_true') save_parser = subparsers.add_parser('save', help='Save menu.') save_parser.set_defaults(which='save') @@ -307,6 +310,17 @@ def main(command_line_args=sys.argv[1:]): args.trigger_word_file ) ) + + if args.ignore_nosec: + nosec_lines = set() + else: + file = open(path, "r") + lines = file.readlines() + nosec_lines = set( + lineno for + (lineno, line) in enumerate(lines, start=1) + if '#nosec' in line or '# nosec' in line) + if args.baseline: vulnerabilities = get_vulnerabilities_not_in_baseline(vulnerabilities, args.baseline) From ae9e8e94de6f567aeef8beffcc5efb2e5bfdb136 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20G=C3=BCnal?= Date: Mon, 23 Apr 2018 12:33:58 +0300 Subject: [PATCH 02/22] nosec_lines --- pyt/__main__.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/pyt/__main__.py b/pyt/__main__.py index fd2ba223..9b197906 100644 --- a/pyt/__main__.py +++ b/pyt/__main__.py @@ -301,6 +301,7 @@ def main(command_line_args=sys.argv[1:]): analyse(cfg_list, analysis_type=analysis) + nosec_lines = set() vulnerabilities = find_vulnerabilities( cfg_list, analysis, @@ -308,7 +309,8 @@ def main(command_line_args=sys.argv[1:]): VulnerabilityFiles( args.blackbox_mapping_file, args.trigger_word_file - ) + ), + nosec_lines ) if args.ignore_nosec: @@ -319,7 +321,17 @@ def main(command_line_args=sys.argv[1:]): nosec_lines = set( lineno for (lineno, line) in enumerate(lines, start=1) - if '#nosec' in line or '# nosec' in line) + if '#nosec' in line or '# nosec' in line) + vulnerabilities = find_vulnerabilities( + cfg_list, + analysis, + ui_mode, + VulnerabilityFiles( + args.blackbox_mapping_file, + args.trigger_word_file + ), + nosec_lines + ) if args.baseline: vulnerabilities = get_vulnerabilities_not_in_baseline(vulnerabilities, args.baseline) From 1735db7be5d600fdc85af9d08bc5b03cb1edac17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20G=C3=BCnal?= Date: Mon, 23 Apr 2018 12:37:20 +0300 Subject: [PATCH 03/22] added nosec_lines parameter --- pyt/vulnerabilities.py | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/pyt/vulnerabilities.py b/pyt/vulnerabilities.py index ba7debff..8d76c040 100644 --- a/pyt/vulnerabilities.py +++ b/pyt/vulnerabilities.py @@ -73,7 +73,8 @@ def identify_triggers( cfg, sources, sinks, - lattice + lattice, + nosec_lines ): """Identify sources, sinks and sanitisers in a CFG. @@ -89,12 +90,12 @@ def identify_triggers( tainted_nodes = filter_cfg_nodes(cfg, TaintedNode) tainted_trigger_nodes = [TriggerNode('Framework function URL parameter', None, node) for node in tainted_nodes] - sources_in_file = find_triggers(assignment_nodes, sources) + sources_in_file = find_triggers(assignment_nodes, sources, nosec_lines) sources_in_file.extend(tainted_trigger_nodes) find_secondary_sources(assignment_nodes, sources_in_file, lattice) - sinks_in_file = find_triggers(cfg.nodes, sinks) + sinks_in_file = find_triggers(cfg.nodes, sinks, nosec_lines) sanitiser_node_dict = build_sanitiser_node_dict(cfg, sinks_in_file) @@ -170,7 +171,8 @@ def append_node_if_reassigned( def find_triggers( nodes, - trigger_words + trigger_words, + nosec_lines ): """Find triggers from the trigger_word_list in the nodes. @@ -183,7 +185,11 @@ def find_triggers( """ trigger_nodes = list() for node in nodes: - trigger_nodes.extend(iter(label_contains(node, trigger_words))) + if node.line_number not in nosec_lines: + trigger_nodes.extend(iter(label_contains(node, trigger_words))) + else: + pass + return trigger_nodes @@ -466,7 +472,8 @@ def find_vulnerabilities_in_cfg( lattice, ui_mode, blackbox_mapping, - vulnerabilities_list + vulnerabilities_list, + nosec_lines ): """Find vulnerabilities in a cfg. @@ -482,7 +489,8 @@ def find_vulnerabilities_in_cfg( cfg, definitions.sources, definitions.sinks, - lattice + lattice, + nosec_lines ) for sink in triggers.sinks: for source in triggers.sources: @@ -503,7 +511,8 @@ def find_vulnerabilities( cfg_list, analysis_type, ui_mode, - vulnerability_files + vulnerability_files, + nosec_lines ): """Find vulnerabilities in a list of CFGs from a trigger_word_file. @@ -518,19 +527,19 @@ def find_vulnerabilities( """ vulnerabilities = list() definitions = parse(vulnerability_files.triggers) - with open(vulnerability_files.blackbox_mapping) as infile: blackbox_mapping = json.load(infile) for cfg in cfg_list: + find_vulnerabilities_in_cfg( cfg, definitions, Lattice(cfg.nodes, analysis_type), ui_mode, blackbox_mapping, - vulnerabilities + vulnerabilities, + nosec_lines ) with open(vulnerability_files.blackbox_mapping, 'w') as outfile: json.dump(blackbox_mapping, outfile, indent=4) - return vulnerabilities From fb88051e1d988d5890127ef3aa6867adf0db07de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20G=C3=BCnal?= Date: Mon, 23 Apr 2018 12:38:13 +0300 Subject: [PATCH 04/22] Update vulnerabilities.py --- pyt/vulnerabilities.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pyt/vulnerabilities.py b/pyt/vulnerabilities.py index 8d76c040..5e9bd675 100644 --- a/pyt/vulnerabilities.py +++ b/pyt/vulnerabilities.py @@ -189,7 +189,6 @@ def find_triggers( trigger_nodes.extend(iter(label_contains(node, trigger_words))) else: pass - return trigger_nodes From d0e8ef0cfbb855ba44b359c11e1cc4fe7924fd26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20G=C3=BCnal?= Date: Wed, 25 Apr 2018 00:21:54 +0300 Subject: [PATCH 05/22] Code edited edited argument, moved code place --- pyt/__main__.py | 50 ++++++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/pyt/__main__.py b/pyt/__main__.py index 9b197906..edb73b2b 100644 --- a/pyt/__main__.py +++ b/pyt/__main__.py @@ -142,9 +142,9 @@ def parse_args(args): '(only JSON-formatted files are accepted)', type=str, default=False) - parser.add_argument('-in', '--ignore-nosec', - help='Ignoring nosec commands', - action='store_true') + parser.add_argument('--ignore-nosec', dest='ignore_nosec', action='store_true', + help='do not skip lines with # nosec comments' + ) save_parser = subparsers.add_parser('save', help='Save menu.') save_parser.set_defaults(which='save') @@ -238,7 +238,28 @@ def main(command_line_args=sys.argv[1:]): elif args.trim_reassigned_in: ui_mode = UImode.TRIM + path = os.path.normpath(args.filepath) cfg_list = list() + if args.ignore_nosec: + nosec_lines = set() + else: + file = open(path, "r") + lines = file.readlines() + nosec_lines = set( + lineno for + (lineno, line) in enumerate(lines, start=1) + if '#nosec' in line or '# nosec' in line) + vulnerabilities = find_vulnerabilities( + cfg_list, + analysis, + ui_mode, + VulnerabilityFiles( + args.blackbox_mapping_file, + args.trigger_word_file + ), + nosec_lines + ) + if args.git_repos: repos = get_repos(args.git_repos) for repo in repos: @@ -266,8 +287,6 @@ def main(command_line_args=sys.argv[1:]): ) exit() - path = os.path.normpath(args.filepath) - directory = None if args.project_root: directory = os.path.normpath(args.project_root) @@ -301,7 +320,6 @@ def main(command_line_args=sys.argv[1:]): analyse(cfg_list, analysis_type=analysis) - nosec_lines = set() vulnerabilities = find_vulnerabilities( cfg_list, analysis, @@ -313,26 +331,6 @@ def main(command_line_args=sys.argv[1:]): nosec_lines ) - if args.ignore_nosec: - nosec_lines = set() - else: - file = open(path, "r") - lines = file.readlines() - nosec_lines = set( - lineno for - (lineno, line) in enumerate(lines, start=1) - if '#nosec' in line or '# nosec' in line) - vulnerabilities = find_vulnerabilities( - cfg_list, - analysis, - ui_mode, - VulnerabilityFiles( - args.blackbox_mapping_file, - args.trigger_word_file - ), - nosec_lines - ) - if args.baseline: vulnerabilities = get_vulnerabilities_not_in_baseline(vulnerabilities, args.baseline) From 8569aa467a6524f3ae11091c2783d3a9f8249f8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20G=C3=BCnal?= Date: Wed, 25 Apr 2018 00:24:20 +0300 Subject: [PATCH 06/22] unnecessary codes deleted --- pyt/vulnerabilities.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/pyt/vulnerabilities.py b/pyt/vulnerabilities.py index 5e9bd675..eeb9404a 100644 --- a/pyt/vulnerabilities.py +++ b/pyt/vulnerabilities.py @@ -187,8 +187,6 @@ def find_triggers( for node in nodes: if node.line_number not in nosec_lines: trigger_nodes.extend(iter(label_contains(node, trigger_words))) - else: - pass return trigger_nodes @@ -529,7 +527,6 @@ def find_vulnerabilities( with open(vulnerability_files.blackbox_mapping) as infile: blackbox_mapping = json.load(infile) for cfg in cfg_list: - find_vulnerabilities_in_cfg( cfg, definitions, From e8cddd06515bb24b46c8d28636cafe8a43e1f885 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20G=C3=BCnal?= Date: Thu, 26 Apr 2018 01:12:43 +0300 Subject: [PATCH 07/22] passed nosec_lines on analyse_repo unnecessary codes deleted, passed nosec_lines on analyse_repo --- pyt/__main__.py | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/pyt/__main__.py b/pyt/__main__.py index edb73b2b..8ce6cc67 100644 --- a/pyt/__main__.py +++ b/pyt/__main__.py @@ -195,7 +195,7 @@ def parse_args(args): return parser.parse_args(args) -def analyse_repo(args, github_repo, analysis_type, ui_mode): +def analyse_repo(args, github_repo, analysis_type, ui_mode, nosec_lines): cfg_list = list() directory = os.path.dirname(github_repo.path) project_modules = get_modules(directory) @@ -218,7 +218,8 @@ def analyse_repo(args, github_repo, analysis_type, ui_mode): VulnerabilityFiles( args.blackbox_mapping_file, args.trigger_word_file - ) + ), + nosec_lines ) return vulnerabilities @@ -248,23 +249,13 @@ def main(command_line_args=sys.argv[1:]): nosec_lines = set( lineno for (lineno, line) in enumerate(lines, start=1) - if '#nosec' in line or '# nosec' in line) - vulnerabilities = find_vulnerabilities( - cfg_list, - analysis, - ui_mode, - VulnerabilityFiles( - args.blackbox_mapping_file, - args.trigger_word_file - ), - nosec_lines - ) + if '#nosec' in line or '# nosec' in line) if args.git_repos: repos = get_repos(args.git_repos) for repo in repos: repo.clone() - vulnerabilities = analyse_repo(args, repo, analysis, ui_mode) + vulnerabilities = analyse_repo(args, repo, analysis, ui_mode, nosec_lines) if args.json: json.report(vulnerabilities, sys.stdout) else: From 3d5867b49f88698430318e79e88803bf14ba234c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20G=C3=BCnal?= Date: Thu, 26 Apr 2018 18:40:20 +0300 Subject: [PATCH 08/22] Added nosec_lines Added empty nosec_lines for tests --- tests/vulnerabilities_test.py | 290 +++++++++++++++------------------- 1 file changed, 130 insertions(+), 160 deletions(-) diff --git a/tests/vulnerabilities_test.py b/tests/vulnerabilities_test.py index f3d77279..7ec1ae1f 100644 --- a/tests/vulnerabilities_test.py +++ b/tests/vulnerabilities_test.py @@ -15,15 +15,14 @@ from pyt.constraint_table import initialize_constraint_table from pyt.fixed_point import analyse from pyt.framework_adaptor import FrameworkAdaptor -from pyt.framework_helper import ( +from pyt.framework_helper import( is_django_view_function, - is_flask_route_function, - is_function + is_flask_route_function ) from pyt.node_types import Node from pyt.reaching_definitions_taint import ReachingDefinitionsTaintAnalysis - +nosec_lines = set() class EngineTest(BaseTestCase): def run_empty(self): return @@ -84,7 +83,7 @@ def test_label_contains(self): self.assert_length(l, expected_length=2) def test_find_triggers(self): - self.cfg_create_from_file('examples/vulnerable_code/XSS.py') + self.cfg_create_from_file('example/vulnerable_code/XSS.py') cfg_list = [self.cfg] @@ -93,27 +92,29 @@ def test_find_triggers(self): XSS1 = cfg_list[1] trigger_words = [('get', [])] - l = vulnerabilities.find_triggers(XSS1.nodes, trigger_words) + l = vulnerabilities.find_triggers(XSS1.nodes, trigger_words, nosec_lines) self.assert_length(l, expected_length=1) + def test_find_sanitiser_nodes(self): cfg_node = Node(None, None, line_number=None, path=None) - sanitiser_tuple = vulnerabilities.Sanitiser('escape', cfg_node) + sanitiser_tuple = vulnerabilities.Sanitiser('escape', cfg_node) sanitiser = 'escape' result = list(vulnerabilities.find_sanitiser_nodes(sanitiser, [sanitiser_tuple])) self.assert_length(result, expected_length=1) self.assertEqual(result[0], cfg_node) + def test_build_sanitiser_node_dict(self): - self.cfg_create_from_file('examples/vulnerable_code/XSS_sanitised.py') + self.cfg_create_from_file('example/vulnerable_code/XSS_sanitised.py') cfg_list = [self.cfg] FrameworkAdaptor(cfg_list, [], [], is_flask_route_function) cfg = cfg_list[1] - cfg_node = Node(None, None, line_number=None, path=None) + cfg_node = Node(None, None, line_number=None, path=None) sinks_in_file = [vulnerabilities.TriggerNode('replace', ['escape'], cfg_node)] sanitiser_dict = vulnerabilities.build_sanitiser_node_dict(cfg, sinks_in_file) @@ -124,6 +125,7 @@ def test_build_sanitiser_node_dict(self): def run_analysis(self, path): self.cfg_create_from_file(path) + cfg_list = [self.cfg] FrameworkAdaptor(cfg_list, [], [], is_flask_route_function) @@ -138,39 +140,40 @@ def run_analysis(self, path): VulnerabilityFiles( default_blackbox_mapping_file, default_trigger_word_file - ) + ), + nosec_lines ) def test_find_vulnerabilities_assign_other_var(self): - vulnerabilities = self.run_analysis('examples/vulnerable_code/XSS_assign_to_other_var.py') + vulnerabilities = self.run_analysis('example/vulnerable_code/XSS_assign_to_other_var.py') self.assert_length(vulnerabilities, expected_length=1) def test_find_vulnerabilities_inter_command_injection(self): - vulnerabilities = self.run_analysis('examples/vulnerable_code/inter_command_injection.py') + vulnerabilities = self.run_analysis('example/vulnerable_code/inter_command_injection.py') self.assert_length(vulnerabilities, expected_length=1) def test_find_vulnerabilities_inter_command_injection_2(self): - vulnerabilities = self.run_analysis('examples/vulnerable_code/inter_command_injection_2.py') + vulnerabilities = self.run_analysis('example/vulnerable_code/inter_command_injection_2.py') self.assert_length(vulnerabilities, expected_length=1) def test_XSS_result(self): - vulnerabilities = self.run_analysis('examples/vulnerable_code/XSS.py') + vulnerabilities = self.run_analysis('example/vulnerable_code/XSS.py') self.assert_length(vulnerabilities, expected_length=1) vulnerability_description = str(vulnerabilities[0]) EXPECTED_VULNERABILITY_DESCRIPTION = """ - File: examples/vulnerable_code/XSS.py + File: example/vulnerable_code/XSS.py > User input at line 6, trigger word "request.args.get(": ~call_1 = ret_request.args.get('param', 'not set') Reassigned in: - File: examples/vulnerable_code/XSS.py + File: example/vulnerable_code/XSS.py > Line 6: param = ~call_1 - File: examples/vulnerable_code/XSS.py + File: example/vulnerable_code/XSS.py > Line 9: ~call_3 = ret_make_response(~call_4) - File: examples/vulnerable_code/XSS.py + File: example/vulnerable_code/XSS.py > Line 9: resp = ~call_3 - File: examples/vulnerable_code/XSS.py + File: example/vulnerable_code/XSS.py > Line 10: ret_XSS1 = resp - File: examples/vulnerable_code/XSS.py + File: example/vulnerable_code/XSS.py > reaches line 9, trigger word "replace(": ~call_4 = ret_html.replace('{{ param }}', param) """ @@ -178,17 +181,17 @@ def test_XSS_result(self): self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION)) def test_command_injection_result(self): - vulnerabilities = self.run_analysis('examples/vulnerable_code/command_injection.py') + vulnerabilities = self.run_analysis('example/vulnerable_code/command_injection.py') self.assert_length(vulnerabilities, expected_length=1) vulnerability_description = str(vulnerabilities[0]) EXPECTED_VULNERABILITY_DESCRIPTION = """ - File: examples/vulnerable_code/command_injection.py + File: example/vulnerable_code/command_injection.py > User input at line 15, trigger word "form[": param = request.form['suggestion'] Reassigned in: - File: examples/vulnerable_code/command_injection.py + File: example/vulnerable_code/command_injection.py > Line 16: command = 'echo ' + param + ' >> ' + 'menu.txt' - File: examples/vulnerable_code/command_injection.py + File: example/vulnerable_code/command_injection.py > reaches line 18, trigger word "subprocess.call(": ~call_1 = ret_subprocess.call(command, shell=True) """ @@ -196,37 +199,37 @@ def test_command_injection_result(self): self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION)) def test_path_traversal_result(self): - vulnerabilities = self.run_analysis('examples/vulnerable_code/path_traversal.py') + vulnerabilities = self.run_analysis('example/vulnerable_code/path_traversal.py') self.assert_length(vulnerabilities, expected_length=1) vulnerability_description = str(vulnerabilities[0]) EXPECTED_VULNERABILITY_DESCRIPTION = """ - File: examples/vulnerable_code/path_traversal.py + File: example/vulnerable_code/path_traversal.py > User input at line 15, trigger word "request.args.get(": ~call_1 = ret_request.args.get('image_name') Reassigned in: - File: examples/vulnerable_code/path_traversal.py + File: example/vulnerable_code/path_traversal.py > Line 15: image_name = ~call_1 - File: examples/vulnerable_code/path_traversal.py + File: example/vulnerable_code/path_traversal.py > Line 6: save_2_image_name = image_name - File: examples/vulnerable_code/path_traversal.py + File: example/vulnerable_code/path_traversal.py > Line 10: save_3_image_name = image_name - File: examples/vulnerable_code/path_traversal.py + File: example/vulnerable_code/path_traversal.py > Line 10: image_name = save_3_image_name - File: examples/vulnerable_code/path_traversal.py + File: example/vulnerable_code/path_traversal.py > Line 19: temp_2_other_arg = image_name - File: examples/vulnerable_code/path_traversal.py + File: example/vulnerable_code/path_traversal.py > Line 6: other_arg = temp_2_other_arg - File: examples/vulnerable_code/path_traversal.py + File: example/vulnerable_code/path_traversal.py > Line 7: outer_ret_val = outer_arg + 'hey' + other_arg - File: examples/vulnerable_code/path_traversal.py + File: example/vulnerable_code/path_traversal.py > Line 8: ret_outer = outer_ret_val - File: examples/vulnerable_code/path_traversal.py + File: example/vulnerable_code/path_traversal.py > Line 6: image_name = save_2_image_name - File: examples/vulnerable_code/path_traversal.py + File: example/vulnerable_code/path_traversal.py > Line 19: ~call_2 = ret_outer - File: examples/vulnerable_code/path_traversal.py + File: example/vulnerable_code/path_traversal.py > Line 19: foo = ~call_2 - File: examples/vulnerable_code/path_traversal.py + File: example/vulnerable_code/path_traversal.py > reaches line 20, trigger word "send_file(": ~call_4 = ret_send_file(foo) """ @@ -234,37 +237,37 @@ def test_path_traversal_result(self): self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION)) def test_ensure_saved_scope(self): - vulnerabilities = self.run_analysis('examples/vulnerable_code/ensure_saved_scope.py') + vulnerabilities = self.run_analysis('example/vulnerable_code/ensure_saved_scope.py') self.assert_length(vulnerabilities, expected_length=1) vulnerability_description = str(vulnerabilities[0]) EXPECTED_VULNERABILITY_DESCRIPTION = """ - File: examples/vulnerable_code/ensure_saved_scope.py + File: example/vulnerable_code/ensure_saved_scope.py > User input at line 15, trigger word "request.args.get(": ~call_1 = ret_request.args.get('image_name') Reassigned in: - File: examples/vulnerable_code/ensure_saved_scope.py + File: example/vulnerable_code/ensure_saved_scope.py > Line 15: image_name = ~call_1 - File: examples/vulnerable_code/ensure_saved_scope.py + File: example/vulnerable_code/ensure_saved_scope.py > Line 6: save_2_image_name = image_name - File: examples/vulnerable_code/ensure_saved_scope.py + File: example/vulnerable_code/ensure_saved_scope.py > Line 10: save_3_image_name = image_name - File: examples/vulnerable_code/ensure_saved_scope.py + File: example/vulnerable_code/ensure_saved_scope.py > Line 10: image_name = save_3_image_name - File: examples/vulnerable_code/ensure_saved_scope.py + File: example/vulnerable_code/ensure_saved_scope.py > Line 19: temp_2_other_arg = image_name - File: examples/vulnerable_code/ensure_saved_scope.py + File: example/vulnerable_code/ensure_saved_scope.py > Line 6: other_arg = temp_2_other_arg - File: examples/vulnerable_code/ensure_saved_scope.py + File: example/vulnerable_code/ensure_saved_scope.py > Line 7: outer_ret_val = outer_arg + 'hey' + other_arg - File: examples/vulnerable_code/ensure_saved_scope.py + File: example/vulnerable_code/ensure_saved_scope.py > Line 8: ret_outer = outer_ret_val - File: examples/vulnerable_code/ensure_saved_scope.py + File: example/vulnerable_code/ensure_saved_scope.py > Line 6: image_name = save_2_image_name - File: examples/vulnerable_code/ensure_saved_scope.py + File: example/vulnerable_code/ensure_saved_scope.py > Line 19: ~call_2 = ret_outer - File: examples/vulnerable_code/ensure_saved_scope.py + File: example/vulnerable_code/ensure_saved_scope.py > Line 19: foo = ~call_2 - File: examples/vulnerable_code/ensure_saved_scope.py + File: example/vulnerable_code/ensure_saved_scope.py > reaches line 20, trigger word "send_file(": ~call_4 = ret_send_file(image_name) """ @@ -272,25 +275,25 @@ def test_ensure_saved_scope(self): self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION)) def test_path_traversal_sanitised_result(self): - vulnerabilities = self.run_analysis('examples/vulnerable_code/path_traversal_sanitised.py') + vulnerabilities = self.run_analysis('example/vulnerable_code/path_traversal_sanitised.py') self.assert_length(vulnerabilities, expected_length=1) vulnerability_description = str(vulnerabilities[0]) EXPECTED_VULNERABILITY_DESCRIPTION = """ - File: examples/vulnerable_code/path_traversal_sanitised.py + File: example/vulnerable_code/path_traversal_sanitised.py > User input at line 8, trigger word "request.args.get(": ~call_1 = ret_request.args.get('image_name') Reassigned in: - File: examples/vulnerable_code/path_traversal_sanitised.py + File: example/vulnerable_code/path_traversal_sanitised.py > Line 8: image_name = ~call_1 - File: examples/vulnerable_code/path_traversal_sanitised.py + File: example/vulnerable_code/path_traversal_sanitised.py > Line 10: ~call_2 = ret_image_name.replace('..', '') - File: examples/vulnerable_code/path_traversal_sanitised.py + File: example/vulnerable_code/path_traversal_sanitised.py > Line 10: image_name = ~call_2 - File: examples/vulnerable_code/path_traversal_sanitised.py + File: example/vulnerable_code/path_traversal_sanitised.py > Line 12: ~call_4 = ret_os.path.join(~call_5, image_name) - File: examples/vulnerable_code/path_traversal_sanitised.py + File: example/vulnerable_code/path_traversal_sanitised.py > Line 12: ret_cat_picture = ~call_3 - File: examples/vulnerable_code/path_traversal_sanitised.py + File: example/vulnerable_code/path_traversal_sanitised.py > reaches line 12, trigger word "send_file(": ~call_3 = ret_send_file(~call_4) This vulnerability is sanitised by: Label: ~call_2 = ret_image_name.replace('..', '') @@ -299,21 +302,21 @@ def test_path_traversal_sanitised_result(self): self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION)) def test_path_traversal_sanitised_2_result(self): - vulnerabilities = self.run_analysis('examples/vulnerable_code/path_traversal_sanitised_2.py') + vulnerabilities = self.run_analysis('example/vulnerable_code/path_traversal_sanitised_2.py') self.assert_length(vulnerabilities, expected_length=1) vulnerability_description = str(vulnerabilities[0]) EXPECTED_VULNERABILITY_DESCRIPTION = """ - File: examples/vulnerable_code/path_traversal_sanitised_2.py + File: example/vulnerable_code/path_traversal_sanitised_2.py > User input at line 8, trigger word "request.args.get(": ~call_1 = ret_request.args.get('image_name') Reassigned in: - File: examples/vulnerable_code/path_traversal_sanitised_2.py + File: example/vulnerable_code/path_traversal_sanitised_2.py > Line 8: image_name = ~call_1 - File: examples/vulnerable_code/path_traversal_sanitised_2.py + File: example/vulnerable_code/path_traversal_sanitised_2.py > Line 12: ~call_3 = ret_os.path.join(~call_4, image_name) - File: examples/vulnerable_code/path_traversal_sanitised_2.py + File: example/vulnerable_code/path_traversal_sanitised_2.py > Line 12: ret_cat_picture = ~call_2 - File: examples/vulnerable_code/path_traversal_sanitised_2.py + File: example/vulnerable_code/path_traversal_sanitised_2.py > reaches line 12, trigger word "send_file(": ~call_2 = ret_send_file(~call_3) This vulnerability is potentially sanitised by: Label: if '..' in image_name: @@ -322,19 +325,19 @@ def test_path_traversal_sanitised_2_result(self): self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION)) def test_sql_result(self): - vulnerabilities = self.run_analysis('examples/vulnerable_code/sql/sqli.py') + vulnerabilities = self.run_analysis('example/vulnerable_code/sql/sqli.py') self.assert_length(vulnerabilities, expected_length=1) vulnerability_description = str(vulnerabilities[0]) EXPECTED_VULNERABILITY_DESCRIPTION = """ - File: examples/vulnerable_code/sql/sqli.py + File: example/vulnerable_code/sql/sqli.py > User input at line 26, trigger word "request.args.get(": ~call_1 = ret_request.args.get('param', 'not set') Reassigned in: - File: examples/vulnerable_code/sql/sqli.py + File: example/vulnerable_code/sql/sqli.py > Line 26: param = ~call_1 - File: examples/vulnerable_code/sql/sqli.py + File: example/vulnerable_code/sql/sqli.py > Line 27: result = ~call_2 - File: examples/vulnerable_code/sql/sqli.py + File: example/vulnerable_code/sql/sqli.py > reaches line 27, trigger word "execute(": ~call_2 = ret_db.engine.execute(param) """ @@ -342,21 +345,21 @@ def test_sql_result(self): self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION)) def test_XSS_form_result(self): - vulnerabilities = self.run_analysis('examples/vulnerable_code/XSS_form.py') + vulnerabilities = self.run_analysis('example/vulnerable_code/XSS_form.py') self.assert_length(vulnerabilities, expected_length=1) vulnerability_description = str(vulnerabilities[0]) EXPECTED_VULNERABILITY_DESCRIPTION = """ - File: examples/vulnerable_code/XSS_form.py + File: example/vulnerable_code/XSS_form.py > User input at line 14, trigger word "form[": data = request.form['my_text'] Reassigned in: - File: examples/vulnerable_code/XSS_form.py + File: example/vulnerable_code/XSS_form.py > Line 15: ~call_1 = ret_make_response(~call_2) - File: examples/vulnerable_code/XSS_form.py + File: example/vulnerable_code/XSS_form.py > Line 15: resp = ~call_1 - File: examples/vulnerable_code/XSS_form.py + File: example/vulnerable_code/XSS_form.py > Line 17: ret_example2_action = resp - File: examples/vulnerable_code/XSS_form.py + File: example/vulnerable_code/XSS_form.py > reaches line 15, trigger word "replace(": ~call_2 = ret_html1.replace('{{ data }}', data) """ @@ -364,23 +367,23 @@ def test_XSS_form_result(self): self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION)) def test_XSS_url_result(self): - vulnerabilities = self.run_analysis('examples/vulnerable_code/XSS_url.py') + vulnerabilities = self.run_analysis('example/vulnerable_code/XSS_url.py') self.assert_length(vulnerabilities, expected_length=1) vulnerability_description = str(vulnerabilities[0]) EXPECTED_VULNERABILITY_DESCRIPTION = """ - File: examples/vulnerable_code/XSS_url.py + File: example/vulnerable_code/XSS_url.py > User input at line 4, trigger word "Framework function URL parameter": url Reassigned in: - File: examples/vulnerable_code/XSS_url.py + File: example/vulnerable_code/XSS_url.py > Line 6: param = url - File: examples/vulnerable_code/XSS_url.py + File: example/vulnerable_code/XSS_url.py > Line 9: ~call_2 = ret_make_response(~call_3) - File: examples/vulnerable_code/XSS_url.py + File: example/vulnerable_code/XSS_url.py > Line 9: resp = ~call_2 - File: examples/vulnerable_code/XSS_url.py + File: example/vulnerable_code/XSS_url.py > Line 10: ret_XSS1 = resp - File: examples/vulnerable_code/XSS_url.py + File: example/vulnerable_code/XSS_url.py > reaches line 9, trigger word "replace(": ~call_3 = ret_html.replace('{{ param }}', param) """ @@ -388,29 +391,29 @@ def test_XSS_url_result(self): self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION)) def test_XSS_no_vuln_result(self): - vulnerabilities = self.run_analysis('examples/vulnerable_code/XSS_no_vuln.py') + vulnerabilities = self.run_analysis('example/vulnerable_code/XSS_no_vuln.py') self.assert_length(vulnerabilities, expected_length=0) def test_XSS_reassign_result(self): - vulnerabilities = self.run_analysis('examples/vulnerable_code/XSS_reassign.py') + vulnerabilities = self.run_analysis('example/vulnerable_code/XSS_reassign.py') self.assert_length(vulnerabilities, expected_length=1) vulnerability_description = str(vulnerabilities[0]) EXPECTED_VULNERABILITY_DESCRIPTION = """ - File: examples/vulnerable_code/XSS_reassign.py + File: example/vulnerable_code/XSS_reassign.py > User input at line 6, trigger word "request.args.get(": ~call_1 = ret_request.args.get('param', 'not set') Reassigned in: - File: examples/vulnerable_code/XSS_reassign.py + File: example/vulnerable_code/XSS_reassign.py > Line 6: param = ~call_1 - File: examples/vulnerable_code/XSS_reassign.py + File: example/vulnerable_code/XSS_reassign.py > Line 8: param = param + '' - File: examples/vulnerable_code/XSS_reassign.py + File: example/vulnerable_code/XSS_reassign.py > Line 11: ~call_3 = ret_make_response(~call_4) - File: examples/vulnerable_code/XSS_reassign.py + File: example/vulnerable_code/XSS_reassign.py > Line 11: resp = ~call_3 - File: examples/vulnerable_code/XSS_reassign.py + File: example/vulnerable_code/XSS_reassign.py > Line 12: ret_XSS1 = resp - File: examples/vulnerable_code/XSS_reassign.py + File: example/vulnerable_code/XSS_reassign.py > reaches line 11, trigger word "replace(": ~call_4 = ret_html.replace('{{ param }}', param) """ @@ -418,27 +421,27 @@ def test_XSS_reassign_result(self): self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION)) def test_XSS_sanitised_result(self): - vulnerabilities = self.run_analysis('examples/vulnerable_code/XSS_sanitised.py') + vulnerabilities = self.run_analysis('example/vulnerable_code/XSS_sanitised.py') self.assert_length(vulnerabilities, expected_length=1) vulnerability_description = str(vulnerabilities[0]) EXPECTED_VULNERABILITY_DESCRIPTION = """ - File: examples/vulnerable_code/XSS_sanitised.py + File: example/vulnerable_code/XSS_sanitised.py > User input at line 7, trigger word "request.args.get(": ~call_1 = ret_request.args.get('param', 'not set') Reassigned in: - File: examples/vulnerable_code/XSS_sanitised.py + File: example/vulnerable_code/XSS_sanitised.py > Line 7: param = ~call_1 - File: examples/vulnerable_code/XSS_sanitised.py + File: example/vulnerable_code/XSS_sanitised.py > Line 9: ~call_2 = ret_Markup.escape(param) - File: examples/vulnerable_code/XSS_sanitised.py + File: example/vulnerable_code/XSS_sanitised.py > Line 9: param = ~call_2 - File: examples/vulnerable_code/XSS_sanitised.py + File: example/vulnerable_code/XSS_sanitised.py > Line 12: ~call_4 = ret_make_response(~call_5) - File: examples/vulnerable_code/XSS_sanitised.py + File: example/vulnerable_code/XSS_sanitised.py > Line 12: resp = ~call_4 - File: examples/vulnerable_code/XSS_sanitised.py + File: example/vulnerable_code/XSS_sanitised.py > Line 13: ret_XSS1 = resp - File: examples/vulnerable_code/XSS_sanitised.py + File: example/vulnerable_code/XSS_sanitised.py > reaches line 12, trigger word "replace(": ~call_5 = ret_html.replace('{{ param }}', param) This vulnerability is sanitised by: Label: ~call_2 = ret_Markup.escape(param) @@ -447,29 +450,29 @@ def test_XSS_sanitised_result(self): self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION)) def test_XSS_variable_assign_no_vuln_result(self): - vulnerabilities = self.run_analysis('examples/vulnerable_code/XSS_variable_assign_no_vuln.py') + vulnerabilities = self.run_analysis('example/vulnerable_code/XSS_variable_assign_no_vuln.py') self.assert_length(vulnerabilities, expected_length=0) def test_XSS_variable_assign_result(self): - vulnerabilities = self.run_analysis('examples/vulnerable_code/XSS_variable_assign.py') + vulnerabilities = self.run_analysis('example/vulnerable_code/XSS_variable_assign.py') self.assert_length(vulnerabilities, expected_length=1) vulnerability_description = str(vulnerabilities[0]) EXPECTED_VULNERABILITY_DESCRIPTION = """ - File: examples/vulnerable_code/XSS_variable_assign.py + File: example/vulnerable_code/XSS_variable_assign.py > User input at line 6, trigger word "request.args.get(": ~call_1 = ret_request.args.get('param', 'not set') Reassigned in: - File: examples/vulnerable_code/XSS_variable_assign.py + File: example/vulnerable_code/XSS_variable_assign.py > Line 6: param = ~call_1 - File: examples/vulnerable_code/XSS_variable_assign.py + File: example/vulnerable_code/XSS_variable_assign.py > Line 8: other_var = param + '' - File: examples/vulnerable_code/XSS_variable_assign.py + File: example/vulnerable_code/XSS_variable_assign.py > Line 11: ~call_3 = ret_make_response(~call_4) - File: examples/vulnerable_code/XSS_variable_assign.py + File: example/vulnerable_code/XSS_variable_assign.py > Line 11: resp = ~call_3 - File: examples/vulnerable_code/XSS_variable_assign.py + File: example/vulnerable_code/XSS_variable_assign.py > Line 12: ret_XSS1 = resp - File: examples/vulnerable_code/XSS_variable_assign.py + File: example/vulnerable_code/XSS_variable_assign.py > reaches line 11, trigger word "replace(": ~call_4 = ret_html.replace('{{ param }}', other_var) """ @@ -477,29 +480,29 @@ def test_XSS_variable_assign_result(self): self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION)) def test_XSS_variable_multiple_assign_result(self): - vulnerabilities = self.run_analysis('examples/vulnerable_code/XSS_variable_multiple_assign.py') + vulnerabilities = self.run_analysis('example/vulnerable_code/XSS_variable_multiple_assign.py') self.assert_length(vulnerabilities, expected_length=1) vulnerability_description = str(vulnerabilities[0]) EXPECTED_VULNERABILITY_DESCRIPTION = """ - File: examples/vulnerable_code/XSS_variable_multiple_assign.py + File: example/vulnerable_code/XSS_variable_multiple_assign.py > User input at line 6, trigger word "request.args.get(": ~call_1 = ret_request.args.get('param', 'not set') Reassigned in: - File: examples/vulnerable_code/XSS_variable_multiple_assign.py + File: example/vulnerable_code/XSS_variable_multiple_assign.py > Line 6: param = ~call_1 - File: examples/vulnerable_code/XSS_variable_multiple_assign.py + File: example/vulnerable_code/XSS_variable_multiple_assign.py > Line 8: other_var = param + '' - File: examples/vulnerable_code/XSS_variable_multiple_assign.py + File: example/vulnerable_code/XSS_variable_multiple_assign.py > Line 10: not_the_same_var = '' + other_var - File: examples/vulnerable_code/XSS_variable_multiple_assign.py + File: example/vulnerable_code/XSS_variable_multiple_assign.py > Line 12: another_one = not_the_same_var + '' - File: examples/vulnerable_code/XSS_variable_multiple_assign.py + File: example/vulnerable_code/XSS_variable_multiple_assign.py > Line 15: ~call_3 = ret_make_response(~call_4) - File: examples/vulnerable_code/XSS_variable_multiple_assign.py + File: example/vulnerable_code/XSS_variable_multiple_assign.py > Line 15: resp = ~call_3 - File: examples/vulnerable_code/XSS_variable_multiple_assign.py + File: example/vulnerable_code/XSS_variable_multiple_assign.py > Line 17: ret_XSS1 = resp - File: examples/vulnerable_code/XSS_variable_multiple_assign.py + File: example/vulnerable_code/XSS_variable_multiple_assign.py > reaches line 15, trigger word "replace(": ~call_4 = ret_html.replace('{{ param }}', another_one) """ @@ -533,57 +536,24 @@ def run_analysis(self, path): VulnerabilityFiles( default_blackbox_mapping_file, trigger_word_file - ) + ), + nosec_lines ) def test_django_view_param(self): - vulnerabilities = self.run_analysis('examples/vulnerable_code/django_XSS.py') + vulnerabilities = self.run_analysis('example/vulnerable_code/django_XSS.py') self.assert_length(vulnerabilities, expected_length=2) vulnerability_description = str(vulnerabilities[0]) EXPECTED_VULNERABILITY_DESCRIPTION = """ - File: examples/vulnerable_code/django_XSS.py + File: example/vulnerable_code/django_XSS.py > User input at line 4, trigger word "Framework function URL parameter": param Reassigned in: - File: examples/vulnerable_code/django_XSS.py + File: example/vulnerable_code/django_XSS.py > Line 5: ret_xss1 = ~call_1 - File: examples/vulnerable_code/django_XSS.py + File: example/vulnerable_code/django_XSS.py > reaches line 5, trigger word "render(": ~call_1 = ret_render(request, 'templates/xss.html', 'param'param) """ self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION)) - - -class EngineEveryTest(BaseTestCase): - def run_empty(self): - return - - def run_analysis(self, path): - self.cfg_create_from_file(path) - cfg_list = [self.cfg] - - FrameworkAdaptor(cfg_list, [], [], is_function) - initialize_constraint_table(cfg_list) - - analyse(cfg_list, analysis_type=ReachingDefinitionsTaintAnalysis) - - trigger_word_file = os.path.join( - 'pyt', - 'vulnerability_definitions', - 'all_trigger_words.pyt' - ) - - return vulnerabilities.find_vulnerabilities( - cfg_list, - ReachingDefinitionsTaintAnalysis, - UImode.NORMAL, - VulnerabilityFiles( - default_blackbox_mapping_file, - trigger_word_file - ) - ) - - def test_self_is_not_tainted(self): - vulnerabilities = self.run_analysis('examples/example_inputs/def_with_self_as_first_arg.py') - self.assert_length(vulnerabilities, expected_length=0) From 1502ee73e81ba6f981ca39658d6f93863091727c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20G=C3=BCnal?= Date: Thu, 26 Apr 2018 18:45:03 +0300 Subject: [PATCH 09/22] added empty nosec lines for tests --- tests/vulnerabilities_test.py | 664 ++++++++++------------------------ 1 file changed, 200 insertions(+), 464 deletions(-) diff --git a/tests/vulnerabilities_test.py b/tests/vulnerabilities_test.py index 7ec1ae1f..b30d415b 100644 --- a/tests/vulnerabilities_test.py +++ b/tests/vulnerabilities_test.py @@ -1,11 +1,6 @@ import os from .base_test_case import BaseTestCase - -from pyt import ( - trigger_definitions_parser, - vulnerabilities -) from pyt.argument_helpers import ( default_blackbox_mapping_file, default_trigger_word_file, @@ -15,125 +10,30 @@ from pyt.constraint_table import initialize_constraint_table from pyt.fixed_point import analyse from pyt.framework_adaptor import FrameworkAdaptor -from pyt.framework_helper import( - is_django_view_function, - is_flask_route_function -) -from pyt.node_types import Node +from pyt.framework_helper import is_flask_route_function +from pyt.project_handler import get_directory_modules, get_modules from pyt.reaching_definitions_taint import ReachingDefinitionsTaintAnalysis +from pyt.vulnerabilities import find_vulnerabilities nosec_lines = set() class EngineTest(BaseTestCase): - def run_empty(self): - return - - def get_lattice_elements(self, cfg_nodes): - """Dummy analysis method""" - return cfg_nodes - - def test_parse(self): - definitions = vulnerabilities.parse( - trigger_word_file=os.path.join( - os.getcwd(), - 'pyt', - 'vulnerability_definitions', - 'test_triggers.pyt' - ) - ) - - self.assert_length(definitions.sources, expected_length=1) - self.assert_length(definitions.sinks, expected_length=3) - self.assert_length(definitions.sinks[0][1], expected_length=1) - self.assert_length(definitions.sinks[1][1], expected_length=3) - - def test_parse_section(self): - l = list(trigger_definitions_parser.parse_section(iter(['get']))) - self.assert_length(l, expected_length=1) - self.assertEqual(l[0][0], 'get') - self.assertEqual(l[0][1], list()) - - l = list(trigger_definitions_parser.parse_section(iter(['get', 'get -> a, b, c d s aq a']))) - self.assert_length(l, expected_length=2) - self.assertEqual(l[0][0], 'get') - self.assertEqual(l[1][0], 'get') - self.assertEqual(l[1][1], ['a', 'b', 'c d s aq a']) - self.assert_length(l[1][1], expected_length=3) - - def test_label_contains(self): - cfg_node = Node('label', None, line_number=None, path=None) - trigger_words = [('get', [])] - l = list(vulnerabilities.label_contains(cfg_node, trigger_words)) - self.assert_length(l, expected_length=0) - - cfg_node = Node('request.get("stefan")', None, line_number=None, path=None) - trigger_words = [('get', []), ('request', [])] - l = list(vulnerabilities.label_contains(cfg_node, trigger_words)) - self.assert_length(l, expected_length=2) - - trigger_node_1 = l[0] - trigger_node_2 = l[1] - self.assertEqual(trigger_node_1.trigger_word, 'get') - self.assertEqual(trigger_node_1.cfg_node, cfg_node) - self.assertEqual(trigger_node_2.trigger_word, 'request') - self.assertEqual(trigger_node_2.cfg_node, cfg_node) - - cfg_node = Node('request.get("stefan")', None, line_number=None, path=None) - trigger_words = [('get', []), ('get', [])] - l = list(vulnerabilities.label_contains(cfg_node, trigger_words)) - self.assert_length(l, expected_length=2) - - def test_find_triggers(self): - self.cfg_create_from_file('example/vulnerable_code/XSS.py') - - cfg_list = [self.cfg] - - FrameworkAdaptor(cfg_list, [], [], is_flask_route_function) - - XSS1 = cfg_list[1] - trigger_words = [('get', [])] - - l = vulnerabilities.find_triggers(XSS1.nodes, trigger_words, nosec_lines) - self.assert_length(l, expected_length=1) - - - def test_find_sanitiser_nodes(self): - cfg_node = Node(None, None, line_number=None, path=None) - sanitiser_tuple = vulnerabilities.Sanitiser('escape', cfg_node) - sanitiser = 'escape' + def run_analysis(self, path): + path = os.path.normpath(path) - result = list(vulnerabilities.find_sanitiser_nodes(sanitiser, [sanitiser_tuple])) - self.assert_length(result, expected_length=1) - self.assertEqual(result[0], cfg_node) + project_modules = get_modules(os.path.dirname(path)) + local_modules = get_directory_modules(os.path.dirname(path)) + self.cfg_create_from_file(path, project_modules, local_modules) - def test_build_sanitiser_node_dict(self): - self.cfg_create_from_file('example/vulnerable_code/XSS_sanitised.py') cfg_list = [self.cfg] FrameworkAdaptor(cfg_list, [], [], is_flask_route_function) - cfg = cfg_list[1] - - cfg_node = Node(None, None, line_number=None, path=None) - sinks_in_file = [vulnerabilities.TriggerNode('replace', ['escape'], cfg_node)] - - sanitiser_dict = vulnerabilities.build_sanitiser_node_dict(cfg, sinks_in_file) - self.assert_length(sanitiser_dict, expected_length=1) - self.assertIn('escape', sanitiser_dict.keys()) - - self.assertEqual(sanitiser_dict['escape'][0], cfg.nodes[3]) - - def run_analysis(self, path): - self.cfg_create_from_file(path) - - cfg_list = [self.cfg] - - FrameworkAdaptor(cfg_list, [], [], is_flask_route_function) initialize_constraint_table(cfg_list) analyse(cfg_list, analysis_type=ReachingDefinitionsTaintAnalysis) - return vulnerabilities.find_vulnerabilities( + return find_vulnerabilities( cfg_list, ReachingDefinitionsTaintAnalysis, UImode.NORMAL, @@ -144,416 +44,252 @@ def run_analysis(self, path): nosec_lines ) - def test_find_vulnerabilities_assign_other_var(self): - vulnerabilities = self.run_analysis('example/vulnerable_code/XSS_assign_to_other_var.py') - self.assert_length(vulnerabilities, expected_length=1) - - def test_find_vulnerabilities_inter_command_injection(self): - vulnerabilities = self.run_analysis('example/vulnerable_code/inter_command_injection.py') - self.assert_length(vulnerabilities, expected_length=1) + def test_find_vulnerabilities_absolute_from_file_command_injection(self): + vulnerabilities = self.run_analysis('examples/vulnerable_code_across_files/absolute_from_file_command_injection.py') - def test_find_vulnerabilities_inter_command_injection_2(self): - vulnerabilities = self.run_analysis('example/vulnerable_code/inter_command_injection_2.py') self.assert_length(vulnerabilities, expected_length=1) - def test_XSS_result(self): - vulnerabilities = self.run_analysis('example/vulnerable_code/XSS.py') + def test_find_vulnerabilities_absolute_from_file_command_injection_2(self): + vulnerabilities = self.run_analysis('examples/vulnerable_code_across_files/absolute_from_file_command_injection_2.py') self.assert_length(vulnerabilities, expected_length=1) - vulnerability_description = str(vulnerabilities[0]) - EXPECTED_VULNERABILITY_DESCRIPTION = """ - File: example/vulnerable_code/XSS.py - > User input at line 6, trigger word "request.args.get(": - ~call_1 = ret_request.args.get('param', 'not set') - Reassigned in: - File: example/vulnerable_code/XSS.py - > Line 6: param = ~call_1 - File: example/vulnerable_code/XSS.py - > Line 9: ~call_3 = ret_make_response(~call_4) - File: example/vulnerable_code/XSS.py - > Line 9: resp = ~call_3 - File: example/vulnerable_code/XSS.py - > Line 10: ret_XSS1 = resp - File: example/vulnerable_code/XSS.py - > reaches line 9, trigger word "replace(": - ~call_4 = ret_html.replace('{{ param }}', param) - """ - self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION)) + def test_no_false_positive_absolute_from_file_command_injection_3(self): + vulnerabilities = self.run_analysis('examples/vulnerable_code_across_files/no_false_positive_absolute_from_file_command_injection_3.py') + self.assert_length(vulnerabilities, expected_length=0) - def test_command_injection_result(self): - vulnerabilities = self.run_analysis('example/vulnerable_code/command_injection.py') + def test_blackbox_library_call(self): + vulnerabilities = self.run_analysis('examples/vulnerable_code_across_files/blackbox_library_call.py') self.assert_length(vulnerabilities, expected_length=1) vulnerability_description = str(vulnerabilities[0]) EXPECTED_VULNERABILITY_DESCRIPTION = """ - File: example/vulnerable_code/command_injection.py - > User input at line 15, trigger word "form[": - param = request.form['suggestion'] + File: examples/vulnerable_code_across_files/blackbox_library_call.py + > User input at line 12, trigger word "request.args.get(": + ~call_1 = ret_request.args.get('suggestion') Reassigned in: - File: example/vulnerable_code/command_injection.py - > Line 16: command = 'echo ' + param + ' >> ' + 'menu.txt' - File: example/vulnerable_code/command_injection.py - > reaches line 18, trigger word "subprocess.call(": - ~call_1 = ret_subprocess.call(command, shell=True) + File: examples/vulnerable_code_across_files/blackbox_library_call.py + > Line 12: param = ~call_1 + File: examples/vulnerable_code_across_files/blackbox_library_call.py + > Line 15: ~call_2 = ret_scrypt.encrypt('echo ' + param + ' >> ' + 'menu.txt', 'password') + File: examples/vulnerable_code_across_files/blackbox_library_call.py + > Line 15: command = ~call_2 + File: examples/vulnerable_code_across_files/blackbox_library_call.py + > Line 16: hey = command + File: examples/vulnerable_code_across_files/blackbox_library_call.py + > reaches line 17, trigger word "subprocess.call(": + ~call_3 = ret_subprocess.call(hey, shell=True) + This vulnerability is unknown due to: Label: ~call_2 = ret_scrypt.encrypt('echo ' + param + ' >> ' + 'menu.txt', 'password') """ self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION)) - def test_path_traversal_result(self): - vulnerabilities = self.run_analysis('example/vulnerable_code/path_traversal.py') + def test_builtin_with_user_defined_inner(self): + vulnerabilities = self.run_analysis('examples/nested_functions_code/builtin_with_user_defined_inner.py') self.assert_length(vulnerabilities, expected_length=1) vulnerability_description = str(vulnerabilities[0]) EXPECTED_VULNERABILITY_DESCRIPTION = """ - File: example/vulnerable_code/path_traversal.py - > User input at line 15, trigger word "request.args.get(": - ~call_1 = ret_request.args.get('image_name') + File: examples/nested_functions_code/builtin_with_user_defined_inner.py + > User input at line 16, trigger word "form[": + req_param = request.form['suggestion'] Reassigned in: - File: example/vulnerable_code/path_traversal.py - > Line 15: image_name = ~call_1 - File: example/vulnerable_code/path_traversal.py - > Line 6: save_2_image_name = image_name - File: example/vulnerable_code/path_traversal.py - > Line 10: save_3_image_name = image_name - File: example/vulnerable_code/path_traversal.py - > Line 10: image_name = save_3_image_name - File: example/vulnerable_code/path_traversal.py - > Line 19: temp_2_other_arg = image_name - File: example/vulnerable_code/path_traversal.py - > Line 6: other_arg = temp_2_other_arg - File: example/vulnerable_code/path_traversal.py - > Line 7: outer_ret_val = outer_arg + 'hey' + other_arg - File: example/vulnerable_code/path_traversal.py - > Line 8: ret_outer = outer_ret_val - File: example/vulnerable_code/path_traversal.py - > Line 6: image_name = save_2_image_name - File: example/vulnerable_code/path_traversal.py - > Line 19: ~call_2 = ret_outer - File: example/vulnerable_code/path_traversal.py - > Line 19: foo = ~call_2 - File: example/vulnerable_code/path_traversal.py - > reaches line 20, trigger word "send_file(": - ~call_4 = ret_send_file(foo) + File: examples/nested_functions_code/builtin_with_user_defined_inner.py + > Line 10: save_2_req_param = req_param + File: examples/nested_functions_code/builtin_with_user_defined_inner.py + > Line 19: temp_2_inner_arg = req_param + File: examples/nested_functions_code/builtin_with_user_defined_inner.py + > Line 10: inner_arg = temp_2_inner_arg + File: examples/nested_functions_code/builtin_with_user_defined_inner.py + > Line 11: yes_vuln = inner_arg + 'hey' + File: examples/nested_functions_code/builtin_with_user_defined_inner.py + > Line 12: ret_inner = yes_vuln + File: examples/nested_functions_code/builtin_with_user_defined_inner.py + > Line 10: req_param = save_2_req_param + File: examples/nested_functions_code/builtin_with_user_defined_inner.py + > Line 19: ~call_2 = ret_inner + File: examples/nested_functions_code/builtin_with_user_defined_inner.py + > Line 19: ~call_1 = ret_scrypt.encrypt(~call_2) + File: examples/nested_functions_code/builtin_with_user_defined_inner.py + > Line 19: foo = ~call_1 + File: examples/nested_functions_code/builtin_with_user_defined_inner.py + > reaches line 20, trigger word "subprocess.call(": + ~call_3 = ret_subprocess.call(foo, shell=True) + This vulnerability is unknown due to: Label: ~call_1 = ret_scrypt.encrypt(~call_2) """ - self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION)) - def test_ensure_saved_scope(self): - vulnerabilities = self.run_analysis('example/vulnerable_code/ensure_saved_scope.py') + def test_sink_with_result_of_blackbox_nested(self): + vulnerabilities = self.run_analysis('examples/nested_functions_code/sink_with_result_of_blackbox_nested.py') self.assert_length(vulnerabilities, expected_length=1) vulnerability_description = str(vulnerabilities[0]) EXPECTED_VULNERABILITY_DESCRIPTION = """ - File: example/vulnerable_code/ensure_saved_scope.py - > User input at line 15, trigger word "request.args.get(": - ~call_1 = ret_request.args.get('image_name') + File: examples/nested_functions_code/sink_with_result_of_blackbox_nested.py + > User input at line 12, trigger word "form[": + req_param = request.form['suggestion'] Reassigned in: - File: example/vulnerable_code/ensure_saved_scope.py - > Line 15: image_name = ~call_1 - File: example/vulnerable_code/ensure_saved_scope.py - > Line 6: save_2_image_name = image_name - File: example/vulnerable_code/ensure_saved_scope.py - > Line 10: save_3_image_name = image_name - File: example/vulnerable_code/ensure_saved_scope.py - > Line 10: image_name = save_3_image_name - File: example/vulnerable_code/ensure_saved_scope.py - > Line 19: temp_2_other_arg = image_name - File: example/vulnerable_code/ensure_saved_scope.py - > Line 6: other_arg = temp_2_other_arg - File: example/vulnerable_code/ensure_saved_scope.py - > Line 7: outer_ret_val = outer_arg + 'hey' + other_arg - File: example/vulnerable_code/ensure_saved_scope.py - > Line 8: ret_outer = outer_ret_val - File: example/vulnerable_code/ensure_saved_scope.py - > Line 6: image_name = save_2_image_name - File: example/vulnerable_code/ensure_saved_scope.py - > Line 19: ~call_2 = ret_outer - File: example/vulnerable_code/ensure_saved_scope.py - > Line 19: foo = ~call_2 - File: example/vulnerable_code/ensure_saved_scope.py - > reaches line 20, trigger word "send_file(": - ~call_4 = ret_send_file(image_name) + File: examples/nested_functions_code/sink_with_result_of_blackbox_nested.py + > Line 13: ~call_2 = ret_scrypt.encrypt(req_param) + File: examples/nested_functions_code/sink_with_result_of_blackbox_nested.py + > Line 13: ~call_1 = ret_scrypt.encrypt(~call_2) + File: examples/nested_functions_code/sink_with_result_of_blackbox_nested.py + > Line 13: result = ~call_1 + File: examples/nested_functions_code/sink_with_result_of_blackbox_nested.py + > reaches line 14, trigger word "subprocess.call(": + ~call_3 = ret_subprocess.call(result, shell=True) + This vulnerability is unknown due to: Label: ~call_2 = ret_scrypt.encrypt(req_param) """ - - self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION)) - - def test_path_traversal_sanitised_result(self): - vulnerabilities = self.run_analysis('example/vulnerable_code/path_traversal_sanitised.py') - self.assert_length(vulnerabilities, expected_length=1) - vulnerability_description = str(vulnerabilities[0]) - EXPECTED_VULNERABILITY_DESCRIPTION = """ - File: example/vulnerable_code/path_traversal_sanitised.py - > User input at line 8, trigger word "request.args.get(": - ~call_1 = ret_request.args.get('image_name') + OTHER_EXPECTED_VULNERABILITY_DESCRIPTION = """ + File: examples/nested_functions_code/sink_with_result_of_blackbox_nested.py + > User input at line 12, trigger word "form[": + req_param = request.form['suggestion'] Reassigned in: - File: example/vulnerable_code/path_traversal_sanitised.py - > Line 8: image_name = ~call_1 - File: example/vulnerable_code/path_traversal_sanitised.py - > Line 10: ~call_2 = ret_image_name.replace('..', '') - File: example/vulnerable_code/path_traversal_sanitised.py - > Line 10: image_name = ~call_2 - File: example/vulnerable_code/path_traversal_sanitised.py - > Line 12: ~call_4 = ret_os.path.join(~call_5, image_name) - File: example/vulnerable_code/path_traversal_sanitised.py - > Line 12: ret_cat_picture = ~call_3 - File: example/vulnerable_code/path_traversal_sanitised.py - > reaches line 12, trigger word "send_file(": - ~call_3 = ret_send_file(~call_4) - This vulnerability is sanitised by: Label: ~call_2 = ret_image_name.replace('..', '') + File: examples/nested_functions_code/sink_with_result_of_blackbox_nested.py + > Line 13: ~call_2 = ret_scrypt.encrypt(req_param) + File: examples/nested_functions_code/sink_with_result_of_blackbox_nested.py + > Line 13: ~call_1 = ret_scrypt.encrypt(~call_2) + File: examples/nested_functions_code/sink_with_result_of_blackbox_nested.py + > Line 13: result = ~call_1 + File: examples/nested_functions_code/sink_with_result_of_blackbox_nested.py + > reaches line 14, trigger word "subprocess.call(": + ~call_3 = ret_subprocess.call(result, shell=True) + This vulnerability is unknown due to: Label: ~call_1 = ret_scrypt.encrypt(~call_2) """ + self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION) + or + self.string_compare_alpha(vulnerability_description, OTHER_EXPECTED_VULNERABILITY_DESCRIPTION)) - self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION)) - - def test_path_traversal_sanitised_2_result(self): - vulnerabilities = self.run_analysis('example/vulnerable_code/path_traversal_sanitised_2.py') + def test_sink_with_result_of_user_defined_nested(self): + vulnerabilities = self.run_analysis('examples/nested_functions_code/sink_with_result_of_user_defined_nested.py') self.assert_length(vulnerabilities, expected_length=1) vulnerability_description = str(vulnerabilities[0]) EXPECTED_VULNERABILITY_DESCRIPTION = """ - File: example/vulnerable_code/path_traversal_sanitised_2.py - > User input at line 8, trigger word "request.args.get(": - ~call_1 = ret_request.args.get('image_name') + File: examples/nested_functions_code/sink_with_result_of_user_defined_nested.py + > User input at line 16, trigger word "form[": + req_param = request.form['suggestion'] Reassigned in: - File: example/vulnerable_code/path_traversal_sanitised_2.py - > Line 8: image_name = ~call_1 - File: example/vulnerable_code/path_traversal_sanitised_2.py - > Line 12: ~call_3 = ret_os.path.join(~call_4, image_name) - File: example/vulnerable_code/path_traversal_sanitised_2.py - > Line 12: ret_cat_picture = ~call_2 - File: example/vulnerable_code/path_traversal_sanitised_2.py - > reaches line 12, trigger word "send_file(": - ~call_2 = ret_send_file(~call_3) - This vulnerability is potentially sanitised by: Label: if '..' in image_name: + File: examples/nested_functions_code/sink_with_result_of_user_defined_nested.py + > Line 6: save_1_req_param = req_param + File: examples/nested_functions_code/sink_with_result_of_user_defined_nested.py + > Line 10: save_2_req_param = req_param + File: examples/nested_functions_code/sink_with_result_of_user_defined_nested.py + > Line 17: temp_2_inner_arg = req_param + File: examples/nested_functions_code/sink_with_result_of_user_defined_nested.py + > Line 10: inner_arg = temp_2_inner_arg + File: examples/nested_functions_code/sink_with_result_of_user_defined_nested.py + > Line 11: inner_ret_val = inner_arg + 'hey' + File: examples/nested_functions_code/sink_with_result_of_user_defined_nested.py + > Line 12: ret_inner = inner_ret_val + File: examples/nested_functions_code/sink_with_result_of_user_defined_nested.py + > Line 10: req_param = save_2_req_param + File: examples/nested_functions_code/sink_with_result_of_user_defined_nested.py + > Line 17: ~call_2 = ret_inner + File: examples/nested_functions_code/sink_with_result_of_user_defined_nested.py + > Line 17: temp_1_outer_arg = ~call_2 + File: examples/nested_functions_code/sink_with_result_of_user_defined_nested.py + > Line 6: outer_arg = temp_1_outer_arg + File: examples/nested_functions_code/sink_with_result_of_user_defined_nested.py + > Line 7: outer_ret_val = outer_arg + 'hey' + File: examples/nested_functions_code/sink_with_result_of_user_defined_nested.py + > Line 8: ret_outer = outer_ret_val + File: examples/nested_functions_code/sink_with_result_of_user_defined_nested.py + > Line 6: req_param = save_1_req_param + File: examples/nested_functions_code/sink_with_result_of_user_defined_nested.py + > Line 17: ~call_1 = ret_outer + File: examples/nested_functions_code/sink_with_result_of_user_defined_nested.py + > Line 17: result = ~call_1 + File: examples/nested_functions_code/sink_with_result_of_user_defined_nested.py + > reaches line 18, trigger word "subprocess.call(": + ~call_3 = ret_subprocess.call(result, shell=True) """ - self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION)) - def test_sql_result(self): - vulnerabilities = self.run_analysis('example/vulnerable_code/sql/sqli.py') + def test_sink_with_blackbox_inner(self): + vulnerabilities = self.run_analysis('examples/nested_functions_code/sink_with_blackbox_inner.py') self.assert_length(vulnerabilities, expected_length=1) vulnerability_description = str(vulnerabilities[0]) EXPECTED_VULNERABILITY_DESCRIPTION = """ - File: example/vulnerable_code/sql/sqli.py - > User input at line 26, trigger word "request.args.get(": - ~call_1 = ret_request.args.get('param', 'not set') + File: examples/nested_functions_code/sink_with_blackbox_inner.py + > User input at line 12, trigger word "form[": + req_param = request.form['suggestion'] Reassigned in: - File: example/vulnerable_code/sql/sqli.py - > Line 26: param = ~call_1 - File: example/vulnerable_code/sql/sqli.py - > Line 27: result = ~call_2 - File: example/vulnerable_code/sql/sqli.py - > reaches line 27, trigger word "execute(": - ~call_2 = ret_db.engine.execute(param) + File: examples/nested_functions_code/sink_with_blackbox_inner.py + > Line 14: ~call_3 = ret_scrypt.encypt(req_param) + File: examples/nested_functions_code/sink_with_blackbox_inner.py + > Line 14: ~call_2 = ret_scrypt.encypt(~call_3) + File: examples/nested_functions_code/sink_with_blackbox_inner.py + > reaches line 14, trigger word "subprocess.call(": + ~call_1 = ret_subprocess.call(~call_2, shell=True) + This vulnerability is unknown due to: Label: ~call_2 = ret_scrypt.encypt(~call_3) """ - self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION)) - - def test_XSS_form_result(self): - vulnerabilities = self.run_analysis('example/vulnerable_code/XSS_form.py') - self.assert_length(vulnerabilities, expected_length=1) - vulnerability_description = str(vulnerabilities[0]) - EXPECTED_VULNERABILITY_DESCRIPTION = """ - File: example/vulnerable_code/XSS_form.py - > User input at line 14, trigger word "form[": - data = request.form['my_text'] + OTHER_EXPECTED_VULNERABILITY_DESCRIPTION = """ + File: examples/nested_functions_code/sink_with_blackbox_inner.py + > User input at line 12, trigger word "form[": + req_param = request.form['suggestion'] Reassigned in: - File: example/vulnerable_code/XSS_form.py - > Line 15: ~call_1 = ret_make_response(~call_2) - File: example/vulnerable_code/XSS_form.py - > Line 15: resp = ~call_1 - File: example/vulnerable_code/XSS_form.py - > Line 17: ret_example2_action = resp - File: example/vulnerable_code/XSS_form.py - > reaches line 15, trigger word "replace(": - ~call_2 = ret_html1.replace('{{ data }}', data) + File: examples/nested_functions_code/sink_with_blackbox_inner.py + > Line 14: ~call_3 = ret_scrypt.encypt(req_param) + File: examples/nested_functions_code/sink_with_blackbox_inner.py + > Line 14: ~call_2 = ret_scrypt.encypt(~call_3) + File: examples/nested_functions_code/sink_with_blackbox_inner.py + > reaches line 14, trigger word "subprocess.call(": + ~call_1 = ret_subprocess.call(~call_2, shell=True) + This vulnerability is unknown due to: Label: ~call_3 = ret_scrypt.encypt(req_param) """ + self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION) + or + self.string_compare_alpha(vulnerability_description, OTHER_EXPECTED_VULNERABILITY_DESCRIPTION)) - self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION)) - - def test_XSS_url_result(self): - vulnerabilities = self.run_analysis('example/vulnerable_code/XSS_url.py') + def test_sink_with_user_defined_inner(self): + vulnerabilities = self.run_analysis('examples/nested_functions_code/sink_with_user_defined_inner.py') self.assert_length(vulnerabilities, expected_length=1) vulnerability_description = str(vulnerabilities[0]) EXPECTED_VULNERABILITY_DESCRIPTION = """ - File: example/vulnerable_code/XSS_url.py - > User input at line 4, trigger word "Framework function URL parameter": - url + File: examples/nested_functions_code/sink_with_user_defined_inner.py + > User input at line 16, trigger word "form[": + req_param = request.form['suggestion'] Reassigned in: - File: example/vulnerable_code/XSS_url.py - > Line 6: param = url - File: example/vulnerable_code/XSS_url.py - > Line 9: ~call_2 = ret_make_response(~call_3) - File: example/vulnerable_code/XSS_url.py - > Line 9: resp = ~call_2 - File: example/vulnerable_code/XSS_url.py - > Line 10: ret_XSS1 = resp - File: example/vulnerable_code/XSS_url.py - > reaches line 9, trigger word "replace(": - ~call_3 = ret_html.replace('{{ param }}', param) + File: examples/nested_functions_code/sink_with_user_defined_inner.py + > Line 6: save_2_req_param = req_param + File: examples/nested_functions_code/sink_with_user_defined_inner.py + > Line 10: save_3_req_param = req_param + File: examples/nested_functions_code/sink_with_user_defined_inner.py + > Line 18: temp_3_inner_arg = req_param + File: examples/nested_functions_code/sink_with_user_defined_inner.py + > Line 10: inner_arg = temp_3_inner_arg + File: examples/nested_functions_code/sink_with_user_defined_inner.py + > Line 11: inner_ret_val = inner_arg + 'hey' + File: examples/nested_functions_code/sink_with_user_defined_inner.py + > Line 12: ret_inner = inner_ret_val + File: examples/nested_functions_code/sink_with_user_defined_inner.py + > Line 10: req_param = save_3_req_param + File: examples/nested_functions_code/sink_with_user_defined_inner.py + > Line 18: ~call_3 = ret_inner + File: examples/nested_functions_code/sink_with_user_defined_inner.py + > Line 18: temp_2_outer_arg = ~call_3 + File: examples/nested_functions_code/sink_with_user_defined_inner.py + > Line 6: outer_arg = temp_2_outer_arg + File: examples/nested_functions_code/sink_with_user_defined_inner.py + > Line 7: outer_ret_val = outer_arg + 'hey' + File: examples/nested_functions_code/sink_with_user_defined_inner.py + > Line 8: ret_outer = outer_ret_val + File: examples/nested_functions_code/sink_with_user_defined_inner.py + > Line 6: req_param = save_2_req_param + File: examples/nested_functions_code/sink_with_user_defined_inner.py + > Line 18: ~call_2 = ret_outer + File: examples/nested_functions_code/sink_with_user_defined_inner.py + > reaches line 18, trigger word "subprocess.call(": + ~call_1 = ret_subprocess.call(~call_2, shell=True) """ - self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION)) - def test_XSS_no_vuln_result(self): - vulnerabilities = self.run_analysis('example/vulnerable_code/XSS_no_vuln.py') - self.assert_length(vulnerabilities, expected_length=0) + def test_find_vulnerabilities_import_file_command_injection(self): + vulnerabilities = self.run_analysis('examples/vulnerable_code_across_files/import_file_command_injection.py') - def test_XSS_reassign_result(self): - vulnerabilities = self.run_analysis('example/vulnerable_code/XSS_reassign.py') self.assert_length(vulnerabilities, expected_length=1) - vulnerability_description = str(vulnerabilities[0]) - EXPECTED_VULNERABILITY_DESCRIPTION = """ - File: example/vulnerable_code/XSS_reassign.py - > User input at line 6, trigger word "request.args.get(": - ~call_1 = ret_request.args.get('param', 'not set') - Reassigned in: - File: example/vulnerable_code/XSS_reassign.py - > Line 6: param = ~call_1 - File: example/vulnerable_code/XSS_reassign.py - > Line 8: param = param + '' - File: example/vulnerable_code/XSS_reassign.py - > Line 11: ~call_3 = ret_make_response(~call_4) - File: example/vulnerable_code/XSS_reassign.py - > Line 11: resp = ~call_3 - File: example/vulnerable_code/XSS_reassign.py - > Line 12: ret_XSS1 = resp - File: example/vulnerable_code/XSS_reassign.py - > reaches line 11, trigger word "replace(": - ~call_4 = ret_html.replace('{{ param }}', param) - """ - - self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION)) - def test_XSS_sanitised_result(self): - vulnerabilities = self.run_analysis('example/vulnerable_code/XSS_sanitised.py') + def test_find_vulnerabilities_import_file_command_injection_2(self): + vulnerabilities = self.run_analysis('examples/vulnerable_code_across_files/import_file_command_injection_2.py') self.assert_length(vulnerabilities, expected_length=1) - vulnerability_description = str(vulnerabilities[0]) - EXPECTED_VULNERABILITY_DESCRIPTION = """ - File: example/vulnerable_code/XSS_sanitised.py - > User input at line 7, trigger word "request.args.get(": - ~call_1 = ret_request.args.get('param', 'not set') - Reassigned in: - File: example/vulnerable_code/XSS_sanitised.py - > Line 7: param = ~call_1 - File: example/vulnerable_code/XSS_sanitised.py - > Line 9: ~call_2 = ret_Markup.escape(param) - File: example/vulnerable_code/XSS_sanitised.py - > Line 9: param = ~call_2 - File: example/vulnerable_code/XSS_sanitised.py - > Line 12: ~call_4 = ret_make_response(~call_5) - File: example/vulnerable_code/XSS_sanitised.py - > Line 12: resp = ~call_4 - File: example/vulnerable_code/XSS_sanitised.py - > Line 13: ret_XSS1 = resp - File: example/vulnerable_code/XSS_sanitised.py - > reaches line 12, trigger word "replace(": - ~call_5 = ret_html.replace('{{ param }}', param) - This vulnerability is sanitised by: Label: ~call_2 = ret_Markup.escape(param) - """ - self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION)) - - def test_XSS_variable_assign_no_vuln_result(self): - vulnerabilities = self.run_analysis('example/vulnerable_code/XSS_variable_assign_no_vuln.py') + def test_no_false_positive_import_file_command_injection_3(self): + vulnerabilities = self.run_analysis('examples/vulnerable_code_across_files/no_false_positive_import_file_command_injection_3.py') self.assert_length(vulnerabilities, expected_length=0) - - def test_XSS_variable_assign_result(self): - vulnerabilities = self.run_analysis('example/vulnerable_code/XSS_variable_assign.py') - self.assert_length(vulnerabilities, expected_length=1) - vulnerability_description = str(vulnerabilities[0]) - EXPECTED_VULNERABILITY_DESCRIPTION = """ - File: example/vulnerable_code/XSS_variable_assign.py - > User input at line 6, trigger word "request.args.get(": - ~call_1 = ret_request.args.get('param', 'not set') - Reassigned in: - File: example/vulnerable_code/XSS_variable_assign.py - > Line 6: param = ~call_1 - File: example/vulnerable_code/XSS_variable_assign.py - > Line 8: other_var = param + '' - File: example/vulnerable_code/XSS_variable_assign.py - > Line 11: ~call_3 = ret_make_response(~call_4) - File: example/vulnerable_code/XSS_variable_assign.py - > Line 11: resp = ~call_3 - File: example/vulnerable_code/XSS_variable_assign.py - > Line 12: ret_XSS1 = resp - File: example/vulnerable_code/XSS_variable_assign.py - > reaches line 11, trigger word "replace(": - ~call_4 = ret_html.replace('{{ param }}', other_var) - """ - - self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION)) - - def test_XSS_variable_multiple_assign_result(self): - vulnerabilities = self.run_analysis('example/vulnerable_code/XSS_variable_multiple_assign.py') - self.assert_length(vulnerabilities, expected_length=1) - vulnerability_description = str(vulnerabilities[0]) - EXPECTED_VULNERABILITY_DESCRIPTION = """ - File: example/vulnerable_code/XSS_variable_multiple_assign.py - > User input at line 6, trigger word "request.args.get(": - ~call_1 = ret_request.args.get('param', 'not set') - Reassigned in: - File: example/vulnerable_code/XSS_variable_multiple_assign.py - > Line 6: param = ~call_1 - File: example/vulnerable_code/XSS_variable_multiple_assign.py - > Line 8: other_var = param + '' - File: example/vulnerable_code/XSS_variable_multiple_assign.py - > Line 10: not_the_same_var = '' + other_var - File: example/vulnerable_code/XSS_variable_multiple_assign.py - > Line 12: another_one = not_the_same_var + '' - File: example/vulnerable_code/XSS_variable_multiple_assign.py - > Line 15: ~call_3 = ret_make_response(~call_4) - File: example/vulnerable_code/XSS_variable_multiple_assign.py - > Line 15: resp = ~call_3 - File: example/vulnerable_code/XSS_variable_multiple_assign.py - > Line 17: ret_XSS1 = resp - File: example/vulnerable_code/XSS_variable_multiple_assign.py - > reaches line 15, trigger word "replace(": - ~call_4 = ret_html.replace('{{ param }}', another_one) - """ - - self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION)) - - -class EngineDjangoTest(BaseTestCase): - def run_empty(self): - return - - def run_analysis(self, path): - self.cfg_create_from_file(path) - cfg_list = [self.cfg] - - FrameworkAdaptor(cfg_list, [], [], is_django_view_function) - initialize_constraint_table(cfg_list) - - analyse(cfg_list, analysis_type=ReachingDefinitionsTaintAnalysis) - - trigger_word_file = os.path.join( - 'pyt', - 'vulnerability_definitions', - 'django_trigger_words.pyt' - ) - - return vulnerabilities.find_vulnerabilities( - cfg_list, - ReachingDefinitionsTaintAnalysis, - UImode.NORMAL, - VulnerabilityFiles( - default_blackbox_mapping_file, - trigger_word_file - ), - nosec_lines - ) - - def test_django_view_param(self): - vulnerabilities = self.run_analysis('example/vulnerable_code/django_XSS.py') - self.assert_length(vulnerabilities, expected_length=2) - vulnerability_description = str(vulnerabilities[0]) - - EXPECTED_VULNERABILITY_DESCRIPTION = """ - File: example/vulnerable_code/django_XSS.py - > User input at line 4, trigger word "Framework function URL parameter": - param - Reassigned in: - File: example/vulnerable_code/django_XSS.py - > Line 5: ret_xss1 = ~call_1 - File: example/vulnerable_code/django_XSS.py - > reaches line 5, trigger word "render(": - ~call_1 = ret_render(request, 'templates/xss.html', 'param'param) - """ - self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION)) From bed2f77fc96e07ad6006df540cd450afd5b6b039 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20G=C3=BCnal?= Date: Thu, 26 Apr 2018 18:53:40 +0300 Subject: [PATCH 10/22] added nosec_lines --- tests/vulnerabilities_test.py | 697 ++++++++++++++++++++++++---------- 1 file changed, 497 insertions(+), 200 deletions(-) diff --git a/tests/vulnerabilities_test.py b/tests/vulnerabilities_test.py index b30d415b..381860ae 100644 --- a/tests/vulnerabilities_test.py +++ b/tests/vulnerabilities_test.py @@ -1,6 +1,11 @@ import os from .base_test_case import BaseTestCase + +from pyt import ( + trigger_definitions_parser, + vulnerabilities +) from pyt.argument_helpers import ( default_blackbox_mapping_file, default_trigger_word_file, @@ -10,30 +15,123 @@ from pyt.constraint_table import initialize_constraint_table from pyt.fixed_point import analyse from pyt.framework_adaptor import FrameworkAdaptor -from pyt.framework_helper import is_flask_route_function -from pyt.project_handler import get_directory_modules, get_modules +from pyt.framework_helper import ( + is_django_view_function, + is_flask_route_function, + is_function +) +from pyt.node_types import Node from pyt.reaching_definitions_taint import ReachingDefinitionsTaintAnalysis -from pyt.vulnerabilities import find_vulnerabilities nosec_lines = set() class EngineTest(BaseTestCase): - def run_analysis(self, path): - path = os.path.normpath(path) + def run_empty(self): + return + + def get_lattice_elements(self, cfg_nodes): + """Dummy analysis method""" + return cfg_nodes + + def test_parse(self): + definitions = vulnerabilities.parse( + trigger_word_file=os.path.join( + os.getcwd(), + 'pyt', + 'vulnerability_definitions', + 'test_triggers.pyt' + ) + ) + + self.assert_length(definitions.sources, expected_length=1) + self.assert_length(definitions.sinks, expected_length=3) + self.assert_length(definitions.sinks[0][1], expected_length=1) + self.assert_length(definitions.sinks[1][1], expected_length=3) + + def test_parse_section(self): + l = list(trigger_definitions_parser.parse_section(iter(['get']))) + self.assert_length(l, expected_length=1) + self.assertEqual(l[0][0], 'get') + self.assertEqual(l[0][1], list()) + + l = list(trigger_definitions_parser.parse_section(iter(['get', 'get -> a, b, c d s aq a']))) + self.assert_length(l, expected_length=2) + self.assertEqual(l[0][0], 'get') + self.assertEqual(l[1][0], 'get') + self.assertEqual(l[1][1], ['a', 'b', 'c d s aq a']) + self.assert_length(l[1][1], expected_length=3) + + def test_label_contains(self): + cfg_node = Node('label', None, line_number=None, path=None) + trigger_words = [('get', [])] + l = list(vulnerabilities.label_contains(cfg_node, trigger_words)) + self.assert_length(l, expected_length=0) + + cfg_node = Node('request.get("stefan")', None, line_number=None, path=None) + trigger_words = [('get', []), ('request', [])] + l = list(vulnerabilities.label_contains(cfg_node, trigger_words)) + self.assert_length(l, expected_length=2) + + trigger_node_1 = l[0] + trigger_node_2 = l[1] + self.assertEqual(trigger_node_1.trigger_word, 'get') + self.assertEqual(trigger_node_1.cfg_node, cfg_node) + self.assertEqual(trigger_node_2.trigger_word, 'request') + self.assertEqual(trigger_node_2.cfg_node, cfg_node) + + cfg_node = Node('request.get("stefan")', None, line_number=None, path=None) + trigger_words = [('get', []), ('get', [])] + l = list(vulnerabilities.label_contains(cfg_node, trigger_words)) + self.assert_length(l, expected_length=2) + + def test_find_triggers(self): + self.cfg_create_from_file('examples/vulnerable_code/XSS.py') + + cfg_list = [self.cfg] + + FrameworkAdaptor(cfg_list, [], [], is_flask_route_function) + + XSS1 = cfg_list[1] + trigger_words = [('get', [])] + + l = vulnerabilities.find_triggers(XSS1.nodes, trigger_words, nosec_lines) + self.assert_length(l, expected_length=1) - project_modules = get_modules(os.path.dirname(path)) - local_modules = get_directory_modules(os.path.dirname(path)) + def test_find_sanitiser_nodes(self): + cfg_node = Node(None, None, line_number=None, path=None) + sanitiser_tuple = vulnerabilities.Sanitiser('escape', cfg_node) + sanitiser = 'escape' - self.cfg_create_from_file(path, project_modules, local_modules) + result = list(vulnerabilities.find_sanitiser_nodes(sanitiser, [sanitiser_tuple])) + self.assert_length(result, expected_length=1) + self.assertEqual(result[0], cfg_node) + def test_build_sanitiser_node_dict(self): + self.cfg_create_from_file('examples/vulnerable_code/XSS_sanitised.py') cfg_list = [self.cfg] FrameworkAdaptor(cfg_list, [], [], is_flask_route_function) + cfg = cfg_list[1] + + cfg_node = Node(None, None, line_number=None, path=None) + sinks_in_file = [vulnerabilities.TriggerNode('replace', ['escape'], cfg_node)] + + sanitiser_dict = vulnerabilities.build_sanitiser_node_dict(cfg, sinks_in_file) + self.assert_length(sanitiser_dict, expected_length=1) + self.assertIn('escape', sanitiser_dict.keys()) + + self.assertEqual(sanitiser_dict['escape'][0], cfg.nodes[3]) + + def run_analysis(self, path): + self.cfg_create_from_file(path) + cfg_list = [self.cfg] + + FrameworkAdaptor(cfg_list, [], [], is_flask_route_function) initialize_constraint_table(cfg_list) analyse(cfg_list, analysis_type=ReachingDefinitionsTaintAnalysis) - return find_vulnerabilities( + return vulnerabilities.find_vulnerabilities( cfg_list, ReachingDefinitionsTaintAnalysis, UImode.NORMAL, @@ -44,252 +142,451 @@ def run_analysis(self, path): nosec_lines ) - def test_find_vulnerabilities_absolute_from_file_command_injection(self): - vulnerabilities = self.run_analysis('examples/vulnerable_code_across_files/absolute_from_file_command_injection.py') + def test_find_vulnerabilities_assign_other_var(self): + vulnerabilities = self.run_analysis('examples/vulnerable_code/XSS_assign_to_other_var.py') + self.assert_length(vulnerabilities, expected_length=1) + def test_find_vulnerabilities_inter_command_injection(self): + vulnerabilities = self.run_analysis('examples/vulnerable_code/inter_command_injection.py') self.assert_length(vulnerabilities, expected_length=1) - def test_find_vulnerabilities_absolute_from_file_command_injection_2(self): - vulnerabilities = self.run_analysis('examples/vulnerable_code_across_files/absolute_from_file_command_injection_2.py') + def test_find_vulnerabilities_inter_command_injection_2(self): + vulnerabilities = self.run_analysis('examples/vulnerable_code/inter_command_injection_2.py') self.assert_length(vulnerabilities, expected_length=1) - def test_no_false_positive_absolute_from_file_command_injection_3(self): - vulnerabilities = self.run_analysis('examples/vulnerable_code_across_files/no_false_positive_absolute_from_file_command_injection_3.py') - self.assert_length(vulnerabilities, expected_length=0) + def test_XSS_result(self): + vulnerabilities = self.run_analysis('examples/vulnerable_code/XSS.py') + self.assert_length(vulnerabilities, expected_length=1) + vulnerability_description = str(vulnerabilities[0]) + EXPECTED_VULNERABILITY_DESCRIPTION = """ + File: examples/vulnerable_code/XSS.py + > User input at line 6, trigger word "request.args.get(": + ~call_1 = ret_request.args.get('param', 'not set') + Reassigned in: + File: examples/vulnerable_code/XSS.py + > Line 6: param = ~call_1 + File: examples/vulnerable_code/XSS.py + > Line 9: ~call_3 = ret_make_response(~call_4) + File: examples/vulnerable_code/XSS.py + > Line 9: resp = ~call_3 + File: examples/vulnerable_code/XSS.py + > Line 10: ret_XSS1 = resp + File: examples/vulnerable_code/XSS.py + > reaches line 9, trigger word "replace(": + ~call_4 = ret_html.replace('{{ param }}', param) + """ - def test_blackbox_library_call(self): - vulnerabilities = self.run_analysis('examples/vulnerable_code_across_files/blackbox_library_call.py') + self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION)) + + def test_command_injection_result(self): + vulnerabilities = self.run_analysis('examples/vulnerable_code/command_injection.py') self.assert_length(vulnerabilities, expected_length=1) vulnerability_description = str(vulnerabilities[0]) EXPECTED_VULNERABILITY_DESCRIPTION = """ - File: examples/vulnerable_code_across_files/blackbox_library_call.py - > User input at line 12, trigger word "request.args.get(": - ~call_1 = ret_request.args.get('suggestion') + File: examples/vulnerable_code/command_injection.py + > User input at line 15, trigger word "form[": + param = request.form['suggestion'] Reassigned in: - File: examples/vulnerable_code_across_files/blackbox_library_call.py - > Line 12: param = ~call_1 - File: examples/vulnerable_code_across_files/blackbox_library_call.py - > Line 15: ~call_2 = ret_scrypt.encrypt('echo ' + param + ' >> ' + 'menu.txt', 'password') - File: examples/vulnerable_code_across_files/blackbox_library_call.py - > Line 15: command = ~call_2 - File: examples/vulnerable_code_across_files/blackbox_library_call.py - > Line 16: hey = command - File: examples/vulnerable_code_across_files/blackbox_library_call.py - > reaches line 17, trigger word "subprocess.call(": - ~call_3 = ret_subprocess.call(hey, shell=True) - This vulnerability is unknown due to: Label: ~call_2 = ret_scrypt.encrypt('echo ' + param + ' >> ' + 'menu.txt', 'password') + File: examples/vulnerable_code/command_injection.py + > Line 16: command = 'echo ' + param + ' >> ' + 'menu.txt' + File: examples/vulnerable_code/command_injection.py + > reaches line 18, trigger word "subprocess.call(": + ~call_1 = ret_subprocess.call(command, shell=True) """ self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION)) - def test_builtin_with_user_defined_inner(self): - vulnerabilities = self.run_analysis('examples/nested_functions_code/builtin_with_user_defined_inner.py') + def test_path_traversal_result(self): + vulnerabilities = self.run_analysis('examples/vulnerable_code/path_traversal.py') self.assert_length(vulnerabilities, expected_length=1) vulnerability_description = str(vulnerabilities[0]) EXPECTED_VULNERABILITY_DESCRIPTION = """ - File: examples/nested_functions_code/builtin_with_user_defined_inner.py - > User input at line 16, trigger word "form[": - req_param = request.form['suggestion'] + File: examples/vulnerable_code/path_traversal.py + > User input at line 15, trigger word "request.args.get(": + ~call_1 = ret_request.args.get('image_name') Reassigned in: - File: examples/nested_functions_code/builtin_with_user_defined_inner.py - > Line 10: save_2_req_param = req_param - File: examples/nested_functions_code/builtin_with_user_defined_inner.py - > Line 19: temp_2_inner_arg = req_param - File: examples/nested_functions_code/builtin_with_user_defined_inner.py - > Line 10: inner_arg = temp_2_inner_arg - File: examples/nested_functions_code/builtin_with_user_defined_inner.py - > Line 11: yes_vuln = inner_arg + 'hey' - File: examples/nested_functions_code/builtin_with_user_defined_inner.py - > Line 12: ret_inner = yes_vuln - File: examples/nested_functions_code/builtin_with_user_defined_inner.py - > Line 10: req_param = save_2_req_param - File: examples/nested_functions_code/builtin_with_user_defined_inner.py - > Line 19: ~call_2 = ret_inner - File: examples/nested_functions_code/builtin_with_user_defined_inner.py - > Line 19: ~call_1 = ret_scrypt.encrypt(~call_2) - File: examples/nested_functions_code/builtin_with_user_defined_inner.py - > Line 19: foo = ~call_1 - File: examples/nested_functions_code/builtin_with_user_defined_inner.py - > reaches line 20, trigger word "subprocess.call(": - ~call_3 = ret_subprocess.call(foo, shell=True) - This vulnerability is unknown due to: Label: ~call_1 = ret_scrypt.encrypt(~call_2) + File: examples/vulnerable_code/path_traversal.py + > Line 15: image_name = ~call_1 + File: examples/vulnerable_code/path_traversal.py + > Line 6: save_2_image_name = image_name + File: examples/vulnerable_code/path_traversal.py + > Line 10: save_3_image_name = image_name + File: examples/vulnerable_code/path_traversal.py + > Line 10: image_name = save_3_image_name + File: examples/vulnerable_code/path_traversal.py + > Line 19: temp_2_other_arg = image_name + File: examples/vulnerable_code/path_traversal.py + > Line 6: other_arg = temp_2_other_arg + File: examples/vulnerable_code/path_traversal.py + > Line 7: outer_ret_val = outer_arg + 'hey' + other_arg + File: examples/vulnerable_code/path_traversal.py + > Line 8: ret_outer = outer_ret_val + File: examples/vulnerable_code/path_traversal.py + > Line 6: image_name = save_2_image_name + File: examples/vulnerable_code/path_traversal.py + > Line 19: ~call_2 = ret_outer + File: examples/vulnerable_code/path_traversal.py + > Line 19: foo = ~call_2 + File: examples/vulnerable_code/path_traversal.py + > reaches line 20, trigger word "send_file(": + ~call_4 = ret_send_file(foo) """ + self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION)) - def test_sink_with_result_of_blackbox_nested(self): - vulnerabilities = self.run_analysis('examples/nested_functions_code/sink_with_result_of_blackbox_nested.py') + def test_ensure_saved_scope(self): + vulnerabilities = self.run_analysis('examples/vulnerable_code/ensure_saved_scope.py') self.assert_length(vulnerabilities, expected_length=1) vulnerability_description = str(vulnerabilities[0]) EXPECTED_VULNERABILITY_DESCRIPTION = """ - File: examples/nested_functions_code/sink_with_result_of_blackbox_nested.py - > User input at line 12, trigger word "form[": - req_param = request.form['suggestion'] + File: examples/vulnerable_code/ensure_saved_scope.py + > User input at line 15, trigger word "request.args.get(": + ~call_1 = ret_request.args.get('image_name') Reassigned in: - File: examples/nested_functions_code/sink_with_result_of_blackbox_nested.py - > Line 13: ~call_2 = ret_scrypt.encrypt(req_param) - File: examples/nested_functions_code/sink_with_result_of_blackbox_nested.py - > Line 13: ~call_1 = ret_scrypt.encrypt(~call_2) - File: examples/nested_functions_code/sink_with_result_of_blackbox_nested.py - > Line 13: result = ~call_1 - File: examples/nested_functions_code/sink_with_result_of_blackbox_nested.py - > reaches line 14, trigger word "subprocess.call(": - ~call_3 = ret_subprocess.call(result, shell=True) - This vulnerability is unknown due to: Label: ~call_2 = ret_scrypt.encrypt(req_param) + File: examples/vulnerable_code/ensure_saved_scope.py + > Line 15: image_name = ~call_1 + File: examples/vulnerable_code/ensure_saved_scope.py + > Line 6: save_2_image_name = image_name + File: examples/vulnerable_code/ensure_saved_scope.py + > Line 10: save_3_image_name = image_name + File: examples/vulnerable_code/ensure_saved_scope.py + > Line 10: image_name = save_3_image_name + File: examples/vulnerable_code/ensure_saved_scope.py + > Line 19: temp_2_other_arg = image_name + File: examples/vulnerable_code/ensure_saved_scope.py + > Line 6: other_arg = temp_2_other_arg + File: examples/vulnerable_code/ensure_saved_scope.py + > Line 7: outer_ret_val = outer_arg + 'hey' + other_arg + File: examples/vulnerable_code/ensure_saved_scope.py + > Line 8: ret_outer = outer_ret_val + File: examples/vulnerable_code/ensure_saved_scope.py + > Line 6: image_name = save_2_image_name + File: examples/vulnerable_code/ensure_saved_scope.py + > Line 19: ~call_2 = ret_outer + File: examples/vulnerable_code/ensure_saved_scope.py + > Line 19: foo = ~call_2 + File: examples/vulnerable_code/ensure_saved_scope.py + > reaches line 20, trigger word "send_file(": + ~call_4 = ret_send_file(image_name) """ - OTHER_EXPECTED_VULNERABILITY_DESCRIPTION = """ - File: examples/nested_functions_code/sink_with_result_of_blackbox_nested.py - > User input at line 12, trigger word "form[": - req_param = request.form['suggestion'] + + self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION)) + + def test_path_traversal_sanitised_result(self): + vulnerabilities = self.run_analysis('examples/vulnerable_code/path_traversal_sanitised.py') + self.assert_length(vulnerabilities, expected_length=1) + vulnerability_description = str(vulnerabilities[0]) + EXPECTED_VULNERABILITY_DESCRIPTION = """ + File: examples/vulnerable_code/path_traversal_sanitised.py + > User input at line 8, trigger word "request.args.get(": + ~call_1 = ret_request.args.get('image_name') Reassigned in: - File: examples/nested_functions_code/sink_with_result_of_blackbox_nested.py - > Line 13: ~call_2 = ret_scrypt.encrypt(req_param) - File: examples/nested_functions_code/sink_with_result_of_blackbox_nested.py - > Line 13: ~call_1 = ret_scrypt.encrypt(~call_2) - File: examples/nested_functions_code/sink_with_result_of_blackbox_nested.py - > Line 13: result = ~call_1 - File: examples/nested_functions_code/sink_with_result_of_blackbox_nested.py - > reaches line 14, trigger word "subprocess.call(": - ~call_3 = ret_subprocess.call(result, shell=True) - This vulnerability is unknown due to: Label: ~call_1 = ret_scrypt.encrypt(~call_2) + File: examples/vulnerable_code/path_traversal_sanitised.py + > Line 8: image_name = ~call_1 + File: examples/vulnerable_code/path_traversal_sanitised.py + > Line 10: ~call_2 = ret_image_name.replace('..', '') + File: examples/vulnerable_code/path_traversal_sanitised.py + > Line 10: image_name = ~call_2 + File: examples/vulnerable_code/path_traversal_sanitised.py + > Line 12: ~call_4 = ret_os.path.join(~call_5, image_name) + File: examples/vulnerable_code/path_traversal_sanitised.py + > Line 12: ret_cat_picture = ~call_3 + File: examples/vulnerable_code/path_traversal_sanitised.py + > reaches line 12, trigger word "send_file(": + ~call_3 = ret_send_file(~call_4) + This vulnerability is sanitised by: Label: ~call_2 = ret_image_name.replace('..', '') """ - self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION) - or - self.string_compare_alpha(vulnerability_description, OTHER_EXPECTED_VULNERABILITY_DESCRIPTION)) - def test_sink_with_result_of_user_defined_nested(self): - vulnerabilities = self.run_analysis('examples/nested_functions_code/sink_with_result_of_user_defined_nested.py') + self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION)) + + def test_path_traversal_sanitised_2_result(self): + vulnerabilities = self.run_analysis('examples/vulnerable_code/path_traversal_sanitised_2.py') self.assert_length(vulnerabilities, expected_length=1) vulnerability_description = str(vulnerabilities[0]) EXPECTED_VULNERABILITY_DESCRIPTION = """ - File: examples/nested_functions_code/sink_with_result_of_user_defined_nested.py - > User input at line 16, trigger word "form[": - req_param = request.form['suggestion'] + File: examples/vulnerable_code/path_traversal_sanitised_2.py + > User input at line 8, trigger word "request.args.get(": + ~call_1 = ret_request.args.get('image_name') Reassigned in: - File: examples/nested_functions_code/sink_with_result_of_user_defined_nested.py - > Line 6: save_1_req_param = req_param - File: examples/nested_functions_code/sink_with_result_of_user_defined_nested.py - > Line 10: save_2_req_param = req_param - File: examples/nested_functions_code/sink_with_result_of_user_defined_nested.py - > Line 17: temp_2_inner_arg = req_param - File: examples/nested_functions_code/sink_with_result_of_user_defined_nested.py - > Line 10: inner_arg = temp_2_inner_arg - File: examples/nested_functions_code/sink_with_result_of_user_defined_nested.py - > Line 11: inner_ret_val = inner_arg + 'hey' - File: examples/nested_functions_code/sink_with_result_of_user_defined_nested.py - > Line 12: ret_inner = inner_ret_val - File: examples/nested_functions_code/sink_with_result_of_user_defined_nested.py - > Line 10: req_param = save_2_req_param - File: examples/nested_functions_code/sink_with_result_of_user_defined_nested.py - > Line 17: ~call_2 = ret_inner - File: examples/nested_functions_code/sink_with_result_of_user_defined_nested.py - > Line 17: temp_1_outer_arg = ~call_2 - File: examples/nested_functions_code/sink_with_result_of_user_defined_nested.py - > Line 6: outer_arg = temp_1_outer_arg - File: examples/nested_functions_code/sink_with_result_of_user_defined_nested.py - > Line 7: outer_ret_val = outer_arg + 'hey' - File: examples/nested_functions_code/sink_with_result_of_user_defined_nested.py - > Line 8: ret_outer = outer_ret_val - File: examples/nested_functions_code/sink_with_result_of_user_defined_nested.py - > Line 6: req_param = save_1_req_param - File: examples/nested_functions_code/sink_with_result_of_user_defined_nested.py - > Line 17: ~call_1 = ret_outer - File: examples/nested_functions_code/sink_with_result_of_user_defined_nested.py - > Line 17: result = ~call_1 - File: examples/nested_functions_code/sink_with_result_of_user_defined_nested.py - > reaches line 18, trigger word "subprocess.call(": - ~call_3 = ret_subprocess.call(result, shell=True) + File: examples/vulnerable_code/path_traversal_sanitised_2.py + > Line 8: image_name = ~call_1 + File: examples/vulnerable_code/path_traversal_sanitised_2.py + > Line 12: ~call_3 = ret_os.path.join(~call_4, image_name) + File: examples/vulnerable_code/path_traversal_sanitised_2.py + > Line 12: ret_cat_picture = ~call_2 + File: examples/vulnerable_code/path_traversal_sanitised_2.py + > reaches line 12, trigger word "send_file(": + ~call_2 = ret_send_file(~call_3) + This vulnerability is potentially sanitised by: Label: if '..' in image_name: """ + self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION)) - def test_sink_with_blackbox_inner(self): - vulnerabilities = self.run_analysis('examples/nested_functions_code/sink_with_blackbox_inner.py') + def test_sql_result(self): + vulnerabilities = self.run_analysis('examples/vulnerable_code/sql/sqli.py') self.assert_length(vulnerabilities, expected_length=1) vulnerability_description = str(vulnerabilities[0]) EXPECTED_VULNERABILITY_DESCRIPTION = """ - File: examples/nested_functions_code/sink_with_blackbox_inner.py - > User input at line 12, trigger word "form[": - req_param = request.form['suggestion'] + File: examples/vulnerable_code/sql/sqli.py + > User input at line 26, trigger word "request.args.get(": + ~call_1 = ret_request.args.get('param', 'not set') Reassigned in: - File: examples/nested_functions_code/sink_with_blackbox_inner.py - > Line 14: ~call_3 = ret_scrypt.encypt(req_param) - File: examples/nested_functions_code/sink_with_blackbox_inner.py - > Line 14: ~call_2 = ret_scrypt.encypt(~call_3) - File: examples/nested_functions_code/sink_with_blackbox_inner.py - > reaches line 14, trigger word "subprocess.call(": - ~call_1 = ret_subprocess.call(~call_2, shell=True) - This vulnerability is unknown due to: Label: ~call_2 = ret_scrypt.encypt(~call_3) + File: examples/vulnerable_code/sql/sqli.py + > Line 26: param = ~call_1 + File: examples/vulnerable_code/sql/sqli.py + > Line 27: result = ~call_2 + File: examples/vulnerable_code/sql/sqli.py + > reaches line 27, trigger word "execute(": + ~call_2 = ret_db.engine.execute(param) """ - OTHER_EXPECTED_VULNERABILITY_DESCRIPTION = """ - File: examples/nested_functions_code/sink_with_blackbox_inner.py - > User input at line 12, trigger word "form[": - req_param = request.form['suggestion'] + self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION)) + + def test_XSS_form_result(self): + vulnerabilities = self.run_analysis('examples/vulnerable_code/XSS_form.py') + self.assert_length(vulnerabilities, expected_length=1) + vulnerability_description = str(vulnerabilities[0]) + EXPECTED_VULNERABILITY_DESCRIPTION = """ + File: examples/vulnerable_code/XSS_form.py + > User input at line 14, trigger word "form[": + data = request.form['my_text'] Reassigned in: - File: examples/nested_functions_code/sink_with_blackbox_inner.py - > Line 14: ~call_3 = ret_scrypt.encypt(req_param) - File: examples/nested_functions_code/sink_with_blackbox_inner.py - > Line 14: ~call_2 = ret_scrypt.encypt(~call_3) - File: examples/nested_functions_code/sink_with_blackbox_inner.py - > reaches line 14, trigger word "subprocess.call(": - ~call_1 = ret_subprocess.call(~call_2, shell=True) - This vulnerability is unknown due to: Label: ~call_3 = ret_scrypt.encypt(req_param) + File: examples/vulnerable_code/XSS_form.py + > Line 15: ~call_1 = ret_make_response(~call_2) + File: examples/vulnerable_code/XSS_form.py + > Line 15: resp = ~call_1 + File: examples/vulnerable_code/XSS_form.py + > Line 17: ret_example2_action = resp + File: examples/vulnerable_code/XSS_form.py + > reaches line 15, trigger word "replace(": + ~call_2 = ret_html1.replace('{{ data }}', data) """ - self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION) - or - self.string_compare_alpha(vulnerability_description, OTHER_EXPECTED_VULNERABILITY_DESCRIPTION)) - def test_sink_with_user_defined_inner(self): - vulnerabilities = self.run_analysis('examples/nested_functions_code/sink_with_user_defined_inner.py') + self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION)) + + def test_XSS_url_result(self): + vulnerabilities = self.run_analysis('examples/vulnerable_code/XSS_url.py') self.assert_length(vulnerabilities, expected_length=1) vulnerability_description = str(vulnerabilities[0]) EXPECTED_VULNERABILITY_DESCRIPTION = """ - File: examples/nested_functions_code/sink_with_user_defined_inner.py - > User input at line 16, trigger word "form[": - req_param = request.form['suggestion'] + File: examples/vulnerable_code/XSS_url.py + > User input at line 4, trigger word "Framework function URL parameter": + url Reassigned in: - File: examples/nested_functions_code/sink_with_user_defined_inner.py - > Line 6: save_2_req_param = req_param - File: examples/nested_functions_code/sink_with_user_defined_inner.py - > Line 10: save_3_req_param = req_param - File: examples/nested_functions_code/sink_with_user_defined_inner.py - > Line 18: temp_3_inner_arg = req_param - File: examples/nested_functions_code/sink_with_user_defined_inner.py - > Line 10: inner_arg = temp_3_inner_arg - File: examples/nested_functions_code/sink_with_user_defined_inner.py - > Line 11: inner_ret_val = inner_arg + 'hey' - File: examples/nested_functions_code/sink_with_user_defined_inner.py - > Line 12: ret_inner = inner_ret_val - File: examples/nested_functions_code/sink_with_user_defined_inner.py - > Line 10: req_param = save_3_req_param - File: examples/nested_functions_code/sink_with_user_defined_inner.py - > Line 18: ~call_3 = ret_inner - File: examples/nested_functions_code/sink_with_user_defined_inner.py - > Line 18: temp_2_outer_arg = ~call_3 - File: examples/nested_functions_code/sink_with_user_defined_inner.py - > Line 6: outer_arg = temp_2_outer_arg - File: examples/nested_functions_code/sink_with_user_defined_inner.py - > Line 7: outer_ret_val = outer_arg + 'hey' - File: examples/nested_functions_code/sink_with_user_defined_inner.py - > Line 8: ret_outer = outer_ret_val - File: examples/nested_functions_code/sink_with_user_defined_inner.py - > Line 6: req_param = save_2_req_param - File: examples/nested_functions_code/sink_with_user_defined_inner.py - > Line 18: ~call_2 = ret_outer - File: examples/nested_functions_code/sink_with_user_defined_inner.py - > reaches line 18, trigger word "subprocess.call(": - ~call_1 = ret_subprocess.call(~call_2, shell=True) + File: examples/vulnerable_code/XSS_url.py + > Line 6: param = url + File: examples/vulnerable_code/XSS_url.py + > Line 9: ~call_2 = ret_make_response(~call_3) + File: examples/vulnerable_code/XSS_url.py + > Line 9: resp = ~call_2 + File: examples/vulnerable_code/XSS_url.py + > Line 10: ret_XSS1 = resp + File: examples/vulnerable_code/XSS_url.py + > reaches line 9, trigger word "replace(": + ~call_3 = ret_html.replace('{{ param }}', param) """ + self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION)) - def test_find_vulnerabilities_import_file_command_injection(self): - vulnerabilities = self.run_analysis('examples/vulnerable_code_across_files/import_file_command_injection.py') + def test_XSS_no_vuln_result(self): + vulnerabilities = self.run_analysis('examples/vulnerable_code/XSS_no_vuln.py') + self.assert_length(vulnerabilities, expected_length=0) + def test_XSS_reassign_result(self): + vulnerabilities = self.run_analysis('examples/vulnerable_code/XSS_reassign.py') self.assert_length(vulnerabilities, expected_length=1) + vulnerability_description = str(vulnerabilities[0]) + EXPECTED_VULNERABILITY_DESCRIPTION = """ + File: examples/vulnerable_code/XSS_reassign.py + > User input at line 6, trigger word "request.args.get(": + ~call_1 = ret_request.args.get('param', 'not set') + Reassigned in: + File: examples/vulnerable_code/XSS_reassign.py + > Line 6: param = ~call_1 + File: examples/vulnerable_code/XSS_reassign.py + > Line 8: param = param + '' + File: examples/vulnerable_code/XSS_reassign.py + > Line 11: ~call_3 = ret_make_response(~call_4) + File: examples/vulnerable_code/XSS_reassign.py + > Line 11: resp = ~call_3 + File: examples/vulnerable_code/XSS_reassign.py + > Line 12: ret_XSS1 = resp + File: examples/vulnerable_code/XSS_reassign.py + > reaches line 11, trigger word "replace(": + ~call_4 = ret_html.replace('{{ param }}', param) + """ + + self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION)) - def test_find_vulnerabilities_import_file_command_injection_2(self): - vulnerabilities = self.run_analysis('examples/vulnerable_code_across_files/import_file_command_injection_2.py') + def test_XSS_sanitised_result(self): + vulnerabilities = self.run_analysis('examples/vulnerable_code/XSS_sanitised.py') self.assert_length(vulnerabilities, expected_length=1) + vulnerability_description = str(vulnerabilities[0]) + EXPECTED_VULNERABILITY_DESCRIPTION = """ + File: examples/vulnerable_code/XSS_sanitised.py + > User input at line 7, trigger word "request.args.get(": + ~call_1 = ret_request.args.get('param', 'not set') + Reassigned in: + File: examples/vulnerable_code/XSS_sanitised.py + > Line 7: param = ~call_1 + File: examples/vulnerable_code/XSS_sanitised.py + > Line 9: ~call_2 = ret_Markup.escape(param) + File: examples/vulnerable_code/XSS_sanitised.py + > Line 9: param = ~call_2 + File: examples/vulnerable_code/XSS_sanitised.py + > Line 12: ~call_4 = ret_make_response(~call_5) + File: examples/vulnerable_code/XSS_sanitised.py + > Line 12: resp = ~call_4 + File: examples/vulnerable_code/XSS_sanitised.py + > Line 13: ret_XSS1 = resp + File: examples/vulnerable_code/XSS_sanitised.py + > reaches line 12, trigger word "replace(": + ~call_5 = ret_html.replace('{{ param }}', param) + This vulnerability is sanitised by: Label: ~call_2 = ret_Markup.escape(param) + """ + + self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION)) + + def test_XSS_variable_assign_no_vuln_result(self): + vulnerabilities = self.run_analysis('examples/vulnerable_code/XSS_variable_assign_no_vuln.py') + self.assert_length(vulnerabilities, expected_length=0) + + def test_XSS_variable_assign_result(self): + vulnerabilities = self.run_analysis('examples/vulnerable_code/XSS_variable_assign.py') + self.assert_length(vulnerabilities, expected_length=1) + vulnerability_description = str(vulnerabilities[0]) + EXPECTED_VULNERABILITY_DESCRIPTION = """ + File: examples/vulnerable_code/XSS_variable_assign.py + > User input at line 6, trigger word "request.args.get(": + ~call_1 = ret_request.args.get('param', 'not set') + Reassigned in: + File: examples/vulnerable_code/XSS_variable_assign.py + > Line 6: param = ~call_1 + File: examples/vulnerable_code/XSS_variable_assign.py + > Line 8: other_var = param + '' + File: examples/vulnerable_code/XSS_variable_assign.py + > Line 11: ~call_3 = ret_make_response(~call_4) + File: examples/vulnerable_code/XSS_variable_assign.py + > Line 11: resp = ~call_3 + File: examples/vulnerable_code/XSS_variable_assign.py + > Line 12: ret_XSS1 = resp + File: examples/vulnerable_code/XSS_variable_assign.py + > reaches line 11, trigger word "replace(": + ~call_4 = ret_html.replace('{{ param }}', other_var) + """ + + self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION)) + + def test_XSS_variable_multiple_assign_result(self): + vulnerabilities = self.run_analysis('examples/vulnerable_code/XSS_variable_multiple_assign.py') + self.assert_length(vulnerabilities, expected_length=1) + vulnerability_description = str(vulnerabilities[0]) + EXPECTED_VULNERABILITY_DESCRIPTION = """ + File: examples/vulnerable_code/XSS_variable_multiple_assign.py + > User input at line 6, trigger word "request.args.get(": + ~call_1 = ret_request.args.get('param', 'not set') + Reassigned in: + File: examples/vulnerable_code/XSS_variable_multiple_assign.py + > Line 6: param = ~call_1 + File: examples/vulnerable_code/XSS_variable_multiple_assign.py + > Line 8: other_var = param + '' + File: examples/vulnerable_code/XSS_variable_multiple_assign.py + > Line 10: not_the_same_var = '' + other_var + File: examples/vulnerable_code/XSS_variable_multiple_assign.py + > Line 12: another_one = not_the_same_var + '' + File: examples/vulnerable_code/XSS_variable_multiple_assign.py + > Line 15: ~call_3 = ret_make_response(~call_4) + File: examples/vulnerable_code/XSS_variable_multiple_assign.py + > Line 15: resp = ~call_3 + File: examples/vulnerable_code/XSS_variable_multiple_assign.py + > Line 17: ret_XSS1 = resp + File: examples/vulnerable_code/XSS_variable_multiple_assign.py + > reaches line 15, trigger word "replace(": + ~call_4 = ret_html.replace('{{ param }}', another_one) + """ + + self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION)) + + +class EngineDjangoTest(BaseTestCase): + def run_empty(self): + return + + def run_analysis(self, path): + self.cfg_create_from_file(path) + cfg_list = [self.cfg] + + FrameworkAdaptor(cfg_list, [], [], is_django_view_function) + initialize_constraint_table(cfg_list) + + analyse(cfg_list, analysis_type=ReachingDefinitionsTaintAnalysis) + + trigger_word_file = os.path.join( + 'pyt', + 'vulnerability_definitions', + 'django_trigger_words.pyt' + ) + + return vulnerabilities.find_vulnerabilities( + cfg_list, + ReachingDefinitionsTaintAnalysis, + UImode.NORMAL, + VulnerabilityFiles( + default_blackbox_mapping_file, + trigger_word_file + ), + nosec_lines + ) + + def test_django_view_param(self): + vulnerabilities = self.run_analysis('examples/vulnerable_code/django_XSS.py') + self.assert_length(vulnerabilities, expected_length=2) + vulnerability_description = str(vulnerabilities[0]) + + EXPECTED_VULNERABILITY_DESCRIPTION = """ + File: examples/vulnerable_code/django_XSS.py + > User input at line 4, trigger word "Framework function URL parameter": + param + Reassigned in: + File: examples/vulnerable_code/django_XSS.py + > Line 5: ret_xss1 = ~call_1 + File: examples/vulnerable_code/django_XSS.py + > reaches line 5, trigger word "render(": + ~call_1 = ret_render(request, 'templates/xss.html', 'param'param) + """ + self.assertTrue(self.string_compare_alpha(vulnerability_description, EXPECTED_VULNERABILITY_DESCRIPTION)) + + +class EngineEveryTest(BaseTestCase): + def run_empty(self): + return + + def run_analysis(self, path): + self.cfg_create_from_file(path) + cfg_list = [self.cfg] + + FrameworkAdaptor(cfg_list, [], [], is_function) + initialize_constraint_table(cfg_list) + + analyse(cfg_list, analysis_type=ReachingDefinitionsTaintAnalysis) + + trigger_word_file = os.path.join( + 'pyt', + 'vulnerability_definitions', + 'all_trigger_words.pyt' + ) + + return vulnerabilities.find_vulnerabilities( + cfg_list, + ReachingDefinitionsTaintAnalysis, + UImode.NORMAL, + VulnerabilityFiles( + default_blackbox_mapping_file, + trigger_word_file + ), + nosec_lines + ) - def test_no_false_positive_import_file_command_injection_3(self): - vulnerabilities = self.run_analysis('examples/vulnerable_code_across_files/no_false_positive_import_file_command_injection_3.py') + def test_self_is_not_tainted(self): + vulnerabilities = self.run_analysis('examples/example_inputs/def_with_self_as_first_arg.py') self.assert_length(vulnerabilities, expected_length=0) From 69d019327eca9f916aefbaad2cc2c433309c36bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20G=C3=BCnal?= Date: Thu, 26 Apr 2018 18:54:51 +0300 Subject: [PATCH 11/22] added empty nosec_lines for tests --- tests/vulnerabilities_across_files_test.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/vulnerabilities_across_files_test.py b/tests/vulnerabilities_across_files_test.py index 7492aee2..b30d415b 100644 --- a/tests/vulnerabilities_across_files_test.py +++ b/tests/vulnerabilities_across_files_test.py @@ -15,7 +15,7 @@ from pyt.reaching_definitions_taint import ReachingDefinitionsTaintAnalysis from pyt.vulnerabilities import find_vulnerabilities - +nosec_lines = set() class EngineTest(BaseTestCase): def run_analysis(self, path): path = os.path.normpath(path) @@ -40,7 +40,8 @@ def run_analysis(self, path): VulnerabilityFiles( default_blackbox_mapping_file, default_trigger_word_file - ) + ), + nosec_lines ) def test_find_vulnerabilities_absolute_from_file_command_injection(self): From 3cb5186942473d7a8fceb851e09cb36133355c7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20G=C3=BCnal?= Date: Thu, 26 Apr 2018 18:57:10 +0300 Subject: [PATCH 12/22] Added ignore-nosec argument --- tests/command_line_test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/command_line_test.py b/tests/command_line_test.py index a4663225..25d06a73 100644 --- a/tests/command_line_test.py +++ b/tests/command_line_test.py @@ -28,6 +28,7 @@ def test_no_args(self): [-m BLACKBOX_MAPPING_FILE] [-py2] [-l LOG_LEVEL] [-a ADAPTOR] [-db] [-dl DRAW_LATTICE [DRAW_LATTICE ...]] [-j] [-li | -re | -rt] [-ppm] [-b BASELINE] + [--ignore-nosec] {save,github_search} ...\n""" + \ "python -m pyt: error: one of the arguments " + \ "-f/--filepath -gr/--git-repos is required\n" From ec6d23acbe87bc0cc0f97fa91bb5bdca4f58a4cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20G=C3=BCnal?= Date: Thu, 26 Apr 2018 20:51:55 +0300 Subject: [PATCH 13/22] Update vulnerabilities_test.py --- tests/vulnerabilities_test.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/tests/vulnerabilities_test.py b/tests/vulnerabilities_test.py index 381860ae..b4698e91 100644 --- a/tests/vulnerabilities_test.py +++ b/tests/vulnerabilities_test.py @@ -23,7 +23,6 @@ from pyt.node_types import Node from pyt.reaching_definitions_taint import ReachingDefinitionsTaintAnalysis -nosec_lines = set() class EngineTest(BaseTestCase): def run_empty(self): return @@ -93,7 +92,7 @@ def test_find_triggers(self): XSS1 = cfg_list[1] trigger_words = [('get', [])] - l = vulnerabilities.find_triggers(XSS1.nodes, trigger_words, nosec_lines) + l = vulnerabilities.find_triggers(XSS1.nodes, trigger_words) self.assert_length(l, expected_length=1) def test_find_sanitiser_nodes(self): @@ -138,8 +137,7 @@ def run_analysis(self, path): VulnerabilityFiles( default_blackbox_mapping_file, default_trigger_word_file - ), - nosec_lines + ) ) def test_find_vulnerabilities_assign_other_var(self): @@ -534,8 +532,7 @@ def run_analysis(self, path): VulnerabilityFiles( default_blackbox_mapping_file, trigger_word_file - ), - nosec_lines + ) ) def test_django_view_param(self): @@ -583,8 +580,7 @@ def run_analysis(self, path): VulnerabilityFiles( default_blackbox_mapping_file, trigger_word_file - ), - nosec_lines + ) ) def test_self_is_not_tainted(self): From 6f099120464ed17dad1546ddd32556d68d842fe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20G=C3=BCnal?= Date: Thu, 26 Apr 2018 20:52:46 +0300 Subject: [PATCH 14/22] unnecessary codes removed --- tests/vulnerabilities_across_files_test.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/vulnerabilities_across_files_test.py b/tests/vulnerabilities_across_files_test.py index b30d415b..25dd1267 100644 --- a/tests/vulnerabilities_across_files_test.py +++ b/tests/vulnerabilities_across_files_test.py @@ -15,7 +15,6 @@ from pyt.reaching_definitions_taint import ReachingDefinitionsTaintAnalysis from pyt.vulnerabilities import find_vulnerabilities -nosec_lines = set() class EngineTest(BaseTestCase): def run_analysis(self, path): path = os.path.normpath(path) @@ -40,8 +39,7 @@ def run_analysis(self, path): VulnerabilityFiles( default_blackbox_mapping_file, default_trigger_word_file - ), - nosec_lines + ) ) def test_find_vulnerabilities_absolute_from_file_command_injection(self): From 9c4dea6e511f76abca8feb396ecf1e6f2e1565fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20G=C3=BCnal?= Date: Thu, 26 Apr 2018 20:53:40 +0300 Subject: [PATCH 15/22] added default nosec_lines --- pyt/vulnerabilities.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyt/vulnerabilities.py b/pyt/vulnerabilities.py index eeb9404a..ecf76e78 100644 --- a/pyt/vulnerabilities.py +++ b/pyt/vulnerabilities.py @@ -172,7 +172,7 @@ def append_node_if_reassigned( def find_triggers( nodes, trigger_words, - nosec_lines + nosec_lines = set() ): """Find triggers from the trigger_word_list in the nodes. @@ -470,7 +470,7 @@ def find_vulnerabilities_in_cfg( ui_mode, blackbox_mapping, vulnerabilities_list, - nosec_lines + nosec_lines = set() ): """Find vulnerabilities in a cfg. From f4ebbffe64fc39961b34f4876927d310c1dc725a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20G=C3=BCnal?= Date: Fri, 27 Apr 2018 11:40:40 +0300 Subject: [PATCH 16/22] Update vulnerabilities.py --- pyt/vulnerabilities.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyt/vulnerabilities.py b/pyt/vulnerabilities.py index ecf76e78..b9a0c96b 100644 --- a/pyt/vulnerabilities.py +++ b/pyt/vulnerabilities.py @@ -74,7 +74,7 @@ def identify_triggers( sources, sinks, lattice, - nosec_lines + nosec_lines = set() ): """Identify sources, sinks and sanitisers in a CFG. From 7c872a039d0817868587be95c6caf1e916cee5d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20G=C3=BCnal?= Date: Fri, 27 Apr 2018 11:41:52 +0300 Subject: [PATCH 17/22] Update vulnerabilities.py --- pyt/vulnerabilities.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyt/vulnerabilities.py b/pyt/vulnerabilities.py index b9a0c96b..d5bc2e1b 100644 --- a/pyt/vulnerabilities.py +++ b/pyt/vulnerabilities.py @@ -509,7 +509,7 @@ def find_vulnerabilities( analysis_type, ui_mode, vulnerability_files, - nosec_lines + nosec_lines = set() ): """Find vulnerabilities in a list of CFGs from a trigger_word_file. From b94331006a4558bae6709f8fd603944d1cdb0161 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20G=C3=BCnal?= Date: Sat, 28 Apr 2018 21:26:09 +0300 Subject: [PATCH 18/22] removed spaces --- pyt/vulnerabilities.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pyt/vulnerabilities.py b/pyt/vulnerabilities.py index d5bc2e1b..4dfe06ed 100644 --- a/pyt/vulnerabilities.py +++ b/pyt/vulnerabilities.py @@ -74,7 +74,7 @@ def identify_triggers( sources, sinks, lattice, - nosec_lines = set() + nosec_lines=set() ): """Identify sources, sinks and sanitisers in a CFG. @@ -172,7 +172,7 @@ def append_node_if_reassigned( def find_triggers( nodes, trigger_words, - nosec_lines = set() + nosec_lines=set() ): """Find triggers from the trigger_word_list in the nodes. @@ -470,7 +470,7 @@ def find_vulnerabilities_in_cfg( ui_mode, blackbox_mapping, vulnerabilities_list, - nosec_lines = set() + nosec_lines ): """Find vulnerabilities in a cfg. @@ -509,7 +509,7 @@ def find_vulnerabilities( analysis_type, ui_mode, vulnerability_files, - nosec_lines = set() + nosec_lines=set() ): """Find vulnerabilities in a list of CFGs from a trigger_word_file. From 175c23597706efab4826154222e15ed73a002a96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20G=C3=BCnal?= Date: Sat, 28 Apr 2018 21:27:31 +0300 Subject: [PATCH 19/22] new line between imports and codes --- tests/vulnerabilities_across_files_test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/vulnerabilities_across_files_test.py b/tests/vulnerabilities_across_files_test.py index 25dd1267..7492aee2 100644 --- a/tests/vulnerabilities_across_files_test.py +++ b/tests/vulnerabilities_across_files_test.py @@ -15,6 +15,7 @@ from pyt.reaching_definitions_taint import ReachingDefinitionsTaintAnalysis from pyt.vulnerabilities import find_vulnerabilities + class EngineTest(BaseTestCase): def run_analysis(self, path): path = os.path.normpath(path) From c79161cb5416e32bf130ec4ea56f532a9eb8078e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20G=C3=BCnal?= Date: Sat, 28 Apr 2018 21:32:33 +0300 Subject: [PATCH 20/22] Update __main__.py --- pyt/__main__.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/pyt/__main__.py b/pyt/__main__.py index 8ce6cc67..b81e6f03 100644 --- a/pyt/__main__.py +++ b/pyt/__main__.py @@ -143,8 +143,7 @@ def parse_args(args): type=str, default=False) parser.add_argument('--ignore-nosec', dest='ignore_nosec', action='store_true', - help='do not skip lines with # nosec comments' - ) + help='do not skip lines with # nosec comments') save_parser = subparsers.add_parser('save', help='Save menu.') save_parser.set_defaults(which='save') @@ -239,7 +238,7 @@ def main(command_line_args=sys.argv[1:]): elif args.trim_reassigned_in: ui_mode = UImode.TRIM - path = os.path.normpath(args.filepath) + path = os.path.normpath(args.filepath) cfg_list = list() if args.ignore_nosec: nosec_lines = set() @@ -249,8 +248,8 @@ def main(command_line_args=sys.argv[1:]): nosec_lines = set( lineno for (lineno, line) in enumerate(lines, start=1) - if '#nosec' in line or '# nosec' in line) - + if '#nosec' in line or '# nosec' in line) + if args.git_repos: repos = get_repos(args.git_repos) for repo in repos: From ed135144641ec398131866a35333626364857d05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20G=C3=BCnal?= Date: Sat, 28 Apr 2018 21:35:07 +0300 Subject: [PATCH 21/22] new line between imports and codes --- tests/vulnerabilities_test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/vulnerabilities_test.py b/tests/vulnerabilities_test.py index b4698e91..f3d77279 100644 --- a/tests/vulnerabilities_test.py +++ b/tests/vulnerabilities_test.py @@ -23,6 +23,7 @@ from pyt.node_types import Node from pyt.reaching_definitions_taint import ReachingDefinitionsTaintAnalysis + class EngineTest(BaseTestCase): def run_empty(self): return From 092870010dfbf5368b9f974920f677ebb67b41a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20G=C3=BCnal?= Date: Sat, 28 Apr 2018 21:36:16 +0300 Subject: [PATCH 22/22] removed set() from nosec_lines --- pyt/vulnerabilities.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyt/vulnerabilities.py b/pyt/vulnerabilities.py index 4dfe06ed..1bc57753 100644 --- a/pyt/vulnerabilities.py +++ b/pyt/vulnerabilities.py @@ -74,7 +74,7 @@ def identify_triggers( sources, sinks, lattice, - nosec_lines=set() + nosec_lines ): """Identify sources, sinks and sanitisers in a CFG.