brakeman-lib 4.5.0 → 4.5.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.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +15 -0
  3. data/README.md +6 -6
  4. data/lib/brakeman.rb +7 -0
  5. data/lib/brakeman/app_tree.rb +34 -22
  6. data/lib/brakeman/checks.rb +7 -7
  7. data/lib/brakeman/checks/base_check.rb +9 -9
  8. data/lib/brakeman/checks/check_cross_site_scripting.rb +5 -0
  9. data/lib/brakeman/checks/check_default_routes.rb +5 -0
  10. data/lib/brakeman/checks/check_deserialize.rb +52 -0
  11. data/lib/brakeman/checks/check_dynamic_finders.rb +1 -1
  12. data/lib/brakeman/checks/check_force_ssl.rb +27 -0
  13. data/lib/brakeman/checks/check_json_parsing.rb +5 -0
  14. data/lib/brakeman/checks/check_link_to_href.rb +6 -1
  15. data/lib/brakeman/checks/check_mail_to.rb +1 -1
  16. data/lib/brakeman/checks/check_model_attr_accessible.rb +1 -1
  17. data/lib/brakeman/checks/check_model_attributes.rb +12 -50
  18. data/lib/brakeman/checks/check_model_serialize.rb +1 -1
  19. data/lib/brakeman/checks/check_nested_attributes_bypass.rb +3 -3
  20. data/lib/brakeman/checks/check_secrets.rb +1 -1
  21. data/lib/brakeman/checks/check_session_settings.rb +10 -10
  22. data/lib/brakeman/checks/check_simple_format.rb +5 -0
  23. data/lib/brakeman/checks/check_skip_before_filter.rb +1 -1
  24. data/lib/brakeman/checks/check_sql.rb +15 -17
  25. data/lib/brakeman/checks/check_validation_regex.rb +1 -1
  26. data/lib/brakeman/file_parser.rb +6 -8
  27. data/lib/brakeman/file_path.rb +71 -0
  28. data/lib/brakeman/options.rb +7 -0
  29. data/lib/brakeman/parsers/template_parser.rb +3 -3
  30. data/lib/brakeman/processor.rb +3 -4
  31. data/lib/brakeman/processors/alias_processor.rb +12 -6
  32. data/lib/brakeman/processors/base_processor.rb +8 -7
  33. data/lib/brakeman/processors/controller_alias_processor.rb +10 -7
  34. data/lib/brakeman/processors/controller_processor.rb +5 -9
  35. data/lib/brakeman/processors/haml_template_processor.rb +5 -0
  36. data/lib/brakeman/processors/lib/module_helper.rb +8 -8
  37. data/lib/brakeman/processors/lib/processor_helper.rb +3 -3
  38. data/lib/brakeman/processors/lib/rails2_config_processor.rb +3 -3
  39. data/lib/brakeman/processors/lib/rails2_route_processor.rb +2 -2
  40. data/lib/brakeman/processors/lib/rails3_config_processor.rb +3 -3
  41. data/lib/brakeman/processors/lib/rails3_route_processor.rb +2 -2
  42. data/lib/brakeman/processors/lib/render_helper.rb +2 -2
  43. data/lib/brakeman/processors/lib/render_path.rb +18 -1
  44. data/lib/brakeman/processors/library_processor.rb +5 -5
  45. data/lib/brakeman/processors/model_processor.rb +4 -5
  46. data/lib/brakeman/processors/output_processor.rb +5 -0
  47. data/lib/brakeman/processors/template_alias_processor.rb +4 -5
  48. data/lib/brakeman/processors/template_processor.rb +4 -4
  49. data/lib/brakeman/report.rb +3 -3
  50. data/lib/brakeman/report/ignore/config.rb +2 -3
  51. data/lib/brakeman/report/ignore/interactive.rb +2 -2
  52. data/lib/brakeman/report/pager.rb +1 -0
  53. data/lib/brakeman/report/report_base.rb +51 -6
  54. data/lib/brakeman/report/report_codeclimate.rb +3 -3
  55. data/lib/brakeman/report/report_hash.rb +1 -1
  56. data/lib/brakeman/report/report_html.rb +2 -2
  57. data/lib/brakeman/report/report_json.rb +1 -24
  58. data/lib/brakeman/report/report_table.rb +20 -4
  59. data/lib/brakeman/report/report_tabs.rb +1 -1
  60. data/lib/brakeman/report/report_text.rb +2 -2
  61. data/lib/brakeman/rescanner.rb +9 -12
  62. data/lib/brakeman/scanner.rb +19 -14
  63. data/lib/brakeman/tracker.rb +4 -4
  64. data/lib/brakeman/tracker/collection.rb +4 -3
  65. data/lib/brakeman/tracker/config.rb +6 -0
  66. data/lib/brakeman/util.rb +1 -147
  67. data/lib/brakeman/version.rb +1 -1
  68. data/lib/brakeman/warning.rb +23 -13
  69. data/lib/brakeman/warning_codes.rb +1 -0
  70. data/lib/ruby_parser/bm_sexp_processor.rb +1 -0
  71. metadata +20 -10
