brakeman 1.6.2 → 1.7.0

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 (33) hide show
  1. data/lib/brakeman/checks.rb +14 -5
  2. data/lib/brakeman/checks/base_check.rb +19 -7
  3. data/lib/brakeman/checks/check_digest_dos.rb +37 -0
  4. data/lib/brakeman/checks/check_escape_function.rb +2 -1
  5. data/lib/brakeman/checks/check_file_access.rb +40 -23
  6. data/lib/brakeman/checks/check_filter_skipping.rb +2 -1
  7. data/lib/brakeman/checks/check_forgery_setting.rb +7 -4
  8. data/lib/brakeman/checks/check_link_to.rb +6 -3
  9. data/lib/brakeman/checks/check_link_to_href.rb +4 -2
  10. data/lib/brakeman/checks/check_nested_attributes.rb +3 -2
  11. data/lib/brakeman/checks/check_quote_table_name.rb +2 -1
  12. data/lib/brakeman/checks/check_response_splitting.rb +2 -1
  13. data/lib/brakeman/checks/check_sql.rb +10 -7
  14. data/lib/brakeman/checks/check_strip_tags.rb +2 -1
  15. data/lib/brakeman/checks/check_validation_regex.rb +1 -1
  16. data/lib/brakeman/checks/check_without_protection.rb +2 -9
  17. data/lib/brakeman/format/style.css +4 -0
  18. data/lib/brakeman/processors/alias_processor.rb +10 -10
  19. data/lib/brakeman/processors/base_processor.rb +4 -11
  20. data/lib/brakeman/processors/controller_processor.rb +9 -1
  21. data/lib/brakeman/processors/lib/rails3_route_processor.rb +96 -31
  22. data/lib/brakeman/processors/lib/render_helper.rb +3 -2
  23. data/lib/brakeman/processors/lib/route_helper.rb +21 -0
  24. data/lib/brakeman/processors/library_processor.rb +10 -1
  25. data/lib/brakeman/processors/model_processor.rb +8 -1
  26. data/lib/brakeman/processors/template_processor.rb +0 -1
  27. data/lib/brakeman/report.rb +10 -0
  28. data/lib/brakeman/scanner.rb +2 -0
  29. data/lib/brakeman/util.rb +1 -2
  30. data/lib/brakeman/version.rb +1 -1
  31. data/lib/brakeman/warning.rb +19 -1
  32. data/lib/ruby_parser/bm_sexp_processor.rb +231 -0
  33. metadata +81 -79
@@ -97,7 +97,12 @@ class Brakeman::Checks
97
97
  Brakeman.notify " - #{check_name}"
98
98
 
99
99
  check = c.new(tracker)
100
- check.run_check
100
+
101
+ begin
102
+ check.run_check
103
+ rescue Exception => e
104
+ tracker.error e
105
+ end
101
106
 
102
107
  check.warnings.each do |w|
103
108
  check_runner.add_warning w
@@ -115,6 +120,7 @@ class Brakeman::Checks
115
120
  #Run checks in parallel threads
116
121
  def self.run_checks_parallel tracker
117
122
  threads = []
123
+ error_mutex = Mutex.new
118
124
 
119
125
  check_runner = self.new :min_confidence => tracker.options[:min_confidence]
120
126
 
@@ -128,14 +134,17 @@ class Brakeman::Checks
128
134
  Brakeman.notify " - #{check_name}"
129
135
 
130
136
  threads << Thread.new do
137
+ check = c.new(tracker)
138
+
131
139
  begin
132
- check = c.new(tracker)
133
140
  check.run_check
134
- check.warnings
135
141
  rescue Exception => e
136
- Brakeman.notify "[#{check_name}] #{e}"
137
- []
142
+ error_mutex.synchronize do
143
+ tracker.error e
144
+ end
138
145
  end
146
+
147
+ check.warnings
139
148
  end
140
149
 
141
150
  #Maintain list of which checks were run
