brakeman-min 3.1.4 → 3.1.5.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +30 -0
  3. data/README.md +3 -2
  4. data/lib/brakeman.rb +4 -4
  5. data/lib/brakeman/app_tree.rb +58 -5
  6. data/lib/brakeman/call_index.rb +22 -31
  7. data/lib/brakeman/checks.rb +59 -73
  8. data/lib/brakeman/checks/base_check.rb +13 -5
  9. data/lib/brakeman/checks/check_basic_auth_timing_attack.rb +33 -0
  10. data/lib/brakeman/checks/check_cross_site_scripting.rb +12 -6
  11. data/lib/brakeman/checks/check_dynamic_finders.rb +49 -0
  12. data/lib/brakeman/checks/check_mime_type_dos.rb +39 -0
  13. data/lib/brakeman/checks/check_nested_attributes_bypass.rb +58 -0
  14. data/lib/brakeman/checks/check_render.rb +33 -3
  15. data/lib/brakeman/checks/check_route_dos.rb +42 -0
  16. data/lib/brakeman/checks/check_sanitize_methods.rb +26 -4
  17. data/lib/brakeman/checks/check_sql.rb +8 -6
  18. data/lib/brakeman/checks/check_strip_tags.rb +27 -2
  19. data/lib/brakeman/options.rb +8 -2
  20. data/lib/brakeman/processors/alias_processor.rb +14 -1
  21. data/lib/brakeman/processors/base_processor.rb +8 -0
  22. data/lib/brakeman/processors/controller_processor.rb +2 -2
  23. data/lib/brakeman/processors/erb_template_processor.rb +1 -1
  24. data/lib/brakeman/processors/erubis_template_processor.rb +1 -1
  25. data/lib/brakeman/processors/haml_template_processor.rb +2 -1
  26. data/lib/brakeman/processors/lib/basic_processor.rb +16 -0
  27. data/lib/brakeman/processors/lib/find_all_calls.rb +4 -2
  28. data/lib/brakeman/processors/lib/find_call.rb +1 -1
  29. data/lib/brakeman/processors/lib/render_path.rb +2 -1
  30. data/lib/brakeman/processors/lib/route_helper.rb +4 -0
  31. data/lib/brakeman/processors/model_processor.rb +2 -2
  32. data/lib/brakeman/report/ignore/config.rb +3 -3
  33. data/lib/brakeman/report/report_csv.rb +1 -2
  34. data/lib/brakeman/report/report_json.rb +1 -4
  35. data/lib/brakeman/scanner.rb +7 -2
  36. data/lib/brakeman/tracker.rb +21 -0
  37. data/lib/brakeman/tracker/config.rb +6 -0
  38. data/lib/brakeman/util.rb +4 -3
  39. data/lib/brakeman/version.rb +1 -1
  40. data/lib/brakeman/warning.rb +2 -2
  41. data/lib/brakeman/warning_codes.rb +9 -0
  42. data/lib/ruby_parser/bm_sexp.rb +23 -23
  43. metadata +13 -30
  44. data/lib/brakeman/report/initializers/faster_csv.rb +0 -7
  45. data/lib/brakeman/report/initializers/multi_json.rb +0 -29
@@ -59,14 +59,14 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
59
59
  call = make_call(nil, :named_scope, args).line(args.line)
60
60
  scope_calls << scope_call_hash(call, name, :named_scope)
61
61
  end
62
- elsif version_between?("3.1.0", "4.9.9")
62
+ elsif version_between?("3.1.0", "9.9.9")
63
63
  ar_scope_calls(:scope) do |name, args|
64
64
  second_arg = args[2]
65
65
  next unless sexp? second_arg
66
66
 
67
- if second_arg.node_type == :iter and node_type? second_arg.block, :block, :call
67
+ if second_arg.node_type == :iter and node_type? second_arg.block, :block, :call, :safe_call
68
68
  process_scope_with_block(name, args)
69
- elsif second_arg.node_type == :call
69
+ elsif call? second_arg
70
70
  call = second_arg
71
71
  scope_calls << scope_call_hash(call, name, call.method)
72
72
  else
@@ -107,7 +107,7 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
107
107
  find_calls = Brakeman::FindAllCalls.new(tracker)
108
108
  find_calls.process_source(block, :class => model_name, :method => scope_name)
109
109
  find_calls.calls.each { |call| process_result(call) if @sql_targets.include?(call[:method]) }
110
- elsif block.node_type == :call
110
+ elsif call? block
111
111
  while call? block