@@ -25,7 +25,7 @@ class Brakeman::CheckModelAttrAccessible < Brakeman::BaseCheck
25
25
 
26
26
  SUSP_ATTRS.each do |susp_attr, confidence|
27
27
  if susp_attr.is_a?(Regexp) and susp_attr =~ attribute.to_s or susp_attr == attribute
28
- warn :model => name,
28
+ warn :model => model,
29
29
  :file => model.file,
30
30
  :warning_type => "Mass Assignment",
31
31
  :warning_code => :dangerous_attr_accessible,
@@ -2,9 +2,6 @@ require 'brakeman/checks/base_check'
2
2
 
3
3
  #Check if mass assignment is used with models
4
4
  #which inherit from ActiveRecord::Base.
5
- #
6
- #If tracker.options[:collapse_mass_assignment] is +true+ (default), all models
7
- #which do not use attr_accessible will be reported in a single warning
8
5
  class Brakeman::CheckModelAttributes < Brakeman::BaseCheck
9
6
  Brakeman::Checks.add self
10
7
 
@@ -15,26 +12,19 @@ class Brakeman::CheckModelAttributes < Brakeman::BaseCheck
15
12
 
16
13
  #Roll warnings into one warning for all models
17
14
  if tracker.options[:collapse_mass_assignment]
18
- no_accessible_names = []
19
- protected_names = []
20
-
21
- check_models do |name, model|
22
- if model.attr_protected.nil?
23
- no_accessible_names << name.to_s
24
- elsif not tracker.options[:ignore_attr_protected]
25
- protected_names << name.to_s
26
- end
27
- end
15
+ Brakeman.notify "[Notice] The `collapse_mass_assignment` option has been removed."
16
+ end
28
17
 
29
- unless no_accessible_names.empty?
30
- warn :model => no_accessible_names.sort.join(", "),
18
+ check_models do |name, model|
19
+ if model.attr_protected.nil?
20
+ warn :model => model,
21
+ :file => model.file,
22
+ :line => model.top_line,
31
23
  :warning_type => "Attribute Restriction",
32
24
  :warning_code => :no_attr_accessible,
33
25
  :message => msg("Mass assignment is not restricted using ", msg_code("attr_accessible")),
34
26
  :confidence => :high
35
- end
36
-
37
- unless protected_names.empty?
27
+ elsif not tracker.options[:ignore_attr_protected]
38
28
  message, confidence, link = check_for_attr_protected_bypass
39
29
 
40
30
  if link
@@ -43,41 +33,13 @@ class Brakeman::CheckModelAttributes < Brakeman::BaseCheck
43
33
  warning_code = :attr_protected_used
44
34
  end
45
35
 
46
- warn :model => protected_names.sort.join(", "),
36
+ warn :model => model,
37
+ :file => model.file,
38
+ :line => model.attr_protected.first.line,
47
39
  :warning_type => "Attribute Restriction",
48
40
  :warning_code => warning_code,
49
41
  :message => message,
