brakeman-lib 5.0.4 → 5.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +49 -1
  3. data/README.md +1 -1
  4. data/lib/brakeman/app_tree.rb +1 -1
  5. data/lib/brakeman/checks/base_check.rb +10 -0
  6. data/lib/brakeman/checks/check_detailed_exceptions.rb +1 -1
  7. data/lib/brakeman/checks/check_eol_rails.rb +23 -0
  8. data/lib/brakeman/checks/check_eol_ruby.rb +26 -0
  9. data/lib/brakeman/checks/check_evaluation.rb +1 -1
  10. data/lib/brakeman/checks/check_execute.rb +10 -0
  11. data/lib/brakeman/checks/check_json_parsing.rb +1 -1
  12. data/lib/brakeman/checks/check_render.rb +15 -1
  13. data/lib/brakeman/checks/check_sql.rb +58 -7
  14. data/lib/brakeman/checks/check_symbol_dos.rb +1 -1
  15. data/lib/brakeman/checks/check_verb_confusion.rb +1 -1
  16. data/lib/brakeman/checks/eol_check.rb +47 -0
  17. data/lib/brakeman/file_parser.rb +45 -15
  18. data/lib/brakeman/options.rb +15 -2
  19. data/lib/brakeman/processors/alias_processor.rb +91 -9
  20. data/lib/brakeman/processors/controller_alias_processor.rb +6 -43
  21. data/lib/brakeman/processors/gem_processor.rb +3 -0
  22. data/lib/brakeman/processors/haml_template_processor.rb +9 -0
  23. data/lib/brakeman/processors/lib/call_conversion_helper.rb +12 -6
  24. data/lib/brakeman/processors/lib/rails3_route_processor.rb +2 -0
  25. data/lib/brakeman/processors/library_processor.rb +9 -0
  26. data/lib/brakeman/processors/model_processor.rb +32 -0
  27. data/lib/brakeman/report/ignore/config.rb +1 -1
  28. data/lib/brakeman/report/ignore/interactive.rb +1 -1
  29. data/lib/brakeman/report/report_csv.rb +1 -1
  30. data/lib/brakeman/report/report_github.rb +31 -0
  31. data/lib/brakeman/report/report_sarif.rb +22 -3
  32. data/lib/brakeman/report/report_text.rb +1 -1
  33. data/lib/brakeman/report.rb +4 -1
  34. data/lib/brakeman/rescanner.rb +1 -1
  35. data/lib/brakeman/scanner.rb +19 -14
  36. data/lib/brakeman/tracker/collection.rb +57 -7
  37. data/lib/brakeman/tracker/config.rb +8 -1
  38. data/lib/brakeman/tracker/method_info.rb +70 -0
  39. data/lib/brakeman/tracker.rb +33 -4
  40. data/lib/brakeman/util.rb +34 -18
  41. data/lib/brakeman/version.rb +1 -1
  42. data/lib/brakeman/warning_codes.rb +2 -0
  43. data/lib/brakeman.rb +8 -2
  44. data/lib/ruby_parser/bm_sexp.rb +24 -0
  45. metadata +24 -5
@@ -208,6 +208,15 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
208
208
  return Sexp.new(:false).line(exp.line)
209
209
  end
210
210
 
211
+ # For the simplest case of `Foo.thing`
212
+ if node_type? target, :const and first_arg.nil?
213
+ if tracker and (klass = tracker.find_class(class_name(target.value)))
214
+ if return_value = klass.get_simple_method_return_value(:class, method)
215
+ return return_value.deep_clone(exp.line)
216
+ end
217
+ end
218
+ end
219
+
211
220
  #See if it is possible to simplify some basic cases
212
221
  #of addition/concatenation.
213
222
  case method
@@ -220,13 +229,28 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
220
229
  exp = math_op(:+, target, first_arg, exp)
221
230
  end
222
231
  when :-, :*, :/