112
112
  process_result :target => block.target, :method => block.method, :call => block,
113
113
  :location => { :type => :class, :class => model_name, :method => scope_name }
@@ -264,8 +264,10 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
264
264
  end
265
265
 
266
266
  if request_value? arg
267
- # Model.where(params[:where])
268
- arg
267
+ unless call? arg and params? arg.target and arg.method == :permit
268
+ # Model.where(params[:where])
269
+ arg
270
+ end
269
271
  elsif hash? arg
270
272
  #This is generally going to be a hash of column names and values, which
271
273
  #would escape the values. But the keys _could_ be user input.
@@ -5,16 +5,21 @@ require 'brakeman/checks/base_check'
5
5
  #
6
6
  #Check for uses of strip_tags in Rails versions before 2.3.13 and 3.0.10:
7
7
  #http://groups.google.com/group/rubyonrails-security/browse_thread/thread/2b9130749b74ea12
8
+ #
9
+ #Check for user of strip_tags with rails-html-sanitizer 1.0.2:
10
+ #https://groups.google.com/d/msg/rubyonrails-security/OU9ugTZcbjc/PjEP46pbFQAJ
8
11
  class Brakeman::CheckStripTags < Brakeman::BaseCheck
9
12
  Brakeman::Checks.add self
10
13
 
11
- @description = "Report strip_tags vulnerabilities CVE-2011-2931 and CVE-2012-3465"
14
+ @description = "Report strip_tags vulnerabilities"
12
15
 
13
16
  def run_check
14
17
  if uses_strip_tags?
15
18
  cve_2011_2931
16
19
  cve_2012_3465
17
20
  end
21
+
22
+ cve_2015_7579
18
23
  end
19
24
 
20
25
  def cve_2011_2931
@@ -56,9 +61,29 @@ class Brakeman::CheckStripTags < Brakeman::BaseCheck
56
61
  :link_path => "https://groups.google.com/d/topic/rubyonrails-security/FgVEtBajcTY/discussion"
57
62
  end
58
63
 
64
+ def cve_2015_7579
65
+ if tracker.config.gem_version(:'rails-html-sanitizer') == '1.0.2'
66
+ if uses_strip_tags?
67
+ confidence = CONFIDENCE[:high]
68
+ else
69
+ confidence = CONFIDENCE[:med]
70
+ end
71
+
72
+ message = "rails-html-sanitizer 1.0.2 is vulnerable (CVE-2015-7579). Upgrade to 1.0.3"
73
+
74
+ warn :warning_type => "Cross Site Scripting",
75
+ :warning_code => :CVE_2015_7579,
76
+ :message => message,
77
+ :confidence => confidence,
78
+ :gem_info => gemfile_or_environment,
79
+ :link_path => "https://groups.google.com/d/msg/rubyonrails-security/OU9ugTZcbjc/PjEP46pbFQAJ"
80
+
81
+ end
82
+ end
83
+
59
84
  def uses_strip_tags?
60
85
  Brakeman.debug "Finding calls to strip_tags()"
61
86
 
62
- not tracker.find_call(:target => false, :method => :strip_tags).empty?
87
+ not tracker.find_call(:target => false, :method => :strip_tags, :nested => true).empty?
63
88
  end
64
89
  end
@@ -52,6 +52,12 @@ module Brakeman::Options
52
52
  options[:rails4] = true
53
53
  end
54
54
 
55
+ opts.on "-5", "--rails5", "Force Rails 5 mode" do
56
+ options[:rails3] = true
57
+ options[:rails4] = true
58
+ options[:rails5] = true
59
+ end
60
+
55
61
  opts.separator ""
56
62
  opts.separator "Scanning options:"
57
63
 
@@ -110,12 +116,12 @@ module Brakeman::Options
110
116
  options[:url_safe_methods].merge methods.map {|e| e.to_sym }
111
117
  end
112
118
 
113
- opts.on "--skip-files file1,file2,etc", Array, "Skip processing of these files" do |files|
119
+ opts.on "--skip-files file1,path2,etc", Array, "Skip processing of these files/directories. Directories are application relative and must end in \"#{File::SEPARATOR}\"" do |files|
114
120
  options[:skip_files] ||= Set.new
115
121
  options[:skip_files].merge files
116
122
  end
117
123
 
118
- opts.on "--only-files file1,file2,etc", Array, "Process only these files" do |files|
124
+ opts.on "--only-files file1,path2,etc", Array, "Process only these files/directories. Directories are application relative and must end in \"#{File::SEPARATOR}\"" do |files|
119
125
  options[:only_files] ||= Set.new