50
- :confidence => confidence,
51
- :link => link
52
- end
53
- else #Output one warning per model
54
-
55
- check_models do |name, model|
56
- if model.attr_protected.nil?
57
- warn :model => name,
58
- :file => model.file,
59
- :line => model.top_line,
60
- :warning_type => "Attribute Restriction",
61
- :warning_code => :no_attr_accessible,
62
- :message => msg("Mass assignment is not restricted using ", msg_code("attr_accessible")),
63
- :confidence => :high
64
- elsif not tracker.options[:ignore_attr_protected]
65
- message, confidence, link = check_for_attr_protected_bypass
66
-
67
- if link
68
- warning_code = :CVE_2013_0276
69
- else
70
- warning_code = :attr_protected_used
71
- end
72
-
73
- warn :model => name,
74
- :file => model.file,
75
- :line => model.attr_protected.first.line,
76
- :warning_type => "Attribute Restriction",
77
- :warning_code => warning_code,
78
- :message => message,
79
- :confidence => confidence
80
- end
42
+ :confidence => confidence
81
43
  end
82
44
  end
83
45
  end
@@ -54,7 +54,7 @@ class Brakeman::CheckModelSerialize < Brakeman::BaseCheck
54
54
  confidence = :high
55
55
  end
56
56
 
57
- warn :model => model.name,
57
+ warn :model => model,
58
58
  :warning_type => "Remote Code Execution",
59
59
  :warning_code => :CVE_2013_0277,
60
60
  :message => msg("Serialized attributes are vulnerable in ", msg_version(rails_version), ", upgrade to ", msg_version(@upgrade_version), " or patch"),
@@ -22,17 +22,17 @@ class Brakeman::CheckNestedAttributesBypass < Brakeman::BaseCheck
22
22
  if opts = model.options[:accepts_nested_attributes_for]
23
23
  opts.each do |args|
24
24
  if args.any? { |a| allow_destroy? a } and args.any? { |a| reject_if? a }
25
- warn_about_nested_attributes name, model, args
25
+ warn_about_nested_attributes model, args
26
26
  end
27
27
  end
28
28
  end
29
29
  end
30
30
  end
31
31
 
32
- def warn_about_nested_attributes name, model, args
32
+ def warn_about_nested_attributes model, args
33
33
  message = msg(msg_version(rails_version), " does not call ", msg_code(":reject_if"), " option when ", msg_code(":allow_destroy"), " is ", msg_code("false"), " ", msg_cve("CVE-2015-7577"))
34
34
 
35
- warn :model => name,
35
+ warn :model => model,
36
36
  :warning_type => "Nested Attributes",
37
37
  :warning_code => :CVE_2015_7577,
38
38
  :message => message,
@@ -35,6 +35,6 @@ class Brakeman::CheckSecrets < Brakeman::BaseCheck
35
35
 
36
36
  def looks_like_secret? name
37
37
  # REST_AUTH_SITE_KEY is the pepper in Devise
38
- name.match /password|secret|(rest_auth_site|api)_key$/i
38
+ name.match(/password|secret|(rest_auth_site|api)_key$/i)
39
39
  end
40
40
  end
@@ -19,7 +19,7 @@ class Brakeman::CheckSessionSettings < Brakeman::BaseCheck
19
19
  def run_check
20
20
  settings = tracker.config.session_settings
21
21
 
22
- check_for_issues settings, @app_tree.expand_path("config/environment.rb")
22
+ check_for_issues settings, @app_tree.file_path("config/environment.rb")
23
23
 
24
24
  ["session_store.rb", "secret_token.rb"].each do |file|
25
25
  if tracker.initializers[file] and not ignored? file
@@ -42,13 +42,13 @@ class Brakeman::CheckSessionSettings < Brakeman::BaseCheck
42
42
  #in Rails 4.x apps
43
43
  def process_attrasgn exp
44
44
  if not tracker.options[:rails3] and exp.target == @session_settings and exp.method == :session=
45
- check_for_issues exp.first_arg, @app_tree.expand_path("config/initializers/session_store.rb")
45
+ check_for_issues exp.first_arg, @app_tree.file_path("config/initializers/session_store.rb")
46
46
  end
47
47
 
48
48
  if tracker.options[:rails3] and settings_target?(exp.target) and