223
- exp = math_op(method, target, first_arg, exp)
232
+ if method == :* and array? target
233
+ if string? first_arg
234
+ exp = process_array_join(target, first_arg)
235
+ end
236
+ else
237
+ exp = math_op(method, target, first_arg, exp)
238
+ end
224
239
  when :[]
225
240
  if array? target
226
241
  exp = process_array_access(target, exp.args, exp)
227
242
  elsif hash? target
228
243
  exp = process_hash_access(target, first_arg, exp)
229
244
  end
245
+ when :fetch
246
+ if array? target
247
+ # Not dealing with default value
248
+ # so just pass in first argument, but process_array_access expects
249
+ # an array of arguments.
250
+ exp = process_array_access(target, [first_arg], exp)
251
+ elsif hash? target
252
+ exp = process_hash_access(target, first_arg, exp)
253
+ end
230
254
  when :merge!, :update
231
255
  if hash? target and hash? first_arg
232
256
  target = process_hash_merge! target, first_arg
@@ -266,6 +290,12 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
266
290
  target = find_push_target(target_var)
267
291
  env[target] = exp unless target.nil? # Happens in TemplateAliasProcessor
268
292
  end
293
+ when :push
294
+ if array? target
295
+ target << first_arg
296
+ env[target_var] = target
297
+ return target
298
+ end
269
299
  when :first
270
300
  if array? target and first_arg.nil? and sexp? target[1]
271
301
  exp = target[1]
@@ -279,7 +309,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
279
309
  exp = target
280
310
  end
281
311
  when :join
282
- if array? target and target.length > 2 and (string? first_arg or first_arg.nil?)
312
+ if array? target and (string? first_arg or first_arg.nil?)
283
313
  exp = process_array_join(target, first_arg)
284
314
  end
285
315
  when :!
@@ -287,6 +317,21 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
287
317
  if call? target and target.method == :!
288
318
  exp = s(:or, s(:true).line(exp.line), s(:false).line(exp.line)).line(exp.line)
289
319
  end
320
+ when :values
321
+ # Hash literal
322
+ if node_type? target, :hash
323
+ exp = hash_values(target)
324
+ end
325
+ when :values_at
326
+ if node_type? target, :hash
327
+ res = hash_values_at target, exp.args
328
+
329
+ # Only convert to array of values if _all_ keys
330
+ # are present in the hash.
331
+ unless res.any?(&:nil?)
332
+ exp = res
333
+ end
334
+ end
290
335
  end
291
336
 
292
337
  exp
@@ -294,6 +339,11 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
294
339
 
295
340
  # Painful conversion of Array#join into string interpolation
296
341
  def process_array_join array, join_str
342
+ # Empty array
343
+ if array.length == 1
344
+ return s(:str, '').line(array.line)
345
+ end
346
+
297
347
  result = s().line(array.line)
298
348
 
299
349
  join_value = if string? join_str
@@ -302,8 +352,10 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
302
352
  nil
303
353
  end
304
354
 
305
- array[1..-2].each do |e|
306
- result << join_item(e, join_value)
355
+ if array.length > 2
356
+ array[1..-2].each do |e|
357
+ result << join_item(e, join_value)
358
+ end
307
359
  end
308
360
 
309
361
  result << join_item(array.last, nil)
@@ -332,7 +384,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
332
384
  result.unshift combined_first
333
385
 
334
386
  # Have to fix up strings that follow interpolation
335
- result.reduce(s(:dstr).line(array.line)) do |memo, e|
387
+ string = result.reduce(s(:dstr).line(array.line)) do |memo, e|
336
388
  if string? e and node_type? memo.last, :evstr
337
389
  e.value = "#{join_value}#{e.value}"
338
390
  elsif join_value and node_type? memo.last, :evstr and node_type? e, :evstr
@@ -341,6 +393,14 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
341
393
 
342
394
  memo << e
343
395
  end
