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/scanner.rb
CHANGED
@@ -7,6 +7,7 @@ begin
|
|
7
7
|
require 'brakeman/file_parser'
|
8
8
|
require 'brakeman/parsers/template_parser'
|
9
9
|
require 'brakeman/processors/lib/file_type_detector'
|
10
|
+
require 'brakeman/tracker/file_cache'
|
10
11
|
rescue LoadError => e
|
11
12
|
$stderr.puts e.message
|
12
13
|
$stderr.puts "Please install the appropriate dependency."
|
@@ -31,6 +32,7 @@ class Brakeman::Scanner
|
|
31
32
|
|
32
33
|
@processor = processor || Brakeman::Processor.new(@app_tree, options)
|
33
34
|
@show_timing = tracker.options[:debug] || tracker.options[:show_timing]
|
35
|
+
@per_file_timing = tracker.options[:debug] && tracker.options[:show_timing]
|
34
36
|
end
|
35
37
|
|
36
38
|
#Returns the Tracker generated from the scan
|
@@ -38,6 +40,10 @@ class Brakeman::Scanner
|
|
38
40
|
@processor.tracked_events
|
39
41
|
end
|
40
42
|
|
43
|
+
def file_cache
|
44
|
+
tracker.file_cache
|
45
|
+
end
|
46
|
+
|
41
47
|
def process_step description
|
42
48
|
Brakeman.notify "#{description}...".ljust(40)
|
43
49
|
|
@@ -53,7 +59,7 @@ class Brakeman::Scanner
|
|
53
59
|
end
|
54
60
|
|
55
61
|
def process_step_file description
|
56
|
-
if @
|
62
|
+
if @per_file_timing
|
57
63
|
Brakeman.notify "Processing #{description}"
|
58
64
|
|
59
65
|
start_t = Time.now
|
@@ -67,7 +73,7 @@ class Brakeman::Scanner
|
|
67
73
|
end
|
68
74
|
|
69
75
|
#Process everything in the Rails application
|
70
|
-
def process
|
76
|
+
def process(ruby_paths: nil, template_paths: nil)
|
71
77
|
process_step 'Processing gems' do
|
72
78
|
process_gems
|
73
79
|
end
|
@@ -77,14 +83,30 @@ class Brakeman::Scanner
|
|
77
83
|
process_config
|
78
84
|
end
|
79
85
|
|
86
|
+
# -
|
87
|
+
# If ruby_paths or template_paths are set,
|
88
|
+
# only parse those files. The rest will be fetched
|
89
|
+
# from the file cache.
|
90
|
+
#
|
91
|
+
# Otherwise, parse everything normally.
|
92
|
+
#
|
93
|
+
astfiles = nil
|
94
|
+
process_step 'Finding files' do
|
95
|
+
ruby_paths ||= tracker.app_tree.ruby_file_paths
|
96
|
+
template_paths ||= tracker.app_tree.template_paths
|
97
|
+
end
|
98
|
+
|
80
99
|
process_step 'Parsing files' do
|
81
|
-
parse_files
|
100
|
+
astfiles = parse_files(ruby_paths: ruby_paths, template_paths: template_paths)
|
82
101
|
end
|
83
102
|
|
84
103
|
process_step 'Detecting file types' do
|
85
|
-
detect_file_types
|
104
|
+
detect_file_types(astfiles)
|
86
105
|
end
|
87
106
|
|
107
|
+
tracker.save_file_cache! if support_rescanning?
|
108
|
+
# -
|
109
|
+
|
88
110
|
process_step 'Processing initializers' do
|
89
111
|
process_initializers
|
90
112
|
end
|
@@ -124,44 +146,37 @@ class Brakeman::Scanner
|
|
124
146
|
tracker
|
125
147
|
end
|
126
148
|
|
127
|
-
def parse_files
|
149
|
+
def parse_files(ruby_paths:, template_paths:)
|
128
150
|
fp = Brakeman::FileParser.new(tracker.app_tree, tracker.options[:parser_timeout], tracker.options[:parallel_checks], tracker.options[:use_prism])
|
129
151
|
|
130
|
-
fp.parse_files
|
152
|
+
fp.parse_files ruby_paths
|
131
153
|
|
132
154
|
template_parser = Brakeman::TemplateParser.new(tracker, fp)
|
133
155
|
|
134
|
-
fp.read_files(
|
135
|
-
template_parser.parse_template
|
156
|
+
fp.read_files(template_paths) do |path, contents|
|
157
|
+
template_parser.parse_template(path, contents)
|
136
158
|
end
|
137
159
|
|
138
160
|
# Collect errors raised during parsing
|
139
161
|
tracker.add_errors(fp.errors)
|
140
162
|
|
141
|
-
|
163
|
+
fp.file_list
|
142
164
|
end
|
143
165
|
|
144
|
-
def detect_file_types
|
145
|
-
@file_list = {
|
146
|
-
controllers: [],
|
147
|
-
initializers: [],
|
148
|
-
libs: [],
|
149
|
-
models: [],
|
150
|
-
templates: [],
|
151
|
-
}
|
152
|
-
|
166
|
+
def detect_file_types(astfiles)
|
153
167
|
detector = Brakeman::FileTypeDetector.new
|
154
168
|
|
155
|
-
|
169
|
+
astfiles.each do |file|
|
156
170
|
if file.is_a? Brakeman::TemplateParser::TemplateFile
|
157
|
-
|
171
|
+
file_cache.add_file file, :template
|
158
172
|
else
|
159
173
|
type = detector.detect_type(file)
|
174
|
+
|
160
175
|
unless type == :skip
|
161
|
-
if
|
162
|
-
|
176
|
+
if file_cache.valid_type? type
|
177
|
+
file_cache.add_file(file, type)
|
163
178
|
else
|
164
|
-
|
179
|
+
raise "Unexpected file type: #{type.inspect}"
|
165
180
|
end
|
166
181
|
end
|
167
182
|
end
|
@@ -216,21 +231,29 @@ class Brakeman::Scanner
|
|
216
231
|
#Process Gemfile
|
217
232
|
def process_gems
|
218
233
|
gem_files = {}
|
234
|
+
gem_file_names = ['Gemfile', 'gems.rb']
|
235
|
+
lock_file_names = ['Gemfile.lock', 'gems.locked']
|
219
236
|
|
220
|
-
if
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
file = @app_tree.file_path("gems.rb")
|
225
|
-
gem_files[:gemfile] = { :src => parse_ruby_file(file), :file => file }
|
237
|
+
if tracker.options[:gemfile]
|
238
|
+
name = tracker.options[:gemfile]
|
239
|
+
gem_file_names.unshift name
|
240
|
+
lock_file_names.unshift "#{name}.lock"
|
226
241
|
end
|
227
242
|
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
243
|
+
gem_file_names.each do |name|
|
244
|
+
if @app_tree.exists? name
|
245
|
+
file = @app_tree.file_path(name)
|
246
|
+
gem_files[:gemfile] = { :src => parse_ruby_file(file), :file => file }
|
247
|
+
break
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
lock_file_names.each do |name|
|
252
|
+
if @app_tree.exists? name
|
253
|
+
file = @app_tree.file_path(name)
|
254
|
+
gem_files[:gemlock] = { :src => file.read, :file => file }
|
255
|
+
break
|
256
|
+
end
|
234
257
|
end
|
235
258
|
|
236
259
|
if @app_tree.gemspec
|
@@ -268,8 +291,8 @@ class Brakeman::Scanner
|
|
268
291
|
#
|
269
292
|
#Adds parsed information to tracker.initializers
|
270
293
|
def process_initializers
|
271
|
-
track_progress
|
272
|
-
process_step_file
|
294
|
+
track_progress file_cache.initializers do |path, init|
|
295
|
+
process_step_file path do
|
273
296
|
process_initializer init
|
274
297
|
end
|
275
298
|
end
|
@@ -289,8 +312,10 @@ class Brakeman::Scanner
|
|
289
312
|
return
|
290
313
|
end
|
291
314
|
|
292
|
-
|
293
|
-
|
315
|
+
libs = file_cache.libs.sort_by { |path, _| path }
|
316
|
+
|
317
|
+
track_progress libs do |path, lib|
|
318
|
+
process_step_file path do
|
294
319
|
process_lib lib
|
295
320
|
end
|
296
321
|
end
|
@@ -322,15 +347,17 @@ class Brakeman::Scanner
|
|
322
347
|
#
|
323
348
|
#Adds processed controllers to tracker.controllers
|
324
349
|
def process_controllers
|
325
|
-
|
326
|
-
|
350
|
+
controllers = file_cache.controllers.sort_by { |path, _| path }
|
351
|
+
|
352
|
+
track_progress controllers do |path, controller|
|
353
|
+
process_step_file path do
|
327
354
|
process_controller controller
|
328
355
|
end
|
329
356
|
end
|
330
357
|
end
|
331
358
|
|
332
359
|
def process_controller_data_flows
|
333
|
-
controllers = tracker.controllers.sort_by { |name, _| name
|
360
|
+
controllers = tracker.controllers.sort_by { |name, _| name }
|
334
361
|
|
335
362
|
track_progress controllers, "controllers" do |name, controller|
|
336
363
|
process_step_file name do
|
@@ -356,10 +383,10 @@ class Brakeman::Scanner
|
|
356
383
|
#
|
357
384
|
#Adds processed views to tracker.views
|
358
385
|
def process_templates
|
359
|
-
templates =
|
386
|
+
templates = file_cache.templates.sort_by { |path, _| path }
|
360
387
|
|
361
|
-
track_progress templates, "templates" do |template|
|
362
|
-
process_step_file
|
388
|
+
track_progress templates, "templates" do |path, template|
|
389
|
+
process_step_file path do
|
363
390
|
process_template template
|
364
391
|
end
|
365
392
|
end
|
@@ -370,7 +397,7 @@ class Brakeman::Scanner
|
|
370
397
|
end
|
371
398
|
|
372
399
|
def process_template_data_flows
|
373
|
-
templates = tracker.templates.sort_by { |name, _| name
|
400
|
+
templates = tracker.templates.sort_by { |name, _| name }
|
374
401
|
|
375
402
|
track_progress templates, "templates" do |name, template|
|
376
403
|
process_step_file name do
|
@@ -383,15 +410,17 @@ class Brakeman::Scanner
|
|
383
410
|
#
|
384
411
|
#Adds the processed models to tracker.models
|
385
412
|
def process_models
|
386
|
-
|
387
|
-
|
388
|
-
|
413
|
+
models = file_cache.models.sort_by { |path, _| path }
|
414
|
+
|
415
|
+
track_progress models do |path, model|
|
416
|
+
process_step_file path do
|
417
|
+
process_model model
|
389
418
|
end
|
390
419
|
end
|
391
420
|
end
|
392
421
|
|
393
|
-
def process_model
|
394
|
-
@processor.process_model(ast, path)
|
422
|
+
def process_model astfile
|
423
|
+
@processor.process_model(astfile.ast, astfile.path)
|
395
424
|
end
|
396
425
|
|
397
426
|
def track_progress list, type = "files"
|
@@ -420,6 +449,10 @@ class Brakeman::Scanner
|
|
420
449
|
tracker.error(e)
|
421
450
|
nil
|
422
451
|
end
|
452
|
+
|
453
|
+
def support_rescanning?
|
454
|
+
tracker.options[:support_rescanning]
|
455
|
+
end
|
423
456
|
end
|
424
457
|
|
425
458
|
# This is to allow operation without loading the Haml library
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module Brakeman
|
2
|
+
class FileCache
|
3
|
+
def initialize(file_list = nil)
|
4
|
+
@file_list = file_list || {
|
5
|
+
controller: {},
|
6
|
+
initializer: {},
|
7
|
+
lib: {},
|
8
|
+
model: {},
|
9
|
+
template: {},
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
def controllers
|
14
|
+
@file_list[:controller]
|
15
|
+
end
|
16
|
+
|
17
|
+
def initializers
|
18
|
+
@file_list[:initializer]
|
19
|
+
end
|
20
|
+
|
21
|
+
def libs
|
22
|
+
@file_list[:lib]
|
23
|
+
end
|
24
|
+
|
25
|
+
def models
|
26
|
+
@file_list[:model]
|
27
|
+
end
|
28
|
+
|
29
|
+
def templates
|
30
|
+
@file_list[:template]
|
31
|
+
end
|
32
|
+
|
33
|
+
def add_file(astfile, type)
|
34
|
+
raise "Unknown type: #{type}" unless valid_type? type
|
35
|
+
@file_list[type][astfile.path] = astfile
|
36
|
+
end
|
37
|
+
|
38
|
+
def valid_type?(type)
|
39
|
+
@file_list.key? type
|
40
|
+
end
|
41
|
+
|
42
|
+
def cached? path
|
43
|
+
@file_list.any? do |name, list|
|
44
|
+
list[path]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def delete path
|
49
|
+
@file_list.each do |name, list|
|
50
|
+
list.delete path
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def diff other
|
55
|
+
@file_list.each do |name, list|
|
56
|
+
other_list = other.send(:"#{name}s")
|
57
|
+
|
58
|
+
if list == other_list
|
59
|
+
next
|
60
|
+
else
|
61
|
+
puts "-- #{name} --"
|
62
|
+
puts "Old: #{other_list.keys - list.keys}"
|
63
|
+
puts "New: #{list.keys - other_list.keys}"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def dup
|
69
|
+
copy_file_list = @file_list.map do |name, list|
|
70
|
+
copy_list = list.map do |path, astfile|
|
71
|
+
copy_astfile = astfile.dup
|
72
|
+
copy_astfile.ast = copy_astfile.ast.deep_clone
|
73
|
+
|
74
|
+
[path, copy_astfile]
|
75
|
+
end.to_h
|
76
|
+
|
77
|
+
[name, copy_list]
|
78
|
+
end.to_h
|
79
|
+
|
80
|
+
FileCache.new(copy_file_list)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
data/lib/brakeman/tracker.rb
CHANGED
@@ -12,7 +12,7 @@ class Brakeman::Tracker
|
|
12
12
|
attr_accessor :controllers, :constants, :templates, :models, :errors,
|
13
13
|
:checks, :initializers, :config, :routes, :processor, :libs,
|
14
14
|
:template_cache, :options, :filter_cache, :start_time, :end_time,
|
15
|
-
:duration, :ignored_filter, :app_tree
|
15
|
+
:duration, :ignored_filter, :app_tree, :file_cache, :pristine_file_cache
|
16
16
|
|
17
17
|
#Place holder when there should be a model, but it is not
|
18
18
|
#clear what model it will be.
|
@@ -26,15 +26,22 @@ class Brakeman::Tracker
|
|
26
26
|
@app_tree = app_tree
|
27
27
|
@processor = processor
|
28
28
|
@options = options
|
29
|
+
@file_cache = Brakeman::FileCache.new
|
30
|
+
@pristine_file_cache = nil
|
29
31
|
|
30
|
-
|
32
|
+
reset_all
|
33
|
+
end
|
34
|
+
|
35
|
+
def reset_all
|
31
36
|
@templates = {}
|
32
37
|
@controllers = {}
|
38
|
+
|
33
39
|
#Initialize models with the unknown model so
|
34
40
|
#we can match models later without knowing precisely what
|
35
41
|
#class they are.
|
36
42
|
@models = {}
|
37
43
|
@models[UNKNOWN_MODEL] = Brakeman::Model.new(UNKNOWN_MODEL, nil, @app_tree.file_path("NOT_REAL.rb"), nil, self)
|
44
|
+
|
38
45
|
@method_cache = {}
|
39
46
|
@routes = {}
|
40
47
|
@initializers = {}
|
@@ -46,11 +53,16 @@ class Brakeman::Tracker
|
|
46
53
|
@template_cache = Set.new
|
47
54
|
@filter_cache = {}
|
48
55
|
@call_index = nil
|
56
|
+
@config = Brakeman::Config.new(self)
|
49
57
|
@start_time = Time.now
|
50
58
|
@end_time = nil
|
51
59
|
@duration = nil
|
52
60
|
end
|
53
61
|
|
62
|
+
def save_file_cache!
|
63
|
+
@pristine_file_cache = @file_cache.dup
|
64
|
+
end
|
65
|
+
|
54
66
|
#Add an error to the list. If no backtrace is given,
|
55
67
|
#the one from the exception will be used.
|
56
68
|
def error exception, backtrace = nil
|
@@ -301,6 +313,11 @@ class Brakeman::Tracker
|
|
301
313
|
method_sets << self.controllers
|
302
314
|
end
|
303
315
|
|
316
|
+
if locations.include? :libs
|
317
|
+
classes_to_reindex.merge self.libs.keys
|
318
|
+
method_sets << self.libs
|
319
|
+
end
|
320
|
+
|
304
321
|
if locations.include? :initializers
|
305
322
|
self.initializers.each do |file_name, src|
|
306
323
|
@call_index.remove_indexes_by_file file_name
|
data/lib/brakeman/version.rb
CHANGED
data/lib/brakeman.rb
CHANGED
@@ -84,6 +84,15 @@ module Brakeman
|
|
84
84
|
options[:report_progress] = false
|
85
85
|
end
|
86
86
|
|
87
|
+
if options[:use_prism]
|
88
|
+
begin
|
89
|
+
require 'prism'
|
90
|
+
notify '[Notice] Using Prism parser'
|
91
|
+
rescue LoadError => e
|
92
|
+
Brakeman.debug "[Notice] Asked to use Prism, but failed to load: #{e}"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
87
96
|
scan options
|
88
97
|
end
|
89
98
|
|
@@ -118,6 +127,13 @@ module Brakeman
|
|
118
127
|
options[:output_formats] = get_output_formats options
|
119
128
|
options[:github_url] = get_github_url options
|
120
129
|
|
130
|
+
|
131
|
+
# Use ENV value only if option was not already explicitly set
|
132
|
+
# (i.e. prefer commandline option over environment variable).
|
133
|
+
if options[:gemfile].nil? and ENV['BUNDLE_GEMFILE']
|
134
|
+
options[:gemfile] = ENV['BUNDLE_GEMFILE']
|
135
|
+
end
|
136
|
+
|
121
137
|
options
|
122
138
|
end
|
123
139
|
|
@@ -196,6 +212,7 @@ module Brakeman
|
|
196
212
|
:pager => true,
|
197
213
|
:parallel_checks => true,
|
198
214
|
:parser_timeout => 10,
|
215
|
+
:use_prism => true,
|
199
216
|
:relative_path => false,
|
200
217
|
:report_progress => true,
|
201
218
|
:safe_methods => Set.new,
|
@@ -464,12 +481,12 @@ module Brakeman
|
|
464
481
|
def self.rescan tracker, files, options = {}
|
465
482
|
require 'brakeman/rescanner'
|
466
483
|
|
467
|
-
tracker.options.merge
|
484
|
+
options = tracker.options.merge options
|
468
485
|
|
469
486
|
@quiet = !!tracker.options[:quiet]
|
470
487
|
@debug = !!tracker.options[:debug]
|
471
488
|
|
472
|
-
Rescanner.new(
|
489
|
+
Rescanner.new(options, tracker.processor, files).recheck
|
473
490
|
end
|
474
491
|
|
475
492
|
def self.notify message
|
metadata
CHANGED
@@ -1,29 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: brakeman-min
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 7.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Collins
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-04-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: csv
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - ">="
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '0'
|
20
|
-
type: :development
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - ">="
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '0'
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: minitest
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,20 +52,6 @@ dependencies:
|
|
66
52
|
- - ">="
|
67
53
|
- !ruby/object:Gem::Version
|
68
54
|
version: '0'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: simplecov-html
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - '='
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: 0.10.2
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - '='
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: 0.10.2
|
83
55
|
- !ruby/object:Gem::Dependency
|
84
56
|
name: parallel
|
85
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -341,6 +313,7 @@ files:
|
|
341
313
|
- lib/brakeman/tracker/config.rb
|
342
314
|
- lib/brakeman/tracker/constants.rb
|
343
315
|
- lib/brakeman/tracker/controller.rb
|
316
|
+
- lib/brakeman/tracker/file_cache.rb
|
344
317
|
- lib/brakeman/tracker/library.rb
|
345
318
|
- lib/brakeman/tracker/method_info.rb
|
346
319
|
- lib/brakeman/tracker/model.rb
|