@@ -1,11 +1,10 @@
1
- require 'sexp_processor'
2
1
  require 'brakeman/processors/output_processor'
3
2
  require 'brakeman/processors/lib/processor_helper'
4
3
  require 'brakeman/warning'
5
4
  require 'brakeman/util'
6
5
 
7
6
  #Basis of vulnerability checks.
8
- class Brakeman::BaseCheck < SexpProcessor
7
+ class Brakeman::BaseCheck < Brakeman::SexpProcessor
9
8
  include Brakeman::ProcessorHelper
10
9
  include Brakeman::Util
11
10
  attr_reader :tracker, :warnings
@@ -24,11 +23,6 @@ class Brakeman::BaseCheck < SexpProcessor
24
23
  @current_set = nil
25
24
  @current_template = @current_module = @current_class = @current_method = nil
26
25
  @mass_assign_disabled = nil
27
- self.strict = false
28
- self.auto_shift_type = false
29
- self.require_empty = false
30
- self.default_method = :process_default
31
- self.warn_on_default = false
32
26
  end
33
27
 
34
28
  #Add result to result list, which is used to check for duplicates
@@ -154,6 +148,7 @@ class Brakeman::BaseCheck < SexpProcessor
154
148
  @mass_assign_disabled = false
155
149
 
156
150
  if version_between?("3.1.0", "4.0.0") and
151
+ tracker.config[:rails] and
157
152
  tracker.config[:rails][:active_record] and
158
153
  tracker.config[:rails][:active_record][:whitelist_attributes] == Sexp.new(:true)
159
154
 
@@ -301,6 +296,9 @@ class Brakeman::BaseCheck < SexpProcessor
301
296
  when :if
302
297
  (sexp? exp[2] and has_immediate_user_input? exp[2]) or
303
298
  (sexp? exp[3] and has_immediate_user_input? exp[3])
299
+ when :or
300
+ has_immediate_user_input? exp[1] or
301
+ has_immediate_user_input? exp[2]
304
302
  else
305
303
  false
306
304
  end
@@ -456,4 +454,18 @@ class Brakeman::BaseCheck < SexpProcessor
456
454
  def self.description
457
455
  @description
458
456
  end
457
+
458
+ def active_record_models
459
+ return @active_record_models if @active_record_models
460
+
461
+ @active_record_models = {}
462
+
463
+ tracker.models.each do |name, model|
464
+ if ancestor? model, :"ActiveRecord::Base"
465
+ @active_record_models[name] = model
466
+ end
467
+ end
468
+
469
+ @active_record_models
470
+ end
459
471
  end
@@ -0,0 +1,37 @@
1
+ require 'brakeman/checks/base_check'
2
+
3
+ class Brakeman::CheckDigestDoS < Brakeman::BaseCheck
4
+ Brakeman::Checks.add self
5
+
6
+ @description = "Checks for digest authentication DoS vulnerability"
7
+
8
+ def run_check
9
+ message = "Vulnerability in digest authentication (CVE-2012-3424). Upgrade to Rails version "
10
+
11
+ if version_between? "3.0.0", "3.0.15"
12
+ message << "3.0.16"
13
+ elsif version_between? "3.1.0", "3.1.6"
14
+ message << "3.1.7"
15
+ elsif version_between? "3.2.0", "3.2.5"
16
+ message << "3.2.7"
17
+ else
18
+ return
19
+ end
20
+
21
+ if with_http_digest?
22
+ confidence = CONFIDENCE[:high]
23
+ else
24
+ confidence = CONFIDENCE[:low]
25
+ end
26
+
27
+ warn :warning_type => "Denial of Service",
28
+ :message => message,
29
+ :confidence => confidence,
30
+ :link_path => "https://groups.google.com/d/topic/rubyonrails-security/vxJjrc15qYM/discussion",
31
+ :file => gemfile_or_environment
32
+ end
33
+
34
+ def with_http_digest?
35
+ not tracker.find_call(:target => false, :method => [:authenticate_or_request_with_http_digest, :authenticate_with_http_digest]).empty?
36
+ end
37
+ end
@@ -13,7 +13,8 @@ class Brakeman::CheckEscapeFunction < Brakeman::BaseCheck
13
13
  warn :warning_type => 'Cross Site Scripting',
