Class: RuboCop::Formatter::SARIFFormatter
| Relationships & Source Files | |
| Super Chains via Extension / Inclusion / Inheritance | |
|
Class Chain:
self,
BaseFormatter
|
|
|
Instance Chain:
self,
BaseFormatter
|
|
| Inherits: |
RuboCop::Formatter::BaseFormatter
|
| Defined in: | lib/rubocop/formatter/sarif_formatter.rb |
Overview
This formatter formats the report data in SARIF 2.1.0, the OASIS standard for static analysis results. It’s understood by GitHub code scanning, Azure DevOps, and other tools that aggregate analyzer output.
SARIF only knows three severity levels, so RuboCop’s info, refactor
and convention severities are all reported as note.
Constant Summary
-
INFORMATION_URI =
# File 'lib/rubocop/formatter/sarif_formatter.rb', line 16'https://rubocop.org' -
SARIF_VERSION =
# File 'lib/rubocop/formatter/sarif_formatter.rb', line 15'2.1.0' -
SCHEMA_URI =
# File 'lib/rubocop/formatter/sarif_formatter.rb', line 14'https://json.schemastore.org/sarif-2.1.0.json' -
SEVERITY_LEVELS =
# File 'lib/rubocop/formatter/sarif_formatter.rb', line 18{ info: 'note', refactor: 'note', convention: 'note', warning: 'warning', error: 'error', fatal: 'error' }.freeze
Class Method Summary
Instance Attribute Summary
BaseFormatter - Inherited
Instance Method Summary
- #file_finished(file, offenses)
- #file_started(_file, options)
- #finished(_inspected_files)
-
#config_for(file)
private
#file_started is not called when a formatter is used directly through the API, so there may be no config to consult.
- #cop_config(file, offense) private
- #default_level_for(file, offense, cop_class) private
- #description_for(file, offense) private
- #driver private
-
#fingerprints_for(offense)
private
GitHub uses these to match an alert across runs.
- #help_for(description, help_uri) private
-
#location_for(file, offense)
private
The URI is relative to the working directory, which is what consumers expect to resolve against their own checkout.
- #region_for(offense) private
- #report private
- #result_for(file, offense) private
- #rule_for(file, offense) private
- #rule_index_for(file, offense) private
- #suppression_for(offense) private
- #uri_for(file) private
BaseFormatter - Inherited
| #file_finished | Invoked at the end of inspecting each files. |
| #file_started | Invoked at the beginning of inspecting each files. |
| #finished | Invoked after all files are inspected or interrupted by user. |
| #started | Invoked once before any files are inspected. |
Constructor Details
.new(output, options = {}) ⇒ SARIFFormatter
# File 'lib/rubocop/formatter/sarif_formatter.rb', line 27
def initialize(output, = {}) super @rules = [] @rule_indexes = {} @results = [] @config_store = nil end
Instance Method Details
#config_for(file) (private)
#file_started is not called when a formatter is used directly through the API, so there may be no config to consult.
# File 'lib/rubocop/formatter/sarif_formatter.rb', line 172
def config_for(file) @config_store&.for_file(file) end
#cop_config(file, offense) (private)
[ GitHub ]# File 'lib/rubocop/formatter/sarif_formatter.rb', line 166
def cop_config(file, offense) config_for(file)&.for_cop(offense.cop_name) || {} end
#default_level_for(file, offense, cop_class) (private)
[ GitHub ]# File 'lib/rubocop/formatter/sarif_formatter.rb', line 159
def default_level_for(file, offense, cop_class) severity = cop_config(file, offense)['Severity'] || (cop_class&.lint? ? :warning : :convention) SEVERITY_LEVELS.fetch(severity.to_sym, 'warning') end
#description_for(file, offense) (private)
[ GitHub ]# File 'lib/rubocop/formatter/sarif_formatter.rb', line 155
def description_for(file, offense) cop_config(file, offense)['Description'] || offense.cop_name end
#driver (private)
[ GitHub ]#file_finished(file, offenses)
[ GitHub ]# File 'lib/rubocop/formatter/sarif_formatter.rb', line 40
def file_finished(file, offenses) offenses.each do |offense| @results << result_for(file, offense) end end
#file_started(_file, options)
[ GitHub ]# File 'lib/rubocop/formatter/sarif_formatter.rb', line 36
def file_started(_file, ) @config_store = [:config_store] end
#fingerprints_for(offense) (private)
GitHub uses these to match an alert across runs. Without them it falls back to the file path, which makes alerts churn whenever a file moves.
# File 'lib/rubocop/formatter/sarif_formatter.rb', line 113
def fingerprints_for(offense) source_line = offense.location.source_line { primaryLocationLineHash: Digest::SHA256.hexdigest("#{offense.cop_name}#{source_line}") } end
#finished(_inspected_files)
[ GitHub ]# File 'lib/rubocop/formatter/sarif_formatter.rb', line 46
def finished(_inspected_files) output.write(report.to_json) end
#help_for(description, help_uri) (private)
[ GitHub ]# File 'lib/rubocop/formatter/sarif_formatter.rb', line 149
def help_for(description, help_uri) help = { text: description } help[:markdown] = "#{description}\n\n[Documentation](#{help_uri})" if help_uri help end
#location_for(file, offense) (private)
The URI is relative to the working directory, which is what consumers
expect to resolve against their own checkout. No uriBaseId: it would
have to be declared under originalUriBaseIds to mean anything, and
every consumer that matters resolves relative paths without it.
# File 'lib/rubocop/formatter/sarif_formatter.rb', line 91
def location_for(file, offense) { physicalLocation: { artifactLocation: { uri: uri_for(file) }, region: region_for(offense) } } end
#region_for(offense) (private)
[ GitHub ]# File 'lib/rubocop/formatter/sarif_formatter.rb', line 100
def region_for(offense) # SARIF columns are 1-based and `endColumn` points at the character # *after* the region, while `real_last_column` is the last character. { startLine: offense.first_line, startColumn: offense.real_column, endLine: offense.last_line, endColumn: offense.real_last_column + 1 } end
#report (private)
[ GitHub ]# File 'lib/rubocop/formatter/sarif_formatter.rb', line 52
def report { '$schema': SCHEMA_URI, version: SARIF_VERSION, runs: [{ tool: { driver: driver }, results: @results }] } end
#result_for(file, offense) (private)
[ GitHub ]# File 'lib/rubocop/formatter/sarif_formatter.rb', line 70
def result_for(file, offense) result = { ruleId: offense.cop_name, ruleIndex: rule_index_for(file, offense), level: SEVERITY_LEVELS.fetch(offense.severity.name, 'warning'), message: { text: offense. }, locations: [location_for(file, offense)], partialFingerprints: fingerprints_for(offense) } # Suppressed offenses are only reported under `--display-suppressed`. # SARIF has a first-class representation for them, so consumers can # tell them apart from offenses that were never triggered. result[:suppressions] = [suppression_for(offense)] if offense.disabled? result end
#rule_for(file, offense) (private)
[ GitHub ]# File 'lib/rubocop/formatter/sarif_formatter.rb', line 131
def rule_for(file, offense) cop_class = Cop::Registry.global.find_by_cop_name(offense.cop_name) description = description_for(file, offense) help_uri = cop_class && Cop::Documentation.url_for(cop_class, config_for(file)) rule = { id: offense.cop_name, name: offense.cop_name, shortDescription: { text: description }, fullDescription: { text: description }, help: help_for(description, help_uri), defaultConfiguration: { level: default_level_for(file, offense, cop_class) }, properties: { tags: [offense.cop_name.split('/').first] } } rule[:helpUri] = help_uri if help_uri rule end
#rule_index_for(file, offense) (private)
[ GitHub ]# File 'lib/rubocop/formatter/sarif_formatter.rb', line 124
def rule_index_for(file, offense) @rule_indexes[offense.cop_name] ||= begin @rules << rule_for(file, offense) @rules.size - 1 end end
#suppression_for(offense) (private)
[ GitHub ]# File 'lib/rubocop/formatter/sarif_formatter.rb', line 118
def suppression_for(offense) suppression = { kind: 'inSource' } suppression[:justification] = offense.justification if offense.justification suppression end
#uri_for(file) (private)
[ GitHub ]# File 'lib/rubocop/formatter/sarif_formatter.rb', line 176
def uri_for(file) PathUtil.smart_path(file).tr('\\', '/') end