actionpack 2.3.4 → 2.3.5
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 +12 -0
- data/Rakefile +1 -1
- data/lib/action_controller.rb +3 -1
- data/lib/action_controller/assertions/dom_assertions.rb +19 -3
- data/lib/action_controller/assertions/selector_assertions.rb +16 -10
- data/lib/action_controller/base.rb +1 -1
- data/lib/action_controller/caching.rb +1 -0
- data/lib/action_controller/cookies.rb +2 -1
- data/lib/action_controller/http_authentication.rb +3 -2
- data/lib/action_controller/integration.rb +15 -3
- data/lib/action_controller/layout.rb +6 -1
- data/lib/action_controller/middlewares.rb +2 -0
- data/lib/action_controller/polymorphic_routes.rb +6 -21
- data/lib/action_controller/rack_lint_patch.rb +36 -0
- data/lib/action_controller/request_forgery_protection.rb +6 -2
- data/lib/action_controller/response.rb +2 -1
- data/lib/action_controller/string_coercion.rb +29 -0
- data/lib/action_controller/test_case.rb +6 -1
- data/lib/action_controller/test_process.rb +1 -1
- data/lib/action_controller/translation.rb +2 -2
- data/lib/action_controller/uploaded_file.rb +2 -2
- data/lib/action_controller/vendor/html-scanner/html/node.rb +1 -1
- data/lib/action_pack/version.rb +1 -1
- data/lib/action_view.rb +3 -3
- data/lib/action_view/base.rb +6 -1
- data/lib/action_view/erb/util.rb +6 -0
- data/lib/action_view/helpers.rb +2 -0
- data/lib/action_view/helpers/active_record_helper.rb +3 -3
- data/lib/action_view/helpers/asset_tag_helper.rb +2 -2
- data/lib/action_view/helpers/capture_helper.rb +2 -2
- data/lib/action_view/helpers/date_helper.rb +27 -15
- data/lib/action_view/helpers/form_helper.rb +32 -11
- data/lib/action_view/helpers/form_options_helper.rb +1 -1
- data/lib/action_view/helpers/form_tag_helper.rb +3 -3
- data/lib/action_view/helpers/number_helper.rb +6 -1
- data/lib/action_view/helpers/prototype_helper.rb +1 -1
- data/lib/action_view/helpers/raw_output_helper.rb +9 -0
- data/lib/action_view/helpers/sanitize_helper.rb +10 -2
- data/lib/action_view/helpers/tag_helper.rb +4 -4
- data/lib/action_view/helpers/translation_helper.rb +1 -1
- data/lib/action_view/helpers/url_helper.rb +3 -3
- data/lib/action_view/locale/en.yml +3 -0
- data/lib/action_view/partials.rb +1 -1
- data/lib/action_view/safe_buffer.rb +28 -0
- data/lib/action_view/template.rb +8 -2
- data/lib/action_view/test_case.rb +102 -27
- data/lib/actionpack.rb +1 -0
- data/test/controller/cookie_test.rb +7 -0
- data/test/controller/dom_assertions_test.rb +53 -0
- data/test/controller/filter_params_test.rb +1 -0
- data/test/controller/html-scanner/sanitizer_test.rb +1 -0
- data/test/controller/http_digest_authentication_test.rb +22 -0
- data/test/controller/integration_test.rb +38 -0
- data/test/controller/layout_test.rb +11 -0
- data/test/controller/polymorphic_routes_test.rb +4 -0
- data/test/controller/request_forgery_protection_test.rb +19 -1
- data/test/controller/routing_test.rb +23 -15
- data/test/controller/session/test_session_test.rb +2 -2
- data/test/fixtures/layout_tests/abs_path_layout.rhtml +1 -0
- data/test/fixtures/test/_from_helper.erb +1 -0
- data/test/template/active_record_helper_test.rb +1 -1
- data/test/template/asset_tag_helper_test.rb +12 -0
- data/test/template/benchmark_helper_test.rb +6 -6
- data/test/template/compiled_templates_test.rb +2 -1
- data/test/template/date_helper_i18n_test.rb +10 -9
- data/test/template/date_helper_test.rb +29 -13
- data/test/template/form_helper_test.rb +90 -10
- data/test/template/number_helper_test.rb +4 -0
- data/test/template/raw_output_helper_test.rb +21 -0
- data/test/template/sanitize_helper_test.rb +10 -1
- data/test/template/tag_helper_test.rb +1 -0
- data/test/view/safe_buffer_test.rb +36 -0
- data/test/view/test_case_test.rb +173 -5
- metadata +13 -4
@@ -49,7 +49,11 @@ module ActionView
|
|
49
49
|
# confuse browsers.
|
50
50
|
#
|
51
51
|
def sanitize(html, options = {})
|
52
|
-
self.class.white_list_sanitizer.sanitize(html, options)
|
52
|
+
returning self.class.white_list_sanitizer.sanitize(html, options) do |sanitized|
|
53
|
+
if sanitized
|
54
|
+
sanitized.html_safe!
|
55
|
+
end
|
56
|
+
end
|
53
57
|
end
|
54
58
|
|
55
59
|
# Sanitizes a block of CSS code. Used by +sanitize+ when it comes across a style attribute.
|
@@ -72,7 +76,11 @@ module ActionView
|
|
72
76
|
# strip_tags("<div id='top-bar'>Welcome to my website!</div>")
|
73
77
|
# # => Welcome to my website!
|
74
78
|
def strip_tags(html)
|
75
|
-
self.class.full_sanitizer.sanitize(html)
|
79
|
+
returning self.class.full_sanitizer.sanitize(html) do |sanitized|
|
80
|
+
if sanitized
|
81
|
+
sanitized.html_safe!
|
82
|
+
end
|
83
|
+
end
|
76
84
|
end
|
77
85
|
|
78
86
|
# Strips all link tags from +text+ leaving just the link text.
|
@@ -38,7 +38,7 @@ module ActionView
|
|
38
38
|
# tag("img", { :src => "open & shut.png" }, false, false)
|
39
39
|
# # => <img src="open & shut.png" />
|
40
40
|
def tag(name, options = nil, open = false, escape = true)
|
41
|
-
"<#{name}#{tag_options(options, escape) if options}#{open ? ">" : " />"}"
|
41
|
+
"<#{name}#{tag_options(options, escape) if options}#{open ? ">" : " />"}".html_safe!
|
42
42
|
end
|
43
43
|
|
44
44
|
# Returns an HTML block tag of type +name+ surrounding the +content+. Add
|
@@ -91,7 +91,7 @@ module ActionView
|
|
91
91
|
# cdata_section(File.read("hello_world.txt"))
|
92
92
|
# # => <![CDATA[<hello from a text file]]>
|
93
93
|
def cdata_section(content)
|
94
|
-
"<![CDATA[#{content}]]>"
|
94
|
+
"<![CDATA[#{content}]]>".html_safe!
|
95
95
|
end
|
96
96
|
|
97
97
|
# Returns an escaped version of +html+ without affecting existing escaped entities.
|
@@ -125,7 +125,7 @@ module ActionView
|
|
125
125
|
|
126
126
|
def content_tag_string(name, content, options, escape = true)
|
127
127
|
tag_options = tag_options(options, escape) if options
|
128
|
-
"<#{name}#{tag_options}>#{content}</#{name}>"
|
128
|
+
"<#{name}#{tag_options}>#{content}</#{name}>".html_safe!
|
129
129
|
end
|
130
130
|
|
131
131
|
def tag_options(options, escape = true)
|
@@ -142,7 +142,7 @@ module ActionView
|
|
142
142
|
else
|
143
143
|
attrs = options.map { |key, value| %(#{key}="#{value}") }
|
144
144
|
end
|
145
|
-
" #{attrs.sort * ' '}" unless attrs.empty?
|
145
|
+
" #{attrs.sort * ' '}".html_safe! unless attrs.empty?
|
146
146
|
end
|
147
147
|
end
|
148
148
|
end
|
@@ -219,7 +219,7 @@ module ActionView
|
|
219
219
|
if block_given?
|
220
220
|
options = args.first || {}
|
221
221
|
html_options = args.second
|
222
|
-
concat(link_to(capture(&block), options, html_options))
|
222
|
+
concat(link_to(capture(&block), options, html_options).html_safe!)
|
223
223
|
else
|
224
224
|
name = args.first
|
225
225
|
options = args.second || {}
|
@@ -237,7 +237,7 @@ module ActionView
|
|
237
237
|
end
|
238
238
|
|
239
239
|
href_attr = "href=\"#{url}\"" unless href
|
240
|
-
"<a #{href_attr}#{tag_options}>#{name || url}</a>"
|
240
|
+
"<a #{href_attr}#{tag_options}>#{name || url}</a>".html_safe!
|
241
241
|
end
|
242
242
|
end
|
243
243
|
|
@@ -309,7 +309,7 @@ module ActionView
|
|
309
309
|
html_options.merge!("type" => "submit", "value" => name)
|
310
310
|
|
311
311
|
"<form method=\"#{form_method}\" action=\"#{escape_once url}\" class=\"button-to\"><div>" +
|
312
|
-
method_tag + tag("input", html_options) + request_token_tag + "</div></form>"
|
312
|
+
method_tag + tag("input", html_options) + request_token_tag + "</div></form>".html_safe!
|
313
313
|
end
|
314
314
|
|
315
315
|
|
data/lib/action_view/partials.rb
CHANGED
@@ -0,0 +1,28 @@
|
|
1
|
+
|
2
|
+
module ActionView #:nodoc:
|
3
|
+
class SafeBuffer < String
|
4
|
+
def <<(value)
|
5
|
+
if value.html_safe?
|
6
|
+
super(value)
|
7
|
+
else
|
8
|
+
super(ERB::Util.h(value))
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def concat(value)
|
13
|
+
self << value
|
14
|
+
end
|
15
|
+
|
16
|
+
def html_safe?
|
17
|
+
true
|
18
|
+
end
|
19
|
+
|
20
|
+
def html_safe!
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_s
|
25
|
+
self
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/action_view/template.rb
CHANGED
@@ -57,9 +57,14 @@ module ActionView #:nodoc:
|
|
57
57
|
end
|
58
58
|
|
59
59
|
class EagerPath < Path
|
60
|
+
def initialize(path)
|
61
|
+
super
|
62
|
+
@loaded = false
|
63
|
+
end
|
64
|
+
|
60
65
|
def load!
|
61
66
|
return if @loaded
|
62
|
-
|
67
|
+
|
63
68
|
@paths = {}
|
64
69
|
templates_in_path do |template|
|
65
70
|
template.load!
|
@@ -103,8 +108,9 @@ module ActionView #:nodoc:
|
|
103
108
|
@@exempt_from_layout.merge(regexps)
|
104
109
|
end
|
105
110
|
|
106
|
-
attr_accessor :template_path, :
|
111
|
+
attr_accessor :template_path, :load_path, :base_path
|
107
112
|
attr_accessor :locale, :name, :format, :extension
|
113
|
+
attr_writer :filename
|
108
114
|
delegate :to_s, :to => :path
|
109
115
|
|
110
116
|
def initialize(template_path, load_path = nil)
|
@@ -22,11 +22,52 @@ module ActionView
|
|
22
22
|
end
|
23
23
|
|
24
24
|
class TestCase < ActiveSupport::TestCase
|
25
|
+
class TestController < ActionController::Base
|
26
|
+
attr_accessor :request, :response, :params
|
27
|
+
|
28
|
+
def self.controller_path
|
29
|
+
''
|
30
|
+
end
|
31
|
+
|
32
|
+
def initialize
|
33
|
+
@request = ActionController::TestRequest.new
|
34
|
+
@response = ActionController::TestResponse.new
|
35
|
+
|
36
|
+
@params = {}
|
37
|
+
send(:initialize_current_url)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
25
41
|
include ActionController::TestCase::Assertions
|
26
42
|
include ActionController::TestProcess
|
27
43
|
|
44
|
+
include ActionController::PolymorphicRoutes
|
45
|
+
include ActionController::RecordIdentifier
|
46
|
+
|
47
|
+
include ActionView::Helpers
|
48
|
+
include ActionController::Helpers
|
49
|
+
|
28
50
|
class_inheritable_accessor :helper_class
|
29
|
-
|
51
|
+
attr_accessor :controller, :output_buffer, :rendered
|
52
|
+
|
53
|
+
setup :setup_with_controller
|
54
|
+
def setup_with_controller
|
55
|
+
@controller = TestController.new
|
56
|
+
@output_buffer = ''
|
57
|
+
@rendered = ''
|
58
|
+
|
59
|
+
self.class.send(:include_helper_modules!)
|
60
|
+
make_test_case_available_to_view!
|
61
|
+
end
|
62
|
+
|
63
|
+
def render(options = {}, local_assigns = {}, &block)
|
64
|
+
@rendered << output = _view.render(options, local_assigns, &block)
|
65
|
+
output
|
66
|
+
end
|
67
|
+
|
68
|
+
def protect_against_forgery?
|
69
|
+
false
|
70
|
+
end
|
30
71
|
|
31
72
|
class << self
|
32
73
|
def tests(helper_class)
|
@@ -46,42 +87,76 @@ module ActionView
|
|
46
87
|
rescue NameError
|
47
88
|
nil
|
48
89
|
end
|
49
|
-
end
|
50
90
|
|
51
|
-
|
52
|
-
|
53
|
-
|
91
|
+
def helper_method(*methods)
|
92
|
+
# Almost a duplicate from ActionController::Helpers
|
93
|
+
methods.flatten.each do |method|
|
94
|
+
master_helper_module.module_eval <<-end_eval
|
95
|
+
def #{method}(*args, &block) # def current_user(*args, &block)
|
96
|
+
_test_case.send(%(#{method}), *args, &block) # test_case.send(%(current_user), *args, &block)
|
97
|
+
end # end
|
98
|
+
end_eval
|
99
|
+
end
|
100
|
+
end
|
54
101
|
|
55
|
-
|
102
|
+
private
|
103
|
+
def include_helper_modules!
|
104
|
+
helper(helper_class) if helper_class
|
105
|
+
include master_helper_module
|
106
|
+
end
|
107
|
+
end
|
56
108
|
|
57
|
-
|
58
|
-
|
59
|
-
self
|
109
|
+
private
|
110
|
+
def make_test_case_available_to_view!
|
111
|
+
test_case_instance = self
|
112
|
+
master_helper_module.module_eval do
|
113
|
+
define_method(:_test_case) { test_case_instance }
|
114
|
+
private :_test_case
|
115
|
+
end
|
60
116
|
end
|
61
117
|
|
62
|
-
|
63
|
-
|
118
|
+
def _view
|
119
|
+
view = ActionView::Base.new(ActionController::Base.view_paths, _assigns, @controller)
|
120
|
+
view.helpers.include master_helper_module
|
121
|
+
view.output_buffer = self.output_buffer
|
122
|
+
view
|
123
|
+
end
|
64
124
|
|
65
|
-
|
66
|
-
|
125
|
+
# Support the selector assertions
|
126
|
+
#
|
127
|
+
# Need to experiment if this priority is the best one: rendered => output_buffer
|
128
|
+
def response_from_page_or_rjs
|
129
|
+
HTML::Document.new(rendered.blank? ? output_buffer : rendered).root
|
130
|
+
end
|
67
131
|
|
68
|
-
|
69
|
-
@
|
70
|
-
@
|
71
|
-
|
72
|
-
@
|
73
|
-
|
132
|
+
EXCLUDE_IVARS = %w{
|
133
|
+
@output_buffer
|
134
|
+
@fixture_cache
|
135
|
+
@method_name
|
136
|
+
@_result
|
137
|
+
@loaded_fixtures
|
138
|
+
@test_passed
|
139
|
+
@view
|
140
|
+
}
|
141
|
+
|
142
|
+
def _instance_variables
|
143
|
+
instance_variables - EXCLUDE_IVARS
|
74
144
|
end
|
75
|
-
end
|
76
145
|
|
77
|
-
|
78
|
-
|
146
|
+
def _assigns
|
147
|
+
_instance_variables.inject({}) do |hash, var|
|
148
|
+
name = var[1..-1].to_sym
|
149
|
+
hash[name] = instance_variable_get(var)
|
150
|
+
hash
|
151
|
+
end
|
152
|
+
end
|
79
153
|
|
80
|
-
private
|
81
154
|
def method_missing(selector, *args)
|
82
|
-
|
83
|
-
|
84
|
-
|
155
|
+
if ActionController::Routing::Routes.named_routes.helpers.include?(selector)
|
156
|
+
@controller.__send__(selector, *args)
|
157
|
+
else
|
158
|
+
super
|
159
|
+
end
|
85
160
|
end
|
86
161
|
end
|
87
|
-
end
|
162
|
+
end
|
data/lib/actionpack.rb
CHANGED
@@ -114,6 +114,13 @@ class CookieTest < ActionController::TestCase
|
|
114
114
|
assert_equal %w{1 2 3}, jar["pages"]
|
115
115
|
end
|
116
116
|
|
117
|
+
def test_cookiejar_delete_removes_item_and_returns_its_value
|
118
|
+
@request.cookies["user_name"] = "david"
|
119
|
+
@controller.response = @response
|
120
|
+
jar = ActionController::CookieJar.new(@controller)
|
121
|
+
assert_equal "david", jar.delete("user_name")
|
122
|
+
end
|
123
|
+
|
117
124
|
def test_delete_cookie_with_path
|
118
125
|
get :delete_cookie_with_path
|
119
126
|
assert_equal ["user_name=; path=/beaten; expires=Thu, 01-Jan-1970 00:00:00 GMT"], @response.headers["Set-Cookie"]
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'abstract_unit'
|
2
|
+
|
3
|
+
class DomAssertionsTest < ActionView::TestCase
|
4
|
+
def setup
|
5
|
+
super
|
6
|
+
@html_only = '<ul><li>foo</li><li>bar</li></ul>'
|
7
|
+
@html_with_meaningless_whitespace = %{
|
8
|
+
<ul>
|
9
|
+
<li>\tfoo </li>
|
10
|
+
<li>
|
11
|
+
bar
|
12
|
+
</li>
|
13
|
+
</ul>
|
14
|
+
}
|
15
|
+
@more_html_with_meaningless_whitespace = %{<ul>
|
16
|
+
|
17
|
+
<li>foo</li>
|
18
|
+
|
19
|
+
<li>bar</li></ul>}
|
20
|
+
end
|
21
|
+
|
22
|
+
test "assert_dom_equal strips meaningless whitespace from expected string" do
|
23
|
+
assert_dom_equal @html_with_meaningless_whitespace, @html_only
|
24
|
+
end
|
25
|
+
|
26
|
+
test "assert_dom_equal strips meaningless whitespace from actual string" do
|
27
|
+
assert_dom_equal @html_only, @html_with_meaningless_whitespace
|
28
|
+
end
|
29
|
+
|
30
|
+
test "assert_dom_equal strips meaningless whitespace from both expected and actual strings" do
|
31
|
+
assert_dom_equal @more_html_with_meaningless_whitespace, @html_with_meaningless_whitespace
|
32
|
+
end
|
33
|
+
|
34
|
+
test "assert_dom_not_equal strips meaningless whitespace from expected string" do
|
35
|
+
assert_assertion_fails { assert_dom_not_equal @html_with_meaningless_whitespace, @html_only }
|
36
|
+
end
|
37
|
+
|
38
|
+
test "assert_dom_not_equal strips meaningless whitespace from actual string" do
|
39
|
+
assert_assertion_fails { assert_dom_not_equal @html_only, @html_with_meaningless_whitespace }
|
40
|
+
end
|
41
|
+
|
42
|
+
test "assert_dom_not_equal strips meaningless whitespace from both expected and actual strings" do
|
43
|
+
assert_assertion_fails do
|
44
|
+
assert_dom_not_equal @more_html_with_meaningless_whitespace, @html_with_meaningless_whitespace
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def assert_assertion_fails
|
51
|
+
assert_raise(ActiveSupport::TestCase::Assertion) { yield }
|
52
|
+
end
|
53
|
+
end
|
@@ -18,6 +18,7 @@ class FilterParamTest < Test::Unit::TestCase
|
|
18
18
|
test_hashes = [[{},{},[]],
|
19
19
|
[{'foo'=>nil},{'foo'=>nil},[]],
|
20
20
|
[{'foo'=>'bar'},{'foo'=>'bar'},[]],
|
21
|
+
[{'foo'=>1},{'foo'=>1},[]],
|
21
22
|
[{'foo'=>'bar'},{'foo'=>'bar'},%w'food'],
|
22
23
|
[{'foo'=>'bar'},{'foo'=>'[FILTERED]'},%w'foo'],
|
23
24
|
[{'foo'=>'bar', 'bar'=>'foo'},{'foo'=>'[FILTERED]', 'bar'=>'foo'},%w'foo baz'],
|
@@ -19,6 +19,7 @@ class SanitizerTest < ActionController::TestCase
|
|
19
19
|
assert_equal "This has a here.", sanitizer.sanitize("This has a <!-- comment --> here.")
|
20
20
|
assert_equal "This has a here.", sanitizer.sanitize("This has a <![CDATA[<section>]]> here.")
|
21
21
|
assert_equal "This has an unclosed ", sanitizer.sanitize("This has an unclosed <![CDATA[<section>]] here...")
|
22
|
+
assert_equal "non printable char is a tag", sanitizer.sanitize("<\x07a href='/hello'>non printable char is a tag</a>")
|
22
23
|
[nil, '', ' '].each { |blank| assert_equal blank, sanitizer.sanitize(blank) }
|
23
24
|
end
|
24
25
|
|
@@ -92,6 +92,23 @@ class HttpDigestAuthenticationTest < ActionController::TestCase
|
|
92
92
|
assert_equal "Authentication Failed", @response.body
|
93
93
|
end
|
94
94
|
|
95
|
+
test "authentication request with missing nonce should return 401" do
|
96
|
+
@request.env['HTTP_AUTHORIZATION'] = encode_credentials(:username => 'pretty', :password => 'please', :remove_nonce => true)
|
97
|
+
get :display
|
98
|
+
|
99
|
+
assert_response :unauthorized
|
100
|
+
assert_equal "Authentication Failed", @response.body
|
101
|
+
end
|
102
|
+
|
103
|
+
test "authentication request with Basic auth credentials should return 401" do
|
104
|
+
ActionController::Base.session_options[:secret] = "session_options_secret"
|
105
|
+
@request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials('pretty', 'please')
|
106
|
+
get :display
|
107
|
+
|
108
|
+
assert_response :unauthorized
|
109
|
+
assert_equal "Authentication Failed", @response.body
|
110
|
+
end
|
111
|
+
|
95
112
|
test "authentication request with invalid opaque" do
|
96
113
|
@request.env['HTTP_AUTHORIZATION'] = encode_credentials(:username => 'pretty', :password => 'foo', :opaque => "xxyyzz")
|
97
114
|
get :display
|
@@ -220,9 +237,14 @@ class HttpDigestAuthenticationTest < ActionController::TestCase
|
|
220
237
|
|
221
238
|
assert_response :unauthorized
|
222
239
|
|
240
|
+
remove_nonce = options.delete(:remove_nonce)
|
241
|
+
|
223
242
|
credentials = decode_credentials(@response.headers['WWW-Authenticate'])
|
224
243
|
credentials.merge!(options)
|
225
244
|
credentials.merge!(:uri => @request.env['REQUEST_URI'].to_s)
|
245
|
+
|
246
|
+
credentials.delete(:nonce) if remove_nonce
|
247
|
+
|
226
248
|
ActionController::HttpAuthentication::Digest.encode_credentials(method, credentials, password, options[:password_is_ha1])
|
227
249
|
end
|
228
250
|
|