14
14
  :message => 'Versions before 2.3.14 have a vulnerability in escape method when used with Ruby 1.8: CVE-2011-2931',
15
15
  :confidence => CONFIDENCE[:high],
16
- :file => gemfile_or_environment
16
+ :file => gemfile_or_environment,
17
+ :link_path => "https://groups.google.com/d/topic/rubyonrails-security/Vr_7WSOrEZU/discussion"
17
18
  end
18
19
  end
19
20
  end
@@ -9,7 +9,7 @@ class Brakeman::CheckFileAccess < Brakeman::BaseCheck
9
9
 
10
10
  def run_check
11
11
  Brakeman.debug "Finding possible file access"
12
- methods = tracker.find_call :targets => [:Dir, :File, :IO, :Kernel, :"Net::FTP", :"Net::HTTP", :PStore, :Pathname, :Shell, :YAML], :methods => [:[], :chdir, :chroot, :delete, :entries, :foreach, :glob, :install, :lchmod, :lchown, :link, :load, :load_file, :makedirs, :move, :new, :open, :read, :read_lines, :rename, :rmdir, :safe_unlink, :symlink, :syscopy, :sysopen, :truncate, :unlink]
12
+ methods = tracker.find_call :targets => [:Dir, :File, :IO, :Kernel, :"Net::FTP", :"Net::HTTP", :PStore, :Pathname, :Shell, :YAML], :methods => [:[], :chdir, :chroot, :delete, :entries, :foreach, :glob, :install, :lchmod, :lchown, :link, :load, :load_file, :makedirs, :move, :new, :open, :read, :readlines, :rename, :rmdir, :safe_unlink, :symlink, :syscopy, :sysopen, :truncate, :unlink]
13
13
 
14
14
  Brakeman.debug "Finding calls to load()"
15
15
  methods.concat tracker.find_call :target => false, :method => :load
@@ -24,32 +24,49 @@ class Brakeman::CheckFileAccess < Brakeman::BaseCheck
24
24
  end
25
25
 
26
26
  def process_result result
27
+ return if duplicate? result
28
+ add_result result
27
29
  call = result[:call]
28
-
29
30
  file_name = call[3][1]
30
31
 
31
- if input = include_user_input?(file_name)
32
- unless duplicate? result
33
- add_result result
34
-
35
- case input.type
36
- when :params
37
- message = "Parameter"
38
- when :cookies
39
- message = "Cookie"
40
- else
41
- message = "User input"
42
- end
43
-
44
- message << " value used in file name"
45
-
46
- warn :result => result,
47
- :warning_type => "File Access",
48
- :message => message,
49
- :confidence => CONFIDENCE[:high],
50
- :code => call,
51
- :user_input => input.match
32
+ if match = has_immediate_user_input?(file_name)
33
+ confidence = CONFIDENCE[:high]
34
+ elsif match = has_immediate_model?(file_name)
35
+ match = Match.new(:model, match)
36
+ confidence = CONFIDENCE[:med]
37
+ elsif tracker.options[:check_arguments] and
38
+ match = include_user_input?(file_name)
39
+
40
+ #Check for string building in file name
41
+ if call?(file_name) and (file_name[2] == :+ or file_name[2] == :<<)
42
+ confidence = CONFIDENCE[:high]
43
+ else
44
+ confidence = CONFIDENCE[:low]
52
45
  end
53
46
  end
47
+
48
+ if match
49
+ case match.type
50
+ when :params
51
+ message = "Parameter"
52
+ when :cookies
53
+ message = "Cookie"
54
+ when :request
55
+ message = "Request"
56
+ when :model
57
+ message = "Model attribute"
58
+ else
59
+ message = "User input"
60
+ end
61
+
62
+ message << " value used in file name"
63
+
64
+ warn :result => result,
65
+ :warning_type => "File Access",
66
+ :message => message,
67
+ :confidence => confidence,
68
+ :code => call,
69
+ :user_input => match.match
70
+ end
54
71
  end
