brakeman 1.9.1 → 1.9.2

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES CHANGED
@@ -1,3 +1,14 @@
1
+ # 1.9.2
2
+
3
+ * Add check for CVE-2013-0269
4
+ * Add check for CVE-2013-0276
5
+ * Add check for CVE-2013-0277
6
+ * Add check for CVE-2013-0333
7
+ * Check for more send-like methods
8
+ * Check for more SQL injection locations
9
+ * Check for more dangerous YAML methods
10
+ * Support MultiJSON 1.2 for Rails 3.0 and 3.1
11
+
1
12
  # 1.9.1
2
13
 
3
14
  * Update to RubyParser 3.1.1 (neersighted)
@@ -23,7 +23,9 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
23
23
  @string_interp = false
24
24
  @current_set = nil
25
25
  @current_template = @current_module = @current_class = @current_method = nil
26
+ @active_record_models = nil
26
27
  @mass_assign_disabled = nil
28
+ @has_user_input = nil
27
29
  @safe_input_attributes = Set[:to_i, :to_f, :arel_table]
28
30
  end
29
31
 
@@ -32,6 +32,7 @@ class Brakeman::CheckContentTag < Brakeman::CheckCrossSiteScripting
32
32
 
33
33
  @models = tracker.models.keys
34
34
  @inspect_arguments = tracker.options[:check_arguments]
35
+ @mark = nil
35
36
 
36
37
  Brakeman.debug "Checking for XSS in content_tag"
37
38
  methods.each do |call|
@@ -158,7 +159,7 @@ class Brakeman::CheckContentTag < Brakeman::CheckCrossSiteScripting
158
159
  :user_input => @matched.match,
159
160
  :confidence => CONFIDENCE[:med],
160
161
  :link_path => "content_tag"
161
- end
162
+ end
162
163
  end
163
164
 
164
165
  def process_call exp
@@ -0,0 +1,100 @@
1
+ require 'brakeman/checks/base_check'
2
+
3
+ class Brakeman::CheckJSONParsing < Brakeman::BaseCheck
4
+ Brakeman::Checks.add self
5
+
6
+ @description = "Checks for JSON parsing vulnerabilities CVE-2013-0333 and CVE-2013-0269"
7
+
8
+ def run_check
9
+ check_cve_2013_0333
10
+ check_cve_2013_0269
11
+ end
12
+
13
+ def check_cve_2013_0333
14
+ return unless version_between? "0.0.0", "2.3.15" or version_between? "3.0.0", "3.0.19"
15
+
16
+ unless uses_yajl? or uses_gem_backend?
17
+ new_version = if version_between? "0.0.0", "2.3.14"
18
+ "2.3.16"
19
+ elsif version_between? "3.0.0", "3.0.19"
20
+ "3.0.20"
21
+ end
22
+
23
+ message = "Rails #{tracker.config[:rails_version]} has a serious JSON parsing vulnerability: upgrade to #{new_version} or patch"
24
+
25
+ warn :warning_type => "Remote Code Execution",
26
+ :message => message,
27
+ :confidence => CONFIDENCE[:high],
28
+ :file => gemfile_or_environment,
29
+ :link_path => "https://groups.google.com/d/topic/rubyonrails-security/1h2DR63ViGo/discussion"
30
+ end
31
+ end
32
+
33
+ #Check if `yajl` is included in Gemfile
34
+ def uses_yajl?
35
+ tracker.config[:gems] and tracker.config[:gems][:yajl]
36
+ end
37
+
38
+ #Check for `ActiveSupport::JSON.backend = "JSONGem"`
39
+ def uses_gem_backend?
40
+ matches = tracker.check_initializers(:'ActiveSupport::JSON', :backend=)
41
+
42
+ unless matches.empty?
43
+ json_gem = s(:str, "JSONGem")
44
+
45
+ matches.each do |result|
46
+ if result.call.first_arg == json_gem
47
+ return true
48
+ end
49
+ end
50
+ end
51
+
52
+ false
53
+ end
54
+
55
+ def check_cve_2013_0269
56
+ [:json, :json_pure].each do |name|
57
+ version = tracker.config[:gems] && tracker.config[:gems][name]
58
+ check_json_version name, version if version
59
+ end
60
+ end
61
+
62
+ def check_json_version name, version
63
+ return if version >= "1.7.7" or
64
+ (version >= "1.6.8" and version < "1.7.0") or
65
+ (version >= "1.5.5" and version < "1.6.0")
66
+
67
+ warning_type = "Denial of Service"
68
+ confidence = CONFIDENCE[:med]
69
+ message = "#{name} gem version #{version} has a symbol creation vulnerablity: upgrade to "
70
+
71
+ if version >= "1.7.0"
72
+ confidence = CONFIDENCE[:high]
73
+ warning_type = "Remote Code Execution"
74
+ message = "#{name} gem version #{version} has a remote code vulnerablity: upgrade to 1.7.7"
75
+ elsif version >= "1.6.0"
76
+ message << "1.6.8"
77
+ elsif version >= "1.5.0"
78
+ message << "1.5.5"
79
+ else
80
+ confidence = CONFIDENCE[:low]
81
+ message << "1.5.5"
82
+ end
83
+
84
+ if confidence == CONFIDENCE[:med] and uses_json_parse?
85
+ confidence = CONFIDENCE[:high]
86
+ end
87
+
88
+ warn :warning_type => warning_type,
89
+ :message => message,
90
+ :confidence => confidence,
91
+ :file => gemfile_or_environment,
92
+ :link => "https://groups.google.com/d/topic/rubyonrails-security/4_YvCpLzL58/discussion"
93
+ end
94
+
95
+ def uses_json_parse?
96
+ return @uses_json_parse unless @uses_json_parse.nil?
97
+
98
+ not tracker.find_call(:target => :JSON, :method => :parse).empty?
99
+ end
100
+ end
@@ -34,10 +34,13 @@ class Brakeman::CheckModelAttributes < Brakeman::BaseCheck
34
34
  end
