actionpack 2.3.10 → 2.3.11

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionpack might be problematic. Click here for more details.

data/CHANGELOG CHANGED
@@ -1,3 +1,7 @@
1
+ *2.3.11 (February 9, 2011)*
2
+
3
+ * Two security fixes. CVE-2011-0446, CVE-2011-0447
4
+
1
5
  *2.3.10 (October 15, 2010)*
2
6
 
3
7
  *2.3.9 (September 4, 2010)*
data/Rakefile CHANGED
@@ -79,7 +79,7 @@ spec = Gem::Specification.new do |s|
79
79
  s.has_rdoc = true
80
80
  s.requirements << 'none'
81
81
 
82
- s.add_dependency('activesupport', '= 2.3.10' + PKG_BUILD)
82
+ s.add_dependency('activesupport', '= 2.3.11' + PKG_BUILD)
83
83
  s.add_dependency('rack', '~> 1.1.0')
84
84
 
85
85
  s.require_path = 'lib'
@@ -60,7 +60,7 @@ module ActionController #:nodoc:
60
60
  attr_reader :controller
61
61
 
62
62
  def initialize(controller)
63
- @controller, @cookies = controller, controller.request.cookies
63
+ @controller, @cookies, @secure = controller, controller.request.cookies, controller.request.ssl?
64
64
  super()
65
65
  update(@cookies)
66
66
  end
@@ -81,7 +81,7 @@ module ActionController #:nodoc:
81
81
 
82
82
  options[:path] = "/" unless options.has_key?(:path)
83
83
  super(key.to_s, options[:value])
84
- @controller.response.set_cookie(key, options)
84
+ @controller.response.set_cookie(key, options) if write_cookie?(options)
85
85
  end
86
86
 
87
87
  # Removes the cookie on the client machine by setting the value to an empty string
@@ -126,6 +126,12 @@ module ActionController #:nodoc:
126
126
  def signed
127
127
  @signed ||= SignedCookieJar.new(self)
128
128
  end
129
+
130
+ private
131
+
132
+ def write_cookie?(cookie)
133
+ @secure || !cookie[:secure] || defined?(Rails.env) && Rails.env.development?
134
+ end
129
135
  end
130
136
 
131
137
  class PermanentCookieJar < CookieJar #:nodoc:
@@ -76,7 +76,11 @@ module ActionController #:nodoc:
76
76
  protected
77
77
  # The actual before_filter that is used. Modify this to change how you handle unverified requests.
78
78
  def verify_authenticity_token
79
- verified_request? || raise(ActionController::InvalidAuthenticityToken)
79
+ verified_request? || handle_unverified_request
80
+ end
81
+
82
+ def handle_unverified_request
83
+ reset_session
80
84
  end
81
85
 
82
86
  # Returns true or false if a request is verified. Checks:
@@ -85,11 +89,10 @@ module ActionController #:nodoc:
85
89
  # * is it a GET request? Gets should be safe and idempotent
86
90
  # * Does the form_authenticity_token match the given token value from the params?
87
91
  def verified_request?
88
- !protect_against_forgery? ||
89
- request.method == :get ||
90
- request.xhr? ||
91
- !verifiable_request_format? ||
92
- form_authenticity_token == form_authenticity_param
92
+ !protect_against_forgery? ||
93
+ request.get? ||
94
+ form_authenticity_token == form_authenticity_param ||
95
+ form_authenticity_token == request.headers['X-CSRF-Token']
93
96
  end
94
97
 
95
98
  def form_authenticity_param
@@ -195,22 +195,8 @@ module ActionController
195
195
  request_cookies = env["rack.request.cookie_hash"]
196
196
 
197
197
  if (request_cookies.nil? || request_cookies[@key] != sid) || options[:expire_after]
198
- cookie = Rack::Utils.escape(@key) + '=' + Rack::Utils.escape(sid)
199
- cookie << "; domain=#{options[:domain]}" if options[:domain]
200
- cookie << "; path=#{options[:path]}" if options[:path]
201
- if options[:expire_after]
202
- expiry = Time.now + options[:expire_after]
203
- cookie << "; expires=#{expiry.httpdate}"
204
- end
205
- cookie << "; secure" if options[:secure]
206
- cookie << "; HttpOnly" if options[:httponly]
207
-
208
- headers = response[1]
209
- unless headers[SET_COOKIE].blank?
210
- headers[SET_COOKIE] << "\n#{cookie}"
211
- else
212
- headers[SET_COOKIE] = cookie
213
- end
198
+ cookie = {:value => sid}
199
+ Rack::Utils.set_cookie_header!(response[1], @key, cookie.merge(options))
214
200
  end
215
201
  end
216
202
 
@@ -52,7 +52,6 @@ module ActionController
52
52
 
