diff --git a/guarddog/analyzer/analyzer.py b/guarddog/analyzer/analyzer.py index 66b98be5..9fdeb1aa 100644 --- a/guarddog/analyzer/analyzer.py +++ b/guarddog/analyzer/analyzer.py @@ -257,6 +257,9 @@ def _format_semgrep_response(self, response, rule=None, targetpath=None): results[rule_name].append({ 'location': location, + 'start': result["start"], + 'end': result["end"], + 'path': result["path"], 'code': code, 'message': result["extra"]["message"] }) diff --git a/guarddog/cli.py b/guarddog/cli.py index 4dde41f2..ae4bbb6a 100644 --- a/guarddog/cli.py +++ b/guarddog/cli.py @@ -16,7 +16,7 @@ from guarddog.analyzer.metadata import get_metadata_detectors from guarddog.analyzer.sourcecode import SOURCECODE_RULES from guarddog.ecosystems import ECOSYSTEM -from guarddog.reporters.sarif import report_verify_sarif +from guarddog.reporters.sarif import report_verify_sarif, report_scan_sarif from guarddog.scanners import get_scanner from guarddog.scanners.scanner import PackageScanner @@ -51,7 +51,7 @@ def verify_options(fn): def scan_options(fn): - fn = click.option("--output-format", default=None, type=click.Choice(["json"], case_sensitive=False))(fn) + fn = click.option("--output-format", default=None, type=click.Choice(["json", "sarif"], case_sensitive=False))(fn) fn = click.option("-v", "--version", default=None, help="Specify a version to scan")(fn) return fn @@ -185,12 +185,13 @@ def _scan(identifier, version, rules, exclude_rules, output_format, exit_non_zer if output_format == "json": import json as js - if len(results) == 1: - # return only a json like {} - print(js.dumps(results[0])) - else: - # Return a list of result like [{},{}] - print(js.dumps(results)) + return_value = js.dumps(results) + + elif output_format == "sarif": + return_value = report_scan_sarif(list(ALL_RULES), results, ecosystem) + + if output_format is not None: + print(return_value) else: for result in results: print_scan_results(result, result['package']) diff --git a/guarddog/reporters/sarif.py b/guarddog/reporters/sarif.py index 04110736..00aa50a8 100644 --- a/guarddog/reporters/sarif.py +++ b/guarddog/reporters/sarif.py @@ -188,3 +188,51 @@ def report_verify_sarif(package_path: str, rule_names: list[str], scan_results: runs = get_run(results, driver) log = get_sarif_log([runs]) return json.dumps(log, indent=2) + + + +def report_scan_sarif(rule_names: list[str], scan_results: list[dict], + ecosystem: ECOSYSTEM) -> str: + rules_documentation = build_rules_help_list() + rules = list(map( + lambda s: get_rule(s, rules_documentation), + rule_names + )) + driver = get_driver(rules, ecosystem.value) + results = [] + + for entry in scan_results: + if entry["issues"] == 0: + continue + + scan_result_details = entry["results"] + for rule_name, rule_records in scan_result_details.items(): + for record in rule_records: + region = { + "startLine": record["start"]["line"], + "endLine": record["end"]["line"], + "startColumn": record["start"]["col"], + "endColumn": record["end"]["col"], + } + uri = record["path"] + physical_location = get_physical_location(uri, region) + location = get_location(physical_location) + package = entry["package"] + + text = f"On package: {package} \n" + "\n".join(map( + lambda x: x["message"], + scan_result_details[rule_name] + )) if type(scan_result_details[rule_name]) == list else scan_result_details[rule_name] + key = f"{rule_name}-{text}" + partial_fingerprints = { + f"guarddog/v1/{rule_name}": hashlib.sha256(key.encode('utf-8')).hexdigest() + } + result = get_result(rule_name, + [location], + text, + partial_fingerprints) + results.append(result) + + runs = get_run(results, driver) + log = get_sarif_log([runs]) + return json.dumps(log, indent=2)