35
35
 
36
36
  unless protected_names.empty?
37
+ message, confidence, link = check_for_attr_protected_bypass
38
+
37
39
  warn :model => protected_names.sort.join(", "),
38
40
  :warning_type => "Attribute Restriction",
39
- :message => "attr_accessible is recommended over attr_protected",
40
- :confidence => CONFIDENCE[:low]
41
+ :message => message,
42
+ :confidence => confidence,
43
+ :link => link
41
44
  end
42
45
  else #Output one warning per model
43
46
 
@@ -49,12 +52,14 @@ class Brakeman::CheckModelAttributes < Brakeman::BaseCheck
49
52
  :message => "Mass assignment is not restricted using attr_accessible",
50
53
  :confidence => CONFIDENCE[:high]
51
54
  elsif not tracker.options[:ignore_attr_protected]
55
+ message, confidence, link = check_for_attr_protected_bypass
56
+
52
57
  warn :model => name,
53
58
  :file => model[:file],
54
59
  :line => model[:options][:attr_protected].first.line,
55
60
  :warning_type => "Attribute Restriction",
56
- :message => "attr_accessible is recommended over attr_protected",
57
- :confidence => CONFIDENCE[:low]
61
+ :message => message,
62
+ :confidence => confidence
58
63
  end
59
64
  end
60
65
  end
@@ -67,4 +72,31 @@ class Brakeman::CheckModelAttributes < Brakeman::BaseCheck
67
72
  end
68
73
  end
69
74
  end
75
+
76
+ def check_for_attr_protected_bypass
77
+ upgrade_version = case
78
+ when version_between?("2.0.0", "2.3.16")
79
+ "2.3.17"
80
+ when version_between?("3.0.0", "3.0.99")
81
+ "3.2.11"
82
+ when version_between?("3.1.0", "3.1.10")
83
+ "3.1.11"
84
+ when version_between?("3.2.0", "3.2.11")
85
+ "3.2.12"
86
+ else
87
+ nil
88
+ end
89
+
90
+ if upgrade_version
91
+ message = "attr_protected is bypassable in #{tracker.config[:rails_version]}, use attr_accessible or upgrade to #{upgrade_version}"
92
+ confidence = CONFIDENCE[:high]
93
+ link = "https://groups.google.com/d/topic/rubyonrails-security/AFBKNY7VSH8/discussion"
94
+ else
95
+ message = "attr_accessible is recommended over attr_protected"
96
+ confidence = CONFIDENCE[:med]
97
+ link = nil
98
+ end
99
+
100
+ return message, confidence, link
101
+ end
70
102
  end
