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.
- checksums.yaml +4 -4
- data/CHANGES.md +49 -1
- data/README.md +1 -1
- data/lib/brakeman/app_tree.rb +1 -1
- data/lib/brakeman/checks/base_check.rb +10 -0
- data/lib/brakeman/checks/check_detailed_exceptions.rb +1 -1
- data/lib/brakeman/checks/check_eol_rails.rb +23 -0
- data/lib/brakeman/checks/check_eol_ruby.rb +26 -0
- data/lib/brakeman/checks/check_evaluation.rb +1 -1
- data/lib/brakeman/checks/check_execute.rb +10 -0
- data/lib/brakeman/checks/check_json_parsing.rb +1 -1
- data/lib/brakeman/checks/check_render.rb +15 -1
- data/lib/brakeman/checks/check_sql.rb +58 -7
- data/lib/brakeman/checks/check_symbol_dos.rb +1 -1
- data/lib/brakeman/checks/check_verb_confusion.rb +1 -1
- data/lib/brakeman/checks/eol_check.rb +47 -0
- data/lib/brakeman/file_parser.rb +45 -15
- data/lib/brakeman/options.rb +15 -2
- data/lib/brakeman/processors/alias_processor.rb +91 -9
- data/lib/brakeman/processors/controller_alias_processor.rb +6 -43
- data/lib/brakeman/processors/gem_processor.rb +3 -0
- data/lib/brakeman/processors/haml_template_processor.rb +9 -0
- data/lib/brakeman/processors/lib/call_conversion_helper.rb +12 -6
- data/lib/brakeman/processors/lib/rails3_route_processor.rb +2 -0
- data/lib/brakeman/processors/library_processor.rb +9 -0
- data/lib/brakeman/processors/model_processor.rb +32 -0
- data/lib/brakeman/report/ignore/config.rb +1 -1
- data/lib/brakeman/report/ignore/interactive.rb +1 -1
- data/lib/brakeman/report/report_csv.rb +1 -1
- data/lib/brakeman/report/report_github.rb +31 -0
- data/lib/brakeman/report/report_sarif.rb +22 -3
- data/lib/brakeman/report/report_text.rb +1 -1
- data/lib/brakeman/report.rb +4 -1
- data/lib/brakeman/rescanner.rb +1 -1
- data/lib/brakeman/scanner.rb +19 -14
- data/lib/brakeman/tracker/collection.rb +57 -7
- data/lib/brakeman/tracker/config.rb +8 -1
- data/lib/brakeman/tracker/method_info.rb +70 -0
- data/lib/brakeman/tracker.rb +33 -4
- data/lib/brakeman/util.rb +34 -18
- data/lib/brakeman/version.rb +1 -1
- data/lib/brakeman/warning_codes.rb +2 -0
- data/lib/brakeman.rb +8 -2
- data/lib/ruby_parser/bm_sexp.rb +24 -0
- metadata +24 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cf18736db7a992849a30cccc66f741442e05b695f9ad1bd27fe06f6bc25db849
|
4
|
+
data.tar.gz: a884f20cc12305c0856bcc296ddae3aea746b024c232bf11d7988d4e37bd96a3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: be93f9f9ba9d808989cbed1bfd450b29c272e806d84707ab0420a8a86b8c797f88d4338f22a1124a462be43ac7bfea605566779321a72d772e9c39d216ded8c3
|
7
|
+
data.tar.gz: cf9aa7f34fa5cc737b2e7bbecf625025b50f72f1b3c43ab8f346832145e3cab2245d2af79fd021584ab0aa5fdbb2f854dd41fee8f5a5c891197f1f1e8a570a65
|
data/CHANGES.md
CHANGED
@@ -1,3 +1,51 @@
|
|
1
|
+
# 5.2.0 - 2021-12-15
|
2
|
+
|
3
|
+
* Initial Rails 7 support
|
4
|
+
* Require Ruby 2.5.0+
|
5
|
+
* Fix issue with calls to `foo.root` in routes
|
6
|
+
* Ignore `I18n.locale` in SQL queries
|
7
|
+
* Do not treat `sanitize_sql_like` as safe
|
8
|
+
* Add new checks for unsupported Ruby and Rails versions
|
9
|
+
|
10
|
+
# 5.1.2 - 2021-10-28
|
11
|
+
|
12
|
+
* Handle cases where enums are not symbols
|
13
|
+
* Support newer Haml with ::Haml::AttributeBuilder.build
|
14
|
+
* Fix issue where the previous output is still visible (Jason Frey)
|
15
|
+
* Fix warning sorting with nil line numbers
|
16
|
+
* Update for latest RubyParser (Ryan Davis)
|
17
|
+
|
18
|
+
# 5.1.1 - 2021-07-19
|
19
|
+
|
20
|
+
* Unrefactor IgnoreConfig's use of `Brakeman::FilePath`
|
21
|
+
|
22
|
+
# 5.1.0 - 2021-07-19
|
23
|
+
|
24
|
+
* Initial support for ActiveRecord enums
|
25
|
+
* Support `Hash#include?`
|
26
|
+
* Interprocedural dataflow from very simple class methods
|
27
|
+
* Fix SARIF report when checks have no description (Eli Block)
|
28
|
+
* Add ignored warnings to SARIF report (Eli Block)
|
29
|
+
* Add `--sql-safe-methods` option (Esty Scheiner)
|
30
|
+
* Update SQL injection check for Rails 6.0/6.1
|
31
|
+
* Fix false positive in command injection with `Open3.capture` (Richard Fitzgerald)
|
32
|
+
* Fix infinite loop on mixin self-includes (Andrew Szczepanski)
|
33
|
+
* Ignore dates in SQL
|
34
|
+
* Refactor `cookie?`/`param?` methods (Keenan Brock)
|
35
|
+
* Ignore renderables in dynamic render path check (Brad Parker)
|
36
|
+
* Support `Array#push`
|
37
|
+
* Better `Array#join` support
|
38
|
+
* Adjust copy of `--interactive` menu (Elia Schito)
|
39
|
+
* Support `Array#*`
|
40
|
+
* Better method definition tracking and lookup
|
41
|
+
* Support `Hash#values` and `Hash#values_at`
|
42
|
+
* Check for user-controlled evaluation even if it's a call target
|
43
|
+
* Support `Array#fetch` and `Hash#fetch`
|
44
|
+
* Ignore `sanitize_sql_like` in SQL
|
45
|
+
* Ignore method calls on numbers in SQL
|
46
|
+
* Add GitHub Actions format (Klaus Badelt)
|
47
|
+
* Read and parse files in parallel
|
48
|
+
|
1
49
|
# 5.0.4 - 2021-06-08
|
2
50
|
|
3
51
|
(brakeman gem release only)
|
@@ -418,7 +466,7 @@
|
|
418
466
|
* Delay loading vendored gems and modifying load path
|
419
467
|
* Avoid warning about SQL injection with `quoted_primary_key`
|
420
468
|
* Support more safe `&.` operations
|
421
|
-
* Allow
|
469
|
+
* Allow multiple line regex in `validates_format_of` (Dmitrij Fedorenko)
|
422
470
|
* Only consider `if` branches in templates
|
423
471
|
* Avoid overwriting instance/class methods with same name (Tim Wade)
|
424
472
|
* Add `--force-scan` option (Neil Matatall)
|
data/README.md
CHANGED
@@ -66,7 +66,7 @@ Outside of Rails root (note that the output file is relative to path/to/rails/ap
|
|
66
66
|
|
67
67
|
Brakeman should work with any version of Rails from 2.3.x to 6.x.
|
68
68
|
|
69
|
-
Brakeman can analyze code written with Ruby 1.8 syntax and newer, but requires at least Ruby 2.
|
69
|
+
Brakeman can analyze code written with Ruby 1.8 syntax and newer, but requires at least Ruby 2.4.0 to run.
|
70
70
|
|
71
71
|
# Basic Options
|
72
72
|
|
data/lib/brakeman/app_tree.rb
CHANGED
@@ -28,7 +28,7 @@ module Brakeman
|
|
28
28
|
# Accepts an array of filenames and paths with the following format and
|
29
29
|
# returns a Regexp to match them:
|
30
30
|
# * "path1/file1.rb" - Matches a specific filename in the project directory.
|
31
|
-
# * "path1/" - Matches any path that
|
31
|
+
# * "path1/" - Matches any path that contains "path1" in the project directory.
|
32
32
|
# * "/path1/ - Matches any path that is rooted at "path1" in the project directory.
|
33
33
|
#
|
34
34
|
def self.regex_for_paths(paths)
|
@@ -513,4 +513,14 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
|
|
513
513
|
string_building? exp.target or
|
514
514
|
string_building? exp.first_arg
|
515
515
|
end
|
516
|
+
|
517
|
+
I18N_CLASS = s(:const, :I18n)
|
518
|
+
|
519
|
+
def locale_call? exp
|
520
|
+
return unless call? exp
|
521
|
+
|
522
|
+
(exp.target == I18N_CLASS and
|
523
|
+
exp.method == :locale) or
|
524
|
+
locale_call? exp.target
|
525
|
+
end
|
516
526
|
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
|
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative 'eol_check'
|
2
|
+
|
3
|
+
class Brakeman::CheckEOLRails < Brakeman::EOLCheck
|
4
|
+
Brakeman::Checks.add self
|
5
|
+
|
6
|
+
@description = "Checks for unsupported versions of Rails"
|
7
|
+
|
8
|
+
def run_check
|
9
|
+
return unless tracker.config.rails_version
|
10
|
+
|
11
|
+
check_eol_version :rails, RAILS_EOL_DATES
|
12
|
+
end
|
13
|
+
|
14
|
+
RAILS_EOL_DATES = {
|
15
|
+
['2.0.0', '2.3.99'] => Date.new(2013, 6, 25),
|
16
|
+
['3.0.0', '3.2.99'] => Date.new(2016, 6, 30),
|
17
|
+
['4.0.0', '4.2.99'] => Date.new(2017, 4, 27),
|
18
|
+
['5.0.0', '5.0.99'] => Date.new(2018, 5, 9),
|
19
|
+
['5.1.0', '5.1.99'] => Date.new(2019, 8, 25),
|
20
|
+
['5.2.0', '5.2.99'] => Date.new(2022, 6, 1),
|
21
|
+
['6.0.0', '6.0.99'] => Date.new(2023, 6, 1),
|
22
|
+
}
|
23
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require_relative 'eol_check'
|
2
|
+
|
3
|
+
class Brakeman::CheckEOLRuby < Brakeman::EOLCheck
|
4
|
+
Brakeman::Checks.add self
|
5
|
+
|
6
|
+
@description = "Checks for unsupported versions of Ruby"
|
7
|
+
|
8
|
+
def run_check
|
9
|
+
return unless tracker.config.ruby_version
|
10
|
+
|
11
|
+
check_eol_version :ruby, RUBY_EOL_DATES
|
12
|
+
end
|
13
|
+
|
14
|
+
RUBY_EOL_DATES = {
|
15
|
+
['0.0.0', '1.9.3'] => Date.new(2015, 2, 23),
|
16
|
+
['2.0.0', '2.0.99'] => Date.new(2016, 2, 24),
|
17
|
+
['2.1.0', '2.1.99'] => Date.new(2017, 3, 31),
|
18
|
+
['2.2.0', '2.2.99'] => Date.new(2018, 3, 31),
|
19
|
+
['2.3.0', '2.3.99'] => Date.new(2019, 3, 31),
|
20
|
+
['2.4.0', '2.4.99'] => Date.new(2020, 3, 31),
|
21
|
+
['2.5.0', '2.5.99'] => Date.new(2021, 3, 31),
|
22
|
+
['2.6.0', '2.6.99'] => Date.new(2022, 3, 31),
|
23
|
+
['2.7.0', '2.7.99'] => Date.new(2023, 3, 31),
|
24
|
+
['3.0.0', '2.8.99'] => Date.new(2024, 3, 31),
|
25
|
+
}
|
26
|
+
end
|
@@ -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|
|
@@ -87,6 +87,16 @@ class Brakeman::CheckExecute < Brakeman::BaseCheck
|
|
87
87
|
dangerous_interp?(first_arg) ||
|
88
88
|
dangerous_string_building?(first_arg)
|
89
89
|
end
|
90
|
+
when :capture2, :capture2e, :capture3
|
91
|
+
# Open3 capture methods can take a :stdin_data argument which is used as the
|
92
|
+
# the input to the called command so it is not succeptable to command injection.
|
93
|
+
# As such if the last argument is a hash (and therefore execution options) it
|
94
|
+
# should be ignored
|
95
|
+
|
96
|
+
args.pop if hash?(args.last) && args.length > 2
|
97
|
+
failure = include_user_input?(args) ||
|
98
|
+
dangerous_interp?(args) ||
|
99
|
+
dangerous_string_building?(args)
|
90
100
|
else
|
91
101
|
failure = include_user_input?(args) ||
|
92
102
|
dangerous_interp?(args) ||
|
@@ -74,7 +74,7 @@ class Brakeman::CheckJSONParsing < Brakeman::BaseCheck
|
|
74
74
|
warning_type = "Denial of Service"
|
75
75
|
confidence = :medium
|
76
76
|
gem_name = "#{name} gem"
|
77
|
-
message = msg(msg_version(version, gem_name), " has a symbol creation
|
77
|
+
message = msg(msg_version(version, gem_name), " has a symbol creation vulnerability. Upgrade to ")
|
78
78
|
|
79
79
|
if version >= "1.7.0"
|
80
80
|
confidence = :high
|
@@ -33,6 +33,7 @@ class Brakeman::CheckRender < Brakeman::BaseCheck
|
|
33
33
|
view = result[:call][2]
|
34
34
|
|
35
35
|
if sexp? view and original? result
|
36
|
+
return if renderable?(view)
|
36
37
|
|
37
38
|
if input = has_immediate_user_input?(view)
|
38
39
|
if string_interp? view
|
@@ -94,4 +95,17 @@ class Brakeman::CheckRender < Brakeman::BaseCheck
|
|
94
95
|
end
|
95
96
|
end
|
96
97
|
end
|
97
|
-
|
98
|
+
|
99
|
+
def renderable? exp
|
100
|
+
return false unless call?(exp) and constant?(exp.target)
|
101
|
+
|
102
|
+
target_class_name = class_name(exp.target)
|
103
|
+
known_renderable_class?(target_class_name) or tracker.find_method(:render_in, target_class_name)
|
104
|
+
end
|
105
|
+
|
106
|
+
def known_renderable_class? class_name
|
107
|
+
klass = tracker.find_class(class_name)
|
108
|
+
return false if klass.nil?
|
109
|
+
klass.ancestor? :"ViewComponent::Base"
|
110
|
+
end
|
111
|
+
end
|
@@ -22,7 +22,19 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
|
|
22
22
|
:find_by_sql, :maximum, :minimum, :pluck, :sum, :update_all]
|
23
23
|
@sql_targets.concat [:from, :group, :having, :joins, :lock, :order, :reorder, :where] if tracker.options[:rails3]
|
24
24
|
@sql_targets.concat [:find_by, :find_by!, :find_or_create_by, :find_or_create_by!, :find_or_initialize_by, :not] if tracker.options[:rails4]
|
25
|
-
|
25
|
+
|
26
|
+
if tracker.options[:rails6]
|
27
|
+
@sql_targets.concat [:delete_by, :destroy_by, :rewhere, :reselect]
|
28
|
+
|
29
|
+
@sql_targets.delete :delete_all
|
30
|
+
@sql_targets.delete :destroy_all
|
31
|
+
end
|
32
|
+
|
33
|
+
if version_between?("6.1.0", "9.9.9")
|
34
|
+
@sql_targets.delete :order
|
35
|
+
@sql_targets.delete :reorder
|
36
|
+
@sql_targets.delete :pluck
|
37
|
+
end
|
26
38
|
|
27
39
|
if version_between?("2.0.0", "3.9.9") or tracker.config.rails_version.nil?
|
28
40
|
@sql_targets << :first << :last << :all
|
@@ -185,7 +197,7 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
|
|
185
197
|
else
|
186
198
|
check_find_arguments call.last_arg
|
187
199
|
end
|
188
|
-
when :where, :having, :find_by, :find_by!, :find_or_create_by, :find_or_create_by!, :find_or_initialize_by,:not, :delete_by, :destroy_by
|
200
|
+
when :where, :rewhere, :having, :find_by, :find_by!, :find_or_create_by, :find_or_create_by!, :find_or_initialize_by,:not, :delete_by, :destroy_by
|
189
201
|
check_query_arguments call.arglist
|
190
202
|
when :order, :group, :reorder
|
191
203
|
check_order_arguments call.arglist
|
@@ -199,7 +211,7 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
|
|
199
211
|
unsafe_sql? call.first_arg
|
200
212
|
when :sql
|
201
213
|
unsafe_sql? call.first_arg
|
202
|
-
when :update_all, :select
|
214
|
+
when :update_all, :select, :reselect
|
203
215
|
check_update_all_arguments call.args
|
204
216
|
when *@connection_calls
|
205
217
|
check_by_sql_arguments call.first_arg
|
@@ -579,6 +591,10 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
|
|
579
591
|
:where_values_hash, :foreign_key, :uuid
|
580
592
|
]
|
581
593
|
|
594
|
+
def ignore_methods_in_sql
|
595
|
+
@ignore_methods_in_sql ||= IGNORE_METHODS_IN_SQL + (tracker.options[:sql_safe_methods] || [])
|
596
|
+
end
|
597
|
+
|
582
598
|
def safe_value? exp
|
583
599
|
return true unless sexp? exp
|
584
600
|
|
@@ -589,10 +605,7 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
|
|
589
605
|
if exp.method == :to_s or exp.method == :to_sym
|
590
606
|
safe_value? exp.target
|
591
607
|
else
|
592
|
-
|
593
|
-
quote_call? exp or
|
594
|
-
arel? exp or
|
595
|
-
exp.method.to_s.end_with? "_id"
|
608
|
+
ignore_call? exp
|
596
609
|
end
|
597
610
|
when :if
|
598
611
|
safe_value? exp.then_clause and safe_value? exp.else_clause
|
@@ -607,6 +620,18 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
|
|
607
620
|
end
|
608
621
|
end
|
609
622
|
|
623
|
+
def ignore_call? exp
|
624
|
+
return unless call? exp
|
625
|
+
|
626
|
+
ignore_methods_in_sql.include? exp.method or
|
627
|
+
quote_call? exp or
|
628
|
+
arel? exp or
|
629
|
+
exp.method.to_s.end_with? "_id" or
|
630
|
+
number_target? exp or
|
631
|
+
date_target? exp or
|
632
|
+
locale_call? exp
|
633
|
+
end
|
634
|
+
|
610
635
|
QUOTE_METHODS = [:quote, :quote_column_name, :quoted_date, :quote_string, :quote_table_name]
|
611
636
|
|
612
637
|
def quote_call? exp
|
@@ -695,4 +720,30 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
|
|
695
720
|
active_record_models.include? klass
|
696
721
|
end
|
697
722
|
end
|
723
|
+
|
724
|
+
def number_target? exp
|
725
|
+
return unless call? exp
|
726
|
+
|
727
|
+
if number? exp.target
|
728
|
+
true
|
729
|
+
elsif call? exp.target
|
730
|
+
number_target? exp.target
|
731
|
+
else
|
732
|
+
false
|
733
|
+
end
|
734
|
+
end
|
735
|
+
|
736
|
+
DATE_CLASS = s(:const, :Date)
|
737
|
+
|
738
|
+
def date_target? exp
|
739
|
+
return unless call? exp
|
740
|
+
|
741
|
+
if exp.target == DATE_CLASS
|
742
|
+
true
|
743
|
+
elsif call? exp.target
|
744
|
+
date_target? exp.target
|
745
|
+
else
|
746
|
+
false
|
747
|
+
end
|
748
|
+
end
|
698
749
|
end
|
@@ -9,7 +9,7 @@ class Brakeman::CheckSymbolDoS < Brakeman::BaseCheck
|
|
9
9
|
|
10
10
|
def run_check
|
11
11
|
return if rails_version and rails_version >= "5.0.0"
|
12
|
-
return if tracker.config.ruby_version >= "2.2"
|
12
|
+
return if tracker.config.ruby_version and tracker.config.ruby_version >= "2.2"
|
13
13
|
|
14
14
|
tracker.find_call(:methods => UNSAFE_METHODS, :nested => true).each do |result|
|
15
15
|
check_unsafe_symbol_creation(result)
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'brakeman/checks/base_check'
|
3
|
+
|
4
|
+
# Not used directly - base check for EOLRails and EOLRuby
|
5
|
+
class Brakeman::EOLCheck < Brakeman::BaseCheck
|
6
|
+
def check_eol_version library, eol_dates
|
7
|
+
version = case library
|
8
|
+
when :rails
|
9
|
+
tracker.config.rails_version
|
10
|
+
when :ruby
|
11
|
+
tracker.config.ruby_version
|
12
|
+
else
|
13
|
+
raise 'Implement using tracker.config.gem_version'
|
14
|
+
end
|
15
|
+
|
16
|
+
eol_dates.each do |(start_version, end_version), eol_date|
|
17
|
+
if version_between? start_version, end_version, version
|
18
|
+
case
|
19
|
+
when Date.today >= eol_date
|
20
|
+
warn_about_unsupported_version library, eol_date, version
|
21
|
+
when (Date.today + 30) >= eol_date
|
22
|
+
warn_about_soon_unsupported_version library, eol_date, version, :medium
|
23
|
+
when (Date.today + 60) >= eol_date
|
24
|
+
warn_about_soon_unsupported_version library, eol_date, version, :low
|
25
|
+
end
|
26
|
+
|
27
|
+
break
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def warn_about_soon_unsupported_version library, eol_date, version, confidence
|
33
|
+
warn warning_type: 'Unmaintained Dependency',
|
34
|
+
warning_code: :"pending_eol_#{library}",
|
35
|
+
message: msg("Support for ", msg_version(version, library.capitalize), " ends on #{eol_date}"),
|
36
|
+
confidence: confidence,
|
37
|
+
gem_info: gemfile_or_environment
|
38
|
+
end
|
39
|
+
|
40
|
+
def warn_about_unsupported_version library, eol_date, version
|
41
|
+
warn warning_type: 'Unmaintained Dependency',
|
42
|
+
warning_code: :"eol_#{library}",
|
43
|
+
message: msg("Support for ", msg_version(version, library.capitalize), " ended on #{eol_date}"),
|
44
|
+
confidence: :high,
|
45
|
+
gem_info: gemfile_or_environment
|
46
|
+
end
|
47
|
+
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
|
|
@@ -5,29 +7,62 @@ module Brakeman
|
|
5
7
|
class FileParser
|
6
8
|
attr_reader :file_list, :errors
|
7
9
|
|
8
|
-
def initialize app_tree, timeout
|
10
|
+
def initialize app_tree, timeout, parallel = true
|
9
11
|
@app_tree = app_tree
|
10
12
|
@timeout = timeout
|
11
13
|
@file_list = []
|
12
14
|
@errors = []
|
15
|
+
@parallel = parallel
|
13
16
|
end
|
14
17
|
|
15
18
|
def parse_files list
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
+
if @parallel
|
20
|
+
parallel_options = {}
|
21
|
+
else
|
22
|
+
# Disable parallelism
|
23
|
+
parallel_options = { in_threads: 0 }
|
24
|
+
end
|
25
|
+
|
26
|
+
# Parse the files in parallel.
|
27
|
+
# By default, the parsing will be in separate processes.
|
28
|
+
# So we map the result to ASTFiles and/or Exceptions
|
29
|
+
# then partition them into ASTFiles and Exceptions
|
30
|
+
# and add the Exceptions to @errors
|
31
|
+
#
|
32
|
+
# Basically just a funky way to deal with two possible
|
33
|
+
# return types that are returned from isolated processes.
|
34
|
+
#
|
35
|
+
# Note this method no longer uses read_files
|
36
|
+
@file_list, new_errors = Parallel.map(list, parallel_options) do |file_name|
|
37
|
+
file_path = @app_tree.file_path(file_name)
|
38
|
+
contents = file_path.read
|
39
|
+
|
40
|
+
begin
|
41
|
+
if ast = parse_ruby(contents, file_path.relative)
|
42
|
+
ASTFile.new(file_name, ast)
|
43
|
+
end
|
44
|
+
rescue Exception => e
|
45
|
+
e
|
19
46
|
end
|
47
|
+
end.compact.partition do |result|
|
48
|
+
result.is_a? ASTFile
|
20
49
|
end
|
50
|
+
|
51
|
+
errors.concat new_errors
|
21
52
|
end
|
22
53
|
|
23
54
|
def read_files list
|
24
55
|
list.each do |path|
|
25
56
|
file = @app_tree.file_path(path)
|
26
57
|
|
27
|
-
|
58
|
+
begin
|
59
|
+
result = yield file, file.read
|
28
60
|
|
29
|
-
|
30
|
-
|
61
|
+
if result
|
62
|
+
@file_list << result
|
63
|
+
end
|
64
|
+
rescue Exception => e
|
65
|
+
@errors << e
|
31
66
|
end
|
32
67
|
end
|
33
68
|
end
|
@@ -42,17 +77,12 @@ module Brakeman
|
|
42
77
|
Brakeman.debug "Parsing #{path}"
|
43
78
|
RubyParser.new.parse input, path, @timeout
|
44
79
|
rescue Racc::ParseError => e
|
45
|
-
|
80
|
+
raise e.exception(e.message + "\nCould not parse #{path}")
|
46
81
|
rescue Timeout::Error => e
|
47
|
-
|
82
|
+
raise Exception.new("Parsing #{path} took too long (> #{@timeout} seconds). Try increasing the limit with --parser-timeout")
|
48
83
|
rescue => e
|
49
|
-
|
84
|
+
raise e.exception(e.message + "\nWhile processing #{path}")
|
50
85
|
end
|
51
86
|
end
|
52
|
-
|
53
|
-
def error exception
|
54
|
-
@errors << exception
|
55
|
-
nil
|
56
|
-
end
|
57
87
|
end
|
58
88
|
end
|
data/lib/brakeman/options.rb
CHANGED
@@ -39,7 +39,7 @@ module Brakeman::Options
|
|
39
39
|
OptionParser.new do |opts|
|
40
40
|
opts.banner = "Usage: brakeman [options] rails/root/path"
|
41
41
|
|
42
|
-
opts.on "-n", "--no-threads", "Run checks sequentially" do
|
42
|
+
opts.on "-n", "--no-threads", "Run checks and file parsing sequentially" do
|
43
43
|
options[:parallel_checks] = false
|
44
44
|
end
|
45
45
|
|
@@ -93,6 +93,14 @@ module Brakeman::Options
|
|
93
93
|
options[:rails6] = true
|
94
94
|
end
|
95
95
|
|
96
|
+
opts.on "-7", "--rails7", "Force Rails 7 mode" do
|
97
|
+
options[:rails3] = true
|
98
|
+
options[:rails4] = true
|
99
|
+
options[:rails5] = true
|
100
|
+
options[:rails6] = true
|
101
|
+
options[:rails7] = true
|
102
|
+
end
|
103
|
+
|
96
104
|
opts.separator ""
|
97
105
|
opts.separator "Scanning options:"
|
98
106
|
|
@@ -151,6 +159,11 @@ module Brakeman::Options
|
|
151
159
|
options[:safe_methods].merge methods.map {|e| e.to_sym }
|
152
160
|
end
|
153
161
|
|
162
|
+
opts.on "--sql-safe-methods meth1,meth2,etc", Array, "Do not warn of SQL if the input is wrapped in a safe method" do |methods|
|
163
|
+
options[:sql_safe_methods] ||= Set.new
|
164
|
+
options[:sql_safe_methods].merge methods.map {|e| e.to_sym }
|
165
|
+
end
|
166
|
+
|
154
167
|
opts.on "--url-safe-methods method1,method2,etc", Array, "Do not warn of XSS if the link_to href parameter is wrapped in a safe method" do |methods|
|
155
168
|
options[:url_safe_methods] ||= Set.new
|
156
169
|
options[:url_safe_methods].merge methods.map {|e| e.to_sym }
|
@@ -233,7 +246,7 @@ module Brakeman::Options
|
|
233
246
|
|
234
247
|
opts.on "-f",
|
235
248
|
"--format TYPE",
|
236
|
-
[:pdf, :text, :html, :csv, :tabs, :json, :markdown, :codeclimate, :cc, :plain, :table, :junit, :sarif, :sonar],
|
249
|
+
[:pdf, :text, :html, :csv, :tabs, :json, :markdown, :codeclimate, :cc, :plain, :table, :junit, :sarif, :sonar, :github],
|
237
250
|
"Specify output formats. Default is text" do |type|
|
238
251
|
|
239
252
|
type = "s" if type == :text
|