53
53
  ENV_SESSION_KEY = "rack.session".freeze
54
54
  ENV_SESSION_OPTIONS_KEY = "rack.session.options".freeze
55
- HTTP_SET_COOKIE = "Set-Cookie".freeze
56
55
 
57
56
  # Raised when storing more than 4K of session data.
58
57
  class CookieOverflow < StandardError; end
@@ -116,9 +115,7 @@ module ActionController
116
115
  cookie[:expires] = Time.now + options[:expire_after]
117
116
  end
118
117
 
119
- cookie = build_cookie(@key, cookie.merge(options))
120
- headers[HTTP_SET_COOKIE] = [] if headers[HTTP_SET_COOKIE].blank?
121
- headers[HTTP_SET_COOKIE] << cookie
118
+ Rack::Utils.set_cookie_header!(headers, @key, cookie.merge(options))
122
119
  end
123
120
 
124
121
  [status, headers, body]
@@ -130,26 +127,6 @@ module ActionController
130
127
  env[ENV_SESSION_KEY] = AbstractStore::SessionHash.new(self, env)
131
128
  env[ENV_SESSION_OPTIONS_KEY] = AbstractStore::OptionsHash.new(self, env, @default_options)
132
129
  end
133
-
134
- # Should be in Rack::Utils soon
135
- def build_cookie(key, value)
136
- case value
137
- when Hash
138
- domain = "; domain=" + value[:domain] if value[:domain]
139
- path = "; path=" + value[:path] if value[:path]
140
- # According to RFC 2109, we need dashes here.
141
- # N.B.: cgi.rb uses spaces...
142
- expires = "; expires=" + value[:expires].clone.gmtime.
143
- strftime("%a, %d-%b-%Y %H:%M:%S GMT") if value[:expires]
144
- secure = "; secure" if value[:secure]
145
- httponly = "; HttpOnly" if value[:httponly]
146
- value = value[:value]
147
- end
148
- value = [value] unless Array === value
149
- cookie = Rack::Utils.escape(key) + "=" +
150
- value.map { |v| Rack::Utils.escape(v) }.join("&") +
151
- "#{domain}#{path}#{expires}#{secure}#{httponly}"
152
- end
153
130
 
154
131
  def load_session(env)
155
132
  data = unpacked_cookie_data(env)
@@ -1,6 +1,6 @@
1
1
  begin
2
2
  require_library_or_gem 'memcache'
3
-
3
+ require 'thread'
4
4
  module ActionController
5
5
  module Session
6
6
  class MemCacheStore < AbstractStore
@@ -2,7 +2,7 @@ module ActionPack #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 2
4
4
  MINOR = 3
5
- TINY = 10
5
+ TINY = 11
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -6,6 +6,7 @@ module ActionView #:nodoc:
6
6
  autoload :BenchmarkHelper, 'action_view/helpers/benchmark_helper'
7
7
  autoload :CacheHelper, 'action_view/helpers/cache_helper'
8
8
  autoload :CaptureHelper, 'action_view/helpers/capture_helper'
9
+ autoload :CsrfHelper, 'action_view/helpers/csrf_helper'
9
10
  autoload :DateHelper, 'action_view/helpers/date_helper'
10
11
  autoload :DebugHelper, 'action_view/helpers/debug_helper'
11
12
  autoload :FormHelper, 'action_view/helpers/form_helper'
@@ -38,6 +39,7 @@ module ActionView #:nodoc:
38
39
  include BenchmarkHelper
39
40
  include CacheHelper
40
41
  include CaptureHelper
42
+ include CsrfHelper
41
43
  include DateHelper
42
44
  include DebugHelper
43
45
  include FormHelper
@@ -1,6 +1,7 @@
1
1
  require 'cgi'
2
2
  require 'action_view/helpers/url_helper'
3
3
  require 'action_view/helpers/tag_helper'
4
+ require 'thread'
4
5
 
5
6
  module ActionView
6
7
  module Helpers #:nodoc:
@@ -0,0 +1,14 @@
1
+ module ActionView
2
+ # = Action View CSRF Helper
3
+ module Helpers
4
+ module CsrfHelper
5
+ # Returns a meta tag with the cross-site request forgery protection token
6
+ # for forms to use. Place this in your head.
7
+ def csrf_meta_tag
8
+ if protect_against_forgery?
9
+ %(<meta name="csrf-param" content="#{h(request_forgery_protection_token)}"/>\n<meta name="csrf-token" content="#{h(form_authenticity_token)}"/>).html_safe
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -665,7 +665,7 @@ module ActionView
665
665
  #
666
666
  # The HTML specification says unchecked check boxes are not successful, and
667
667
  # thus web browsers do not send them. Unfortunately this introduces a gotcha:
