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 +4 -0
- data/Rakefile +1 -1
- data/lib/action_controller/cookies.rb +8 -2
- data/lib/action_controller/request_forgery_protection.rb +9 -6
- data/lib/action_controller/session/abstract_store.rb +2 -16
- data/lib/action_controller/session/cookie_store.rb +1 -24
- data/lib/action_controller/session/mem_cache_store.rb +1 -1
- data/lib/action_pack/version.rb +1 -1
- data/lib/action_view/helpers.rb +2 -0
- data/lib/action_view/helpers/asset_tag_helper.rb +1 -0
- data/lib/action_view/helpers/csrf_helper.rb +14 -0
- data/lib/action_view/helpers/form_helper.rb +21 -14
- data/lib/action_view/helpers/url_helper.rb +2 -1
- data/lib/action_view/renderable_partial.rb +7 -1
- data/test/controller/cookie_test.rb +28 -0
- data/test/controller/reloader_test.rb +1 -0
- data/test/controller/render_test.rb +12 -0
- data/test/controller/request_forgery_protection_test.rb +92 -124
- data/test/controller/session/cookie_store_test.rb +5 -5
- data/test/template/url_helper_test.rb +4 -4
- metadata +8 -7
data/CHANGELOG
CHANGED
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.
|
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? ||
|
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.
|
90
|
-
|
91
|
-
|
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 =
|
199
|
-
|
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
|
-
|
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)
|
data/lib/action_pack/version.rb
CHANGED
data/lib/action_view/helpers.rb
CHANGED
@@ -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
|
@@ -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
|
677
|
-
# the
|
678
|
-
#
|
679
|
-
#
|
680
|
-
#
|
681
|
-
#
|
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
|
698
|
-
# # <input name="post[validated]"
|
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
|
703
|
-
# # <input name="puppy[gooddog]"
|
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
|
707
|
-
# # <input name="eula[accepted]"
|
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
|
-
|
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
|
@@ -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
|
58
|
-
|
59
|
-
end
|
60
|
-
|
71
|
+
def setup
|
72
|
+
@token = "cf50faa3fe97702ca1ae"
|
61
73
|
|
62
|
-
|
63
|
-
|
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
|
98
|
-
|
99
|
-
|
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
|
109
|
-
|
110
|
-
|
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
|
115
|
-
|
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
|
121
|
-
|
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
|
128
|
-
|
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
|
135
|
-
|
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
|
142
|
-
|
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
|
149
|
-
|
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
|
156
|
-
|
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
|
195
|
-
@request.env['
|
196
|
-
post :index
|
197
|
-
|
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
|
201
|
-
@request.env['
|
202
|
-
put :index
|
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
|
207
|
-
|
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
|
-
|
225
|
-
|
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<=?"/>), @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, :
|
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
|
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
|
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
|
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
|
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
|
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=\"mailto:%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d\">me(at)domain.com</a>", mail_to("me@domain.com", nil, :encode => "hex", :replace_at => "(at)")
|
362
362
|
assert_dom_equal "<a href=\"mailto:%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=\"mailto:%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d\">me(at)domain(dot)com</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:
|
4
|
+
hash: 21
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 2
|
8
8
|
- 3
|
9
|
-
-
|
10
|
-
version: 2.3.
|
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:
|
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:
|
29
|
+
hash: 21
|
30
30
|
segments:
|
31
31
|
- 2
|
32
32
|
- 3
|
33
|
-
-
|
34
|
-
version: 2.3.
|
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
|