@@ -0,0 +1,64 @@
1
+ require 'brakeman/checks/base_check'
2
+
3
+ class Brakeman::CheckModelSerialize < Brakeman::BaseCheck
4
+ Brakeman::Checks.add self
5
+
6
+ @description = "Report uses of serialize in versions vulnerable to CVE-2013-0277"
7
+
8
+ def run_check
9
+ @upgrade_version = case
10
+ when version_between?("2.0.0", "2.3.16")
11
+ "2.3.17"
12
+ when version_between?("3.0.0", "3.0.99")
13
+ "3.2.11"
14
+ else
15
+ nil
16
+ end
17
+
18
+ return unless @upgrade_version
19
+
20
+ tracker.models.each do |name, model|
21
+ check_for_serialize model
22
+ end
23
+ end
24
+
25
+ #High confidence warning on serialized, unprotected attributes.
26
+ #Medium confidence warning for serialized, protected attributes.
27
+ def check_for_serialize model
28
+ if serialized_attrs = model[:options] && model[:options][:serialize]
29
+ attrs = Set.new
30
+
31
+ serialized_attrs.each do |arglist|
32
+ arglist.each do |arg|
33
+ attrs << arg if symbol? arg
34
+ end
35
+ end
36
+
37
+ if unsafe_attrs = model[:attr_accessible]
38
+ attrs.delete_if { |attr| not unsafe_attrs.include? attr.value }
39
+ elsif protected_attrs = model[:options][:attr_protected]
40
+ safe_attrs = Set.new
41
+
42
+ protected_attrs.each do |arglist|
43
+ arglist.each do |arg|
44
+ safe_attrs << arg if symbol? arg
45
+ end
46
+ end
47
+
48
+ attrs.delete_if { |attr| safe_attrs.include? attr }
49
+ end
50
+
51
+ if attrs.empty?
52
+ confidence = CONFIDENCE[:med]
53
+ else
54
+ confidence = CONFIDENCE[:high]
55
+ end
56
+
57
+ warn :model => model[:name],
58
+ :warning_type => "Remote Code Execution",
59
+ :message => "Serialized attributes are vulnerable in Rails #{tracker.config[:rails_version]}, upgrade to #{@upgrade_version} or patch.",
60
+ :confidence => confidence,
61
+ :link => "https://groups.google.com/d/topic/rubyonrails-security/KtmwSbEpzrU/discussion"
62
+ end
63
+ end
64
+ end
@@ -8,7 +8,7 @@ class Brakeman::CheckSend < Brakeman::BaseCheck
8
8
 
9
9
  def run_check
10
10
  Brakeman.debug("Finding instances of #send")
11
- calls = tracker.find_call :method => :send
11
+ calls = tracker.find_call :methods => [:send, :try, :__send__, :public_send]
12
12
 
13
13
  calls.each do |call|
14
14
  process_result call
@@ -17,7 +17,7 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
17
17
  @rails_version = tracker.config[:rails_version]
18
18
 
19
19
  @sql_targets = [:all, :average, :calculate, :count, :count_by_sql, :exists?,
20
- :find, :find_by_sql, :first, :last, :maximum, :minimum, :sum]
20
+ :find, :find_by_sql, :first, :last, :maximum, :minimum, :pluck, :sum, :update_all]
21
21
 
22
22
  if tracker.options[:rails3]
23
23
  @sql_targets.concat [:from, :group, :having, :joins, :lock, :order, :reorder, :select, :where]
@@ -138,9 +138,15 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
138
138
  end
139
139
 
140
140
  def check_rails_version_for_cve_2013_0155
141
- if version_between?("2.0.0", "2.3.15") || version_between?("3.0.0", "3.0.18") || version_between?("3.1.0", "3.1.9") || version_between?("3.2.0", "3.2.10")
141
+ if version_between?("3.0.0", "3.0.18") || version_between?("3.1.0", "3.1.9") || version_between?("3.2.0", "3.2.10")
142
+ message = 'All versions of Rails before 3.0.19, 3.1.10, and 3.2.11 contain a SQL Injection Vulnerability: CVE-2013-0155; Upgrade to 3.2.11, 3.1.10, 3.0.19'
143
+ elsif version_between?("2.0.0", "2.3.15")
144
+ message = "Rails #{@rails_version} contains a SQL Injection Vulnerability: CVE-2013-0155; Upgrade to 2.3.16"
145
+ end
146
+
147
+ if message
142
148
  warn :warning_type => 'SQL Injection',
