def find_issues(self, config, results, package): """Identify issues by comparing tool results with tool configuration.""" issues = [] for key, val in results.items(): for item in val.keys(): val_id = self.convert_name_to_id(item) if val_id != '' and val_id in config.keys(): if val[item]["value"] == '------' or \ val[item]["value"] == '******': continue result = float(val[item]["value"]) thresh_error = float(config[val_id]["error"]) thresh_warn = float(config[val_id]["warn"]) msg = key + ' - ' + config[val_id]['name'] if result > thresh_error: msg += ' - value: {}, theshold: {}'.format( result, thresh_error) issues.append( Issue(package['c_src'][0], 0, self.get_name(), 'error', 5, msg, None)) elif result > thresh_warn: msg += ' - value: {}, theshold: {}'.format( result, thresh_warn) issues.append( Issue(package['c_src'][0], 0, self.get_name(), 'warn', 3, msg, None)) return issues
def parse_output(self, total_output): """Parse tool output and report issues.""" tool_re = r"(.+):(\d+):\s\[(.+)\]\s(.+)" parse = re.compile(tool_re) issues = [] for output in total_output: for line in output.splitlines(): match = parse.match(line) if match: if "," in match.group(3): parts = match.group(3).split(",") if parts[1].strip() == "": issues.append(Issue(match.group(1), match.group(2), self.get_name(), parts[0], "5", match.group(4), None)) else: issues.append(Issue(match.group(1), match.group(2), self.get_name(), parts[0], "5", parts[1].strip() + ": " + match.group(4), None)) else: issues.append(Issue(match.group(1), match.group(2), self.get_name(), match.group(3), "5", match.group(4), None)) return issues
def parse_output(self, package: Package, output: str) -> List[Issue]: """Parse tool output and report issues.""" lint_re = r"(.+):\s(.+)\((\d+)\):\s(.+):\s(.+)" lint2_re = r"(.+):\s(.+):\s(.+)" parse = re.compile(lint_re) # type: Pattern[str] parse2 = re.compile(lint2_re) # type: Pattern[str] issues = [] for line in output.splitlines(): match = parse.match(line) # type: Optional[Match[str]] if match: if self.check_for_exceptions_has_file(match, package): continue norm_path = os.path.normpath(package.path + "/" + match.group(2)) issues.append( Issue( norm_path, match.group(3), self.get_name(), match.group(4), self.get_level(match.group(4)), match.group(5), None, )) else: match2 = parse2.match(line) # type: Optional[Match[str]] if match2: norm_path = os.path.normpath(package.path + "/package.xml") message = match2.group(3) if message == "missing build_depend on 'rostest'": message = "missing test_depend on 'rostest'" elif message.startswith("unconfigured build_depend on"): message += (" (Make sure you aren't missing " "COMPONENTS in find_package(catkin ...) " "in CMakeLists.txt)") message += (" (I can't really tell if this applies for " "package.xml or CMakeLists.txt. Make sure to " "check both for this issue)") issues.append( Issue( norm_path, "1", self.get_name(), match2.group(2), self.get_level(match2.group(2)), message, None, )) return issues
def parse_output(self, package, output): # pylint: disable=too-many-locals, too-many-branches """Parse tool output and report issues.""" make_re = r"(.+):(\d+):(\d+):\s(.+):\s(.+)" make_warning_re = r".*\[(.+)\].*" parse = re.compile(make_re) warning_parse = re.compile(make_warning_re) matches = [] # Load the plugin mapping if possible warnings_mapping = self.load_mapping() for line in output.split('\n'): match = parse.match(line) if match and not self.check_for_exceptions(match): matches.append(match.groups()) matches = self.filter_matches(matches, package) issues = [] for match in matches: cert_reference = None warning_list = warning_parse.match(match[4]) if warning_list is not None and warning_list.groups( 1)[0] in warnings_mapping: cert_reference = warnings_mapping[warning_list.groups(1)[0]] if warning_list is None: # Something's gone wrong if we don't match the [warning] format if "fatal error" in match[3]: warning_level = 5 category = "fatal-error" else: category = "unknown-error" else: category = warning_list.groups(1)[0] if match[3].lower() == "warning": warning_level = 3 elif match[3].lower() == "error": warning_level = 5 elif match[3].lower() == "note": warning_level = 1 else: warning_level = 3 issue = Issue(match[0], match[1], self.get_name(), category, warning_level, match[4], cert_reference) if issue not in issues: issues.append(issue) lines = output.split('\n') if "collect2: ld returned 1 exit status" in lines: issues.append( Issue("Linker", "0", self.get_name(), "linker", "5", "Linking failed")) return issues
def parse_output( # pylint: disable=too-many-locals self, total_output: List[str], files: List[str] ) -> List[Issue]: """Parse tool output and report issues.""" clangformat_re = r"<replacement offset=" parse: Pattern[str] = re.compile(clangformat_re) issues: List[Issue] = [] if ( not self.plugin_context or not self.plugin_context.args.clang_format_issue_per_line ): for output in total_output: lines = output.splitlines() filename = lines[0] count = 0 for line in lines: match: Optional[Match[str]] = parse.match(line) if match: count += 1 if count > 0: issues.append( Issue( filename, "0", self.get_name(), "format", "1", str(count) + " replacements", None, ) ) else: parser = ClangFormatXMLParser() for output, filename in zip(total_output, files): report = parser.parse_xml_output(output, filename) for issue in report: msg: str = ( f"Replace\n{issue['deletion']}\nwith\n{issue['addition']}\n" ) issues.append( Issue( filename, str(issue["line_no"]), self.get_name(), "format", "1", msg, None, ) ) return issues
def test_write_jenkins_warnings_ng_reporting_plugin_report_severities(): """Test the output of the reporting plugin with different severities.""" with TemporaryDirectory() as tmp_dir: wfrp = setup_write_jenkins_warnings_ng_reporting_plugin(tmp_dir) package = Package( "valid_package", os.path.join(os.path.dirname(__file__), "valid_package")) issues = { "tool_a": [ Issue("test.txt", 1, "tool_a", "type", "0", "This is a test", None), Issue( "test.txt", 1, "tool_a", "type", "invalid-severity", "This is a test", None, ), Issue("test.txt", 1, "tool_a", "type", "1", "This is a test", None), Issue("test.txt", 1, "tool_a", "type", 3, "This is a test", None), Issue("test.txt", 1, "tool_a", "type", "5", "This is a test", None), ] } _, success = wfrp.report(package, issues, "level") assert success with open( os.path.join(tmp_dir, "valid_package-level", "valid_package-level.json.statick")) as outfile: for line in outfile: line = line.strip() assert re.match(output_regex, line) expected_dict = { "fileName": "test.txt", "severity": "ERROR", "lineStart": 1, "message": "This is a test", "category": "tool_a", "type": "type", } output_dict = json.loads(line) assert output_dict == expected_dict assert re.match(output_regex, line) assert ( line == '{"category": "tool_a", "fileName": "test.txt", "lineStart": 1, "message": "This is a test", "severity": "ERROR", "type": "type"}' ) assert line == json.dumps(expected_dict, sort_keys=True)
def parse_output(self, total_output: List[str]) -> List[Issue]: """Parse tool output and report issues.""" pylint_re = r"(.+):(\d+):\s\[(.+)\]\s(.+)" parse: Pattern[str] = re.compile(pylint_re) issues: List[Issue] = [] for output in total_output: for line in output.splitlines(): match: Optional[Match[str]] = parse.match(line) if match: if "," in match.group(3): parts = match.group(3).split(",") if parts[1].strip() == "": issues.append( Issue( match.group(1), match.group(2), self.get_name(), parts[0], "5", match.group(4), None, ) ) else: issues.append( Issue( match.group(1), match.group(2), self.get_name(), parts[0], "5", parts[1].strip() + ": " + match.group(4), None, ) ) else: issues.append( Issue( match.group(1), match.group(2), self.get_name(), match.group(3), "5", match.group(4), None, ) ) return issues
def parse_output(self, total_output: List[str]) -> List[Issue]: """Parse tool output and report issues.""" eslint_re = r"(.+):(\d+):(\d+):\s(.+)\s\[(.+)\/(.+)\]" eslint_error_re = r"(.+):(\d+):(\d+):\s(.+):\s(.+)\s\[(.+)]" parse: Pattern[str] = re.compile(eslint_re) err_parse: Pattern[str] = re.compile(eslint_error_re) issues: List[Issue] = [] for output in total_output: lines = output.split("\n") for line in lines: match: Optional[Match[str]] = parse.match(line) err_match: Optional[Match[str]] = err_parse.match(line) if match: severity_str = match.group(5).lower() severity = 3 if severity_str == "warning": severity = 3 elif severity_str == "error": severity = 5 issues.append( Issue( match.group(1), match.group(2), self.get_name(), match.group(6), severity, match.group(4), None, )) elif err_match: severity_str = err_match.group(6).lower() severity = 3 if severity_str == "error": severity = 5 issues.append( Issue( err_match.group(1), err_match.group(2), self.get_name(), err_match.group(4), severity, err_match.group(5), None, )) return issues
def find_issues(self, config: Dict[Any, Any], results: Dict[Any, Any], src: str) -> List[Issue]: """Identify issues by comparing tool results with tool configuration.""" issues: List[Issue] = [] dummy = [] logging.debug("Results") logging.debug(results) for key, val in results.items(): for item in val.keys(): val_id = self.convert_name_to_id(item) if val_id != "" and val_id in config.keys(): if val[item]["value"] == "------" or val[item][ "value"] == "******": dummy.append("only here for code coverage") continue result = float(val[item]["value"]) thresh_error = float(config[val_id]["error"]) thresh_warn = float(config[val_id]["warn"]) msg = key + " - " + config[val_id]["name"] msg += f" - value: {result}, thresholds warning: {thresh_warn}" msg += f", error: {thresh_error}" if ("level" in val[item] and val[item]["level"] == "2") or (result > thresh_error): issues.append( Issue( src, "0", self.get_name(), "error", "5", msg, None, )) elif ("level" in val[item] and val[item]["level"] == "1") or (result > thresh_warn): issues.append( Issue( src, "0", self.get_name(), "warn", "3", msg, None, )) return issues
def parse_output(self, output: Any) -> List[Issue]: """Parse tool output and report issues.""" issues: List[Issue] = [] for item in output: if ("level" not in item or "file" not in item or "line" not in item or "code" not in item or "message" not in item): logging.debug(" Found invalid shellcheck output: %s", item) continue if item["level"] == "style": warning_level = "1" elif item["level"] == "info": warning_level = "1" elif item["level"] == "warning": warning_level = "3" elif item["level"] == "error": warning_level = "5" else: warning_level = "3" issue = Issue( item["file"], str(item["line"]), self.get_name(), "SC" + str(item["code"]), warning_level, item["message"], None, ) issues.append(issue) return issues
def parse_output(self, total_output: List[str]) -> List[Issue]: """Parse tool output and report issues.""" tool_re = r"(.+)\s(.+)\s(\d+):\s(.+)" # type: str parse = re.compile(tool_re) # type: Pattern[str] issues = [] # type: List[Issue] filename = "" # type: str line_number = "0" # type: str issue_type = "" # type: str message = "" # type: str for output in total_output: for line in output.splitlines(): match = parse.match(line) # type: Optional[Match[str]] if match: filename = match.group(1)[1:-2] issue_type = "lacheck" line_number = match.group(3) message = match.group(4) issues.append( Issue( filename, line_number, self.get_name(), issue_type, "3", message, None, )) return issues
def parse_output(self, output: str) -> List[Issue]: """Parse tool output and report issues.""" clang_tidy_re = r"(.+):(\d+):(\d+):\s(.+):\s(.+)\s\[(.+)\]" parse: Pattern[str] = re.compile(clang_tidy_re) issues: List[Issue] = [] # Load the plugin mapping if possible warnings_mapping = self.load_mapping() for line in output.splitlines(): match: Optional[Match[str]] = parse.match(line) if match and not self.check_for_exceptions(match): if (line[1] != "*" and match.group(3) != "information" and match.group(4) != "note"): cert_reference = None if match.group(6) in warnings_mapping: cert_reference = warnings_mapping[match.group(6)] issues.append( Issue( match.group(1), match.group(2), self.get_name(), match.group(4) + "/" + match.group(6), "3", match.group(5), cert_reference, )) return issues
def parse_output(self, total_output: List[str]) -> List[Issue]: """Parse tool output and report issues.""" re_str = r"(.+):\s.+\s(\d+),\s.+,\s(.+)" parse = re.compile(re_str) # type: Pattern[str] issues = [] # type: List[Issue] for output in total_output: lines = output.split("\n") for line in lines: match = parse.match(line) # type: Optional[Match[str]] if match: filename = match.group(1) line_number = match.group(2) issue_type = "format" severity = 3 message = match.group(3) issues.append( Issue( filename, line_number, self.get_name(), issue_type, severity, message, None, )) return issues
def parse_output(self, output: str) -> List[Issue]: """Parse tool output and report issues.""" cppcheck_re = r"\[(.+):(\d+)\]:\s\((.+?)\s(.+?)\)\s(.+)" parse = re.compile(cppcheck_re) # type: Pattern[str] issues = [] warnings_mapping = self.load_mapping() for line in output.splitlines(): match = parse.match(line) # type: Optional[Match[str]] if ( match and line[1] != "*" and match.group(3) != "information" and not self.check_for_exceptions(match) ): dummy, extension = os.path.splitext(match.group(1)) if extension in self.valid_extensions: cert_reference = None if match.group(4) in warnings_mapping: cert_reference = warnings_mapping[match.group(4)] issues.append( Issue( match.group(1), match.group(2), self.get_name(), match.group(3) + "/" + match.group(4), "5", match.group(5), cert_reference, ) ) return issues
def parse_output(self, output: str) -> List[Issue]: """Parse tool output and report issues.""" cmakelint_re = r"(.+):(\d+):\s(.+)\s\[(.+)\]" parse: Pattern[str] = re.compile(cmakelint_re) issues: List[Issue] = [] for line in output.splitlines(): match: Optional[Match[str]] = parse.match(line) if match: issue_type = match.group(4) if issue_type == "syntax": level = "5" else: level = "3" issues.append( Issue( match.group(1), match.group(2), self.get_name(), match.group(4), level, match.group(3), None, ) ) return issues
def parse_output(self, total_output: List[str]) -> List[Issue]: """Parse tool output and report issues.""" yamllint_re = r"(.+):(\d+):(\d+):\s\[(.+)\]\s(.+)\s\((.+)\)" parse = re.compile(yamllint_re) # type: Pattern[str] issues = [] for output in total_output: for line in output.splitlines(): match = parse.match(line) # type: Optional[Match[str]] if match: issue_type = match.group(4) if issue_type == "error": level = "5" else: level = "3" issues.append( Issue( match.group(1), match.group(2), self.get_name(), match.group(6), level, match.group(5), None, )) return issues
def parse_output(self, total_output: List[str]) -> List[Issue]: """Parse tool output and report issues.""" jshint_re = r"(.+):(\d+):(\d+):\s(.+)" parse: Pattern[str] = re.compile(jshint_re) issues: List[Issue] = [] for output in total_output: lines = output.split("\n") for line in lines: match: Optional[Match[str]] = parse.match(line) if match: filename = match.group(1) line_number = match.group(2) issue_type = "jshint" severity = 5 message = match.group(4) issues.append( Issue( filename, line_number, self.get_name(), issue_type, severity, message, None, )) return issues
def parse_output(self, output): """Parse tool output and report issues.""" issues = [] # Load the plugin mapping if possible warnings_mapping = self.load_mapping() # Copy output for modification output_minus_log = list(output) # Bandit prints a bunch of log messages out and you can't suppress # them, so iterate over the list until we find the CSV header for line in output: # Intentionally output, not output_minus_log if line.startswith('filename'): # Found the CSV header, stop removing things break else: output_minus_log.remove(line) csvreader = csv.DictReader(output_minus_log) for line in csvreader: cert_reference = None if line['test_id'] in warnings_mapping: cert_reference = warnings_mapping[line['test_id']] severity = '1' if line['issue_confidence'] == "MEDIUM": severity = '3' elif line['issue_confidence'] == "HIGH": severity = '5' issues.append(Issue(line['filename'], line['line_number'], self.get_name(), line['test_id'], severity, line['issue_text'], cert_reference)) return issues
def parse_output(self, total_output: List[str]) -> List[Issue]: """Parse tool output and report issues.""" clangformat_re = r"<replacement offset=" parse = re.compile(clangformat_re) # type: Pattern[str] issues = [] for output in total_output: lines = output.splitlines() filename = lines[0] count = 0 for line in lines: match = parse.match(line) # type: Optional[Match[str]] if match: count += 1 if count > 0: issues.append( Issue( filename, "0", self.get_name(), "format", "1", str(count) + " replacements", None, )) return issues
def test_write_jenkins_warnings_ng_reporting_plugin_report_nocert(): """Test the output of the reporting plugin without a CERT reference.""" with TemporaryDirectory() as tmp_dir: wfrp = setup_write_jenkins_warnings_ng_reporting_plugin(tmp_dir) package = Package( 'valid_package', os.path.join(os.path.dirname(__file__), 'valid_package')) issues = { 'tool_a': [ Issue('test.txt', 1, 'tool_a', 'type', "1", 'This is a test', None) ] } _, success = wfrp.report(package, issues, 'level') assert success with open( os.path.join(tmp_dir, 'valid_package-level', 'valid_package-level.json.statick')) as outfile: line = outfile.readline().strip() expected_dict = { "fileName": "test.txt", "severity": "NORMAL", "lineStart": 1, "message": "This is a test", "category": "tool_a", "type": "type" } output_dict = json.loads(line) assert output_dict == expected_dict assert re.match(output_regex, line) assert line == "{\"category\": \"tool_a\", \"fileName\": \"test.txt\", \"lineStart\": 1, \"message\": \"This is a test\", \"severity\": \"NORMAL\", \"type\": \"type\"}" assert line == json.dumps(expected_dict, sort_keys=True)
def parse_output(self, output: List[str]) -> List[Issue]: """Parse tool output and report issues.""" issues = [] # Load the plugin mapping if possible warnings_mapping = self.load_mapping() for line in output: split_line = line.strip().split(":::") # Should split into five segments, anything less is invalid. if len(split_line) < 5: continue cert_reference = None if split_line[2].replace("::", "__") in warnings_mapping.keys(): cert_reference = warnings_mapping[split_line[2].replace( "::", "__")] issues.append( Issue( split_line[0], split_line[1], self.get_name(), split_line[2], split_line[4], split_line[3], cert_reference, )) return issues
def parse_output(self, total_output): # pylint: disable=too-many-locals """Parse tool output and report issues.""" eslint_re = r"(.+):(\d+):(\d+):\s(.+)\s\[(.+)\/(.+)\]" parse = re.compile(eslint_re) issues = [] for output in total_output: lines = output.split('\n') count = 0 for line in lines: match = parse.match(line) if match: severity_str = match.group(5).lower() severity = 3 if severity_str == "warning": severity = 3 elif severity_str == "error": severity = 5 count += 1 filename = match.group(1) line_number = match.group(2) issue_type = match.group(6) message = match.group(4) issues.append( Issue(filename, line_number, self.get_name(), issue_type, severity, message, None)) return issues
def test_filter_issues_nolint_not_abs_path(): """ Test that issues are not filtered based on NOLINT comment when not absolute path. Expected result: one issue found """ package = Package( "valid_package", os.path.join(os.path.dirname(__file__), "valid_package") ) exceptions = Exceptions( os.path.join(os.path.dirname(__file__), "valid_exceptions.yaml") ) filename = "valid_package/x.py" line_number = "3" tool = "pylint" issue_type = "missing-docstring" severity = "3" message = "C0111: Missing module docstring" tool_issue = Issue(filename, line_number, tool, issue_type, severity, message, None) issues = {} issues["pylint"] = [tool_issue] issues = exceptions.filter_issues(package, issues) assert len(issues["pylint"]) == 1
def test_filter_issues_filename_abs_path(): """ Test that issues are filtered based on regex exceptions with absolute path. Expected result: no issues found """ package = Package( "valid_package", os.path.join(os.path.dirname(__file__), "valid_package") ) exceptions = Exceptions( os.path.join(os.path.dirname(__file__), "valid_exceptions.yaml") ) filename = "/home/travis/build/x.py" line_number = "4" tool = "pylint" issue_type = "R0205(useless-object-inheritance)" severity = "5" message = "R0205: Class 'Example' inherits from object, can be safely removed from bases in python3" tool_issue = Issue(filename, line_number, tool, issue_type, severity, message, None) issues = {} issues["pylint"] = [tool_issue] issues = exceptions.filter_issues(package, issues) assert not issues["pylint"]
def parse_output(self, total_output): # pylint: disable=too-many-locals """Parse tool output and report issues.""" tool_re = r"(.+):(\d+)" parse_first = re.compile(tool_re) tool_re_second = r"\s(.+):\s(.+)" parse_second = re.compile(tool_re_second) issues = [] filename = '' line_number = 0 issue_type = '' message = '' for output in total_output: first_line = True for line in output.split("\n"): if first_line: match = parse_first.match(line) first_line = False if match: filename = match.group(1) line_number = match.group(2) else: match = parse_second.match(line) first_line = True if match: issue_type = match.group(1) message = match.group(2) issues.append( Issue(filename, line_number, self.get_name(), issue_type, "5", message, None)) return issues
def parse_output(self, total_output: List[str]) -> List[Issue]: """Parse tool output and report issues.""" # file:line: severity: msg type tool_re = r"(.+):(\d+):\s(.+):\s(.+)\s(.+)" parse: Pattern[str] = re.compile(tool_re) issues: List[Issue] = [] for output in total_output: lines = output.splitlines() for line in lines: if sys.platform != "win32" and not line.startswith("/"): continue match: Optional[Match[str]] = parse.match(line) if match: issue_type = match.group(5).strip("[]") issues.append( Issue( match.group(1), match.group(2), self.get_name(), issue_type, "5", match.group(4), None, )) return issues
def parse_output(self, output: List[str]) -> List[Issue]: """Parse tool output and report issues.""" issues: List[Issue] = [] # Copy output for modification output_minus_log = list(output) # Bandit prints a bunch of log messages out and you can't suppress # them, so iterate over the list until we find the CSV header for line in output: # Intentionally output, not output_minus_log if line.startswith("filename"): # Found the CSV header, stop removing things break output_minus_log.remove(line) csvreader = csv.DictReader(output_minus_log) for csv_line in csvreader: severity = "1" if csv_line["issue_confidence"] == "MEDIUM": severity = "3" elif csv_line["issue_confidence"] == "HIGH": severity = "5" issues.append( Issue( csv_line["filename"], csv_line["line_number"], self.get_name(), csv_line["test_id"], severity, csv_line["issue_text"], None, )) return issues
def parse_output(self, total_output: List[str]) -> List[Issue]: """Parse tool output and report issues.""" issues: List[Issue] = [] for output in total_output: lines = output.split("\n") for line in lines: try: err_dict = json.loads(line)[0] for issue in err_dict["warnings"]: severity_str = issue["severity"] severity = 3 if severity_str == "warning": severity = 3 elif severity_str == "error": severity = 5 issues.append( Issue( err_dict["source"], issue["line"], self.get_name(), issue["rule"], severity, issue["text"], None, ) ) except ValueError as ex: logging.warning("ValueError: %s", ex) return issues
def parse_output(self, total_output: List[str]) -> List[Issue]: """Parse tool output and report issues.""" flawfinder_re = r"(.+):(\d+):\s+\[(\d+)\]\s+(.+):\s*(.+)" parse = re.compile(flawfinder_re) # type: Pattern[str] issues = [] warnings_mapping = self.load_mapping() for output in total_output: for line in output.splitlines(): match = parse.match(line) # type: Optional[Match[str]] if match: cert_reference = None if match.group(4) in warnings_mapping: cert_reference = warnings_mapping[match.group(4)] issues.append( Issue( match.group(1), match.group(2), self.get_name(), match.group(4), match.group(3), match.group(5), cert_reference, )) return issues
def test_filter_issues_nolint(): """ Test that issues are filtered based on NOLINT comment. Expected result: no issues found """ package = Package('valid_package', os.path.join(os.path.dirname(__file__), 'valid_package')) exceptions = Exceptions( os.path.join(os.path.dirname(__file__), 'valid_exceptions.yaml')) filename = os.path.join(os.path.dirname(__file__), 'valid_package') + '/x.py' line_number = '3' tool = 'pylint' issue_type = 'missing-docstring' severity = '3' message = 'C0111: Missing module docstring' tool_issue = Issue(filename, line_number, tool, issue_type, severity, message, None) issues = {} issues['pylint'] = [tool_issue] issues = exceptions.filter_issues(package, issues) assert not issues['pylint']