668
- # if an Invoice model has a +paid+ flag, and in the form that edits a paid
668
+ # if an +Invoice+ model has a +paid+ flag, and in the form that edits a paid
669
669
  # invoice the user unchecks its check box, no +paid+ parameter is sent. So,
670
670
  # any mass-assignment idiom like
671
671
  #
@@ -673,12 +673,15 @@ module ActionView
673
673
  #
674
674
  # wouldn't update the flag.
675
675
  #
676
- # To prevent this the helper generates a hidden field with the same name as
677
- # the checkbox after the very check box. So, the client either sends only the
678
- # hidden field (representing the check box is unchecked), or both fields.
679
- # Since the HTML specification says key/value pairs have to be sent in the
680
- # same order they appear in the form and Rails parameters extraction always
681
- # gets the first occurrence of any given key, that works in ordinary forms.
676
+ # To prevent this the helper generates an auxiliary hidden field before
677
+ # the very check box. The hidden field has the same name and its
678
+ # attributes mimick an unchecked check box.
679
+ #
680
+ # This way, the client either sends only the hidden field (representing
681
+ # the check box is unchecked), or both fields. Since the HTML specification
682
+ # says key/value pairs have to be sent in the same order they appear in the
683
+ # form, and parameters extraction gets the last occurrence of any repeated
684
+ # key in the query string, that works for ordinary forms.
682
685
  #
683
686
  # Unfortunately that workaround does not work when the check box goes
684
687
  # within an array-like parameter, as in
@@ -689,22 +692,26 @@ module ActionView
689
692
  # <% end %>
690
693
  #
691
694
  # because parameter name repetition is precisely what Rails seeks to distinguish
692
- # the elements of the array.
695
+ # the elements of the array. For each item with a checked check box you
696
+ # get an extra ghost item with only that attribute, assigned to "0".
697
+ #
698
+ # In that case it is preferable to either use +check_box_tag+ or to use
699
+ # hashes instead of arrays.
693
700
  #
694
701
  # ==== Examples
695
702
  # # Let's say that @post.validated? is 1:
696
703
  # check_box("post", "validated")
697
- # # => <input type="checkbox" id="post_validated" name="post[validated]" value="1" />
698
- # # <input name="post[validated]" type="hidden" value="0" />
704
+ # # => <input name="post[validated]" type="hidden" value="0" />
705
+ # # <input type="checkbox" id="post_validated" name="post[validated]" value="1" />
699
706
  #
700
707
  # # Let's say that @puppy.gooddog is "no":
701
708
  # check_box("puppy", "gooddog", {}, "yes", "no")
702
- # # => <input type="checkbox" id="puppy_gooddog" name="puppy[gooddog]" value="yes" />
703
- # # <input name="puppy[gooddog]" type="hidden" value="no" />
709
+ # # => <input name="puppy[gooddog]" type="hidden" value="no" />
710
+ # # <input type="checkbox" id="puppy_gooddog" name="puppy[gooddog]" value="yes" />
704
711
  #
705
712
  # check_box("eula", "accepted", { :class => 'eula_check' }, "yes", "no")
706
- # # => <input type="checkbox" class="eula_check" id="eula_accepted" name="eula[accepted]" value="yes" />
707
- # # <input name="eula[accepted]" type="hidden" value="no" />
713
+ # # => <input name="eula[accepted]" type="hidden" value="no" />
714
+ # # <input type="checkbox" class="eula_check" id="eula_accepted" name="eula[accepted]" value="yes" />
708
715
  #
709
716
  def check_box(object_name, method, options = {}, checked_value = "1", unchecked_value = "0")
710
717
  InstanceTag.new(object_name, method, self, options.delete(:object)).to_check_box_tag(options, checked_value, unchecked_value)
@@ -471,7 +471,8 @@ module ActionView
471
471
  email_address_obfuscated.gsub!(/\./, html_options.delete("replace_dot")) if html_options.has_key?("replace_dot")
472
472
 
473
473
  if encode == "javascript"
474
- "document.write('#{content_tag("a", name || email_address_obfuscated.html_safe, html_options.merge({ "href" => "mailto:"+email_address+extras }))}');".each_byte do |c|
474
+ html = content_tag("a", name || email_address_obfuscated.html_safe, html_options.merge({ "href" => "mailto:"+html_escape(email_address)+extras }))
475
+ "document.write('#{escape_javascript(html)}');".each_byte do |c|
475
476
  string << sprintf("%%%x", c)
476
477
  end
477
478
  "<script type=\"#{Mime::JS}\">eval(decodeURIComponent('#{string}'))</script>"
@@ -27,7 +27,7 @@ module ActionView
27
27
  def render_partial(view, object = nil, local_assigns = {}, as = nil)
28
28
  object ||= local_assigns[:object] || local_assigns[variable_name]
29
29
 