120
126
  options[:only_files].merge files
121
127
  end
@@ -212,6 +212,10 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
212
212
  def process_iter exp
213
213
  @exp_context.push exp
214
214
  exp[1] = process exp.block_call
215
+ if array_detect_all_literals? exp[1]
216
+ return exp.block_call.target[1]
217
+ end
218
+
215
219
  @exp_context.pop
216
220
 
217
221
  env.scope do
@@ -292,7 +296,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
292
296
 
293
297
  # Handles x = y = z = 1
294
298
  def get_rhs exp
295
- if node_type? exp, :lasgn, :iasgn, :gasgn, :attrasgn, :cvdecl, :cdecl
299
+ if node_type? exp, :lasgn, :iasgn, :gasgn, :attrasgn, :safe_attrasgn, :cvdecl, :cdecl
296
300
  get_rhs(exp.rhs)
297
301
  else
298
302
  exp
@@ -524,6 +528,15 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
524
528
  exp.target.all? { |e| e.is_a? Symbol or node_type? e, :lit, :str }
525
529
  end
526
530
 
531
+ def array_detect_all_literals? exp
532
+ call? exp and
533
+ [:detect, :find].include? exp.method and
534
+ node_type? exp.target, :array and
535
+ exp.target.length > 1 and
536
+ exp.first_arg.nil? and
537
+ exp.target.all? { |e| e.is_a? Symbol or node_type? e, :lit, :str }
538
+ end
539
+
527
540
  #Sets @inside_if = true
528
541
  def process_if exp
529
542
  if @ignore_ifs.nil?
@@ -68,6 +68,14 @@ class Brakeman::BaseProcessor < Brakeman::SexpProcessor
68
68
  call
69
69
  end
70
70
 
71
+ def process_safe_call exp
72
+ if self.respond_to? :process_call
73
+ process_call exp
74
+ else
75
+ process_default exp
76
+ end
77
+ end
78
+
71
79
  #String with interpolation.
72
80
  def process_dstr exp
73
81
  exp = exp.dup
@@ -208,11 +208,11 @@ class Brakeman::ControllerProcessor < Brakeman::BaseProcessor
208
208
  def process_defs exp
209
209
  name = exp.method_name
210
210
 
211
- if exp[1].node_type == :self
211
+ if node_type? exp[1], :self
212
212
  if @current_class
213
213
  target = @current_class.name
214
214
  elsif @current_module
215
- target = @current_module
215
+ target = @current_module.name
216
216
  else
217
217
  target = nil
218
218
  end
@@ -25,7 +25,7 @@ class Brakeman::ErbTemplateProcessor < Brakeman::TemplateProcessor
25
25
 
26
26
  arg = exp.first_arg
27
27
 
28
- if arg.node_type == :call and arg.method == :to_s #erb always calls to_s on output
28
+ if call? arg and arg.method == :to_s #erb always calls to_s on output
29
29
  arg = arg.target
30
30
  end
31
31
 
@@ -21,7 +21,7 @@ class Brakeman::ErubisTemplateProcessor < Brakeman::TemplateProcessor
21
21
  arg = exp.first_arg
22
22
 
23
23
  #We want the actual content
24
- if arg.node_type == :call and (arg.method == :to_s or arg.method == :html_safe!)
24
+ if call? arg and (arg.method == :to_s or arg.method == :html_safe!)
25
25
  arg = arg.target
26
26
  end
27
27
 
@@ -5,6 +5,7 @@ class Brakeman::HamlTemplateProcessor < Brakeman::TemplateProcessor
5
5
  HAML_FORMAT_METHOD = /format_script_(true|false)_(true|false)_(true|false)_(true|false)_(true|false)_(true|false)_(true|false)/
6
6
  HAML_HELPERS = s(:colon2, s(:const, :Haml), :Helpers)
7
7
  JAVASCRIPT_FILTER = s(:colon2, s(:colon2, s(:const, :Haml), :Filters), :Javascript)
8
+ COFFEE_FILTER = s(:colon2, s(:colon2, s(:const, :Haml), :Filters), :Coffee)
8
9
 
9
10
  #Processes call, looking for template output
10
11
  def process_call exp
@@ -90,7 +91,7 @@ class Brakeman::HamlTemplateProcessor < Brakeman::TemplateProcessor
90
91
  elsif target == nil and method == :find_and_preserve
91
92
  process exp.first_arg
92
93
  elsif method == :render_with_options