396
+
397
+ # Convert (:dstr, "hello world")
398
+ # to (:str, "hello world")
399
+ if string.length == 2 and string.last.is_a? String
400
+ string[0] = :str
401
+ end
402
+
403
+ string
344
404
  end
345
405
 
346
406
  def join_item item, join_value
@@ -749,6 +809,18 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
749
809
  exp
750
810
  end
751
811
 
812
+ def hash_or_array_include_all_literals? exp
813
+ return unless call? exp and sexp? exp.target
814
+ target = exp.target
815
+
816
+ case target.node_type
817
+ when :hash
818
+ hash_include_all_literals? exp
819
+ else
820
+ array_include_all_literals? exp
821
+ end
822
+ end
823
+
752
824
  # Check if exp is a call to Array#include? on an array literal
753
825
  # that contains all literal values. For example:
754
826
  #
@@ -767,6 +839,16 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
767
839
  (all_literals? exp.target or dir_glob? exp.target)
768
840
  end
769
841
 
842
+ # Check if exp is a call to Hash#include? on a hash literal
843
+ # that contains all literal values. For example:
844
+ #
845
+ # {x: 1}.include? x
846
+ def hash_include_all_literals? exp
847
+ call? exp and
848
+ exp.method == :include? and
849
+ all_literals? exp.target, :hash
850
+ end
851
+
770
852
  #Sets @inside_if = true
771
853
  def process_if exp
772
854
  if @ignore_ifs.nil?
@@ -807,7 +889,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
807
889
  scope do
808
890
  @branch_env = env.current
809
891
  branch_index = 2 + i # s(:if, condition, then_branch, else_branch)
810
- if i == 0 and array_include_all_literals? condition
892
+ if i == 0 and hash_or_array_include_all_literals? condition
811
893
  # If the condition is ["a", "b"].include? x
812
894
  # set x to "a" inside the true branch
813
895
  var = condition.first_arg
@@ -815,7 +897,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
815
897
  env.current[var] = safe_literal(var.line)
816
898
  exp[branch_index] = process_if_branch branch
817
899
  env.current[var] = previous_value
818
- elsif i == 1 and array_include_all_literals? condition and early_return? branch
900
+ elsif i == 1 and hash_or_array_include_all_literals? condition and early_return? branch
819
901
  var = condition.first_arg
820
902
  env.current[var] = safe_literal(var.line)
821
903
  exp[branch_index] = process_if_branch branch
@@ -1013,8 +1095,8 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
1013
1095
  method_name = call.method
1014
1096
 
1015
1097
  #Look for helper methods and see if we can get a return value
1016
- if found_method = find_method(method_name, @current_class)
1017
- helper = found_method[:method]
1098
+ if found_method = tracker.find_method(method_name, @current_class)
1099
+ helper = found_method.src
1018
1100
 
1019
1101
  if sexp? helper
1020
1102
  value = process_helper_method helper, call.args
@@ -51,7 +51,7 @@ class Brakeman::ControllerAliasProcessor < Brakeman::AliasProcessor
51
51
  #Need to process the method like it was in a controller in order
52
52
  #to get the renders set
53
53
  processor = Brakeman::ControllerProcessor.new(@tracker, mixin.file)
54
- method = mixin.get_method(name)[:src].deep_clone
54
+ method = mixin.get_method(name).src.deep_clone
55
55
 
56
56
  if node_type? method, :defn
57
57
  method = processor.process_defn method
@@ -143,16 +143,16 @@ class Brakeman::ControllerAliasProcessor < Brakeman::AliasProcessor
143
143
  #Basically, adds any instance variable assignments to the environment.
144
144
  #TODO: method arguments?
145
145
  def process_before_filter name
146
- filter = find_method name, @current_class
146
+ filter = tracker.find_method name, @current_class
147
147
 
148
148
  if filter.nil?
