brakeman 1.6.2 → 1.7.0

Sign up to get free protection for your applications and to get access to all the features.
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