93
- if target == JAVASCRIPT_FILTER
94
+ if target == JAVASCRIPT_FILTER or target == COFFEE_FILTER
94
95
  @javascript = true
95
96
  end
96
97
 
@@ -14,4 +14,20 @@ class Brakeman::BasicProcessor < Brakeman::SexpProcessor
14
14
  def process_default exp
15
15
  process_all exp
16
16
  end
17
+
18
+ def process_safe_call exp
19
+ if self.respond_to? :process_call
20
+ process_call exp
21
+ else
22
+ process_default exp
23
+ end
24
+ end
25
+
26
+ def process_safe_attrasgn exp
27
+ if self.respond_to? :process_attrasgn
28
+ process_attrasgn exp
29
+ else
30
+ process_default exp
31
+ end
32
+ end
17
33
  end
@@ -24,11 +24,13 @@ class Brakeman::FindAllCalls < Brakeman::BasicProcessor
24
24
 
25
25
  #Process body of method
26
26
  def process_defn exp
27
+ return exp unless @current_method
27
28
  process_all exp.body
28
29
  end
29
30
 
30
31
  #Process body of method
31
32
  def process_defs exp
33
+ return exp unless @current_method
32
34
  process_all exp.body
33
35
  end
34
36
 
@@ -139,7 +141,7 @@ class Brakeman::FindAllCalls < Brakeman::BasicProcessor
139
141
  @current_class || @current_module || nil
140
142
  when :params, :session, :cookies
141
143
  exp.node_type
142
- when :call
144
+ when :call, :safe_call
143
145
  if include_calls
144
146
  if exp.target.nil?
145
147
  exp.method
@@ -165,7 +167,7 @@ class Brakeman::FindAllCalls < Brakeman::BasicProcessor
165
167
  #Returns method chain as an array
166
168
  #For example, User.human.alive.all would return [:User, :human, :alive, :all]
167
169
  def get_chain call
168
- if node_type? call, :call, :attrasgn
170
+ if node_type? call, :call, :attrasgn, :safe_call, :safe_attrasgn
169
171
  get_chain(call.target) + [call.method]
170
172
  elsif call.nil?
171
173
  []
@@ -102,7 +102,7 @@ class Brakeman::FindCall < Brakeman::BasicProcessor
102
102
  # User.find(:first, :conditions => "user = '#{params['user']}').name
103
103
  #
104
104
  #A search for User.find will not match this unless @in_depth is true.
105
- if @in_depth and node_type? exp.target, :call
105
+ if @in_depth and call? exp.target
106
106
  process exp.target
107
107
  end
108
108
 
@@ -95,7 +95,8 @@ module Brakeman
95
95
  end
96
96
 
97
97
  def to_json *args
98
- MultiJson.dump(@path)
98
+ require 'json'
99
+ JSON.generate(@path)
99
100
  end
100
101
 
101
102
  def initialize_copy original
@@ -31,6 +31,10 @@ module Brakeman::RouteHelper
31
31
 
32
32
  return unless route.is_a? String or route.is_a? Symbol
33
33
 
34
+ if route.is_a? String and controller.nil? and route.include? ":controller"
35
+ controller = ":controller"
36
+ end
37
+
34
38
  route = route.to_sym
35
39
 
36
40
  if controller
@@ -163,11 +163,11 @@ class Brakeman::ModelProcessor < Brakeman::BaseProcessor
163
163
  return exp unless @current_class
164
164
  name = exp.method_name
165
165
 
166
- if exp[1].node_type == :self
166
+ if node_type? exp[1], :self
167
167
  if @current_class
168
168
  target = @current_class.name
169
169
  elsif @current_module
170
- target = @current_module
170
+ target = @current_module.name
171
171
  else
172
172
  target = nil
173
173
  end
@@ -1,5 +1,5 @@
1
1
  require 'set'
2
- require 'multi_json'
2
+ require 'json'
3
3
 
4
4
  module Brakeman
5
5
  class IgnoreConfig
@@ -75,7 +75,7 @@ module Brakeman
75
75
  # Read configuration to file
76
76
  def read_from_file file = @file
77
77
  if File.exist? file
78
- @already_ignored = MultiJson.load(File.read(file), :symbolize_keys => true)[:ignored_warnings]
78
+ @already_ignored = JSON.parse(File.read(file), :symbolize_names => true)[:ignored_warnings]
79
79
  else
80
80
  Brakeman.notify "[Notice] Could not find ignore configuration in #{file}"
81
81
  @already_ignored = []