49
49
  (exp.method == :secret_token= or exp.method == :secret_key_base=) and string? exp.first_arg
50
50
 
51
- warn_about_secret_token exp.line, @app_tree.expand_path("config/initializers/secret_token.rb")
51
+ warn_about_secret_token exp.line, @app_tree.file_path("config/initializers/secret_token.rb")
52
52
  end
53
53
 
54
54
  exp
@@ -58,7 +58,7 @@ class Brakeman::CheckSessionSettings < Brakeman::BaseCheck
58
58
  #in Rails 3.x apps
59
59
  def process_call exp
60
60
  if tracker.options[:rails3] and settings_target?(exp.target) and exp.method == :session_store
61
- check_for_rails3_issues exp.second_arg, @app_tree.expand_path("config/initializers/session_store.rb")
61
+ check_for_rails3_issues exp.second_arg, @app_tree.file_path("config/initializers/session_store.rb")
62
62
  end
63
63
 
64
64
  exp
@@ -109,10 +109,10 @@ class Brakeman::CheckSessionSettings < Brakeman::BaseCheck
109
109
  end
110
110
 
111
111
  def check_secrets_yaml
112
- secrets_file = "config/secrets.yml"
112
+ secrets_file = @app_tree.file_path("config/secrets.yml")
113
113
 
114
- if @app_tree.exists? secrets_file and not ignored? "secrets.yml" and not ignored? "config/*.yml"
115
- yaml = @app_tree.read secrets_file
114
+ if secrets_file.exists? and not ignored? "secrets.yml" and not ignored? "config/*.yml"
115
+ yaml = secrets_file.read
116
116
  require 'date' # https://github.com/dtao/safe_yaml/issues/80
117
117
  require 'safe_yaml/load'
118
118
  begin
@@ -127,7 +127,7 @@ class Brakeman::CheckSessionSettings < Brakeman::BaseCheck
127
127
  unless secret.include? "<%="
128
128
  line = yaml.lines.find_index { |l| l.include? secret } + 1
129
129
 
130
- warn_about_secret_token line, @app_tree.expand_path(secrets_file)
130
+ warn_about_secret_token line, @app_tree.file_path(secrets_file)
131
131
  end
132
132
  end
133
133
  end
@@ -163,9 +163,9 @@ class Brakeman::CheckSessionSettings < Brakeman::BaseCheck
163
163
 
164
164
  def ignored? file
165
165
  [".", "config", "config/initializers"].each do |dir|
166
- ignore_file = "#{dir}/.gitignore"
166
+ ignore_file = @app_tree.file_path("#{dir}/.gitignore")
167
167
  if @app_tree.exists? ignore_file
168
- input = @app_tree.read(ignore_file)
168
+ input = ignore_file.read
169
169
 
170
170
  return true if input.include? file
171
171
  end
@@ -5,6 +5,11 @@ class Brakeman::CheckSimpleFormat < Brakeman::CheckCrossSiteScripting
5
5
 
6
6
  @description = "Checks for simple_format XSS vulnerability (CVE-2013-6416) in certain versions"
7
7
 
8
+ def initialize *args
9
+ super
10
+ @found_any = false
11
+ end
12
+
8
13
  def run_check
9
14
  if version_between? "4.0.0", "4.0.1"
10
15
  @inspect_arguments = true
@@ -38,7 +38,7 @@ class Brakeman::CheckSkipBeforeFilter < Brakeman::BaseCheck
38
38
  :message => msg("Use whitelist (", msg_code(":only => [..]"), ") when skipping authentication"),
39
39
  :code => filter,
40
40
  :confidence => :medium,
41
- :link => "authentication_whitelist",
41
+ :link_path => "authentication_whitelist",
42
42
  :file => controller.file
43
43
  end
44
44
  end
