diff --git a/pyt/__main__.py b/pyt/__main__.py index 4ceb67d1..b81e6f03 100644 --- a/pyt/__main__.py +++ b/pyt/__main__.py @@ -142,6 +142,8 @@ def parse_args(args): '(only JSON-formatted files are accepted)', type=str, default=False) + 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') @@ -192,7 +194,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) @@ -215,7 +217,8 @@ def analyse_repo(args, github_repo, analysis_type, ui_mode): VulnerabilityFiles( args.blackbox_mapping_file, args.trigger_word_file - ) + ), + nosec_lines ) return vulnerabilities @@ -235,12 +238,23 @@ 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) + 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: @@ -263,8 +277,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) @@ -305,8 +317,10 @@ def main(command_line_args=sys.argv[1:]): VulnerabilityFiles( args.blackbox_mapping_file, args.trigger_word_file - ) + ), + nosec_lines ) + if args.baseline: vulnerabilities = get_vulnerabilities_not_in_baseline(vulnerabilities, args.baseline) diff --git a/pyt/vulnerabilities.py b/pyt/vulnerabilities.py index ba7debff..1bc57753 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=set() ): """Find triggers from the trigger_word_list in the nodes. @@ -183,7 +185,8 @@ 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))) return trigger_nodes @@ -466,7 +469,8 @@ def find_vulnerabilities_in_cfg( lattice, ui_mode, blackbox_mapping, - vulnerabilities_list + vulnerabilities_list, + nosec_lines ): """Find vulnerabilities in a cfg. @@ -482,7 +486,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 +508,8 @@ def find_vulnerabilities( cfg_list, analysis_type, ui_mode, - vulnerability_files + vulnerability_files, + nosec_lines=set() ): """Find vulnerabilities in a list of CFGs from a trigger_word_file. @@ -518,7 +524,6 @@ 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: @@ -528,9 +533,9 @@ def find_vulnerabilities( 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 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"