30
- if object.nil? && view.respond_to?(:controller)
30
+ if object.nil? && !local_assigns_key?(local_assigns) && view.respond_to?(:controller)
31
31
  ivar = :"@#{variable_name}"
32
32
  object =
33
33
  if view.controller.instance_variable_defined?(ivar)
@@ -43,5 +43,11 @@ module ActionView
43
43
 
44
44
  render_template(view, local_assigns)
45
45
  end
46
+
47
+ private
48
+
49
+ def local_assigns_key?(local_assigns)
50
+ local_assigns.key?(:object) || local_assigns.key?(variable_name)
51
+ end
46
52
  end
47
53
  end
@@ -100,11 +100,26 @@ class CookieTest < ActionController::TestCase
100
100
  end
101
101
 
102
102
  def test_setting_cookie_with_secure
103
+ @request.env["HTTPS"] = "on"
103
104
  get :authenticate_with_secure
104
105
  assert_equal ["user_name=david; path=/; secure"], @response.headers["Set-Cookie"]
105
106
  assert_equal({"user_name" => "david"}, @response.cookies)
106
107
  end
107
108
 
109
+ def test_setting_cookie_with_secure_in_development
110
+ with_environment(:development) do
111
+ get :authenticate_with_secure
112
+ assert_equal ["user_name=david; path=/; secure"], @response.headers["Set-Cookie"]
113
+ assert_equal({"user_name" => "david"}, @response.cookies)
114
+ end
115
+ end
116
+
117
+ def test_not_setting_cookie_with_secure
118
+ get :authenticate_with_secure
119
+ assert_not_equal ["user_name=david; path=/; secure"], @response.headers["Set-Cookie"]
120
+ assert_not_equal({"user_name" => "david"}, @response.cookies)
121
+ end
122
+
108
123
  def test_multiple_cookies
109
124
  get :set_multiple_cookies
110
125
  assert_equal 2, @response.cookies.size
