brakeman 5.0.1 → 5.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES.md +41 -0
- data/bundle/load.rb +3 -2
- data/bundle/ruby/2.7.0/gems/parallel-1.20.1/MIT-LICENSE.txt +20 -0
- data/bundle/ruby/2.7.0/gems/parallel-1.20.1/lib/parallel.rb +523 -0
- data/bundle/ruby/2.7.0/gems/parallel-1.20.1/lib/parallel/processor_count.rb +42 -0
- data/bundle/ruby/2.7.0/gems/parallel-1.20.1/lib/parallel/version.rb +3 -0
- data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/History.rdoc +19 -0
- data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/Manifest.txt +2 -0
- data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/README.rdoc +0 -0
- data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/compare/normalize.rb +2 -2
- data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/debugging.md +0 -0
- data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/rp_extensions.rb +0 -0
- data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/rp_stringscanner.rb +0 -0
- data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/ruby20_parser.rb +0 -0
- data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/ruby20_parser.y +0 -0
- data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/ruby21_parser.rb +0 -0
- data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/ruby21_parser.y +0 -0
- data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/ruby22_parser.rb +0 -0
- data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/ruby22_parser.y +0 -0
- data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/ruby23_parser.rb +0 -0
- data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/ruby23_parser.y +0 -0
- data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/ruby24_parser.rb +0 -0
- data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/ruby24_parser.y +0 -0
- data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/ruby25_parser.rb +0 -0
- data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/ruby25_parser.y +0 -0
- data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/ruby26_parser.rb +0 -0
- data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/ruby26_parser.y +0 -0
- data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/ruby27_parser.rb +0 -0
- data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/ruby27_parser.y +0 -0
- data/bundle/ruby/2.7.0/gems/ruby_parser-3.16.0/lib/ruby30_parser.rb +7358 -0
- data/bundle/ruby/2.7.0/gems/ruby_parser-3.16.0/lib/ruby30_parser.y +2703 -0
- data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/ruby_lexer.rb +0 -0
- data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/ruby_lexer.rex +0 -0
- data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/ruby_lexer.rex.rb +0 -0
- data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/ruby_parser.rb +2 -0
- data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/ruby_parser.yy +2 -0
- data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/ruby_parser_extras.rb +2 -2
- data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/tools/munge.rb +0 -0
- data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/tools/ripper.rb +0 -0
- data/bundle/ruby/2.7.0/gems/{sexp_processor-4.15.2 → sexp_processor-4.15.3}/History.rdoc +6 -0
- data/bundle/ruby/2.7.0/gems/{sexp_processor-4.15.2 → sexp_processor-4.15.3}/Manifest.txt +0 -0
- data/bundle/ruby/2.7.0/gems/{sexp_processor-4.15.2 → sexp_processor-4.15.3}/README.rdoc +0 -0
- data/bundle/ruby/2.7.0/gems/{sexp_processor-4.15.2 → sexp_processor-4.15.3}/lib/composite_sexp_processor.rb +0 -0
- data/bundle/ruby/2.7.0/gems/{sexp_processor-4.15.2 → sexp_processor-4.15.3}/lib/pt_testcase.rb +2 -2
- data/bundle/ruby/2.7.0/gems/{sexp_processor-4.15.2 → sexp_processor-4.15.3}/lib/sexp.rb +0 -0
- data/bundle/ruby/2.7.0/gems/{sexp_processor-4.15.2 → sexp_processor-4.15.3}/lib/sexp_matcher.rb +0 -0
- data/bundle/ruby/2.7.0/gems/{sexp_processor-4.15.2 → sexp_processor-4.15.3}/lib/sexp_processor.rb +1 -1
- data/bundle/ruby/2.7.0/gems/{sexp_processor-4.15.2 → sexp_processor-4.15.3}/lib/strict_sexp.rb +0 -0
- data/bundle/ruby/2.7.0/gems/{sexp_processor-4.15.2 → sexp_processor-4.15.3}/lib/unique.rb +0 -0
- data/lib/brakeman.rb +6 -0
- data/lib/brakeman/checks/check_detailed_exceptions.rb +1 -1
- data/lib/brakeman/checks/check_evaluation.rb +1 -1
- data/lib/brakeman/checks/check_execute.rb +10 -0
- data/lib/brakeman/checks/check_render.rb +15 -1
- data/lib/brakeman/checks/check_sanitize_methods.rb +2 -1
- data/lib/brakeman/checks/check_sql.rb +58 -8
- data/lib/brakeman/checks/check_verb_confusion.rb +1 -1
- data/lib/brakeman/file_parser.rb +45 -15
- data/lib/brakeman/options.rb +7 -2
- data/lib/brakeman/processors/alias_processor.rb +85 -9
- data/lib/brakeman/processors/controller_alias_processor.rb +6 -43
- data/lib/brakeman/processors/lib/call_conversion_helper.rb +10 -6
- data/lib/brakeman/processors/library_processor.rb +9 -0
- data/lib/brakeman/processors/model_processor.rb +31 -0
- data/lib/brakeman/report.rb +4 -1
- data/lib/brakeman/report/ignore/interactive.rb +1 -1
- data/lib/brakeman/report/report_github.rb +31 -0
- data/lib/brakeman/report/report_sarif.rb +21 -2
- data/lib/brakeman/rescanner.rb +1 -1
- data/lib/brakeman/scanner.rb +4 -1
- data/lib/brakeman/tracker.rb +33 -4
- data/lib/brakeman/tracker/collection.rb +57 -7
- data/lib/brakeman/tracker/method_info.rb +70 -0
- data/lib/brakeman/util.rb +34 -18
- data/lib/brakeman/version.rb +1 -1
- data/lib/ruby_parser/bm_sexp.rb +14 -0
- metadata +51 -43
@@ -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
|
-
|
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
|
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,15 @@ 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
|
+
exp = hash_values_at target, exp.args
|
328
|
+
end
|
290
329
|
end
|
291
330
|
|
292
331
|
exp
|
@@ -294,6 +333,11 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
294
333
|
|
295
334
|
# Painful conversion of Array#join into string interpolation
|
296
335
|
def process_array_join array, join_str
|
336
|
+
# Empty array
|
337
|
+
if array.length == 1
|
338
|
+
return s(:str, '').line(array.line)
|
339
|
+
end
|
340
|
+
|
297
341
|
result = s().line(array.line)
|
298
342
|
|
299
343
|
join_value = if string? join_str
|
@@ -302,8 +346,10 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
302
346
|
nil
|
303
347
|
end
|
304
348
|
|
305
|
-
array
|
306
|
-
|
349
|
+
if array.length > 2
|
350
|
+
array[1..-2].each do |e|
|
351
|
+
result << join_item(e, join_value)
|
352
|
+
end
|
307
353
|
end
|
308
354
|
|
309
355
|
result << join_item(array.last, nil)
|
@@ -332,7 +378,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
332
378
|
result.unshift combined_first
|
333
379
|
|
334
380
|
# Have to fix up strings that follow interpolation
|
335
|
-
result.reduce(s(:dstr).line(array.line)) do |memo, e|
|
381
|
+
string = result.reduce(s(:dstr).line(array.line)) do |memo, e|
|
336
382
|
if string? e and node_type? memo.last, :evstr
|
337
383
|
e.value = "#{join_value}#{e.value}"
|
338
384
|
elsif join_value and node_type? memo.last, :evstr and node_type? e, :evstr
|
@@ -341,6 +387,14 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
341
387
|
|
342
388
|
memo << e
|
343
389
|
end
|
390
|
+
|
391
|
+
# Convert (:dstr, "hello world")
|
392
|
+
# to (:str, "hello world")
|
393
|
+
if string.length == 2 and string.last.is_a? String
|
394
|
+
string[0] = :str
|
395
|
+
end
|
396
|
+
|
397
|
+
string
|
344
398
|
end
|
345
399
|
|
346
400
|
def join_item item, join_value
|
@@ -749,6 +803,18 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
749
803
|
exp
|
750
804
|
end
|
751
805
|
|
806
|
+
def hash_or_array_include_all_literals? exp
|
807
|
+
return unless call? exp and sexp? exp.target
|
808
|
+
target = exp.target
|
809
|
+
|
810
|
+
case target.node_type
|
811
|
+
when :hash
|
812
|
+
hash_include_all_literals? exp
|
813
|
+
else
|
814
|
+
array_include_all_literals? exp
|
815
|
+
end
|
816
|
+
end
|
817
|
+
|
752
818
|
# Check if exp is a call to Array#include? on an array literal
|
753
819
|
# that contains all literal values. For example:
|
754
820
|
#
|
@@ -767,6 +833,16 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
767
833
|
(all_literals? exp.target or dir_glob? exp.target)
|
768
834
|
end
|
769
835
|
|
836
|
+
# Check if exp is a call to Hash#include? on a hash literal
|
837
|
+
# that contains all literal values. For example:
|
838
|
+
#
|
839
|
+
# {x: 1}.include? x
|
840
|
+
def hash_include_all_literals? exp
|
841
|
+
call? exp and
|
842
|
+
exp.method == :include? and
|
843
|
+
all_literals? exp.target, :hash
|
844
|
+
end
|
845
|
+
|
770
846
|
#Sets @inside_if = true
|
771
847
|
def process_if exp
|
772
848
|
if @ignore_ifs.nil?
|
@@ -807,7 +883,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
807
883
|
scope do
|
808
884
|
@branch_env = env.current
|
809
885
|
branch_index = 2 + i # s(:if, condition, then_branch, else_branch)
|
810
|
-
if i == 0 and
|
886
|
+
if i == 0 and hash_or_array_include_all_literals? condition
|
811
887
|
# If the condition is ["a", "b"].include? x
|
812
888
|
# set x to "a" inside the true branch
|
813
889
|
var = condition.first_arg
|
@@ -815,7 +891,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
815
891
|
env.current[var] = safe_literal(var.line)
|
816
892
|
exp[branch_index] = process_if_branch branch
|
817
893
|
env.current[var] = previous_value
|
818
|
-
elsif i == 1 and
|
894
|
+
elsif i == 1 and hash_or_array_include_all_literals? condition and early_return? branch
|
819
895
|
var = condition.first_arg
|
820
896
|
env.current[var] = safe_literal(var.line)
|
821
897
|
exp[branch_index] = process_if_branch branch
|
@@ -1013,8 +1089,8 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
1013
1089
|
method_name = call.method
|
1014
1090
|
|
1015
1091
|
#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
|
1092
|
+
if found_method = tracker.find_method(method_name, @current_class)
|
1093
|
+
helper = found_method.src
|
1018
1094
|
|
1019
1095
|
if sexp? helper
|
1020
1096
|
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)
|
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
|
153
|
+
method = filter.src
|
154
154
|
|
155
|
-
if ivars = @tracker.filter_cache[[filter
|
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
|
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
|
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
|
@@ -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,13 @@ module Brakeman
|
|
92
88
|
original_exp
|
93
89
|
end
|
94
90
|
end
|
91
|
+
|
92
|
+
def hash_values_at hash, keys
|
93
|
+
values = keys.map do |key|
|
94
|
+
process_hash_access hash, key
|
95
|
+
end
|
96
|
+
|
97
|
+
Sexp.new(:array).concat(values).line(hash.line)
|
98
|
+
end
|
95
99
|
end
|
96
100
|
end
|
@@ -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,33 @@ 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
|
+
|
97
|
+
enum_name = arg[1].value # first key
|
98
|
+
enums = arg[2] # first value
|
99
|
+
enums_name = pluralize(enum_name.to_s).to_sym
|
100
|
+
|
101
|
+
call_line = call.line
|
102
|
+
|
103
|
+
if hash? enums
|
104
|
+
enum_values = enums
|
105
|
+
elsif array? enums
|
106
|
+
# Build hash for enum values like Rails does
|
107
|
+
enum_values = s(:hash).line(call_line)
|
108
|
+
|
109
|
+
enums.each_sexp.with_index do |v, index|
|
110
|
+
enum_values << v
|
111
|
+
enum_values << s(:lit, index).line(call_line)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
enum_method = s(:defn, enum_name, s(:args), safe_literal(call_line))
|
116
|
+
enums_method = s(:defs, s(:self), enums_name, s(:args), enum_values)
|
117
|
+
|
118
|
+
@current_class.add_method :public, enum_name, enum_method, @current_file
|
119
|
+
@current_class.add_method :public, enums_name, enums_method, @current_file
|
120
|
+
end
|
90
121
|
end
|
data/lib/brakeman/report.rb
CHANGED
@@ -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
|
@@ -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,6 +72,23 @@ 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
|
@@ -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
|