55
72
  end
@@ -13,7 +13,8 @@ class Brakeman::CheckFilterSkipping < Brakeman::BaseCheck
13
13
  warn :warning_type => "Default Routes",
14
14
  :message => "Versions before 3.0.10 have a vulnerability which allows filters to be bypassed: CVE-2011-2929",
15
15
  :confidence => CONFIDENCE[:high],
16
- :file => gemfile_or_environment
16
+ :file => gemfile_or_environment,
17
+ :link_path => "https://groups.google.com/d/topic/rubyonrails-security/NCCsca7TEtY/discussion"
17
18
  end
18
19
  end
19
20
 
@@ -11,11 +11,12 @@ class Brakeman::CheckForgerySetting < Brakeman::BaseCheck
11
11
 
12
12
  def run_check
13
13
  app_controller = tracker.controllers[:ApplicationController]
14
- if tracker.config[:rails][:action_controller] and
14
+ if tracker.config[:rails] and
15
+ tracker.config[:rails][:action_controller] and
15
16
  tracker.config[:rails][:action_controller][:allow_forgery_protection] == Sexp.new(:false)
16
17
 
17
18
  warn :controller => :ApplicationController,
18
- :warning_type => "Cross Site Request Forgery",
19
+ :warning_type => "Cross-Site Request Forgery",
19
20
  :message => "Forgery protection is disabled",
20
21
  :confidence => CONFIDENCE[:high]
21
22
 
@@ -32,7 +33,8 @@ class Brakeman::CheckForgerySetting < Brakeman::BaseCheck
32
33
  :warning_type => "Cross-Site Request Forgery",
33
34
  :message => "CSRF protection is flawed in unpatched versions of Rails #{tracker.config[:rails_version]} (CVE-2011-0447). Upgrade to 2.3.11 or apply patches as needed",
34
35
  :confidence => CONFIDENCE[:high],
35
- :file => gemfile_or_environment
36
+ :file => gemfile_or_environment,
37
+ :link_path => "https://groups.google.com/d/topic/rubyonrails-security/LZWjzCPgNmU/discussion"
36
38
 
37
39
  elsif version_between? "3.0.0", "3.0.3"
38
40
 
@@ -40,7 +42,8 @@ class Brakeman::CheckForgerySetting < Brakeman::BaseCheck
40
42
  :warning_type => "Cross-Site Request Forgery",
41
43
  :message => "CSRF protection is flawed in unpatched versions of Rails #{tracker.config[:rails_version]} (CVE-2011-0447). Upgrade to 3.0.4 or apply patches as needed",
42
44
  :confidence => CONFIDENCE[:high],
43
- :file => gemfile_or_environment
45
+ :file => gemfile_or_environment,
46
+ :link_path => "https://groups.google.com/d/topic/rubyonrails-security/LZWjzCPgNmU/discussion"
44
47
  end
45
48
  end
46
49
  end
@@ -76,7 +76,8 @@ class Brakeman::CheckLinkTo < Brakeman::CheckCrossSiteScripting
76
76
  :warning_type => "Cross Site Scripting",
77
77
  :message => message,
78
78
  :user_input => input.match,
79
- :confidence => CONFIDENCE[:high]
79
+ :confidence => CONFIDENCE[:high],
80
+ :link_path => "link_to"
80
81
 
81
82
  elsif not tracker.options[:ignore_model_output] and match = has_immediate_model?(arg)
82
83
  method = match[2]
@@ -94,7 +95,8 @@ class Brakeman::CheckLinkTo < Brakeman::CheckCrossSiteScripting
94
95
  :warning_type => "Cross Site Scripting",
95
96
  :message => "Unescaped model attribute in link_to",
