From e3a1c726985e1231590356c281da0ee4c6f2b184 Mon Sep 17 00:00:00 2001 From: Justin Chu Date: Wed, 9 Nov 2022 19:56:10 -0800 Subject: [PATCH 1/8] Create convert_to_sarif.py --- tools/convert_to_sarif.py | 120 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 tools/convert_to_sarif.py diff --git a/tools/convert_to_sarif.py b/tools/convert_to_sarif.py new file mode 100644 index 0000000..b535992 --- /dev/null +++ b/tools/convert_to_sarif.py @@ -0,0 +1,120 @@ +"""Convert the output of lintrunner json to SARIF.""" + +import argparse +import json +import os + + +def format_rule_name(lintrunner_result: dict) -> str: + return f"{lintrunner_result['code']}/{lintrunner_result['name']}" + + +def severity_to_github_level(severity: str) -> str: + if severity == "advice" or severity == "disabled": + return "warning" + return severity + + +def parse_single_lintrunner_result(lintrunner_result: dict) -> tuple: + r"""Parse a single lintrunner result. + + A result looks like this: + { + "path":"/adapters/pytorch/grep_linter.py", + "line":227, + "char":80, + "code":"FLAKE8", + "severity":"advice", + "name":"E501", + "description":"line too long (81 > 79 characters)\nSee https://www.flake8rules.com/rules/E501.html" + } + """ + result = { + "ruleId": format_rule_name(lintrunner_result), + "level": severity_to_github_level(lintrunner_result["severity"]), + "message": { + "text": format_rule_name(lintrunner_result) + + "\n" + + lintrunner_result["description"], + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "file://" + lintrunner_result["path"], + }, + "region": { + "startLine": lintrunner_result["line"] or 1, + "startColumn": lintrunner_result["char"] or 1, + }, + }, + }, + ], + } + + rule = { + "id": format_rule_name(lintrunner_result), + "rule": { + "id": format_rule_name(lintrunner_result), + "name": format_rule_name(lintrunner_result), + "shortDescription": { + "text": format_rule_name(lintrunner_result) + + ": " + + lintrunner_result["description"].split("\n")[0], + }, + "fullDescription": { + "text": format_rule_name(lintrunner_result) + + "\n" + + lintrunner_result["description"], + }, + "defaultConfiguration": { + "level": severity_to_github_level(lintrunner_result["severity"]), + }, + }, + } + + return result, rule + + +def main(args): + """Convert the output of lintrunner json to SARIF.""" + + rules = {} + results = [] + with open(args.input, "r") as f: + for line in f: + lintrunner_json = json.loads(line) + result, rule = parse_single_lintrunner_result(lintrunner_json) + results.append(result) + rules[rule["id"]] = rule["rule"] + + sarif = { + "$schema": "https://json.schemastore.org/sarif-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "lintrunner", + "rules": list(rules.values()), + }, + }, + "results": results, + }, + ], + } + + output_dir = os.path.dirname(args.output) + if output_dir: + os.makedirs(os.path.dirname(args.output), exist_ok=True) + + with open(args.output, "w") as f: + json.dump(sarif, f) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--input", type=str, required=True) + parser.add_argument("--output", type=str, required=True) + args = parser.parse_args() + main(args) From c38441e788e96feb31123d300c9cbb64dcf9445f Mon Sep 17 00:00:00 2001 From: Justin Chu Date: Thu, 10 Nov 2022 09:43:03 -0800 Subject: [PATCH 2/8] Update convert_to_sarif.py --- tools/convert_to_sarif.py | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/tools/convert_to_sarif.py b/tools/convert_to_sarif.py index b535992..e963c91 100644 --- a/tools/convert_to_sarif.py +++ b/tools/convert_to_sarif.py @@ -3,6 +3,7 @@ import argparse import json import os +from typing import Iterable def format_rule_name(lintrunner_result: dict) -> str: @@ -11,7 +12,7 @@ def format_rule_name(lintrunner_result: dict) -> str: def severity_to_github_level(severity: str) -> str: if severity == "advice" or severity == "disabled": - return "warning" + return "recommendation" return severity @@ -76,17 +77,15 @@ def parse_single_lintrunner_result(lintrunner_result: dict) -> tuple: return result, rule -def main(args): +def produce_sarif(lintrunner_results: Iterable[dict]) -> dict: """Convert the output of lintrunner json to SARIF.""" rules = {} results = [] - with open(args.input, "r") as f: - for line in f: - lintrunner_json = json.loads(line) - result, rule = parse_single_lintrunner_result(lintrunner_json) - results.append(result) - rules[rule["id"]] = rule["rule"] + for lintrunner_json in lintrunner_results: + result, rule = parse_single_lintrunner_result(lintrunner_json) + results.append(result) + rules[rule["id"]] = rule["rule"] sarif = { "$schema": "https://json.schemastore.org/sarif-2.1.0.json", @@ -104,6 +103,17 @@ def main(args): ], } + return sarif + + +def main(args): + """Convert the output of lintrunner json to SARIF.""" + + with open(args.input, "r", encoding="utf-8") as f: + lintrunner_jsons = [json.loads(line) for line in f] + + sarif = produce_sarif(lintrunner_jsons) + output_dir = os.path.dirname(args.output) if output_dir: os.makedirs(os.path.dirname(args.output), exist_ok=True) @@ -114,7 +124,9 @@ def main(args): if __name__ == "__main__": parser = argparse.ArgumentParser() - parser.add_argument("--input", type=str, required=True) - parser.add_argument("--output", type=str, required=True) + parser.add_argument( + "--input", type=str, required=True, help="json file generated by lintrunner" + ) + parser.add_argument("--output", type=str, required=True, help="output sarif file") args = parser.parse_args() main(args) From 92e685b40663e87dca3ff669332cebe11a5677a4 Mon Sep 17 00:00:00 2001 From: Justin Chu Date: Thu, 10 Nov 2022 09:44:01 -0800 Subject: [PATCH 3/8] Create convert_to_sarif_test --- tools/convert_to_sarif_test | 120 ++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 tools/convert_to_sarif_test diff --git a/tools/convert_to_sarif_test b/tools/convert_to_sarif_test new file mode 100644 index 0000000..884032b --- /dev/null +++ b/tools/convert_to_sarif_test @@ -0,0 +1,120 @@ +import unittest + +import convert_to_sarif + + +class TestConvertToSarif(unittest.TestCase): + def test_produce_sarif_returns_correct_sarif_result(self): + lintrunner_results = [ + { + "path": "test.py", + "line": 1, + "char": 2, + "code": "FLAKE8", + "severity": "error", + "description": "test description", + "name": "test-code", + }, + { + "path": "test.py", + "line": 1, + "char": 2, + "code": "FLAKE8", + "severity": "error", + "description": "test description", + "name": "test-code-2", + }, + { + "path": "test2.py", + "line": 3, + "char": 4, + "code": "FLAKE8", + "severity": "advice", + "description": "test description", + "name": "test-code", + }, + ] + actual = convert_to_sarif.produce_sarif(lintrunner_results) + expected = { + "$schema": "https://json.schemastore.org/sarif-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "lintrunner", + "rules": [ + { + "id": "FLAKE8/test-code", + "name": "FLAKE8/test-code", + "shortDescription": { + "text": "FLAKE8/test-code: test description" + }, + "fullDescription": { + "text": "FLAKE8/test-code\ntest description" + }, + "defaultConfiguration": {"level": "recommendation"}, + }, + { + "id": "FLAKE8/test-code-2", + "name": "FLAKE8/test-code-2", + "shortDescription": { + "text": "FLAKE8/test-code-2: test description" + }, + "fullDescription": { + "text": "FLAKE8/test-code-2\ntest description" + }, + "defaultConfiguration": {"level": "error"}, + }, + ], + } + }, + "results": [ + { + "ruleId": "FLAKE8/test-code", + "level": "error", + "message": {"text": "FLAKE8/test-code\ntest description"}, + "locations": [ + { + "physicalLocation": { + "artifactLocation": {"uri": "file://test.py"}, + "region": {"startLine": 1, "startColumn": 2}, + } + } + ], + }, + { + "ruleId": "FLAKE8/test-code-2", + "level": "error", + "message": {"text": "FLAKE8/test-code-2\ntest description"}, + "locations": [ + { + "physicalLocation": { + "artifactLocation": {"uri": "file://test.py"}, + "region": {"startLine": 1, "startColumn": 2}, + } + } + ], + }, + { + "ruleId": "FLAKE8/test-code", + "level": "recommendation", + "message": {"text": "FLAKE8/test-code\ntest description"}, + "locations": [ + { + "physicalLocation": { + "artifactLocation": {"uri": "file://test2.py"}, + "region": {"startLine": 3, "startColumn": 4}, + } + } + ], + }, + ], + } + ], + } + self.assertEqual(actual, expected) + + +if __name__ == "__main__": + unittest.main() From ae39775aba719dce221d0fa41e1a413f205ade7f Mon Sep 17 00:00:00 2001 From: Justin Chu Date: Thu, 10 Nov 2022 09:44:15 -0800 Subject: [PATCH 4/8] Rename convert_to_sarif_test to convert_to_sarif_test.py --- tools/{convert_to_sarif_test => convert_to_sarif_test.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tools/{convert_to_sarif_test => convert_to_sarif_test.py} (100%) diff --git a/tools/convert_to_sarif_test b/tools/convert_to_sarif_test.py similarity index 100% rename from tools/convert_to_sarif_test rename to tools/convert_to_sarif_test.py From a8606833b801d8dd4f9fac5c43244019e680fe26 Mon Sep 17 00:00:00 2001 From: Justin Chu Date: Fri, 11 Nov 2022 21:46:55 -0800 Subject: [PATCH 5/8] Fix severity level --- tools/convert_to_sarif.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/convert_to_sarif.py b/tools/convert_to_sarif.py index e963c91..d3ed148 100644 --- a/tools/convert_to_sarif.py +++ b/tools/convert_to_sarif.py @@ -11,8 +11,8 @@ def format_rule_name(lintrunner_result: dict) -> str: def severity_to_github_level(severity: str) -> str: - if severity == "advice" or severity == "disabled": - return "recommendation" + if severity in {"advice", "disabled"}: + return "note" return severity @@ -118,7 +118,7 @@ def main(args): if output_dir: os.makedirs(os.path.dirname(args.output), exist_ok=True) - with open(args.output, "w") as f: + with open(args.output, "w", encoding="utf-8") as f: json.dump(sarif, f) From d004e4f34e92e60c5398df13ed28c52a0d24057a Mon Sep 17 00:00:00 2001 From: Justin Chu Date: Fri, 11 Nov 2022 21:51:02 -0800 Subject: [PATCH 6/8] Fix related path --- tools/convert_to_sarif.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/convert_to_sarif.py b/tools/convert_to_sarif.py index d3ed148..819c4a8 100644 --- a/tools/convert_to_sarif.py +++ b/tools/convert_to_sarif.py @@ -42,7 +42,9 @@ def parse_single_lintrunner_result(lintrunner_result: dict) -> tuple: { "physicalLocation": { "artifactLocation": { - "uri": "file://" + lintrunner_result["path"], + "uri": ("file://" + lintrunner_result["path"]) + if lintrunner_result["path"].startswith("/") + else lintrunner_result["path"], }, "region": { "startLine": lintrunner_result["line"] or 1, From 162cdf8e82b693d2abb9681ace16f1118b9b4190 Mon Sep 17 00:00:00 2001 From: Justin Chu Date: Fri, 11 Nov 2022 22:00:12 -0800 Subject: [PATCH 7/8] Fix test --- tools/convert_to_sarif_test.py | 71 +++++++++++++++++++++++++++++++--- 1 file changed, 66 insertions(+), 5 deletions(-) diff --git a/tools/convert_to_sarif_test.py b/tools/convert_to_sarif_test.py index 884032b..d053b25 100644 --- a/tools/convert_to_sarif_test.py +++ b/tools/convert_to_sarif_test.py @@ -53,7 +53,7 @@ def test_produce_sarif_returns_correct_sarif_result(self): "fullDescription": { "text": "FLAKE8/test-code\ntest description" }, - "defaultConfiguration": {"level": "recommendation"}, + "defaultConfiguration": {"level": "note"}, }, { "id": "FLAKE8/test-code-2", @@ -77,7 +77,7 @@ def test_produce_sarif_returns_correct_sarif_result(self): "locations": [ { "physicalLocation": { - "artifactLocation": {"uri": "file://test.py"}, + "artifactLocation": {"uri": "test.py"}, "region": {"startLine": 1, "startColumn": 2}, } } @@ -90,7 +90,7 @@ def test_produce_sarif_returns_correct_sarif_result(self): "locations": [ { "physicalLocation": { - "artifactLocation": {"uri": "file://test.py"}, + "artifactLocation": {"uri": "test.py"}, "region": {"startLine": 1, "startColumn": 2}, } } @@ -98,12 +98,12 @@ def test_produce_sarif_returns_correct_sarif_result(self): }, { "ruleId": "FLAKE8/test-code", - "level": "recommendation", + "level": "note", "message": {"text": "FLAKE8/test-code\ntest description"}, "locations": [ { "physicalLocation": { - "artifactLocation": {"uri": "file://test2.py"}, + "artifactLocation": {"uri": "test2.py"}, "region": {"startLine": 3, "startColumn": 4}, } } @@ -113,8 +113,69 @@ def test_produce_sarif_returns_correct_sarif_result(self): } ], } + self.maxDiff = None self.assertEqual(actual, expected) + def test_it_handles_relative_paths(self): + lintrunner_results = [ + { + "path": "test.py", + "line": 1, + "char": 2, + "code": "FLAKE8", + "severity": "error", + "description": "test description", + "name": "test-code", + }, + ] + actual = convert_to_sarif.produce_sarif(lintrunner_results) + expected_results = [ + { + "ruleId": "FLAKE8/test-code", + "level": "error", + "message": {"text": "FLAKE8/test-code\ntest description"}, + "locations": [ + { + "physicalLocation": { + "artifactLocation": {"uri": "test.py"}, + "region": {"startLine": 1, "startColumn": 2}, + } + } + ], + }, + ] + self.assertEqual(actual["runs"][0]["results"], expected_results) + + def test_it_handles_absolute_paths(self): + lintrunner_results = [ + { + "path": "/path/to/test.py", + "line": 1, + "char": 2, + "code": "FLAKE8", + "severity": "error", + "description": "test description", + "name": "test-code", + }, + ] + actual = convert_to_sarif.produce_sarif(lintrunner_results) + expected_results = [ + { + "ruleId": "FLAKE8/test-code", + "level": "error", + "message": {"text": "FLAKE8/test-code\ntest description"}, + "locations": [ + { + "physicalLocation": { + "artifactLocation": {"uri": "file:///path/to/test.py"}, + "region": {"startLine": 1, "startColumn": 2}, + } + } + ], + }, + ] + self.assertEqual(actual["runs"][0]["results"], expected_results) + if __name__ == "__main__": unittest.main() From 4221e5bc803cb648842e3b32164f2347a3879e19 Mon Sep 17 00:00:00 2001 From: Justin Chu Date: Fri, 11 Nov 2022 22:22:01 -0800 Subject: [PATCH 8/8] Path can be none --- tools/convert_to_sarif.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tools/convert_to_sarif.py b/tools/convert_to_sarif.py index 819c4a8..bac50ef 100644 --- a/tools/convert_to_sarif.py +++ b/tools/convert_to_sarif.py @@ -30,6 +30,14 @@ def parse_single_lintrunner_result(lintrunner_result: dict) -> tuple: "description":"line too long (81 > 79 characters)\nSee https://www.flake8rules.com/rules/E501.html" } """ + if lintrunner_result["path"] is None: + artifact_uri = None + else: + artifact_uri = ( + ("file://" + lintrunner_result["path"]) + if lintrunner_result["path"].startswith("/") + else lintrunner_result["path"] + ) result = { "ruleId": format_rule_name(lintrunner_result), "level": severity_to_github_level(lintrunner_result["severity"]), @@ -42,9 +50,7 @@ def parse_single_lintrunner_result(lintrunner_result: dict) -> tuple: { "physicalLocation": { "artifactLocation": { - "uri": ("file://" + lintrunner_result["path"]) - if lintrunner_result["path"].startswith("/") - else lintrunner_result["path"], + "uri": artifact_uri, }, "region": { "startLine": lintrunner_result["line"] or 1,