149
149
  Brakeman.debug "[Notice] Could not find filter #{name}"
150
150
  return
151
151
  end
152
152
 
153
- method = filter[:method]
153
+ method = filter.src
154
154
 
155
- if ivars = @tracker.filter_cache[[filter[:controller], name]]
155
+ if ivars = @tracker.filter_cache[[filter.owner, name]]
156
156
  ivars.each do |variable, value|
157
157
  env[variable] = value
158
158
  end
@@ -162,7 +162,7 @@ class Brakeman::ControllerAliasProcessor < Brakeman::AliasProcessor
162
162
 
163
163
  ivars = processor.only_ivars(:include_request_vars).all
164
164
 
165
- @tracker.filter_cache[[filter[:controller], name]] = ivars
165
+ @tracker.filter_cache[[filter.owner, name]] = ivars
166
166
 
167
167
  ivars.each do |variable, value|
168
168
  env[variable] = value
@@ -182,7 +182,7 @@ class Brakeman::ControllerAliasProcessor < Brakeman::AliasProcessor
182
182
  # method as the line number
183
183
  if line.nil? and controller = @tracker.controllers[@current_class]
184
184
  if meth = controller.get_method(@current_method)
185
- if line = meth[:src] && meth[:src].last && meth[:src].last.line
185
+ if line = meth.src && meth.src.last && meth.src.last.line
186
186
  line += 1
187
187
  else
188
188
  line = 1
@@ -241,41 +241,4 @@ class Brakeman::ControllerAliasProcessor < Brakeman::AliasProcessor
241
241
  []
242
242
  end
243
243
  end
244
-
245
- #Finds a method in the given class or a parent class
246
- #
247
- #Returns nil if the method could not be found.
248
- #
249
- #If found, returns hash table with controller name and method sexp.
250
- def find_method method_name, klass
251
- return nil if sexp? method_name
252
- method_name = method_name.to_sym
253
-
254
- if method = @method_cache[method_name]
255
- return method
256
- end
257
-
258
- controller = @tracker.controllers[klass]
259
- controller ||= @tracker.libs[klass]
260
-
261
- if klass and controller
262
- method = controller.get_method method_name
263
-
264
- if method.nil?
265
- controller.includes.each do |included|
266
- method = find_method method_name, included
267
- if method
268
- @method_cache[method_name] = method
269
- return method
270
- end
271
- end
272
-
273
- @method_cache[method_name] = find_method method_name, controller.parent
274
- else
275
- @method_cache[method_name] = { :controller => controller.name, :method => method[:src] }
276
- end
277
- else
278
- nil
279
- end
280
- end
281
244
  end
@@ -6,6 +6,7 @@ class Brakeman::GemProcessor < Brakeman::BasicProcessor
6
6
  def initialize *args
7
7
  super
8
8
  @gem_name_version = /^\s*([-_+.A-Za-z0-9]+) \((\w(\.\w+)*)\)/
9
+ @ruby_version = /^\s+ruby (\d\.\d.\d+)/
9
10
  end
10
11
 
11
12
  def process_gems gem_files
@@ -95,6 +96,8 @@ class Brakeman::GemProcessor < Brakeman::BasicProcessor
95
96
  def set_gem_version_and_file line, file, line_num
96
97
  if line =~ @gem_name_version
97
98
  @tracker.config.add_gem $1, $2, file, line_num
99
+ elsif line =~ @ruby_version
100
+ @tracker.config.set_ruby_version $1
98
101
  end
99
102
  end
100
103
  end
@@ -8,6 +8,7 @@ class Brakeman::HamlTemplateProcessor < Brakeman::TemplateProcessor
8
8
  HAML_HELPERS2 = s(:colon2, s(:colon3, :Haml), :Helpers)
9
9
  JAVASCRIPT_FILTER = s(:colon2, s(:colon2, s(:const, :Haml), :Filters), :Javascript)