143
- :message => 'All versions of Rails before 3.0.19, 3.1.10, and 3.2.11 contain a SQL Injection Vulnerability: CVE-2013-0155; Upgrade to 3.2.11, 3.1.10, 3.0.19',
149
+ :message => message,
144
150
  :confidence => CONFIDENCE[:high],
145
151
  :file => gemfile_or_environment,
146
152
  :link_path => "https://groups.google.com/d/topic/rubyonrails-security/c7jT-EeN9eI/discussion"
@@ -229,6 +235,10 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
229
235
  unsafe_sql? call.first_arg
230
236
  when :lock
231
237
  check_lock_arguments call.first_arg
238
+ when :pluck
239
+ unsafe_sql? call.first_arg
240
+ when :update_all
241
+ check_update_all_arguments call.args
232
242
  else
233
243
  Brakeman.debug "Unhandled SQL method: #{method}"
234
244
  end
@@ -372,6 +382,15 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
372
382
  end
373
383
  end
374
384
 
385
+ def check_update_all_arguments args
386
+ args.each do |arg|
387
+ res = unsafe_sql? arg
388
+ return res if res
389
+ end
390
+
391
+ nil
392
+ end
393
+
375
394
  #Model#lock essentially only cares about strings. But those strings can be
376
395
  #any SQL fragment. This does not apply to all databases. (For those who do not
377
396
  #support it, the lock method does nothing).
@@ -7,7 +7,9 @@ class Brakeman::CheckYAMLLoad < Brakeman::BaseCheck
7
7
  @description = "Checks for uses of YAML.load"
8
8
 
9
9
  def run_check
10
- tracker.find_call(:target => :YAML, :method => :load).each do |result|
10
+ yaml_methods = [:load, :load_documents, :load_stream, :parse_documents, :parse_stream]
11
+
12
+ tracker.find_call(:target => :YAML, :methods => yaml_methods ).each do |result|
11
13
  check_yaml_load result
12
14
  end
13
15
  end
@@ -17,6 +19,7 @@ class Brakeman::CheckYAMLLoad < Brakeman::BaseCheck
17
19
  add_result result
18
20
 
19
21
  arg = result[:call].first_arg
22
+ method = result[:call].method
20
23
 
21
24
  if input = has_immediate_user_input?(arg)
22
25
  confidence = CONFIDENCE[:high]
@@ -38,7 +41,7 @@ class Brakeman::CheckYAMLLoad < Brakeman::BaseCheck
38
41
  "user input"
39
42
  end
40
43
 
41
- message = "YAML.load called with #{input_type}"
44
+ message = "YAML.#{method} called with #{input_type}"
42
45
 
43
46
  warn :result => result,
44
47
  :warning_type => "Remote Code Execution",
@@ -38,6 +38,7 @@ class Brakeman::CheckYAMLParsing < Brakeman::BaseCheck
38
38
  warn :warning_type => "Remote Code Execution",
39
39
  :message => message,
40
40
  :confidence => CONFIDENCE[:high],
41
+ :file => gemfile_or_environment,
41
42
  :link_path => "https://groups.google.com/d/topic/rubyonrails-security/61bkgvnSGTQ/discussion"
42
43
  end
43
44
  end
@@ -14,6 +14,7 @@ class Brakeman::GemProcessor < Brakeman::BaseProcessor
14
14
 
15
15
  if gem_lock
16
16
  get_rails_version gem_lock
17
+ get_json_version gem_lock
17
18
  elsif @tracker.config[:gems][:rails] =~ /(\d+.\d+.\d+)/
18
19
  @tracker.config[:rails_version] = $1
19
20
  end
@@ -40,9 +41,18 @@ class Brakeman::GemProcessor < Brakeman::BaseProcessor
40
41
  exp
41
42
  end
42
43
 
