Add support for SARIF format

Signed-off-by: Mathieu Rul <mathroule@gmail.com>
This commit is contained in:
Mathieu Rul
2023-06-16 15:21:39 +02:00
parent ea560f0fb4
commit 73e8798369
2 changed files with 117 additions and 2 deletions

View File

@@ -22,7 +22,7 @@ import sys
from yamllint import APP_DESCRIPTION, APP_NAME, APP_VERSION from yamllint import APP_DESCRIPTION, APP_NAME, APP_VERSION
from yamllint import linter from yamllint import linter
from yamllint.config import YamlLintConfig, YamlLintConfigError from yamllint.config import YamlLintConfig, YamlLintConfigError
from yamllint.formatters import colored, github, parsable, standard from yamllint.formatters import colored, github, parsable, sarif, standard
from yamllint.linter import PROBLEM_LEVELS from yamllint.linter import PROBLEM_LEVELS
@@ -59,6 +59,8 @@ def show_results(results, args_format, no_warn):
return parsable.format_results(results, no_warn) return parsable.format_results(results, no_warn)
elif args_format == 'github': elif args_format == 'github':
return github.format_results(results, no_warn) return github.format_results(results, no_warn)
elif args_format == 'sarif':
return sarif.format_results(results, no_warn)
elif args_format == 'colored': elif args_format == 'colored':
return colored.format_results(results, no_warn) return colored.format_results(results, no_warn)
else: else:
@@ -98,7 +100,7 @@ def run(argv=None):
help='list files to lint and exit') help='list files to lint and exit')
parser.add_argument('-f', '--format', parser.add_argument('-f', '--format',
choices=('parsable', 'standard', 'colored', 'github', choices=('parsable', 'standard', 'colored', 'github',
'auto'), 'sarif', 'auto'),
default='auto', help='format for parsing output') default='auto', help='format for parsing output')
parser.add_argument('-s', '--strict', parser.add_argument('-s', '--strict',
action='store_true', action='store_true',

View File

@@ -0,0 +1,113 @@
# Copyright (C) 2016 Adrien Vergé
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import json
from yamllint import APP_VERSION
from yamllint.linter import PROBLEM_LEVELS
def format_results(results, no_warn):
max_level = 0
sarif = {
'$schema': 'https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json',
'version': '2.1.0',
'runs': [
{
'tool': {
'driver': {
'name': 'yamllint',
'version': APP_VERSION,
'informationUri': 'https://yamllint.readthedocs.io',
'rules': []
},
},
'results': []
}
]
}
rules = {}
max_rule_index = 0
for file in results:
for problem in results[file]:
max_level = max(max_level, PROBLEM_LEVELS[problem.level])
if problem.rule in rules:
rule_index = rules[problem.rule]
else:
rule_index = max_rule_index
rules[problem.rule] = max_rule_index
sarif['runs'][0]['tool']['driver']['rules'].append(format_rule(problem))
max_rule_index += 1
sarif['runs'][0]['results'].append(format_result(rule_index, problem, file))
print(json.dumps(sarif))
return max_level
def format_rule(problem):
uri = 'https://yamllint.readthedocs.io/en/v{}/rules.html#module-yamllint.rules.{}'.format(APP_VERSION, problem.rule)
return {
'id': problem.rule,
'name': ''.join([word.capitalize() for word in problem.rule.split('-')]),
'defaultConfiguration': {
'level': problem.level
},
'properties': {
'description': problem.desc,
'tags': [],
'queryUri': uri,
},
'shortDescription': {
'text': problem.desc
},
'fullDescription': {
'text': problem.desc
},
'helpUri': uri,
'help': {
'text': 'More info: {}'.format(uri),
'markdown': '[More info]({})'.format(uri)
}
}
def format_result(rule_index, problem, filename):
return {
'ruleId': problem.rule,
'ruleIndex': rule_index,
'message': {
'text': problem.message
},
'locations': [
{
'physicalLocation': {
'artifactLocation': {
'uri': filename,
'uriBaseId': '%SRCROOT%'
},
'region': {
'startLine': problem.line,
'startColumn': problem.column
}
}
}
]
}