10
10
  COFFEE_FILTER = s(:colon2, s(:colon2, s(:const, :Haml), :Filters), :Coffee)
11
+ ATTRIBUTE_BUILDER = s(:colon2, s(:colon3, :Haml), :AttributeBuilder)
11
12
 
12
13
  def initialize *args
13
14
  super
@@ -133,6 +134,8 @@ class Brakeman::HamlTemplateProcessor < Brakeman::TemplateProcessor
133
134
 
134
135
  get_pushed_value(exp.first_arg, default)
135
136
  @javascript = false
137
+ elsif haml_attribute_builder? exp
138
+ ignore # probably safe... seems escaped by default?
136
139
  else
137
140
  add_output exp, default
138
141
  end
@@ -154,6 +157,12 @@ class Brakeman::HamlTemplateProcessor < Brakeman::TemplateProcessor
154
157
  exp.method == :attributes
155
158
  end
156
159
 
160
+ def haml_attribute_builder? exp
161
+ call? exp and
162
+ exp.target == ATTRIBUTE_BUILDER and
163
+ exp.method == :build
164
+ end
165
+
157
166
  def fix_textareas? exp
158
167
  call? exp and
159
168
  exp.target == HAMLOUT and
@@ -1,11 +1,5 @@
1
1
  module Brakeman
2
2
  module CallConversionHelper
3
- def all_literals? exp, expected_type = :array
4
- node_type? exp, expected_type and
5
- exp.length > 1 and
6
- exp.all? { |e| e.is_a? Symbol or node_type? e, :lit, :str }
7
- end
8
-
9
3
  # Join two array literals into one.
10
4
  def join_arrays lhs, rhs, original_exp = nil
11
5
  if array? lhs and array? rhs
@@ -76,6 +70,8 @@ module Brakeman
76
70
 
77
71
  #Have to do this because first element is :array and we have to skip it
78
72
  array[1..-1][index] or original_exp
73
+ elsif all_literals? array
74
+ safe_literal(array.line)
79
75
  else
80
76
  original_exp
81
77
  end
@@ -92,5 +88,15 @@ module Brakeman
92
88
  original_exp
93
89
  end
94
90
  end
91
+
92
+ # You must check the return value for `nil`s -
93
+ # which indicate a key could not be found.
94
+ def hash_values_at hash, keys
95
+ values = keys.map do |key|
96
+ process_hash_access hash, key
97
+ end
98
+
99
+ Sexp.new(:array).concat(values).line(hash.line)
100
+ end
95
101
  end
96
102
  end
@@ -78,6 +78,8 @@ class Brakeman::Rails3RoutesProcessor < Brakeman::BasicProcessor
78
78
 
79
79
  #TODO: Need test for this
80
80
  def process_root exp
81
+ return exp unless hash? exp.first_arg
82
+
81
83
  if value = hash_access(exp.first_arg, :to)
82
84
  if string? value
83
85
  add_route_from_string value
@@ -54,6 +54,15 @@ class Brakeman::LibraryProcessor < Brakeman::BaseProcessor
54
54
 
55
55
  def process_call exp
56
56
  if process_call_defn? exp
57
+ exp
58
+ elsif @current_method.nil? and exp.target.nil? and (@current_class or @current_module)
59
+ # Methods called inside class / module
60
+ case exp.method
61
+ when :include
62
+ module_name = class_name(exp.first_arg)
63
+ (@current_class || @current_module).add_include module_name
64
+ end
65
+
57
66
  exp
58
67
  else
59
68
  process_default exp
@@ -73,6 +73,8 @@ class Brakeman::ModelProcessor < Brakeman::BaseProcessor
73
73
  @current_class.set_attr_accessible exp
74
74
  when :attr_protected
75
75
  @current_class.set_attr_protected exp
76
+ when :enum
77
+ add_enum_method exp
76
78
  else
77
79
  if @current_class
78
80
  @current_class.add_option method, exp