@@ -107,7 +107,7 @@ module Brakeman
107
107
  }
108
108
 
109
109
  File.open file, "w" do |f|
110
- f.puts MultiJson.dump(output, :pretty => true)
110
+ f.puts JSON.pretty_generate(output)
111
111
  end
112
112
  end
113
113
 
@@ -1,5 +1,4 @@
1
- Brakeman.load_brakeman_dependency 'csv'
2
- require "brakeman/report/initializers/faster_csv"
1
+ require 'csv'
3
2
  require "brakeman/report/report_table"
4
3
 
5
4
  class Brakeman::Report::CSV < Brakeman::Report::Table
@@ -1,6 +1,3 @@
1
- Brakeman.load_brakeman_dependency 'multi_json'
2
- require 'brakeman/report/initializers/multi_json'
3
-
4
1
  class Brakeman::Report::JSON < Brakeman::Report::Base
5
2
  def generate_report
6
3
  errors = tracker.errors.map{|e| { :error => e[:error], :location => e[:backtrace][0] }}
@@ -32,7 +29,7 @@ class Brakeman::Report::JSON < Brakeman::Report::Base
32
29
  :errors => errors
33
30
  }
34
31
 
35
- MultiJson.dump(report_info, :pretty => true)
32
+ JSON.pretty_generate report_info
36
33
  end
37
34
 
38
35
  def convert_to_hashes warnings
@@ -96,7 +96,7 @@ class Brakeman::Scanner
96
96
  #
97
97
  #Stores parsed information in tracker.config
98
98
  def process_config
99
- if options[:rails3]
99
+ if options[:rails3] or options[:rails4] or options[:rails5]
100
100
  process_config_file "application.rb"
101
101
  process_config_file "environments/production.rb"
102
102
  else
@@ -155,8 +155,13 @@ class Brakeman::Scanner
155
155
  if @app_tree.exists?("script/rails")
156
156
  tracker.options[:rails3] = true
157
157
  Brakeman.notify "[Notice] Detected Rails 3 application"
158
+ elsif @app_tree.exists?("app/channels")
159
+ tracker.options[:rails3] = true
160
+ tracker.options[:rails4] = true
161
+ tracker.options[:rails5] = true
162
+ Brakeman.notify "[Notice] Detected Rails 5 application"
158
163
  elsif not @app_tree.exists?("script")
159
- tracker.options[:rails3] = true # Probably need to do some refactoring
164
+ tracker.options[:rails3] = true
160
165
  tracker.options[:rails4] = true
161
166
  Brakeman.notify "[Notice] Detected Rails 4 application"
162
167
  end
@@ -115,6 +115,23 @@ class Brakeman::Tracker
115
115
  end
116
116
  end
117
117
 
118
+
119
+ def each_class
120
+ classes = [self.controllers, self.models]
121
+
122
+ if @options[:index_libs]
123
+ classes << self.libs
124
+ end
125
+
126
+ classes.each do |set|
127
+ set.each do |set_name, collection|
128
+ collection.src.each do |file, src|
129
+ yield src, set_name, file
130
+ end
131
+ end
132
+ end
133
+ end
134
+
118
135
  #Find a method call.
119
136
  #
120
137
  #Options:
@@ -178,6 +195,10 @@ class Brakeman::Tracker
178
195
  finder.process_source definition, :class => set_name, :method => method_name, :file => file
179
196
  end
180
197
 
198
+ self.each_class do |definition, set_name, file|
199
+ finder.process_source definition, :class => set_name, :file => file
200
+ end
201
+
181
202
  self.each_template do |name, template|
182
203
  finder.process_source template.src, :template => template, :file => template.file
183
204
  end
@@ -7,6 +7,7 @@ module Brakeman
7
7
  attr_reader :rails, :tracker
8
8
  attr_accessor :rails_version
9
9
  attr_writer :erubis, :escape_html
10
+ attr_reader :gems
10
11
 
11
12
  def initialize tracker
12
13
  @tracker = tracker
@@ -76,6 +77,11 @@ module Brakeman
76
77
  tracker.options[:rails3] = true
77
78
  tracker.options[:rails4] = true
78
79
  Brakeman.notify "[Notice] Detected Rails 4 application"
80
+ elsif @rails_version.start_with? "5"
81
+ tracker.options[:rails3] = true
82
+ tracker.options[:rails4] = true
83
+ tracker.options[:rails5] = true
84
+ Brakeman.notify "[Notice] Detected Rails 5 application"
79
85
  end
80
86
  end
81
87
  end