96
97
  :user_input => match,
97
- :confidence => confidence
98
+ :confidence => confidence,
99
+ :link_path => "link_to"
98
100
  end
99
101
 
100
102
  elsif @matched
@@ -111,7 +113,8 @@ class Brakeman::CheckLinkTo < Brakeman::CheckCrossSiteScripting
111
113
  :warning_type => "Cross Site Scripting",
112
114
  :message => message,
113
115
  :user_input => @matched.match,
114
- :confidence => CONFIDENCE[:med]
116
+ :confidence => CONFIDENCE[:med],
117
+ :link_path => "link_to"
115
118
  end
116
119
  end
117
120
  end
@@ -57,7 +57,8 @@ class Brakeman::CheckLinkToHref < Brakeman::CheckLinkTo
57
57
  :warning_type => "Cross Site Scripting",
58
58
  :message => message,
59
59
  :user_input => input.match,
60
- :confidence => CONFIDENCE[:high]
60
+ :confidence => CONFIDENCE[:high],
61
+ :link_path => "link_to_href"
61
62
  end
62
63
  elsif has_immediate_model? url_arg
63
64
 
@@ -84,7 +85,8 @@ class Brakeman::CheckLinkToHref < Brakeman::CheckLinkTo
84
85
  :warning_type => "Cross Site Scripting",
85
86
  :message => message,
86
87
  :user_input => @matched.match,
87
- :confidence => CONFIDENCE[:med]
88
+ :confidence => CONFIDENCE[:med],
89
+ :link_path => "link_to_href"
88
90
  end
89
91
  end
90
92
  end
@@ -22,12 +22,13 @@ class Brakeman::CheckNestedAttributes < Brakeman::BaseCheck
22
22
  warn :warning_type => "Nested Attributes",
23
23
  :message => message,
24
24
  :confidence => CONFIDENCE[:high],
25
- :file => gemfile_or_environment
25
+ :file => gemfile_or_environment,
26
+ :link_path => "https://groups.google.com/d/topic/rubyonrails-security/-fkT0yja_gw/discussion"
26
27
  end
27
28
  end
28
29
 
29
30
  def uses_nested_attributes?
30
- tracker.models.each do |name, model|
31
+ active_record_models.each do |name, model|
31
32
  return true if model[:options][:accepts_nested_attributes_for]
32
33
  end
33
34
 
@@ -26,7 +26,8 @@ class Brakeman::CheckQuoteTableName < Brakeman::BaseCheck
26
26
  warn :warning_type => "SQL Injection",
27
27
  :message => message,
28
28
  :confidence => confidence,
29
- :file => gemfile_or_environment
29
+ :file => gemfile_or_environment,
30
+ :link_path => "https://groups.google.com/d/topic/rubyonrails-security/ah5HN0S8OJs/discussion"
30
31
  end
31
32
  end
32
33
 
@@ -13,7 +13,8 @@ class Brakeman::CheckResponseSplitting < Brakeman::BaseCheck
13
13
  warn :warning_type => "Response Splitting",
14
14
  :message => "Versions before 2.3.14 have a vulnerability content type handling allowing injection of headers: CVE-2011-3186",
15
15
  :confidence => CONFIDENCE[:med],
16
- :file => gemfile_or_environment
16
+ :file => gemfile_or_environment,
17
+ :link_path => "https://groups.google.com/d/topic/rubyonrails-security/b_yTveAph2g/discussion"
17
18
  end
18
19
  end
19
20
  end
@@ -24,7 +24,7 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
24
24
  end
25
25
 
26
26
  Brakeman.debug "Finding possible SQL calls on models"
27
- calls = tracker.find_call :targets => tracker.models.keys,
27
+ calls = tracker.find_call :targets => active_record_models.keys,
28
28
  :methods => @sql_targets,
29
29
  :chained => true
30
30
 
@@ -57,7 +57,7 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
57
57
  scope_calls = []
58
58
 
59
59
  if version_between? "2.1.0", "3.0.9"