@@ -87,4 +89,34 @@ class Brakeman::ModelProcessor < Brakeman::BaseProcessor
87
89
  call
88
90
  end
89
91
  end
92
+
93
+ def add_enum_method call
94
+ arg = call.first_arg
95
+ return unless hash? arg
96
+ return unless symbol? arg[1]
97
+
98
+ enum_name = arg[1].value # first key
99
+ enums = arg[2] # first value
100
+ enums_name = pluralize(enum_name.to_s).to_sym
101
+
102
+ call_line = call.line
103
+
104
+ if hash? enums
105
+ enum_values = enums
106
+ elsif array? enums
107
+ # Build hash for enum values like Rails does
108
+ enum_values = s(:hash).line(call_line)
109
+
110
+ enums.each_sexp.with_index do |v, index|
111
+ enum_values << v
112
+ enum_values << s(:lit, index).line(call_line)
113
+ end
114
+ end
115
+
116
+ enum_method = s(:defn, enum_name, s(:args), safe_literal(call_line))
117
+ enums_method = s(:defs, s(:self), enums_name, s(:args), enum_values)
118
+
119
+ @current_class.add_method :public, enum_name, enum_method, @current_file
120
+ @current_class.add_method :public, enums_name, enums_method, @current_file
121
+ end
90
122
  end
@@ -126,7 +126,7 @@ module Brakeman
126
126
 
127
127
  w[:note] = @notes[w[:fingerprint]] || ""
128
128
  w
129
- end.sort_by { |w| [w[:fingerprint], w[:line]] }
129
+ end.sort_by { |w| [w[:fingerprint], w[:line] || 0] }
130
130
 
131
131
  output = {
132
132
  :ignored_warnings => warnings,
@@ -62,7 +62,7 @@ module Brakeman
62
62
  process_warnings
63
63
  end
64
64
 
65
- m.choice "Hide previously ignored warnings" do
65
+ m.choice "Inspect new warnings" do
66
66
  @skip_ignored = true
67
67
  pre_show_help
68
68
  process_warnings
@@ -17,7 +17,7 @@ class Brakeman::Report::CSV < Brakeman::Report::Base
17
17
  ]
18
18
 
19
19
  rows = tracker.filtered_warnings.sort_by do |w|
20
- [w.confidence, w.warning_type, w.file, w.line, w.fingerprint]
20
+ [w.confidence, w.warning_type, w.file, w.line || 0, w.fingerprint]
21
21
  end.map do |warning|
22
22
  generate_row(headers, warning)
23
23
  end
@@ -0,0 +1,31 @@
1
+ # Github Actions Formatter
2
+ # Formats warnings as workflow commands to create annotations in GitHub UI
3
+ class Brakeman::Report::Github < Brakeman::Report::Base
4
+ def generate_report
5
+ # @see https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-a-warning-message
6
+ errors.concat(warnings).join("\n")
7
+ end
8
+
9
+ def warnings
10
+ all_warnings
11
+ .map { |warning| "::warning file=#{warning_file(warning)},line=#{warning.line}::#{warning.message}" }
12
+ end
13
+
14
+ def errors
15
+ tracker.errors.map do |error|
16
+ if error[:exception].is_a?(Racc::ParseError)
17
+ # app/services/balance.rb:4 :: parse error on value "..." (tDOT3)
18
+ file, line = error[:exception].message.split(':').map(&:strip)[0,2]
19
+ "::error file=#{file},line=#{line}::#{clean_message(error[:error])}"
20
+ else
21
+ "::error ::#{clean_message(error[:error])}"
22
+ end
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def clean_message(msg)
29
+ msg.gsub('::','').squeeze(' ')
30
+ end
31
+ end
@@ -48,7 +48,7 @@ class Brakeman::Report::SARIF < Brakeman::Report::Base
48
48
  end
49
49
 
50
50
  def results
