Class: Brakeman::Report::SARIF

Inherits:
Base
  • Object
show all
Defined in:
lib/brakeman/report/report_sarif.rb

Constant Summary

Constants included from Util

Util::ALL_COOKIES, Util::ALL_PARAMETERS, Util::COOKIES, Util::COOKIES_SEXP, Util::DIR_CONST, Util::LITERALS, Util::PARAMETERS, Util::PARAMS_SEXP, Util::PATH_PARAMETERS, Util::QUERY_PARAMETERS, Util::REQUEST_COOKIES, Util::REQUEST_ENV, Util::REQUEST_PARAMETERS, Util::REQUEST_PARAMS, Util::REQUEST_REQUEST_PARAMETERS, Util::SAFE_LITERAL, Util::SESSION, Util::SESSION_SEXP, Util::SIMPLE_LITERALS

Instance Attribute Summary

Attributes inherited from Base

#checks, #tracker

Instance Method Summary collapse

Methods inherited from Base

#absolute_paths?, #all_warnings, #context_for, #controller_information, #controller_warnings, #filter_warnings, #generic_warnings, #github_url, #ignored_warnings, #initialize, #model_warnings, #number_of_templates, #rails_version, #template_warnings, #warning_file, #warnings_summary

Methods included from Util

#all_literals?, #array?, #block?, #call?, #camelize, #class_name, #constant?, #contains_class?, #cookies?, #dir_glob?, #false?, #hash?, #hash_access, #hash_insert, #hash_iterate, #hash_values, #integer?, #kwsplat?, #literal?, #make_call, #node_type?, #number?, #params?, #pluralize, #rails_version, #recurse_check?, #regexp?, #remove_kwsplat, #request_headers?, #request_value?, #result?, #safe_literal, #safe_literal?, #safe_literal_target?, #set_env_defaults, #sexp?, #simple_literal?, #string?, #string_interp?, #symbol?, #template_path_to_name, #true?, #underscore

Constructor Details

This class inherits a constructor from Brakeman::Report::Base

Instance Method Details

#check_descriptionsObject

Returns a hash of all check descriptions, keyed by check name



211
212
213
214
215
# File 'lib/brakeman/report/report_sarif.rb', line 211

def check_descriptions
  @check_descriptions ||= Brakeman::Checks.checks.map do |check|
    [check.name.gsub(/^Check/, ''), check.description]
  end.to_h
end

#file_uri(path) ⇒ Object

File URI as a string with trailing forward-slash as required by SARIF standard



250
251
252
# File 'lib/brakeman/report/report_sarif.rb', line 250

def file_uri(path)
  URI::File.build(path: File.join(path, '/')).to_s
end

#generate_reportObject



4
5
6
7
8
9
10
11
# File 'lib/brakeman/report/report_sarif.rb', line 4

def generate_report
  sarif_log = {
    :version => '2.1.0',
    :$schema => 'https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0.json',
    :runs => runs,
  }
  JSON.pretty_generate sarif_log
end

#infer_level(warning) ⇒ Object



238
239
240
241
242
243
244
245
246
# File 'lib/brakeman/report/report_sarif.rb', line 238

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

#original_uri_base_idsObject

Output base URIs based on what the user specified for the application path and whether or not –absolute-paths was set.



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
134
135
136
137
138
139
# File 'lib/brakeman/report/report_sarif.rb', line 32

def original_uri_base_ids
  if tracker.options[:app_path] == '.'
    # Probably no app_path was specified, as that's the default

    if absolute_paths?
      # Set %SRCROOT% to absolute path
      {
        originalUriBaseIds: {
          '%SRCROOT%' => {
            uri: file_uri(tracker.app_tree.root),
            description: {
              text: 'Base path for application'
            }
          }
        }
      }
    else
      # Empty %SRCROOT%
      # This avoids any paths appearing in the report
      # that are not part of the application directory.
      # Seems fine!
      {
        originalUriBaseIds: {
          '%SRCROOT%' => {
            description: {
              text: 'Base path for application'
            }
          },
        }
      }

    end
  elsif tracker.options[:app_path] != tracker.app_tree.root
    # Path was specified and it was relative

    if absolute_paths?
      # Include absolute root and relative application path
      {
        originalUriBaseIds: {
          PROJECTROOT: {
            uri: file_uri(tracker.app_tree.root),
            description: {
              text: 'Base path for all project files'
            }
          },
          '%SRCROOT%' => {
            # Technically should ensure this doesn't have any '..'
            # but... TODO
            uri: File.join(tracker.options[:app_path], '/'),
            uriBaseId: 'PROJECTROOT',
            description: {
              text: 'Base path for application'
            }
          }
        }
      }
    else
      # Just include relative application path.
      # Not clear this is 100% valid, but there is one example in the spec like this
      {
        originalUriBaseIds: {
          PROJECTROOT: {
            description: {
              text: 'Base path for all project files'
            }
          },
          '%SRCROOT%' => {
            # Technically should ensure this doesn't have any '..'
            # but... TODO
            uri: File.join(tracker.options[:app_path], '/'),
            uriBaseId: 'PROJECTROOT',
            description: {
              text: 'Base path for application'
            }
          }
        }
      }
    end
  else
    # app_path was absolute

    if absolute_paths?
      # Set %SRCROOT% to absolute path
      {
        originalUriBaseIds: {
          '%SRCROOT%' => {
            uri: file_uri(tracker.app_tree.root),
            description: {
              text: 'Base path for application'
            }
          }
        }
      }
    else
      # Empty %SRCROOT%
      # Seems fine!
      {
        originalUriBaseIds: {
          '%SRCROOT%' => {
            description: {
              text: 'Base path for application'
            }
          },
        }
      }
    end
  end
end

#render_id(warning) ⇒ Object



222
223
224
225
# File 'lib/brakeman/report/report_sarif.rb', line 222

def render_id warning
  # Include alpha prefix to provide 'compiler error' appearance
  "BRAKE#{'%04d' % warning.warning_code}" # 46 becomes BRAKE0046, for example
end

#render_message(message) ⇒ Object



227
228
229
230
231
232
233
234
235
236
# File 'lib/brakeman/report/report_sarif.rb', line 227

def render_message message
  return message if message.nil?

  # Ensure message ends with a period
  if message.end_with? "."
    message
  else
    "#{message}."
  end
end

#resultsObject



164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/brakeman/report/report_sarif.rb', line 164

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

#rulesObject



141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/brakeman/report/report_sarif.rb', line 141

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

#runsObject



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/brakeman/report/report_sarif.rb', line 13

def runs
  [
    {
      :tool => {
        :driver => {
          :name => 'Brakeman',
          :informationUri => 'https://brakemanscanner.org',
          :semanticVersion => Brakeman::Version,
          :rules => rules,
        },
      },
      :results => results,
    }.merge(original_uri_base_ids)
  ]
end

#unique_warnings_by_warning_codeObject

Returns a de-duplicated set of warnings, used to generate rules



218
219
220
# File 'lib/brakeman/report/report_sarif.rb', line 218

def unique_warnings_by_warning_code
  @unique_warnings_by_warning_code ||= tracker.checks.all_warnings.uniq { |w| w.warning_code }
end