60
- tracker.models.each do |name, model|
60
+ active_record_models.each do |name, model|
61
61
  if model[:options][:named_scope]
62
62
  model[:options][:named_scope].each do |args|
63
63
  call = Sexp.new(:call, nil, :named_scope, args).line(args.line)
@@ -66,7 +66,7 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
66
66
  end
67
67
  end
68
68
  elsif version_between? "3.1.0", "3.9.9"
69
- tracker.models.each do |name, model|
69
+ active_record_models.each do |name, model|
70
70
  if model[:options][:scope]
71
71
  model[:options][:scope].each do |args|
72
72
  second_arg = args[2]
@@ -94,7 +94,8 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
94
94
  warn :warning_type => 'SQL Injection',
95
95
  :message => 'All versions of Rails before 3.0.13, 3.1.5, and 3.2.5 contain a SQL Query Generation Vulnerability: CVE-2012-2660; Upgrade to 3.2.5, 3.1.5, 3.0.13',
96
96
  :confidence => CONFIDENCE[:high],
97
- :file => gemfile_or_environment
97
+ :file => gemfile_or_environment,
98
+ :link_path => "https://groups.google.com/d/topic/rubyonrails-security/8SA-M3as7A8/discussion"
98
99
  end
99
100
  end
100
101
 
@@ -103,7 +104,8 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
103
104
  warn :warning_type => 'SQL Injection',
104
105
  :message => 'All versions of Rails before 3.0.13, 3.1.5, and 3.2.5 contain a SQL Injection Vulnerability: CVE-2012-2661; Upgrade to 3.2.5, 3.1.5, 3.0.13',
105
106
  :confidence => CONFIDENCE[:high],
106
- :file => gemfile_or_environment
107
+ :file => gemfile_or_environment,
108
+ :link_path => "https://groups.google.com/d/topic/rubyonrails-security/dUaiOOGWL1k/discussion"
107
109
  end
108
110
  end
109
111
 
@@ -112,7 +114,8 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
112
114
  warn :warning_type => 'SQL Injection',
113
115
  :message => 'All versions of Rails before 3.0.14, 3.1.6, and 3.2.6 contain SQL Injection Vulnerabilities: CVE-2012-2694 and CVE-2012-2695; Upgrade to 3.2.6, 3.1.6, 3.0.14',
114
116
  :confidence => CONFIDENCE[:high],
115
- :file => gemfile_or_environment
117
+ :file => gemfile_or_environment,
118
+ :link_path => "https://groups.google.com/d/topic/rubyonrails-security/l4L0TEVAz1k/discussion"
116
119
  end
117
120
  end
118
121
 
@@ -270,7 +273,7 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
270
273
  if node_type? arg, :arglist
271
274
  if arg.length > 2 and node_type? arg[1], :string_interp, :dstr
272
275
  # Model.where("blah = ?", blah)
273
- return string_interp arg[1]
276
+ return check_string_interp arg[1]
274
277
  else
275
278
  arg = arg[1]
276
279
  end
@@ -20,7 +20,8 @@ class Brakeman::CheckStripTags < Brakeman::BaseCheck
20
20
  warn :warning_type => "Cross Site Scripting",
21
21
  :message => message,
22
22
  :confidence => CONFIDENCE[:high],
23
- :file => gemfile_or_environment
23
+ :file => gemfile_or_environment,
24
+ :link_path => "https://groups.google.com/d/topic/rubyonrails-security/K5EwdJt06hI/discussion"
24
25
  end
25
26
  end
26
27
 
@@ -15,7 +15,7 @@ class Brakeman::CheckValidationRegex < Brakeman::BaseCheck
15
15
  WITH = Sexp.new(:lit, :with)
16
16
 
17
17
  def run_check
18
- tracker.models.each do |name, model|
18
+ active_record_models.each do |name, model|
19
19
  @current_model = name
20
20
  format_validations = model[:options][:validates_format_of]
21
21
  if format_validations