51
- @results ||= all_warnings.map do |warning|
51
+ @results ||= tracker.checks.all_warnings.map do |warning|
52
52
  rule_id = render_id warning
53
53
  result_level = infer_level warning
54
54
  message_text = render_message warning.message.to_s
@@ -72,11 +72,28 @@ class Brakeman::Report::SARIF < Brakeman::Report::Base
72
72
  ],
73
73
  }
74
74
 
75
+ if @ignore_filter && @ignore_filter.ignored?(warning)
76
+ result[:suppressions] = [
77
+ {
78
+ :kind => 'external',
79
+ :justification => @ignore_filter.note_for(warning),
80
+ :location => {
81
+ :physicalLocation => {
82
+ :artifactLocation => {
83
+ :uri => Brakeman::FilePath.from_app_tree(@app_tree, @ignore_filter.file).relative,
84
+ :uriBaseId => '%SRCROOT%',
85
+ },
86
+ },
87
+ },
88
+ }
89
+ ]
90
+ end
91
+
75
92
  result
76
93
  end
77
94
  end
78
95
 
79
- # Returns a hash of all check descriptions, keyed by check namne
96
+ # Returns a hash of all check descriptions, keyed by check name
80
97
  def check_descriptions
81
98
  @check_descriptions ||= Brakeman::Checks.checks.map do |check|
82
99
  [check.name.gsub(/^Check/, ''), check.description]
@@ -85,7 +102,7 @@ class Brakeman::Report::SARIF < Brakeman::Report::Base
85
102
 
86
103
  # Returns a de-duplicated set of warnings, used to generate rules
87
104
  def unique_warnings_by_warning_code
88
- @unique_warnings_by_warning_code ||= all_warnings.uniq { |w| w.warning_code }
105
+ @unique_warnings_by_warning_code ||= tracker.checks.all_warnings.uniq { |w| w.warning_code }
89
106
  end
90
107
 
91
108
  def render_id warning
@@ -94,6 +111,8 @@ class Brakeman::Report::SARIF < Brakeman::Report::Base
94
111
  end
95
112
 
96
113
  def render_message message
114
+ return message if message.nil?
115
+
97
116
  # Ensure message ends with a period
98
117
  if message.end_with? "."
99
118
  message
@@ -92,7 +92,7 @@ class Brakeman::Report::Text < Brakeman::Report::Base
92
92
  HighLine.color("No warnings found", :bold, :green)
93
93
  else
94
94
  warnings = tracker.filtered_warnings.sort_by do |w|
95
- [w.confidence, w.warning_type, w.file, w.line, w.fingerprint]
95
+ [w.confidence, w.warning_type, w.file, w.line || 0, w.fingerprint]
96
96
  end.map do |w|
97
97
  output_warning w
98
98
  end
@@ -6,7 +6,7 @@ require 'brakeman/report/report_base'
6
6
  class Brakeman::Report
7
7
  attr_reader :tracker
8
8
 
9
- VALID_FORMATS = [:to_html, :to_pdf, :to_csv, :to_json, :to_tabs, :to_hash, :to_s, :to_markdown, :to_codeclimate, :to_plain, :to_text, :to_junit]
9
+ VALID_FORMATS = [:to_html, :to_pdf, :to_csv, :to_json, :to_tabs, :to_hash, :to_s, :to_markdown, :to_codeclimate, :to_plain, :to_text, :to_junit, :to_github]
10
10
 
11
11
  def initialize tracker
12
12
  @app_tree = tracker.app_tree
@@ -48,6 +48,9 @@ class Brakeman::Report
48
48
  when :to_sonar
49
49
  require_report 'sonar'
50
50
  Brakeman::Report::Sonar
51
+ when :to_github
52
+ require_report 'github'
53
+ Brakeman::Report::Github
51
54
  else
52
55
  raise "Invalid format: #{format}. Should be one of #{VALID_FORMATS.inspect}"
