brakeman-lib 5.0.1 → 5.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGES.md +4 -0
- data/lib/brakeman.rb +4 -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_sanitize_methods.rb +2 -1
- data/lib/brakeman/checks/check_sql.rb +15 -2
- data/lib/brakeman/checks/check_verb_confusion.rb +1 -1
- data/lib/brakeman/file_parser.rb +36 -14
- data/lib/brakeman/options.rb +1 -1
- data/lib/brakeman/processors/alias_processor.rb +52 -7
- data/lib/brakeman/processors/controller_alias_processor.rb +6 -43
- data/lib/brakeman/processors/lib/call_conversion_helper.rb +10 -0
- data/lib/brakeman/processors/library_processor.rb +9 -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/scanner.rb +3 -0
- data/lib/brakeman/tracker.rb +33 -4
- data/lib/brakeman/tracker/collection.rb +27 -5
- data/lib/brakeman/tracker/method_info.rb +29 -0
- data/lib/brakeman/util.rb +8 -0
- data/lib/brakeman/version.rb +1 -1
- metadata +18 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d16867dd5c48de9ec2975e1dc420e3a5154939361988d70c4217f251881452ed
|
4
|
+
data.tar.gz: f5cae624d83a1298fb3f07108c76d1f55c756404d6998fccfd9e5fcfe69a068e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f8724b266165ef9ed4ad926432e0786b955cb2e98b56e7100354b0ad04a51cc0eaa139343a769980852ae615bd209e8854f6f83616b0703d3a2aaf08229860c6
|
7
|
+
data.tar.gz: 963f46a856d6f943c74c6aca8ec3f3dc61ae3d82758fbfdda63bb4fc789ff95535f49cc73f9abef4cf1553323606d4fd50c8a03082178a6dd3cea0883e544a40
|
data/CHANGES.md
CHANGED
data/lib/brakeman.rb
CHANGED
@@ -250,6 +250,8 @@ module Brakeman
|
|
250
250
|
[:to_sarif]
|
251
251
|
when :sonar, :to_sonar
|
252
252
|
[:to_sonar]
|
253
|
+
when :github, :to_github
|
254
|
+
[:to_github]
|
253
255
|
else
|
254
256
|
[:to_text]
|
255
257
|
end
|
@@ -283,6 +285,8 @@ module Brakeman
|
|
283
285
|
:to_sarif
|
284
286
|
when /\.sonar$/i
|
285
287
|
:to_sonar
|
288
|
+
when /\.github$/i
|
289
|
+
:to_github
|
286
290
|
else
|
287
291
|
:to_text
|
288
292
|
end
|
@@ -26,7 +26,7 @@ class Brakeman::CheckDetailedExceptions < Brakeman::BaseCheck
|
|
26
26
|
def check_detailed_exceptions
|
27
27
|
tracker.controllers.each do |_name, controller|
|
28
28
|
controller.methods_public.each do |method_name, definition|
|
29
|
-
src = definition
|
29
|
+
src = definition.src
|
30
30
|
body = src.body.last
|
31
31
|
next unless body
|
32
32
|
|
@@ -10,7 +10,7 @@ class Brakeman::CheckEvaluation < Brakeman::BaseCheck
|
|
10
10
|
#Process calls
|
11
11
|
def run_check
|
12
12
|
Brakeman.debug "Finding eval-like calls"
|
13
|
-
calls = tracker.find_call :
|
13
|
+
calls = tracker.find_call methods: [:eval, :instance_eval, :class_eval, :module_eval], nested: true
|
14
14
|
|
15
15
|
Brakeman.debug "Processing eval-like calls"
|
16
16
|
calls.each do |call|
|
@@ -90,7 +90,8 @@ class Brakeman::CheckSanitizeMethods < Brakeman::BaseCheck
|
|
90
90
|
def loofah_vulnerable_cve_2018_8048?
|
91
91
|
loofah_version = tracker.config.gem_version(:loofah)
|
92
92
|
|
93
|
-
|
93
|
+
# 2.2.1 is fix version
|
94
|
+
loofah_version and version_between?("0.0.0", "2.2.0", loofah_version)
|
94
95
|
end
|
95
96
|
|
96
97
|
def warn_sanitizer_cve cve, link, upgrade_version
|
@@ -572,7 +572,7 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
|
|
572
572
|
end
|
573
573
|
|
574
574
|
IGNORE_METHODS_IN_SQL = Set[:id, :merge_conditions, :table_name, :quoted_table_name,
|
575
|
-
:quoted_primary_key, :to_i, :to_f, :sanitize_sql, :sanitize_sql_array,
|
575
|
+
:quoted_primary_key, :to_i, :to_f, :sanitize_sql, :sanitize_sql_array, :sanitize_sql_like,
|
576
576
|
:sanitize_sql_for_assignment, :sanitize_sql_for_conditions, :sanitize_sql_hash,
|
577
577
|
:sanitize_sql_hash_for_assignment, :sanitize_sql_hash_for_conditions,
|
578
578
|
:to_sql, :sanitize, :primary_key, :table_name_prefix, :table_name_suffix,
|
@@ -592,7 +592,8 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
|
|
592
592
|
IGNORE_METHODS_IN_SQL.include? exp.method or
|
593
593
|
quote_call? exp or
|
594
594
|
arel? exp or
|
595
|
-
exp.method.to_s.end_with? "_id"
|
595
|
+
exp.method.to_s.end_with? "_id" or
|
596
|
+
number_target? exp
|
596
597
|
end
|
597
598
|
when :if
|
598
599
|
safe_value? exp.then_clause and safe_value? exp.else_clause
|
@@ -695,4 +696,16 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
|
|
695
696
|
active_record_models.include? klass
|
696
697
|
end
|
697
698
|
end
|
699
|
+
|
700
|
+
def number_target? exp
|
701
|
+
return unless call? exp
|
702
|
+
|
703
|
+
if number? exp.target
|
704
|
+
true
|
705
|
+
elsif call? exp.target
|
706
|
+
number_target? exp.target
|
707
|
+
else
|
708
|
+
false
|
709
|
+
end
|
710
|
+
end
|
698
711
|
end
|
data/lib/brakeman/file_parser.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'parallel'
|
2
|
+
|
1
3
|
module Brakeman
|
2
4
|
ASTFile = Struct.new(:path, :ast)
|
3
5
|
|
@@ -13,21 +15,46 @@ module Brakeman
|
|
13
15
|
end
|
14
16
|
|
15
17
|
def parse_files list
|
16
|
-
|
17
|
-
|
18
|
-
|
18
|
+
# Parse the files in parallel.
|
19
|
+
# By default, the parsing will be in separate processes.
|
20
|
+
# So we map the result to ASTFiles and/or Exceptions
|
21
|
+
# then partition them into ASTFiles and Exceptions
|
22
|
+
# and add the Exceptions to @errors
|
23
|
+
#
|
24
|
+
# Basically just a funky way to deal with two possible
|
25
|
+
# return types that are returned from isolated processes.
|
26
|
+
#
|
27
|
+
# Note this method no longer uses read_files
|
28
|
+
@file_list, new_errors = Parallel.map(list) do |file_name|
|
29
|
+
file_path = @app_tree.file_path(file_name)
|
30
|
+
contents = file_path.read
|
31
|
+
|
32
|
+
begin
|
33
|
+
if ast = parse_ruby(contents, file_path.relative)
|
34
|
+
ASTFile.new(file_name, ast)
|
35
|
+
end
|
36
|
+
rescue Exception => e
|
37
|
+
e
|
19
38
|
end
|
39
|
+
end.compact.partition do |result|
|
40
|
+
result.is_a? ASTFile
|
20
41
|
end
|
42
|
+
|
43
|
+
errors.concat new_errors
|
21
44
|
end
|
22
45
|
|
23
46
|
def read_files list
|
24
47
|
list.each do |path|
|
25
48
|
file = @app_tree.file_path(path)
|
26
49
|
|
27
|
-
|
50
|
+
begin
|
51
|
+
result = yield file, file.read
|
28
52
|
|
29
|
-
|
30
|
-
|
53
|
+
if result
|
54
|
+
@file_list << result
|
55
|
+
end
|
56
|
+
rescue Exception => e
|
57
|
+
@errors << e
|
31
58
|
end
|
32
59
|
end
|
33
60
|
end
|
@@ -42,17 +69,12 @@ module Brakeman
|
|
42
69
|
Brakeman.debug "Parsing #{path}"
|
43
70
|
RubyParser.new.parse input, path, @timeout
|
44
71
|
rescue Racc::ParseError => e
|
45
|
-
|
72
|
+
raise e.exception(e.message + "\nCould not parse #{path}")
|
46
73
|
rescue Timeout::Error => e
|
47
|
-
|
74
|
+
raise Exception.new("Parsing #{path} took too long (> #{@timeout} seconds). Try increasing the limit with --parser-timeout")
|
48
75
|
rescue => e
|
49
|
-
|
76
|
+
raise e.exception(e.message + "\nWhile processing #{path}")
|
50
77
|
end
|
51
78
|
end
|
52
|
-
|
53
|
-
def error exception
|
54
|
-
@errors << exception
|
55
|
-
nil
|
56
|
-
end
|
57
79
|
end
|
58
80
|
end
|
data/lib/brakeman/options.rb
CHANGED
@@ -233,7 +233,7 @@ module Brakeman::Options
|
|
233
233
|
|
234
234
|
opts.on "-f",
|
235
235
|
"--format TYPE",
|
236
|
-
[:pdf, :text, :html, :csv, :tabs, :json, :markdown, :codeclimate, :cc, :plain, :table, :junit, :sarif, :sonar],
|
236
|
+
[:pdf, :text, :html, :csv, :tabs, :json, :markdown, :codeclimate, :cc, :plain, :table, :junit, :sarif, :sonar, :github],
|
237
237
|
"Specify output formats. Default is text" do |type|
|
238
238
|
|
239
239
|
type = "s" if type == :text
|
@@ -220,13 +220,28 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
220
220
|
exp = math_op(:+, target, first_arg, exp)
|
221
221
|
end
|
222
222
|
when :-, :*, :/
|
223
|
-
|
223
|
+
if method == :* and array? target
|
224
|
+
if string? first_arg
|
225
|
+
exp = process_array_join(target, first_arg)
|
226
|
+
end
|
227
|
+
else
|
228
|
+
exp = math_op(method, target, first_arg, exp)
|
229
|
+
end
|
224
230
|
when :[]
|
225
231
|
if array? target
|
226
232
|
exp = process_array_access(target, exp.args, exp)
|
227
233
|
elsif hash? target
|
228
234
|
exp = process_hash_access(target, first_arg, exp)
|
229
235
|
end
|
236
|
+
when :fetch
|
237
|
+
if array? target
|
238
|
+
# Not dealing with default value
|
239
|
+
# so just pass in first argument, but process_array_access expects
|
240
|
+
# an array of arguments.
|
241
|
+
exp = process_array_access(target, [first_arg], exp)
|
242
|
+
elsif hash? target
|
243
|
+
exp = process_hash_access(target, first_arg, exp)
|
244
|
+
end
|
230
245
|
when :merge!, :update
|
231
246
|
if hash? target and hash? first_arg
|
232
247
|
target = process_hash_merge! target, first_arg
|
@@ -266,6 +281,12 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
266
281
|
target = find_push_target(target_var)
|
267
282
|
env[target] = exp unless target.nil? # Happens in TemplateAliasProcessor
|
268
283
|
end
|
284
|
+
when :push
|
285
|
+
if array? target
|
286
|
+
target << first_arg
|
287
|
+
env[target_var] = target
|
288
|
+
return target
|
289
|
+
end
|
269
290
|
when :first
|
270
291
|
if array? target and first_arg.nil? and sexp? target[1]
|
271
292
|
exp = target[1]
|
@@ -279,7 +300,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
279
300
|
exp = target
|
280
301
|
end
|
281
302
|
when :join
|
282
|
-
if array? target and
|
303
|
+
if array? target and (string? first_arg or first_arg.nil?)
|
283
304
|
exp = process_array_join(target, first_arg)
|
284
305
|
end
|
285
306
|
when :!
|
@@ -287,6 +308,15 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
287
308
|
if call? target and target.method == :!
|
288
309
|
exp = s(:or, s(:true).line(exp.line), s(:false).line(exp.line)).line(exp.line)
|
289
310
|
end
|
311
|
+
when :values
|
312
|
+
# Hash literal
|
313
|
+
if node_type? target, :hash
|
314
|
+
exp = hash_values(target)
|
315
|
+
end
|
316
|
+
when :values_at
|
317
|
+
if hash? target
|
318
|
+
exp = hash_values_at target, exp.args
|
319
|
+
end
|
290
320
|
end
|
291
321
|
|
292
322
|
exp
|
@@ -294,6 +324,11 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
294
324
|
|
295
325
|
# Painful conversion of Array#join into string interpolation
|
296
326
|
def process_array_join array, join_str
|
327
|
+
# Empty array
|
328
|
+
if array.length == 1
|
329
|
+
return s(:str, '').line(array.line)
|
330
|
+
end
|
331
|
+
|
297
332
|
result = s().line(array.line)
|
298
333
|
|
299
334
|
join_value = if string? join_str
|
@@ -302,8 +337,10 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
302
337
|
nil
|
303
338
|
end
|
304
339
|
|
305
|
-
array
|
306
|
-
|
340
|
+
if array.length > 2
|
341
|
+
array[1..-2].each do |e|
|
342
|
+
result << join_item(e, join_value)
|
343
|
+
end
|
307
344
|
end
|
308
345
|
|
309
346
|
result << join_item(array.last, nil)
|
@@ -332,7 +369,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
332
369
|
result.unshift combined_first
|
333
370
|
|
334
371
|
# Have to fix up strings that follow interpolation
|
335
|
-
result.reduce(s(:dstr).line(array.line)) do |memo, e|
|
372
|
+
string = result.reduce(s(:dstr).line(array.line)) do |memo, e|
|
336
373
|
if string? e and node_type? memo.last, :evstr
|
337
374
|
e.value = "#{join_value}#{e.value}"
|
338
375
|
elsif join_value and node_type? memo.last, :evstr and node_type? e, :evstr
|
@@ -341,6 +378,14 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
341
378
|
|
342
379
|
memo << e
|
343
380
|
end
|
381
|
+
|
382
|
+
# Convert (:dstr, "hello world")
|
383
|
+
# to (:str, "hello world")
|
384
|
+
if string.length == 2 and string.last.is_a? String
|
385
|
+
string[0] = :str
|
386
|
+
end
|
387
|
+
|
388
|
+
string
|
344
389
|
end
|
345
390
|
|
346
391
|
def join_item item, join_value
|
@@ -1013,8 +1058,8 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
1013
1058
|
method_name = call.method
|
1014
1059
|
|
1015
1060
|
#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
|
1061
|
+
if found_method = tracker.find_method(method_name, @current_class)
|
1062
|
+
helper = found_method.src
|
1018
1063
|
|
1019
1064
|
if sexp? helper
|
1020
1065
|
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
|
@@ -76,6 +76,8 @@ module Brakeman
|
|
76
76
|
|
77
77
|
#Have to do this because first element is :array and we have to skip it
|
78
78
|
array[1..-1][index] or original_exp
|
79
|
+
elsif all_literals? array
|
80
|
+
safe_literal(array.line)
|
79
81
|
else
|
80
82
|
original_exp
|
81
83
|
end
|
@@ -92,5 +94,13 @@ module Brakeman
|
|
92
94
|
original_exp
|
93
95
|
end
|
94
96
|
end
|
97
|
+
|
98
|
+
def hash_values_at hash, keys
|
99
|
+
values = keys.map do |key|
|
100
|
+
process_hash_access hash, key
|
101
|
+
end
|
102
|
+
|
103
|
+
Sexp.new(:array).concat(values).line(hash.line)
|
104
|
+
end
|
95
105
|
end
|
96
106
|
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
|
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
|
data/lib/brakeman/scanner.rb
CHANGED
data/lib/brakeman/tracker.rb
CHANGED
@@ -35,6 +35,7 @@ class Brakeman::Tracker
|
|
35
35
|
#class they are.
|
36
36
|
@models = {}
|
37
37
|
@models[UNKNOWN_MODEL] = Brakeman::Model.new(UNKNOWN_MODEL, nil, @app_tree.file_path("NOT_REAL.rb"), nil, self)
|
38
|
+
@method_cache = {}
|
38
39
|
@routes = {}
|
39
40
|
@initializers = {}
|
40
41
|
@errors = []
|
@@ -99,8 +100,8 @@ class Brakeman::Tracker
|
|
99
100
|
classes.each do |set|
|
100
101
|
set.each do |set_name, collection|
|
101
102
|
collection.each_method do |method_name, definition|
|
102
|
-
src = definition
|
103
|
-
yield src, set_name, method_name, definition
|
103
|
+
src = definition.src
|
104
|
+
yield src, set_name, method_name, definition.file
|
104
105
|
end
|
105
106
|
end
|
106
107
|
end
|
@@ -220,6 +221,34 @@ class Brakeman::Tracker
|
|
220
221
|
nil
|
221
222
|
end
|
222
223
|
|
224
|
+
def find_method method_name, class_name, method_type = :instance
|
225
|
+
return nil unless method_name.is_a? Symbol
|
226
|
+
|
227
|
+
klass = find_class(class_name)
|
228
|
+
return nil unless klass
|
229
|
+
|
230
|
+
cache_key = [klass, method_name, method_type]
|
231
|
+
|
232
|
+
if method = @method_cache[cache_key]
|
233
|
+
return method
|
234
|
+
end
|
235
|
+
|
236
|
+
if method = klass.get_method(method_name, method_type)
|
237
|
+
return method
|
238
|
+
else
|
239
|
+
# Check modules included for method definition
|
240
|
+
# TODO: only for instance methods, otherwise check extends!
|
241
|
+
klass.includes.each do |included_name|
|
242
|
+
if method = find_method(method_name, included_name, method_type)
|
243
|
+
return (@method_cache[cache_key] = method)
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
# Not in any included modules, check the parent
|
248
|
+
@method_cache[cache_key] = find_method(method_name, klass.parent)
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
223
252
|
def index_call_sites
|
224
253
|
finder = Brakeman::FindAllCalls.new self
|
225
254
|
|
@@ -285,8 +314,8 @@ class Brakeman::Tracker
|
|
285
314
|
method_sets.each do |set|
|
286
315
|
set.each do |set_name, info|
|
287
316
|
info.each_method do |method_name, definition|
|
288
|
-
src = definition
|
289
|
-
finder.process_source src, :class => set_name, :method => method_name, :file => definition
|
317
|
+
src = definition.src
|
318
|
+
finder.process_source src, :class => set_name, :method => method_name, :file => definition.file
|
290
319
|
end
|
291
320
|
end
|
292
321
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'brakeman/util'
|
2
|
+
require 'brakeman/tracker/method_info'
|
2
3
|
|
3
4
|
module Brakeman
|
4
5
|
class Collection
|
@@ -13,6 +14,7 @@ module Brakeman
|
|
13
14
|
@src = {}
|
14
15
|
@includes = []
|
15
16
|
@methods = { :public => {}, :private => {}, :protected => {} }
|
17
|
+
@class_methods = {}
|
16
18
|
@options = {}
|
17
19
|
@tracker = tracker
|
18
20
|
|
@@ -46,11 +48,16 @@ module Brakeman
|
|
46
48
|
end
|
47
49
|
|
48
50
|
def add_method visibility, name, src, file_name
|
51
|
+
meth_info = Brakeman::MethodInfo.new(name, src, self, file_name)
|
52
|
+
|
49
53
|
if src.node_type == :defs
|
54
|
+
@class_methods[name] = meth_info
|
55
|
+
|
56
|
+
# TODO fix this weirdness
|
50
57
|
name = :"#{src[1]}.#{name}"
|
51
58
|
end
|
52
59
|
|
53
|
-
@methods[visibility][name] =
|
60
|
+
@methods[visibility][name] = meth_info
|
54
61
|
end
|
55
62
|
|
56
63
|
def each_method
|
@@ -61,16 +68,31 @@ module Brakeman
|
|
61
68
|
end
|
62
69
|
end
|
63
70
|
|
64
|
-
def get_method name
|
65
|
-
|
66
|
-
|
67
|
-
|
71
|
+
def get_method name, type = :instance
|
72
|
+
case type
|
73
|
+
when :class
|
74
|
+
get_class_method name
|
75
|
+
when :instance
|
76
|
+
get_instance_method name
|
77
|
+
else
|
78
|
+
raise "Unexpected method type: #{type.inspect}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def get_instance_method name
|
83
|
+
@methods.each do |_vis, meths|
|
84
|
+
if meths[name]
|
85
|
+
return meths[name]
|
68
86
|
end
|
69
87
|
end
|
70
88
|
|
71
89
|
nil
|
72
90
|
end
|
73
91
|
|
92
|
+
def get_class_method name
|
93
|
+
@class_methods[name]
|
94
|
+
end
|
95
|
+
|
74
96
|
def file
|
75
97
|
@files.first
|
76
98
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'brakeman/util'
|
2
|
+
|
3
|
+
module Brakeman
|
4
|
+
class MethodInfo
|
5
|
+
include Brakeman::Util
|
6
|
+
|
7
|
+
attr_reader :name, :src, :owner, :file, :type
|
8
|
+
|
9
|
+
def initialize name, src, owner, file
|
10
|
+
@name = name
|
11
|
+
@src = src
|
12
|
+
@owner = owner
|
13
|
+
@file = file
|
14
|
+
@type = case src.node_type
|
15
|
+
when :defn
|
16
|
+
:instance
|
17
|
+
when :defs
|
18
|
+
:class
|
19
|
+
else
|
20
|
+
raise "Expected sexp type: #{src.node_type}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# To support legacy code that expected a Hash
|
25
|
+
def [] attr
|
26
|
+
self.send(attr)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/brakeman/util.rb
CHANGED
@@ -142,6 +142,14 @@ module Brakeman::Util
|
|
142
142
|
nil
|
143
143
|
end
|
144
144
|
|
145
|
+
def hash_values hash
|
146
|
+
values = hash.each_sexp.each_slice(2).map do |_, value|
|
147
|
+
value
|
148
|
+
end
|
149
|
+
|
150
|
+
Sexp.new(:array).concat(values).line(hash.line)
|
151
|
+
end
|
152
|
+
|
145
153
|
#These are never modified
|
146
154
|
PARAMS_SEXP = Sexp.new(:params)
|
147
155
|
SESSION_SEXP = Sexp.new(:session)
|
data/lib/brakeman/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: brakeman-lib
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.0.
|
4
|
+
version: 5.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Collins
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-06-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
@@ -66,6 +66,20 @@ dependencies:
|
|
66
66
|
- - '='
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: 0.10.2
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: parallel
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.20'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.20'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
84
|
name: ruby_parser
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -380,6 +394,7 @@ files:
|
|
380
394
|
- lib/brakeman/report/report_base.rb
|
381
395
|
- lib/brakeman/report/report_codeclimate.rb
|
382
396
|
- lib/brakeman/report/report_csv.rb
|
397
|
+
- lib/brakeman/report/report_github.rb
|
383
398
|
- lib/brakeman/report/report_hash.rb
|
384
399
|
- lib/brakeman/report/report_html.rb
|
385
400
|
- lib/brakeman/report/report_json.rb
|
@@ -409,6 +424,7 @@ files:
|
|
409
424
|
- lib/brakeman/tracker/constants.rb
|
410
425
|
- lib/brakeman/tracker/controller.rb
|
411
426
|
- lib/brakeman/tracker/library.rb
|
427
|
+
- lib/brakeman/tracker/method_info.rb
|
412
428
|
- lib/brakeman/tracker/model.rb
|
413
429
|
- lib/brakeman/tracker/template.rb
|
414
430
|
- lib/brakeman/util.rb
|