44
+ #Need to implement generic gem version check
45
+ def get_version name, gem_lock
46
+ match = gem_lock.match(/\s#{name} \((\d+.\d+.\d+.*)\)$/)
47
+ match[1] if match
48
+ end
49
+
43
50
  def get_rails_version gem_lock
44
- if gem_lock =~ /\srails \((\d+.\d+.\d+.*)\)$/
45
- @tracker.config[:rails_version] = $1
46
- end
51
+ @tracker.config[:rails_version] = get_version("rails", gem_lock)
52
+ end
53
+
54
+ def get_json_version gem_lock
55
+ @tracker.config[:gems][:json] = get_version("json", gem_lock)
56
+ @tracker.config[:gems][:json_pure] = get_version("json_pure", gem_lock)
47
57
  end
48
58
  end
@@ -13,7 +13,7 @@ module Brakeman::RenderHelper
13
13
  when :default
14
14
  begin
15
15
  process_template template_name, exp[3]
16
- rescue ArgumentError => e
16
+ rescue ArgumentError
17
17
  Brakeman.debug "Problem processing render: #{exp}"
18
18
  end
19
19
  when :partial, :layout
@@ -18,8 +18,26 @@ else
18
18
  # CSV is now FasterCSV in ruby 1.9
19
19
  end
20
20
 
21
+ #MultiJson interface changed in 1.3.0, but need
22
+ #to support older MultiJson for Rails 3.1.
23
+ if MultiJson.respond_to? :default_adapter
24
+ mj_engine = MultiJson.default_adapter
25
+ else
26
+ mj_engine = MultiJson.default_engine
27
+
28
+ module MultiJson
29
+ def self.dump *args
30
+ encode *args
31
+ end
32
+
33
+ def self.load *args
34
+ decode *args
35
+ end
36
+ end
37
+ end
38
+
21
39
  #This is so OkJson will work with symbol values
22
- if MultiJson.default_adapter == :ok_json
40
+ if mj_engine == :ok_json
23
41
  class Symbol
24
42
  def to_json
25
43
  self.to_s.inspect
@@ -694,11 +712,13 @@ HEADER
694
712
  :brakeman_version => Brakeman::Version
695
713
  }
696
714
 
697
- MultiJson.dump({
715
+ report_info = {
698
716
  :scan_info => scan_info,
699
717
  :warnings => warnings,
700
718
  :errors => errors
701
- }, :pretty => true)
719
+ }
720
+
721
+ MultiJson.dump(report_info, :pretty => true)
702
722
  end
703
723
 
704
724
  def all_warnings
@@ -1,3 +1,3 @@
1
1
  module Brakeman
2
- Version = "1.9.1"
2
+ Version = "1.9.2"
3
3
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: brakeman
3
3
  version: !ruby/object:Gem::Version
4
- hash: 49
4
+ hash: 55
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
8
  - 9
9
- - 1
10
- version: 1.9.1
9
+ - 2
10
+ version: 1.9.2
11
11
  platform: ruby
12
12
  authors:
13
13
  - Justin Collins
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2013-01-19 00:00:00 Z
18
+ date: 2013-02-14 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: ruby_parser
@@ -146,11 +146,11 @@ dependencies:
146
146
  requirements:
147
147
  - - ~>
148
148
  - !ruby/object:Gem::Version
149
- hash: 9
149
+ hash: 11
150
150
  segments:
151
151
  - 1
152
- - 3
153
- version: "1.3"
152
+ - 2
153
+ version: "1.2"
154
154
  type: :runtime
155
155
  version_requirements: *id009
156
156
  description: Brakeman detects security vulnerabilities in Ruby on Rails applications via static analysis.
@@ -181,6 +181,7 @@ files:
181
181
  - lib/brakeman/checks/check_select_vulnerability.rb
182
182
  - lib/brakeman/checks/check_escape_function.rb
183
183
  - lib/brakeman/checks/check_single_quotes.rb
184
+ - lib/brakeman/checks/check_model_serialize.rb
184
185
  - lib/brakeman/checks/check_basic_auth.rb
185
186
  - lib/brakeman/checks/check_safe_buffer_manipulation.rb
186
187
  - lib/brakeman/checks/check_forgery_setting.rb
@@ -204,6 +205,7 @@ files:
204
205
  - lib/brakeman/checks/check_digest_dos.rb
205
206
  - lib/brakeman/checks/check_render.rb
206
207
  - lib/brakeman/checks/check_send_file.rb
208
+ - lib/brakeman/checks/check_json_parsing.rb
207
209
  - lib/brakeman/checks/check_execute.rb
208
210
  - lib/brakeman/checks/check_translate_bug.rb
209
211
  - lib/brakeman/checks/check_default_routes.rb