forked from presidentbeef/brakeman
/
report_sarif.rb
133 lines (122 loc) · 3.76 KB
/
report_sarif.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
class Brakeman::Report::SARIF < Brakeman::Report::Base
def generate_report
sarif_log = {
:version => '2.1.0',
:$schema => 'https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0-rtm.5.json',
:runs => runs,
}
JSON.pretty_generate sarif_log
end
def runs
[
{
:tool => {
:driver => {
:name => 'Brakeman',
:informationUri => 'https://brakemanscanner.org',
:semanticVersion => Brakeman::Version,
:rules => rules,
},
},
:results => results,
},
]
end
def rules
@rules ||= unique_warnings_by_warning_code.map do |warning|
rule_id = render_id warning
check_name = warning.check_name
check_description = render_message check_descriptions[check_name]
{
:id => rule_id,
:name => "#{check_name}/#{warning.warning_type}",
:fullDescription => {
:text => check_description,
},
:helpUri => warning.link,
:help => {
:text => "More info: #{warning.link}.",
:markdown => "[More info](#{warning.link}).",
},
:properties => {
:tags => [check_name],
},
}
end
end
def results
@results ||= tracker.checks.all_warnings.map do |warning|
rule_id = render_id warning
result_level = infer_level warning
message_text = render_message warning.message.to_s
result = {
:ruleId => rule_id,
:ruleIndex => rules.index { |r| r[:id] == rule_id },
:level => result_level,
:message => {
:text => message_text,
},
:locations => [
:physicalLocation => {
:artifactLocation => {
:uri => warning.file.relative,
:uriBaseId => '%SRCROOT%',
},
:region => {
:startLine => warning.line.is_a?(Integer) ? warning.line : 1,
},
},
],
}
if @ignore_filter && @ignore_filter.ignored?(warning)
result[:suppressions] = [
{
:kind => 'external',
:justification => @ignore_filter.note_for(warning),
:location => {
:physicalLocation => {
:artifactLocation => {
:uri => Brakeman::FilePath.from_app_tree(@app_tree, @ignore_filter.file).relative,
:uriBaseId => '%SRCROOT%',
},
},
},
}
]
end
result
end
end
# Returns a hash of all check descriptions, keyed by check namne
def check_descriptions
@check_descriptions ||= Brakeman::Checks.checks.map do |check|
[check.name.gsub(/^Check/, ''), check.description]
end.to_h
end
# Returns a de-duplicated set of warnings, used to generate rules
def unique_warnings_by_warning_code
@unique_warnings_by_warning_code ||= tracker.checks.all_warnings.uniq { |w| w.warning_code }
end
def render_id warning
# Include alpha prefix to provide 'compiler error' appearance
"BRAKE#{'%04d' % warning.warning_code}" # 46 becomes BRAKE0046, for example
end
def render_message message
return message if message.nil?
# Ensure message ends with a period
if message.end_with? "."
message
else
"#{message}."
end
end
def infer_level warning
# Infer result level from warning confidence
@@levels_from_confidence ||= Hash.new('warning').update({
0 => 'error', # 0 represents 'high confidence', which we infer as 'error'
1 => 'warning', # 1 represents 'medium confidence' which we infer as 'warning'
2 => 'note', # 2 represents 'weak, or low, confidence', which we infer as 'note'
})
@@levels_from_confidence[warning.confidence]
end
end