brakeman-min 6.2.2 → 7.0.1
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 +26 -0
- data/README.md +1 -1
- data/lib/brakeman/app_tree.rb +29 -19
- data/lib/brakeman/checks/check_deserialize.rb +4 -1
- data/lib/brakeman/checks/check_evaluation.rb +45 -8
- data/lib/brakeman/checks/check_model_attr_accessible.rb +1 -0
- data/lib/brakeman/checks/check_weak_rsa_key.rb +1 -1
- data/lib/brakeman/file_parser.rb +2 -1
- data/lib/brakeman/options.rb +12 -5
- data/lib/brakeman/processors/alias_processor.rb +9 -4
- data/lib/brakeman/processors/lib/file_type_detector.rb +9 -7
- data/lib/brakeman/report/ignore/config.rb +0 -1
- data/lib/brakeman/report/report_sarif.rb +122 -2
- data/lib/brakeman/rescanner.rb +40 -390
- data/lib/brakeman/scanner.rb +84 -51
- data/lib/brakeman/tracker/file_cache.rb +83 -0
- data/lib/brakeman/tracker.rb +19 -2
- data/lib/brakeman/version.rb +1 -1
- data/lib/brakeman.rb +19 -2
- metadata +3 -30
data/lib/brakeman/rescanner.rb
CHANGED
@@ -6,15 +6,15 @@ require 'brakeman/differ'
|
|
6
6
|
class Brakeman::Rescanner < Brakeman::Scanner
|
7
7
|
include Brakeman::Util
|
8
8
|
KNOWN_TEMPLATE_EXTENSIONS = Brakeman::TemplateParser::KNOWN_TEMPLATE_EXTENSIONS
|
9
|
-
SCAN_ORDER = [:gemfile, :config, :initializer, :lib, :routes, :template,
|
10
|
-
:model, :controller]
|
11
9
|
|
12
10
|
#Create new Rescanner to scan changed files
|
13
11
|
def initialize options, processor, changed_files
|
14
|
-
super(options
|
12
|
+
super(options)
|
13
|
+
|
14
|
+
@old_tracker = processor.tracked_events
|
15
15
|
|
16
16
|
@paths = changed_files.map {|f| tracker.app_tree.file_path(f) }
|
17
|
-
@old_results =
|
17
|
+
@old_results = @old_tracker.filtered_warnings.dup #Old warnings from previous scan
|
18
18
|
@changes = nil #True if files had to be rescanned
|
19
19
|
@reindex = Set.new
|
20
20
|
end
|
@@ -24,379 +24,55 @@ class Brakeman::Rescanner < Brakeman::Scanner
|
|
24
24
|
def recheck
|
25
25
|
rescan if @changes.nil?
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
27
|
+
if @changes
|
28
|
+
tracker.run_checks
|
29
|
+
Brakeman.filter_warnings(tracker, options) # Actually sets ignored_filter
|
30
|
+
Brakeman::RescanReport.new @old_results, tracker
|
31
|
+
else
|
32
|
+
# No changes, fake no new results
|
33
|
+
Brakeman::RescanReport.new @old_results, @old_tracker
|
34
|
+
end
|
30
35
|
end
|
31
36
|
|
32
37
|
#Rescans changed files
|
33
38
|
def rescan
|
34
|
-
|
39
|
+
raise "Cannot rescan: set `support_rescanning: true`" unless @old_tracker.options[:support_rescanning]
|
35
40
|
|
36
|
-
|
41
|
+
tracker.file_cache = @old_tracker.pristine_file_cache
|
37
42
|
|
38
|
-
|
39
|
-
|
40
|
-
end
|
43
|
+
template_paths = []
|
44
|
+
ruby_paths = []
|
41
45
|
|
46
|
+
# Remove changed files from the cache.
|
47
|
+
# Collect files to re-parse.
|
42
48
|
@paths.each do |path|
|
43
|
-
|
44
|
-
paths_by_type[type] << path unless type == :unknown
|
45
|
-
end
|
46
|
-
|
47
|
-
@changes = false
|
48
|
-
|
49
|
-
SCAN_ORDER.each do |type|
|
50
|
-
paths_by_type[type].each do |path|
|
51
|
-
Brakeman.debug "Rescanning #{path} as #{type}"
|
52
|
-
|
53
|
-
if rescan_file path, type
|
54
|
-
@changes = true
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
if @changes and not @reindex.empty?
|
60
|
-
tracker.reindex_call_sites @reindex
|
61
|
-
end
|
62
|
-
|
63
|
-
self
|
64
|
-
end
|
65
|
-
|
66
|
-
#Rescans a single file
|
67
|
-
def rescan_file path, type = nil
|
68
|
-
type ||= file_type path
|
69
|
-
|
70
|
-
unless path.exists?
|
71
|
-
return rescan_deleted_file path, type
|
72
|
-
end
|
73
|
-
|
74
|
-
case type
|
75
|
-
when :controller
|
76
|
-
rescan_controller path
|
77
|
-
when :template
|
78
|
-
rescan_template path
|
79
|
-
when :model
|
80
|
-
rescan_model path
|
81
|
-
when :lib
|
82
|
-
rescan_lib path
|
83
|
-
when :config
|
84
|
-
process_config
|
85
|
-
when :initializer
|
86
|
-
rescan_initializer path
|
87
|
-
when :routes
|
88
|
-
rescan_routes
|
89
|
-
when :gemfile
|
90
|
-
if tracker.config.has_gem? :rails_xss and tracker.config.escape_html?
|
91
|
-
tracker.config.escape_html = false
|
92
|
-
end
|
93
|
-
|
94
|
-
process_gems
|
95
|
-
else
|
96
|
-
return false #Nothing to do, file hopefully does not need to be rescanned
|
97
|
-
end
|
98
|
-
|
99
|
-
true
|
100
|
-
end
|
101
|
-
|
102
|
-
def rescan_controller path
|
103
|
-
controller = tracker.reset_controller path
|
104
|
-
paths = controller.nil? ? [path] : controller.files
|
105
|
-
parse_ruby_files(paths).each do |astfile|
|
106
|
-
process_controller astfile
|
107
|
-
end
|
108
|
-
|
109
|
-
#Process data flow and template rendering
|
110
|
-
#from the controller
|
111
|
-
tracker.controllers.each do |name, controller|
|
112
|
-
if controller.files.include?(path)
|
113
|
-
tracker.templates.each do |template_name, template|
|
114
|
-
next unless template.render_path
|
115
|
-
if template.render_path.include_controller? name
|
116
|
-
tracker.reset_template template_name
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
controller.src.each do |file, src|
|
121
|
-
@processor.process_controller_alias controller.name, src, nil, file
|
122
|
-
end
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
|
-
@reindex << :templates << :controllers
|
127
|
-
end
|
128
|
-
|
129
|
-
def rescan_template path
|
130
|
-
return unless path.relative.match KNOWN_TEMPLATE_EXTENSIONS and path.exists?
|
131
|
-
|
132
|
-
template_name = template_path_to_name(path)
|
133
|
-
|
134
|
-
tracker.reset_template template_name
|
135
|
-
fp = Brakeman::FileParser.new(tracker.app_tree, tracker.options[:parser_timeout])
|
136
|
-
template_parser = Brakeman::TemplateParser.new(tracker, fp)
|
137
|
-
template_parser.parse_template path, path.read
|
138
|
-
tracker.add_errors(fp.errors)
|
139
|
-
process_template fp.file_list.first
|
140
|
-
|
141
|
-
@processor.process_template_alias tracker.templates[template_name]
|
142
|
-
|
143
|
-
rescan = Set.new
|
49
|
+
file_cache.delete path
|
144
50
|
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
template.render_path.each do |from|
|
152
|
-
case from[:type]
|
153
|
-
when :template
|
154
|
-
rescan << [:template, from[:name]]
|
155
|
-
when :controller
|
156
|
-
rescan << [:controller, from[:class], from[:method]]
|
157
|
-
end
|
158
|
-
end
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
rescan.each do |r|
|
163
|
-
if r[0] == :controller
|
164
|
-
controller = tracker.controllers[r[1]]
|
165
|
-
|
166
|
-
controller.src.each do |file, src|
|
167
|
-
unless @paths.include? file
|
168
|
-
@processor.process_controller_alias controller.name, src, r[2], file
|
169
|
-
end
|
51
|
+
if path.exists?
|
52
|
+
if path.relative.match? KNOWN_TEMPLATE_EXTENSIONS
|
53
|
+
template_paths << path
|
54
|
+
elsif path.relative.end_with? '.rb'
|
55
|
+
ruby_paths << path
|
170
56
|
end
|
171
|
-
elsif r[0] == :template
|
172
|
-
template = tracker.templates[r[1]]
|
173
|
-
|
174
|
-
rescan_template template.file
|
175
57
|
end
|
176
58
|
end
|
177
59
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
num_models = tracker.models.length
|
183
|
-
model = tracker.reset_model path
|
184
|
-
paths = model.nil? ? [path] : model.files
|
185
|
-
parse_ruby_files(paths).each do |astfile|
|
186
|
-
process_model astfile.path, astfile.ast
|
187
|
-
end
|
188
|
-
|
189
|
-
#Only need to rescan other things if a model is added or removed
|
190
|
-
if num_models != tracker.models.length
|
191
|
-
process_template_data_flows
|
192
|
-
process_controller_data_flows
|
193
|
-
@reindex << :templates << :controllers
|
194
|
-
end
|
195
|
-
|
196
|
-
@reindex << :models
|
197
|
-
end
|
198
|
-
|
199
|
-
def rescan_lib path
|
200
|
-
lib = tracker.reset_lib path
|
201
|
-
paths = lib.nil? ? [path] : lib.files
|
202
|
-
parse_ruby_files(paths).each do |astfile|
|
203
|
-
process_lib astfile
|
204
|
-
end
|
205
|
-
|
206
|
-
lib = nil
|
207
|
-
|
208
|
-
tracker.libs.each do |_name, library|
|
209
|
-
if library.files.include?(path)
|
210
|
-
lib = library
|
211
|
-
break
|
212
|
-
end
|
213
|
-
end
|
214
|
-
|
215
|
-
rescan_mixin lib if lib
|
216
|
-
end
|
217
|
-
|
218
|
-
def rescan_routes
|
219
|
-
# Routes affect which controller methods are treated as actions
|
220
|
-
# which affects which templates are rendered, so routes, controllers,
|
221
|
-
# and templates rendered from controllers must be rescanned
|
222
|
-
tracker.reset_routes
|
223
|
-
tracker.reset_templates :only_rendered => true
|
224
|
-
process_routes
|
225
|
-
process_controller_data_flows
|
226
|
-
@reindex << :controllers << :templates
|
227
|
-
end
|
228
|
-
|
229
|
-
def rescan_initializer path
|
230
|
-
tracker.reset_initializer path
|
231
|
-
|
232
|
-
parse_ruby_files([path]).each do |astfile|
|
233
|
-
process_initializer astfile
|
234
|
-
end
|
235
|
-
|
236
|
-
@reindex << :initializers
|
237
|
-
end
|
238
|
-
|
239
|
-
#Handle rescanning when a file is deleted
|
240
|
-
def rescan_deleted_file path, type
|
241
|
-
case type
|
242
|
-
when :controller
|
243
|
-
rescan_controller path
|
244
|
-
when :template
|
245
|
-
rescan_deleted_template path
|
246
|
-
when :model
|
247
|
-
rescan_model path
|
248
|
-
when :lib
|
249
|
-
rescan_deleted_lib path
|
250
|
-
when :initializer
|
251
|
-
rescan_deleted_initializer path
|
60
|
+
# Try to skip rescanning files that do not impact
|
61
|
+
# Brakeman results
|
62
|
+
if @paths.all? { |path| ignorable? path }
|
63
|
+
@changes = false
|
252
64
|
else
|
253
|
-
|
254
|
-
|
255
|
-
else
|
256
|
-
Brakeman.notify "Ignoring deleted file: #{path}"
|
257
|
-
end
|
65
|
+
@changes = true
|
66
|
+
process(ruby_paths:, template_paths:)
|
258
67
|
end
|
259
68
|
|
260
|
-
|
261
|
-
end
|
262
|
-
|
263
|
-
def rescan_deleted_template path
|
264
|
-
return unless path.relative.match KNOWN_TEMPLATE_EXTENSIONS
|
265
|
-
|
266
|
-
template_name = template_path_to_name(path)
|
267
|
-
|
268
|
-
#Remove template
|
269
|
-
tracker.reset_template template_name
|
270
|
-
|
271
|
-
#Remove any rendered versions, or partials rendered from it
|
272
|
-
tracker.templates.delete_if do |_name, template|
|
273
|
-
template.file == path or template.name.to_sym == template_name.to_sym
|
274
|
-
end
|
275
|
-
end
|
276
|
-
|
277
|
-
def rescan_deleted_lib path
|
278
|
-
deleted_lib = nil
|
279
|
-
|
280
|
-
tracker.libs.delete_if do |_name, lib|
|
281
|
-
if lib.files.include?(path)
|
282
|
-
deleted_lib = lib
|
283
|
-
true
|
284
|
-
end
|
285
|
-
end
|
286
|
-
|
287
|
-
rescan_mixin deleted_lib if deleted_lib
|
288
|
-
end
|
289
|
-
|
290
|
-
def rescan_deleted_initializer path
|
291
|
-
tracker.initializers.delete Pathname.new(path).basename.to_s
|
292
|
-
end
|
293
|
-
|
294
|
-
#Check controllers, templates, models and libs for data from file
|
295
|
-
#and delete it.
|
296
|
-
def remove_deleted_file path
|
297
|
-
deleted = false
|
298
|
-
|
299
|
-
[:controllers, :models, :libs].each do |collection|
|
300
|
-
tracker.send(collection).delete_if do |_name, data|
|
301
|
-
if data.files.include?(path)
|
302
|
-
deleted = true
|
303
|
-
true
|
304
|
-
end
|
305
|
-
end
|
306
|
-
end
|
307
|
-
|
308
|
-
tracker.templates.delete_if do |_name, data|
|
309
|
-
if data.file == path
|
310
|
-
deleted = true
|
311
|
-
true
|
312
|
-
end
|
313
|
-
end
|
314
|
-
|
315
|
-
deleted
|
316
|
-
end
|
317
|
-
|
318
|
-
#Guess at what kind of file the path contains
|
319
|
-
def file_type path
|
320
|
-
case path
|
321
|
-
when /\/app\/controllers/
|
322
|
-
:controller
|
323
|
-
when /\/app\/views/
|
324
|
-
:template
|
325
|
-
when /\/app\/models/
|
326
|
-
:model
|
327
|
-
when /\/lib/
|
328
|
-
:lib
|
329
|
-
when /\/config\/initializers/
|
330
|
-
:initializer
|
331
|
-
when /config\/routes\.rb/
|
332
|
-
:routes
|
333
|
-
when /\/config\/.+\.(rb|yml)/
|
334
|
-
:config
|
335
|
-
when /\.ruby-version/
|
336
|
-
:config
|
337
|
-
when /Gemfile|gems\./
|
338
|
-
:gemfile
|
339
|
-
else
|
340
|
-
:unknown
|
341
|
-
end
|
69
|
+
self
|
342
70
|
end
|
343
71
|
|
344
|
-
|
345
|
-
method_names = []
|
346
|
-
|
347
|
-
lib.each_method do |name, _meth|
|
348
|
-
method_names << name
|
349
|
-
end
|
72
|
+
IGNORE_PATTERN = /\.(md|txt|js|ts|tsx|json|scss|css|xml|ru|png|jpg|pdf|gif|svg|webm|ttf|sql)$/
|
350
73
|
|
351
|
-
|
352
|
-
|
353
|
-
#Rescan controllers that mixed in library
|
354
|
-
tracker.controllers.each do |_name, controller|
|
355
|
-
if controller.includes.include? lib.name
|
356
|
-
controller.files.each do |path|
|
357
|
-
unless @paths.include? path
|
358
|
-
to_rescan << path
|
359
|
-
end
|
360
|
-
end
|
361
|
-
end
|
362
|
-
end
|
363
|
-
|
364
|
-
to_rescan.each do |controller|
|
365
|
-
tracker.reset_controller controller
|
366
|
-
rescan_file controller
|
367
|
-
end
|
368
|
-
|
369
|
-
to_rescan = []
|
370
|
-
|
371
|
-
#Check if a method from this mixin was used to render a template.
|
372
|
-
#This is not precise, because a different controller might have the
|
373
|
-
#same method...
|
374
|
-
tracker.templates.each do |name, template|
|
375
|
-
next unless template.render_path
|
376
|
-
|
377
|
-
if template.render_path.include_any_method? method_names
|
378
|
-
name.to_s.match(/^([^.]+)/)
|
379
|
-
|
380
|
-
original = tracker.templates[$1.to_sym]
|
381
|
-
|
382
|
-
if original
|
383
|
-
to_rescan << [name, original.file]
|
384
|
-
end
|
385
|
-
end
|
386
|
-
end
|
387
|
-
|
388
|
-
to_rescan.each do |template|
|
389
|
-
tracker.reset_template template[0]
|
390
|
-
rescan_file template[1]
|
391
|
-
end
|
392
|
-
end
|
393
|
-
|
394
|
-
def parse_ruby_files list
|
395
|
-
paths = list.select(&:exists?)
|
396
|
-
file_parser = Brakeman::FileParser.new(tracker.app_tree, tracker.options[:parser_timeout], tracker.options[:parallel_checks])
|
397
|
-
file_parser.parse_files paths
|
398
|
-
tracker.add_errors(file_parser.errors)
|
399
|
-
file_parser.file_list
|
74
|
+
def ignorable? path
|
75
|
+
path.relative.match? IGNORE_PATTERN
|
400
76
|
end
|
401
77
|
end
|
402
78
|
|
@@ -452,37 +128,11 @@ class Brakeman::RescanReport
|
|
452
128
|
end
|
453
129
|
|
454
130
|
#Output total, fixed, and new warnings
|
455
|
-
def to_s
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
Fixed warnings: #{fixed_warnings.length}
|
462
|
-
New warnings: #{new_warnings.length}
|
463
|
-
OUTPUT
|
464
|
-
else
|
465
|
-
#Eventually move this to different method, or make default to_s
|
466
|
-
out = ""
|
467
|
-
|
468
|
-
{:fixed => fixed_warnings, :new => new_warnings, :existing => existing_warnings}.each do |warning_type, warnings|
|
469
|
-
if warnings.length > 0
|
470
|
-
out << "#{warning_type.to_s.titleize} warnings: #{warnings.length}\n"
|
471
|
-
|
472
|
-
table = Terminal::Table.new(:headings => ["Confidence", "Class", "Method", "Warning Type", "Message"]) do |t|
|
473
|
-
warnings.sort_by { |w| w.confidence}.each do |warning|
|
474
|
-
w = warning.to_row
|
475
|
-
|
476
|
-
w["Confidence"] = Brakeman::Report::TEXT_CONFIDENCE[w["Confidence"]]
|
477
|
-
|
478
|
-
t << [w["Confidence"], w["Class"], w["Method"], w["Warning Type"], w["Message"]]
|
479
|
-
end
|
480
|
-
end
|
481
|
-
out << truncate_table(table.to_s)
|
482
|
-
end
|
483
|
-
end
|
484
|
-
|
485
|
-
out
|
486
|
-
end
|
131
|
+
def to_s
|
132
|
+
<<~OUTPUT
|
133
|
+
Total warnings: #{all_warnings.length}
|
134
|
+
Fixed warnings: #{fixed_warnings.length}
|
135
|
+
New warnings: #{new_warnings.length}
|
136
|
+
OUTPUT
|
487
137
|
end
|
488
138
|
end
|