53
56
  end
@@ -391,7 +391,7 @@ class Brakeman::Rescanner < Brakeman::Scanner
391
391
 
392
392
  def parse_ruby_files list
393
393
  paths = list.select(&:exists?)
394
- file_parser = Brakeman::FileParser.new(tracker.app_tree, tracker.options[:parser_timeout])
394
+ file_parser = Brakeman::FileParser.new(tracker.app_tree, tracker.options[:parser_timeout], tracker.options[:parallel_checks])
395
395
  file_parser.parse_files paths
396
396
  tracker.add_errors(file_parser.errors)
397
397
  file_parser.file_list
@@ -40,38 +40,38 @@ class Brakeman::Scanner
40
40
 
41
41
  #Process everything in the Rails application
42
42
  def process
43
- Brakeman.notify "Processing gems..."
43
+ Brakeman.notify "Processing gems... "
44
44
  process_gems
45
45
  guess_rails_version
46
- Brakeman.notify "Processing configuration..."
46
+ Brakeman.notify "Processing configuration... "
47
47
  process_config
48
- Brakeman.notify "Parsing files..."
48
+ Brakeman.notify "Parsing files... "
49
49
  parse_files
50
- Brakeman.notify "Detecting file types..."
50
+ Brakeman.notify "Detecting file types... "
51
51
  detect_file_types
52
- Brakeman.notify "Processing initializers..."
52
+ Brakeman.notify "Processing initializers... "
53
53
  process_initializers
54
- Brakeman.notify "Processing libs..."
54
+ Brakeman.notify "Processing libs... "
55
55
  process_libs
56
- Brakeman.notify "Processing routes... "
56
+ Brakeman.notify "Processing routes... "
57
57
  process_routes
58
- Brakeman.notify "Processing templates... "
58
+ Brakeman.notify "Processing templates... "
59
59
  process_templates
60
- Brakeman.notify "Processing data flow in templates..."
60
+ Brakeman.notify "Processing data flow in templates... "
61
61
  process_template_data_flows
62
- Brakeman.notify "Processing models... "
62
+ Brakeman.notify "Processing models... "
63
63
  process_models
64
- Brakeman.notify "Processing controllers... "
64
+ Brakeman.notify "Processing controllers... "
65
65
  process_controllers
66
66
  Brakeman.notify "Processing data flow in controllers..."
67
67
  process_controller_data_flows
68
- Brakeman.notify "Indexing call sites... "
68
+ Brakeman.notify "Indexing call sites... "
69
69
  index_call_sites
70
70
  tracker
71
71
  end
72
72
 
73
73
  def parse_files
74
- fp = Brakeman::FileParser.new(tracker.app_tree, tracker.options[:parser_timeout])
74
+ fp = Brakeman::FileParser.new(tracker.app_tree, tracker.options[:parser_timeout], tracker.options[:parallel_checks])
75
75
 
76
76
  fp.parse_files tracker.app_tree.ruby_file_paths
77
77
 
@@ -137,7 +137,9 @@ class Brakeman::Scanner
137
137
  end
138
138
 
139
139
  if @app_tree.exists? ".ruby-version"
140
- tracker.config.set_ruby_version @app_tree.file_path(".ruby-version").read
140
+ if version = @app_tree.file_path(".ruby-version").read[/(\d\.\d.\d+)/]
141
+ tracker.config.set_ruby_version version
142
+ end
141
143
  end
142
144
 
143
145
  tracker.config.load_rails_defaults
@@ -353,6 +355,9 @@ class Brakeman::Scanner
353
355
  def parse_ruby_file file
354
356
  fp = Brakeman::FileParser.new(tracker.app_tree, tracker.options[:parser_timeout])
355
357
  fp.parse_ruby(file.read, file)
358
+ rescue Exception => e
359
+ tracker.error(e)
360
+ nil
356
361
  end
357
362
  end
358
363