@@ -177,4 +192,17 @@ class CookieTest < ActionController::TestCase
177
192
  assert_match %r(#{20.years.from_now.year}), @response.headers["Set-Cookie"].first
178
193
  assert_equal 100, @controller.send(:cookies).signed[:remember_me]
179
194
  end
195
+
196
+ private
197
+ def with_environment(enviroment)
198
+ old_rails = Object.const_get(:Rails) rescue nil
199
+ mod = Object.const_set(:Rails, Module.new)
200
+ (class << mod; self; end).instance_eval do
201
+ define_method(:env) { @_env ||= ActiveSupport::StringInquirer.new(enviroment.to_s) }
202
+ end
203
+ yield
204
+ ensure
205
+ Object.module_eval { remove_const(:Rails) } if defined?(Rails)
206
+ Object.const_set(:Rails, old_rails) if old_rails
207
+ end
180
208
  end
@@ -1,4 +1,5 @@
1
1
  require 'abstract_unit'
2
+ require 'thread'
2
3
 
3
4
  class ReloaderTests < ActiveSupport::TestCase
4
5
  Reloader = ActionController::Reloader
@@ -716,6 +716,11 @@ class TestController < ActionController::Base
716
716
  render :partial => "customer"
717
717
  end
718
718
 
719
+ def partial_with_implicit_local_assignment_and_nil_local
720
+ @customer = Customer.new("Marcel")
721
+ render :partial => "customer", :locals => { :customer => nil }
722
+ end
723
+
719
724
  def render_call_to_partial_with_layout
720
725
  render :action => "calling_partial_with_layout"
721
726
  end
@@ -1543,6 +1548,13 @@ class RenderTest < ActionController::TestCase
1543
1548
  end
1544
1549
  end
1545
1550
 
1551
+ def test_partial_with_implicit_local_assignment_and_nil_local
1552
+ assert_not_deprecated do
1553
+ get :partial_with_implicit_local_assignment_and_nil_local
1554
+ assert_equal "Hello: Anonymous", @response.body
1555
+ end
1556
+ end
1557
+
1546
1558
  def test_render_missing_partial_template
1547
1559
  assert_raise(ActionView::MissingTemplate) do
1548
1560
  get :missing_partial
@@ -23,6 +23,10 @@ module RequestForgeryProtectionActions
23
23
  render :text => 'pwn'
24
24
  end
25
25
 
26
+ def meta
27
+ render :inline => "<%= csrf_meta_tag %>"
28
+ end
29
+
26
30
  def rescue_action(e) raise e end
27
31
  end
28
32
 
@@ -32,6 +36,16 @@ class RequestForgeryProtectionController < ActionController::Base
32
36
  protect_from_forgery :only => :index
33
37
  end
34
38
 
39
+ class RequestForgeryProtectionControllerUsingOldBehaviour < ActionController::Base
40
+ include RequestForgeryProtectionActions
41
+ protect_from_forgery :only => %w(index meta)
42
+
43
+ def handle_unverified_request
44
+ raise(ActionController::InvalidAuthenticityToken)
45
+ end
46
+ end
47
+
48
+
35
49
  class FreeCookieController < RequestForgeryProtectionController
36
50
  self.allow_forgery_protection = false
37
51
 
@@ -54,158 +68,92 @@ end
54
68
  # common test methods
55
69
 
56
70
  module RequestForgeryProtectionTests
57
- def teardown
58
- ActionController::Base.request_forgery_protection_token = nil
59
- end
60
-
71
+ def setup
72
+ @token = "cf50faa3fe97702ca1ae"
61
73
 
62
- def test_should_render_form_with_token_tag
63
- get :index
64
- assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token
65
- end
66
-
67
- def test_should_render_button_to_with_token_tag
68
- get :show_button
69
- assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token
70
- end
71
-
72
- def test_should_render_remote_form_with_only_one_token_parameter
73
- get :remote_form
74
- assert_equal 1, @response.body.scan(@token).size
75
- end
76
-
77
- def test_should_allow_get
78
- get :index
79
- assert_response :success
80
- end
81
-
82
- def test_should_allow_post_without_token_on_unsafe_action
83
- post :unsafe
84
- assert_response :success
85
- end
86
-
87
- def test_should_not_allow_html_post_without_token
88
- @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
89
- assert_raise(ActionController::InvalidAuthenticityToken) { post :index, :format => :html }
74
+ ActiveSupport::SecureRandom.stubs(:base64).returns(@token)
75
+ ActionController::Base.request_forgery_protection_token = :authenticity_token
90
76
  end
91
77
 
92
- def test_should_not_allow_html_put_without_token
93
- @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
94
- assert_raise(ActionController::InvalidAuthenticityToken) { put :index, :format => :html }
95
- end
96
78
 
97
- def test_should_not_allow_html_delete_without_token
98
- @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
99
- assert_raise(ActionController::InvalidAuthenticityToken) { delete :index, :format => :html }
100
- end
101
-
102
- def test_should_allow_api_formatted_post_without_token
103
- assert_nothing_raised do
104
- post :index, :format => 'xml'
79
+ def test_should_render_form_with_token_tag
80
+ assert_not_blocked do
81
+ get :index
105
82
  end
83
+ assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token
106
84
  end
107
85
 
108
- def test_should_not_allow_api_formatted_put_without_token
109
- assert_nothing_raised do
110
- put :index, :format => 'xml'
86
+ def test_should_render_button_to_with_token_tag
87
+ assert_not_blocked do
88
+ get :show_button
111
89
  end
90
+ assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token
112
91
  end
113
92
 
114
- def test_should_allow_api_formatted_delete_without_token
115
- assert_nothing_raised do
116
- delete :index, :format => 'xml'
117
- end
93
+ def test_should_allow_get
94
+ assert_not_blocked { get :index }
118
95
  end
119
96
 
120
- def test_should_not_allow_api_formatted_post_sent_as_url_encoded_form_without_token
121
- assert_raise(ActionController::InvalidAuthenticityToken) do
122
- @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
123
- post :index, :format => 'xml'
124
- end
97
+ def test_should_allow_post_without_token_on_unsafe_action
98
+ assert_not_blocked { post :unsafe }
125
99
  end
126
100
 
127
- def test_should_not_allow_api_formatted_put_sent_as_url_encoded_form_without_token
128
- assert_raise(ActionController::InvalidAuthenticityToken) do
129
- @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
130
- put :index, :format => 'xml'
131
- end
101
+ def test_should_not_allow_post_without_token
102
+ assert_blocked { post :index }
132
103
  end
133
104
 
134
- def test_should_not_allow_api_formatted_delete_sent_as_url_encoded_form_without_token
135
- assert_raise(ActionController::InvalidAuthenticityToken) do
136
- @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
137
- delete :index, :format => 'xml'
138
- end
105
+ def test_should_not_allow_post_without_token_irrespective_of_format
106
+ assert_blocked { post :index, :format=>'xml' }
139
107
  end
140
108
 
141
- def test_should_not_allow_api_formatted_post_sent_as_multipart_form_without_token
142
- assert_raise(ActionController::InvalidAuthenticityToken) do
143
- @request.env['CONTENT_TYPE'] = Mime::MULTIPART_FORM.to_s
144
- post :index, :format => 'xml'
145
- end
109
+ def test_should_not_allow_put_without_token
110
+ assert_blocked { put :index }
146
111
  end
147
112
 
148
- def test_should_not_allow_api_formatted_put_sent_as_multipart_form_without_token
149
- assert_raise(ActionController::InvalidAuthenticityToken) do
150
- @request.env['CONTENT_TYPE'] = Mime::MULTIPART_FORM.to_s
151
- put :index, :format => 'xml'
152
- end
113
+ def test_should_not_allow_delete_without_token
114
+ assert_blocked { delete :index }
153
115
  end
154
116
 
155
- def test_should_not_allow_api_formatted_delete_sent_as_multipart_form_without_token
156
- assert_raise(ActionController::InvalidAuthenticityToken) do
157
- @request.env['CONTENT_TYPE'] = Mime::MULTIPART_FORM.to_s
158
- delete :index, :format => 'xml'
159
- end
160
- end
161
-
162
- def test_should_allow_xhr_post_without_token
163
- assert_nothing_raised { xhr :post, :index }
164
- end
165
-
166
- def test_should_allow_xhr_put_without_token
167
- assert_nothing_raised { xhr :put, :index }
168
- end
169
-
170
- def test_should_allow_xhr_delete_without_token
171
- assert_nothing_raised { xhr :delete, :index }
117
+ def test_should_not_allow_xhr_post_without_token
118
+ assert_blocked { xhr :post, :index }
172
119
  end
173
-
174
- def test_should_allow_xhr_post_with_encoded_form_content_type_without_token
175
- @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
176
- assert_nothing_raised { xhr :post, :index }
177
- end
178
-
120
+
179
121
  def test_should_allow_post_with_token
180
- post :index, :authenticity_token => @token
181
- assert_response :success
122
+ assert_not_blocked { post :index, :authenticity_token => @token }
182
123
  end
183
124
 
184
125
  def test_should_allow_put_with_token
185
- put :index, :authenticity_token => @token
186
- assert_response :success
126
+ assert_not_blocked { put :index, :authenticity_token => @token }
187
127
  end
188
128
 
189
129
  def test_should_allow_delete_with_token
190
- delete :index, :authenticity_token => @token
191
- assert_response :success
130
+ assert_not_blocked { delete :index, :authenticity_token => @token }
192
131
  end
193
132
 
194
- def test_should_allow_post_with_xml
195
- @request.env['CONTENT_TYPE'] = Mime::XML.to_s
196
- post :index, :format => 'xml'
197
- assert_response :success
133
+ def test_should_allow_post_with_token_in_header
134
+ @request.env['HTTP_X_CSRF_TOKEN'] = @token
135
+ assert_not_blocked { post :index }
136
+ end
137
+
138
+ def test_should_allow_delete_with_token_in_header
139
+ @request.env['HTTP_X_CSRF_TOKEN'] = @token
140
+ assert_not_blocked { delete :index }
198
141
  end
199
142
 
200
- def test_should_allow_put_with_xml
201
- @request.env['CONTENT_TYPE'] = Mime::XML.to_s
202
- put :index, :format => 'xml'
143
+ def test_should_allow_put_with_token_in_header
144
+ @request.env['HTTP_X_CSRF_TOKEN'] = @token
145
+ assert_not_blocked { put :index }
146
+ end
147
+
148
+ def assert_blocked
149
+ session[:something_like_user_id] = 1
150
+ yield
151
+ assert_nil session[:something_like_user_id], "session values are still present"
203
152
  assert_response :success
204
153
  end
205
154
 
206
- def test_should_allow_delete_with_xml
207
- @request.env['CONTENT_TYPE'] = Mime::XML.to_s
208
- delete :index, :format => 'xml'
155
+ def assert_not_blocked
156
+ assert_nothing_raised { yield }
209
157
  assert_response :success
210
158
  end
211
159
  end
@@ -214,15 +162,20 @@ end
214
162
 
215
163
  class RequestForgeryProtectionControllerTest < ActionController::TestCase
216
164
  include RequestForgeryProtectionTests
217
- def setup
218
- @controller = RequestForgeryProtectionController.new
219
- @request = ActionController::TestRequest.new
220
- @request.format = :html
221
- @response = ActionController::TestResponse.new
222
- @token = "cf50faa3fe97702ca1ae"
223
165
 
224
- ActiveSupport::SecureRandom.stubs(:base64).returns(@token)
225
- ActionController::Base.request_forgery_protection_token = :authenticity_token
166
+ test 'should emit a csrf-token meta tag' do
167
+ ActiveSupport::SecureRandom.stubs(:base64).returns(@token + '<=?')
168
+ get :meta
169
+ assert_equal %(<meta name="csrf-param" content="authenticity_token"/>\n<meta name="csrf-token" content="cf50faa3fe97702ca1ae&lt;=?"/>), @response.body
170
+ end
171
+ end
172
+
173
+ class RequestForgeryProtectionControllerUsingOldBehaviourTest < ActionController::TestCase
174
+ include RequestForgeryProtectionTests
175
+ def assert_blocked
176
+ assert_raises(ActionController::InvalidAuthenticityToken) do
177
+ yield
178
+ end
226
179
  end
227
180
  end
228
181
 
@@ -251,15 +204,30 @@ class FreeCookieControllerTest < ActionController::TestCase
251
204
  assert_nothing_raised { send(method, :index)}
252
205
  end
253
206
  end
207
+
208
+ test 'should not emit a csrf-token meta tag' do
209
+ get :meta
210
+ assert_blank @response.body
211
+ end
254
212
  end
255
213
 
214
+
215
+
216
+
217
+
256
218
  class CustomAuthenticityParamControllerTest < ActionController::TestCase
257
219
  def setup
220
+ ActionController::Base.request_forgery_protection_token = :custom_token_name
221
+ super
222
+ end
223
+
224
+ def teardown
258
225
  ActionController::Base.request_forgery_protection_token = :authenticity_token
226
+ super
259
227
  end
260
228
 
261
229
  def test_should_allow_custom_token
262
- post :index, :authenticity_token => 'foobar'
230
+ post :index, :custom_token_name => 'foobar'
263
231
  assert_response :ok
264
232
  end
265
233
  end
@@ -106,7 +106,7 @@ class CookieStoreTest < ActionController::IntegrationTest
106
106
  with_test_route_set do
107
107
  get '/set_session_value'
108
108
  assert_response :success
109
- assert_equal ["_myapp_session=#{response.body}; path=/; HttpOnly"],
109
+ assert_equal "_myapp_session=#{response.body}; path=/; HttpOnly",
110
110
  headers['Set-Cookie']
111
111
  end
112
112
  end
@@ -159,7 +159,7 @@ class CookieStoreTest < ActionController::IntegrationTest
159
159
  with_test_route_set(:secure => true) do
160
160
  get '/set_session_value', nil, 'HTTPS' => 'on'
161
161
  assert_response :success
162
- assert_equal ["_myapp_session=#{response.body}; path=/; secure; HttpOnly"],
162
+ assert_equal "_myapp_session=#{response.body}; path=/; secure; HttpOnly",
163
163
  headers['Set-Cookie']
164
164
  end
165
165
  end
@@ -195,12 +195,12 @@ class CookieStoreTest < ActionController::IntegrationTest
195
195
  get '/set_session_value'
196
196
  assert_response :success
197
197
  session_payload = response.body
198
- assert_equal ["_myapp_session=#{response.body}; path=/; HttpOnly"],
198
+ assert_equal "_myapp_session=#{response.body}; path=/; HttpOnly",
199
199
  headers['Set-Cookie']
200
200
 
201
201
  get '/call_reset_session'
202
202
  assert_response :success
203
- assert_not_equal [], headers['Set-Cookie']
203
+ assert_not_equal "", headers['Set-Cookie']
204
204
  assert_not_equal session_payload, cookies[SessionKey]
205
205
 
206
206
  get '/get_session_value'
@@ -214,7 +214,7 @@ class CookieStoreTest < ActionController::IntegrationTest
214
214
  get '/set_session_value'
215
215
  assert_response :success
216
216
  session_payload = response.body
217
- assert_equal ["_myapp_session=#{response.body}; path=/; HttpOnly"],
217
+ assert_equal "_myapp_session=#{response.body}; path=/; HttpOnly",
218
218
  headers['Set-Cookie']
219
219
 
220
220
  get '/call_session_clear'
@@ -333,11 +333,11 @@ class UrlHelperTest < ActionView::TestCase
333
333
  end
334
334
 
335
335
  def test_mail_to_with_javascript
336
- assert_dom_equal "<script type=\"text/javascript\">eval(decodeURIComponent('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%22%3e%4d%79%20%65%6d%61%69%6c%3c%2f%61%3e%27%29%3b'))</script>", mail_to("me@domain.com", "My email", :encode => "javascript")
336
+ assert_dom_equal "<script type=\"text/javascript\">eval(decodeURIComponent('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%5c%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%5c%22%3e%4d%79%20%65%6d%61%69%6c%3c%5c%2f%61%3e%27%29%3b'))</script>", mail_to("me@domain.com", "My email", :encode => "javascript")
337
337
  end
338
338
 
339
339
  def test_mail_to_with_javascript_unicode
340
- assert_dom_equal "<script type=\"text/javascript\">eval(decodeURIComponent('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%22%6d%61%69%6c%74%6f%3a%75%6e%69%63%6f%64%65%40%65%78%61%6d%70%6c%65%2e%63%6f%6d%22%3e%c3%ba%6e%69%63%6f%64%65%3c%2f%61%3e%27%29%3b'))</script>", mail_to("unicode@example.com", "únicode", :encode => "javascript")
340
+ assert_dom_equal "<script type=\"text/javascript\">eval(decodeURIComponent('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%5c%22%6d%61%69%6c%74%6f%3a%75%6e%69%63%6f%64%65%40%65%78%61%6d%70%6c%65%2e%63%6f%6d%5c%22%3e%c3%ba%6e%69%63%6f%64%65%3c%5c%2f%61%3e%27%29%3b'))</script>", mail_to("unicode@example.com", "únicode", :encode => "javascript")
341
341
  end
342
342
 
343
343
  def test_mail_with_options
@@ -361,8 +361,8 @@ class UrlHelperTest < ActionView::TestCase
361
361
  assert_dom_equal "<a href=\"&#109;&#97;&#105;&#108;&#116;&#111;&#58;%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d\">&#109;&#101;&#40;&#97;&#116;&#41;&#100;&#111;&#109;&#97;&#105;&#110;&#46;&#99;&#111;&#109;</a>", mail_to("me@domain.com", nil, :encode => "hex", :replace_at => "(at)")
362
362
  assert_dom_equal "<a href=\"&#109;&#97;&#105;&#108;&#116;&#111;&#58;%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d\">My email</a>", mail_to("me@domain.com", "My email", :encode => "hex", :replace_at => "(at)")
363
363
  assert_dom_equal "<a href=\"&#109;&#97;&#105;&#108;&#116;&#111;&#58;%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d\">&#109;&#101;&#40;&#97;&#116;&#41;&#100;&#111;&#109;&#97;&#105;&#110;&#40;&#100;&#111;&#116;&#41;&#99;&#111;&#109;</a>", mail_to("me@domain.com", nil, :encode => "hex", :replace_at => "(at)", :replace_dot => "(dot)")
364
- assert_dom_equal "<script type=\"text/javascript\">eval(decodeURIComponent('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%22%3e%4d%79%20%65%6d%61%69%6c%3c%2f%61%3e%27%29%3b'))</script>", mail_to("me@domain.com", "My email", :encode => "javascript", :replace_at => "(at)", :replace_dot => "(dot)")
365
- assert_dom_equal "<script type=\"text/javascript\">eval(decodeURIComponent('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%22%3e%6d%65%28%61%74%29%64%6f%6d%61%69%6e%28%64%6f%74%29%63%6f%6d%3c%2f%61%3e%27%29%3b'))</script>", mail_to("me@domain.com", nil, :encode => "javascript", :replace_at => "(at)", :replace_dot => "(dot)")
364
+ assert_dom_equal "<script type=\"text/javascript\">eval(decodeURIComponent('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%5c%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%5c%22%3e%4d%79%20%65%6d%61%69%6c%3c%5c%2f%61%3e%27%29%3b'))</script>", mail_to("me@domain.com", "My email", :encode => "javascript", :replace_at => "(at)", :replace_dot => "(dot)")
365
+ assert_dom_equal "<script type=\"text/javascript\">eval(decodeURIComponent('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%5c%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%5c%22%3e%6d%65%28%61%74%29%64%6f%6d%61%69%6e%28%64%6f%74%29%63%6f%6d%3c%5c%2f%61%3e%27%29%3b'))</script>", mail_to("me@domain.com", nil, :encode => "javascript", :replace_at => "(at)", :replace_dot => "(dot)")
366
366
  end
367
367
 
368
368
  def protect_against_forgery?
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: actionpack
3
3
  version: !ruby/object:Gem::Version
4
- hash: 23
4
+ hash: 21
5
5
  prerelease: false
6
6
  segments:
7
7
  - 2
8
8
  - 3
9
- - 10
10
- version: 2.3.10
9
+ - 11
10
+ version: 2.3.11
11
11
  platform: ruby
12
12
  authors:
13
13
  - David Heinemeier Hansson
@@ -15,7 +15,7 @@ autorequire: action_controller
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-10-15 00:00:00 +13:00
18
+ date: 2011-02-09 00:00:00 +13:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -26,12 +26,12 @@ dependencies:
26
26
  requirements:
27
27
  - - "="
28
28
  - !ruby/object:Gem::Version
29
- hash: 23
29
+ hash: 21
30
30
  segments:
31
31
  - 2
32
32
  - 3
33
- - 10
34
- version: 2.3.10
33
+ - 11
34
+ version: 2.3.11
35
35
  type: :runtime
36
36
  version_requirements: *id001
37
37
  - !ruby/object:Gem::Dependency
@@ -156,6 +156,7 @@ files:
156
156
  - lib/action_view/helpers/benchmark_helper.rb
157
157
  - lib/action_view/helpers/cache_helper.rb
158
158
  - lib/action_view/helpers/capture_helper.rb
159
+ - lib/action_view/helpers/csrf_helper.rb
159
160
  - lib/action_view/helpers/date_helper.rb
160
161
  - lib/action_view/helpers/debug_helper.rb
161
162
  - lib/action_view/helpers/form_helper.rb