diff --git a/docs/usage.rst b/docs/usage.rst index 18fff34f..8e33da3c 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -4,6 +4,7 @@ Command Line Usage .. _issue_16: https://github.com/PyCQA/prospector/issues/16 .. _vscode_python_plugin: https://marketplace.visualstudio.com/items?itemName=donjayamanne.python .. _gitlab_code_climate_report_format: https://docs.gitlab.com/ci/testing/code_quality/#code-quality-report-format +.. _sarif_report_format: https://www.oasis-open.org/committees/sarif/charter.php The simplest way to run prospector is from the project root with no arguments. It will try to figure everything else out itself and provide sensible defaults:: @@ -39,6 +40,8 @@ The default output format of ``prospector`` is designed to be human readable. Yo +----------------------+----------------------------------------------------------------------------+ | ``gitlab`` | | Support for `gitlab_code_climate_report_format`_ | +----------------------+----------------------------------------------------------------------------+ +| ``sarif`` | | Support for `sarif_report_format`_ | ++----------------------+----------------------------------------------------------------------------+ | ``grouped`` | | Similar to ``text``, but groups all message on the same line together | | | | rather than having a separate entry per message. | +----------------------+----------------------------------------------------------------------------+ diff --git a/prospector/formatters/__init__.py b/prospector/formatters/__init__.py index 0cfa985b..16592afd 100644 --- a/prospector/formatters/__init__.py +++ b/prospector/formatters/__init__.py @@ -1,4 +1,4 @@ -from . import emacs, gitlab, grouped, json, pylint, pylint_parseable, text, vscode, xunit, yaml +from . import emacs, gitlab, grouped, json, pylint, pylint_parseable, sarif, text, vscode, xunit, yaml from .base import Formatter __all__ = ("FORMATTERS", "Formatter") @@ -15,4 +15,5 @@ "pylint_parseable": pylint_parseable.PylintParseableFormatter, "xunit": xunit.XunitFormatter, "vscode": vscode.VSCodeFormatter, + "sarif": sarif.SarifFormatter, } diff --git a/prospector/formatters/sarif.py b/prospector/formatters/sarif.py new file mode 100644 index 00000000..d871288a --- /dev/null +++ b/prospector/formatters/sarif.py @@ -0,0 +1,66 @@ +import importlib +import json +from typing import Any + +from prospector.formatters.base import Formatter + +__all__ = ("SarifFormatter",) + +_VERSION = importlib.metadata.version("prospector") + + +class SarifFormatter(Formatter): + """ + This formatter outputs messages in the SARIF format. + https://www.oasis-open.org/committees/sarif/charter.php + """ + + def render(self, summary: bool = True, messages: bool = True, profile: bool = False) -> str: + results: list[dict[str, Any]] = [] + + if messages: + for message in sorted(self.messages): + region: dict[str, int] = {} + if message.location.line: + region["startLine"] = message.location.line + if message.location.line_end: + region["endLine"] = message.location.line_end + if message.location.character: + region["startColumn"] = message.location.character + if message.location.character_end: + region["endColumn"] = message.location.character_end + + results.append( + { + "ruleId": message.code, + "level": "warning", + "message": {"text": f"{message.source}[{message.code}]: {message.message.strip()}"}, + "locations": [ + { + "physicalLocation": { + "artifactLocation": {"uri": str(self._make_path(message.location))}, + "region": region, + } + } + ], + } + ) + + output = { + "version": "2.1.0", + "$schema": "https://json.schemastore.org/sarif-2.1.0.json", + "runs": [ + { + "tool": { + "driver": { + "name": "Prospector", + "informationUri": "https://github.com/prospector-dev/prospector", + "version": _VERSION, + } + }, + "results": results, + } + ], + } + + return json.dumps(output, indent=2)