@@ -21,7 +21,8 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
21
21
  @sql_targets = [:average, :calculate, :count, :count_by_sql, :delete_all, :destroy_all,
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
- @sql_targets << :find_by << :find_by! << :not if tracker.options[:rails4]
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
+ @sql_targets << :delete_by << :destroy_by if tracker.options[:rails6]
25
26
 
26
27
  if version_between?("2.0.0", "3.9.9") or tracker.config.rails_version.nil?
27
28
  @sql_targets << :first << :last << :all
@@ -71,23 +72,23 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
71
72
  scope_calls = []
72
73
 
73
74
  if version_between?("2.1.0", "3.0.9")
74
- ar_scope_calls(:named_scope) do |name, args|
75
+ ar_scope_calls(:named_scope) do |model, args|
75
76
  call = make_call(nil, :named_scope, args).line(args.line)
76
- scope_calls << scope_call_hash(call, name, :named_scope)
77
+ scope_calls << scope_call_hash(call, model, :named_scope)
77
78
  end
78
79
  elsif version_between?("3.1.0", "9.9.9")
79
- ar_scope_calls(:scope) do |name, args|
80
+ ar_scope_calls(:scope) do |model, args|
80
81
  second_arg = args[2]
81
82
  next unless sexp? second_arg
82
83
 
83
84
  if second_arg.node_type == :iter and node_type? second_arg.block, :block, :call, :safe_call
84
- process_scope_with_block(name, args)
85
+ process_scope_with_block(model, args)
85
86
  elsif call? second_arg
86
87
  call = second_arg
87
- scope_calls << scope_call_hash(call, name, call.method)
88
+ scope_calls << scope_call_hash(call, model, call.method)
88
89
  else
89
90
  call = make_call(nil, :scope, args).line(args.line)
90
- scope_calls << scope_call_hash(call, name, :scope)
91
+ scope_calls << scope_call_hash(call, model, :scope)
91
92
  end
92
93
  end
93
94
  end
@@ -96,37 +97,34 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
96
97
  end
97
98
 
98
99
  def ar_scope_calls(symbol_name = :named_scope, &block)
99
- return_array = []
100
100
  active_record_models.each do |name, model|
101
101
  model_args = model.options[symbol_name]
102
102
  if model_args
103
103
  model_args.each do |args|
104
- yield name, args
105
- return_array << [name, args]
104
+ yield model, args
106
105
  end
107
106
  end
108
107
  end
109
- return_array
110
108
  end
111
109
 
112
- def scope_call_hash(call, name, method)
113
- { :call => call, :location => { :type => :class, :class => name }, :method => :named_scope }
110
+ def scope_call_hash(call, model, method)
111
+ { :call => call, :location => { :type => :class, :class => model.name, :file => model.file }, :method => :named_scope }
114
112
  end
115
113
 
116
114
 
117
- def process_scope_with_block model_name, args
115
+ def process_scope_with_block model, args
118
116
  scope_name = args[1][1]
119
117
  block = args[-1][-1]
120
118
 
121
119
  # Search lambda for calls to query methods
122
120
  if block.node_type == :block
123
121
  find_calls = Brakeman::FindAllCalls.new(tracker)
124
- find_calls.process_source(block, :class => model_name, :method => scope_name)
122
+ find_calls.process_source(block, :class => model.name, :method => scope_name, :file => model.file)
125
123
  find_calls.calls.each { |call| process_result(call) if @sql_targets.include?(call[:method]) }
126
124
  elsif call? block
127
125
  while call? block
128
126
  process_result :target => block.target, :method => block.method, :call => block,
129
- :location => { :type => :class, :class => model_name, :method => scope_name }
127
+ :location => { :type => :class, :class => model.name, :method => scope_name, :file => model.file }
130
128
 
131
129
  block = block.target
132
130
  end
@@ -187,7 +185,7 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
187
185
  else
188
186
  check_find_arguments call.last_arg
189
187
  end
190
- when :where, :having, :find_by, :find_by!, :not
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
191
189
  check_query_arguments call.arglist
192
190
  when :order, :group, :reorder
193
191
  check_order_arguments call.arglist
@@ -17,7 +17,7 @@ class Brakeman::CheckValidationRegex < Brakeman::BaseCheck
17
17
 
18
18
  def run_check
19
19
  active_record_models.each do |name, model|
20
- @current_model = name
20
+ @current_model = model
21
21
  format_validations = model.options[:validates_format_of]
22
22
 
23
23
  if format_validations
@@ -5,16 +5,16 @@ module Brakeman
5
5
  class FileParser
6
6
  attr_reader :file_list
7
7
 
8
- def initialize tracker, app_tree
8
+ def initialize tracker
9
9
  @tracker = tracker
10
10
  @timeout = @tracker.options[:parser_timeout]
11
- @app_tree = app_tree
11
+ @app_tree = @tracker.app_tree
12
12
  @file_list = {}
13
13
  end
14
14
 
15
15
  def parse_files list, type
16
16
  read_files list, type do |path, contents|
17
- if ast = parse_ruby(contents, path)
17
+ if ast = parse_ruby(contents, path.relative)
18
18
  ASTFile.new(path, ast)
19
19
  end
20
20
  end
@@ -24,7 +24,9 @@ module Brakeman
24
24
  @file_list[type] ||= []
25
25
 
26
26
  list.each do |path|
27
- result = yield path, read_path(path)
27
+ file = @app_tree.file_path(path)
28
+
29
+ result = yield file, file.read
28
30
  if result
29
31
  @file_list[type] << result
30
32
  end
@@ -50,9 +52,5 @@ module Brakeman
50
52
  nil
51
53
  end
52
54
  end
53
-
54
- def read_path path
55
- @app_tree.read_path path
56
- end
57
55
  end
58
56
  end
@@ -0,0 +1,71 @@
1
+ require 'pathname'
2
+
3
+ module Brakeman
4
+ # Class to represent file paths within Brakeman.
5
+ # FilePath objects track both the relative and absolute paths
6
+ # to make it easier to manage paths.
7
+ class FilePath
8
+ attr_reader :absolute, :relative
9
+ @cache = {}
10
+
11
+ # Create a new FilePath using an AppTree object.
12
+ #
13
+ # Note that if the path is already a FilePath, that path will
14
+ # be returned unaltered.
15
+ #
16
+ # Additionally, paths are cached. If the absolute path already has
17
+ # a FilePath in the cache, that existing FilePath will be returned.
18
+ def self.from_app_tree app_tree, path
19
+ return path if path.is_a? Brakeman::FilePath
20
+
21
+ absolute = app_tree.expand_path(path).freeze
22
+
23
+ if fp = @cache[absolute]
24
+ return fp
25
+ end
26
+
27
+ relative = app_tree.relative_path(path).freeze
28
+
29
+ self.new(absolute, relative).tap { |fp| @cache[absolute] = fp }
30
+ end
31
+
32
+ # Create a new FilePath with the given absolute and relative paths.
33
+ def initialize absolute_path, relative_path
34
+ @absolute = absolute_path
35
+ @relative = relative_path
36
+ end
37
+
38
+ # Read file from absolute path.
39
+ def read
40
+ File.read self.absolute
41
+ end
42
+
43
+ # Check if absolute path exists.
44
+ def exists?
45
+ File.exist? self.absolute
46
+ end
47
+
48
+ # Compare FilePaths. Raises an ArgumentError unless both objects are FilePaths.
49
+ def <=> rhs
50
+ raise ArgumentError unless rhs.is_a? Brakeman::FilePath
51
+ self.relative <=> rhs.relative
52
+ end
53
+
54
+ # Compare FilePaths. Raises an ArgumentError unless both objects are FilePaths.
55
+ def == rhs
56
+ return false unless rhs.is_a? Brakeman::FilePath
57
+
58
+ self.absolute == rhs.absolute
59
+ end
60
+
61
+ # Returns a string with the absolute path.
62
+ def to_str
63
+ self.absolute
64
+ end
65
+
66
+ # Returns a string with the absolute path.
67
+ def to_s
68
+ self.to_str
69
+ end
70
+ end
71
+ end
@@ -82,6 +82,13 @@ module Brakeman::Options
82
82
  options[:rails5] = true
83
83
  end
84
84
 
85
+ opts.on "-6", "--rails6", "Force Rails 6 mode" do
86
+ options[:rails3] = true
87
+ options[:rails4] = true
88
+ options[:rails5] = true
89
+ options[:rails6] = true
90
+ end
91
+
85
92
  opts.separator ""
86
93
  opts.separator "Scanning options:"
87
94