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.

Files changed (74) hide show
  1. data/CHANGELOG +12 -0
  2. data/Rakefile +1 -1
  3. data/lib/action_controller.rb +3 -1
  4. data/lib/action_controller/assertions/dom_assertions.rb +19 -3
  5. data/lib/action_controller/assertions/selector_assertions.rb +16 -10
  6. data/lib/action_controller/base.rb +1 -1
  7. data/lib/action_controller/caching.rb +1 -0
  8. data/lib/action_controller/cookies.rb +2 -1
  9. data/lib/action_controller/http_authentication.rb +3 -2
  10. data/lib/action_controller/integration.rb +15 -3
  11. data/lib/action_controller/layout.rb +6 -1
  12. data/lib/action_controller/middlewares.rb +2 -0
  13. data/lib/action_controller/polymorphic_routes.rb +6 -21
  14. data/lib/action_controller/rack_lint_patch.rb +36 -0
  15. data/lib/action_controller/request_forgery_protection.rb +6 -2
  16. data/lib/action_controller/response.rb +2 -1
  17. data/lib/action_controller/string_coercion.rb +29 -0
  18. data/lib/action_controller/test_case.rb +6 -1
  19. data/lib/action_controller/test_process.rb +1 -1
  20. data/lib/action_controller/translation.rb +2 -2
  21. data/lib/action_controller/uploaded_file.rb +2 -2
  22. data/lib/action_controller/vendor/html-scanner/html/node.rb +1 -1
  23. data/lib/action_pack/version.rb +1 -1
  24. data/lib/action_view.rb +3 -3
  25. data/lib/action_view/base.rb +6 -1
  26. data/lib/action_view/erb/util.rb +6 -0
  27. data/lib/action_view/helpers.rb +2 -0
  28. data/lib/action_view/helpers/active_record_helper.rb +3 -3
  29. data/lib/action_view/helpers/asset_tag_helper.rb +2 -2
  30. data/lib/action_view/helpers/capture_helper.rb +2 -2
  31. data/lib/action_view/helpers/date_helper.rb +27 -15
  32. data/lib/action_view/helpers/form_helper.rb +32 -11
  33. data/lib/action_view/helpers/form_options_helper.rb +1 -1
  34. data/lib/action_view/helpers/form_tag_helper.rb +3 -3
  35. data/lib/action_view/helpers/number_helper.rb +6 -1
  36. data/lib/action_view/helpers/prototype_helper.rb +1 -1
  37. data/lib/action_view/helpers/raw_output_helper.rb +9 -0
  38. data/lib/action_view/helpers/sanitize_helper.rb +10 -2
  39. data/lib/action_view/helpers/tag_helper.rb +4 -4
  40. data/lib/action_view/helpers/translation_helper.rb +1 -1
  41. data/lib/action_view/helpers/url_helper.rb +3 -3
  42. data/lib/action_view/locale/en.yml +3 -0
  43. data/lib/action_view/partials.rb +1 -1
  44. data/lib/action_view/safe_buffer.rb +28 -0
  45. data/lib/action_view/template.rb +8 -2
  46. data/lib/action_view/test_case.rb +102 -27
  47. data/lib/actionpack.rb +1 -0
  48. data/test/controller/cookie_test.rb +7 -0
  49. data/test/controller/dom_assertions_test.rb +53 -0
  50. data/test/controller/filter_params_test.rb +1 -0
  51. data/test/controller/html-scanner/sanitizer_test.rb +1 -0
  52. data/test/controller/http_digest_authentication_test.rb +22 -0
  53. data/test/controller/integration_test.rb +38 -0
  54. data/test/controller/layout_test.rb +11 -0
  55. data/test/controller/polymorphic_routes_test.rb +4 -0
  56. data/test/controller/request_forgery_protection_test.rb +19 -1
  57. data/test/controller/routing_test.rb +23 -15
  58. data/test/controller/session/test_session_test.rb +2 -2
  59. data/test/fixtures/layout_tests/abs_path_layout.rhtml +1 -0
  60. data/test/fixtures/test/_from_helper.erb +1 -0
  61. data/test/template/active_record_helper_test.rb +1 -1
  62. data/test/template/asset_tag_helper_test.rb +12 -0
  63. data/test/template/benchmark_helper_test.rb +6 -6
  64. data/test/template/compiled_templates_test.rb +2 -1
  65. data/test/template/date_helper_i18n_test.rb +10 -9
  66. data/test/template/date_helper_test.rb +29 -13
  67. data/test/template/form_helper_test.rb +90 -10
  68. data/test/template/number_helper_test.rb +4 -0
  69. data/test/template/raw_output_helper_test.rb +21 -0
  70. data/test/template/sanitize_helper_test.rb +10 -1
  71. data/test/template/tag_helper_test.rb +1 -0
  72. data/test/view/safe_buffer_test.rb +36 -0
  73. data/test/view/test_case_test.rb +173 -5
  74. metadata +13 -4
data/CHANGELOG CHANGED
@@ -1,3 +1,15 @@
1
+ *2.3.5 (November 25, 2009)*
2
+
3
+ * Minor Bug Fixes and deprecation warnings
4
+
5
+ * Ruby 1.9 Support
6
+
7
+ * Fix filtering parameters when there are Fixnum or other un-dupable values.
8
+
9
+ * Improvements to ActionView::TestCase
10
+
11
+ * Compatiblity with the rails_xss plugin
12
+
1
13
  *2.3.4 (September 4, 2009)*
2
14
 
3
15
  * Sanitize multibyte strings before escaping them with escape_once. CVE-2009-3009
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.4' + PKG_BUILD)
82
+ s.add_dependency('activesupport', '= 2.3.5' + PKG_BUILD)
83
83
  s.add_dependency('rack', '~> 1.0.0')
84
84
 
85
85
  s.require_path = 'lib'
@@ -31,8 +31,9 @@ rescue LoadError
31
31
  end
32
32
  end
33
33
 
34
- gem 'rack', '~> 1.0.0'
34
+ gem 'rack', '~> 1.0.1'
35
35
  require 'rack'
36
+ require 'action_controller/cgi_ext'
36
37
 
37
38
  module ActionController
38
39
  # TODO: Review explicit to see if they will automatically be handled by
@@ -70,6 +71,7 @@ module ActionController
70
71
  autoload :SessionManagement, 'action_controller/session_management'
71
72
  autoload :StatusCodes, 'action_controller/status_codes'
72
73
  autoload :Streaming, 'action_controller/streaming'
74
+ autoload :StringCoercion, 'action_controller/string_coercion'
73
75
  autoload :TestCase, 'action_controller/test_case'
74
76
  autoload :TestProcess, 'action_controller/test_process'
75
77
  autoload :Translation, 'action_controller/translation'
@@ -1,6 +1,18 @@
1
1
  module ActionController
2
2
  module Assertions
3
3
  module DomAssertions
4
+ def self.strip_whitespace!(nodes)
5
+ nodes.reject! do |node|
6
+ if node.is_a?(HTML::Text)
7
+ node.content.strip!
8
+ node.content.empty?
9
+ else
10
+ strip_whitespace! node.children
11
+ false
12
+ end
13
+ end
14
+ end
15
+
4
16
  # Test two HTML strings for equivalency (e.g., identical up to reordering of attributes)
5
17
  #
6
18
  # ==== Examples
@@ -12,13 +24,15 @@ module ActionController
12
24
  clean_backtrace do
13
25
  expected_dom = HTML::Document.new(expected).root
14
26
  actual_dom = HTML::Document.new(actual).root
15
- full_message = build_message(message, "<?> expected to be == to\n<?>.", expected_dom.to_s, actual_dom.to_s)
27
+ DomAssertions.strip_whitespace!(expected_dom.children)
28
+ DomAssertions.strip_whitespace!(actual_dom.children)
16
29
 
30
+ full_message = build_message(message, "<?> expected but was\n<?>.", expected_dom.to_s, actual_dom.to_s)
17
31
  assert_block(full_message) { expected_dom == actual_dom }
18
32
  end
19
33
  end
20
34
 
21
- # The negated form of +assert_dom_equivalent+.
35
+ # The negated form of +assert_dom_equal+.
22
36
  #
23
37
  # ==== Examples
24
38
  #
@@ -29,8 +43,10 @@ module ActionController
29
43
  clean_backtrace do
30
44
  expected_dom = HTML::Document.new(expected).root
31
45
  actual_dom = HTML::Document.new(actual).root
32
- full_message = build_message(message, "<?> expected to be != to\n<?>.", expected_dom.to_s, actual_dom.to_s)
46
+ DomAssertions.strip_whitespace!(expected_dom.children)
47
+ DomAssertions.strip_whitespace!(actual_dom.children)
33
48
 
49
+ full_message = build_message(message, "<?> expected to be != to\n<?>.", expected_dom.to_s, actual_dom.to_s)
34
50
  assert_block(full_message) { expected_dom != actual_dom }
35
51
  end
36
52
  end
@@ -16,7 +16,7 @@ module ActionController
16
16
  #
17
17
  # Use +css_select+ to select elements without making an assertions, either
18
18
  # from the response HTML or elements selected by the enclosing assertion.
19
- #
19
+ #
20
20
  # In addition to HTML responses, you can make the following assertions:
21
21
  # * +assert_select_rjs+ - Assertions on HTML content of RJS update and insertion operations.
22
22
  # * +assert_select_encoded+ - Assertions on HTML encoded inside XML, for example for dealing with feed item descriptions.
@@ -24,6 +24,12 @@ module ActionController
24
24
  #
25
25
  # Also see HTML::Selector to learn how to use selectors.
26
26
  module SelectorAssertions
27
+
28
+ def initialize(*args)
29
+ super
30
+ @selected = nil
31
+ end
32
+
27
33
  # :call-seq:
28
34
  # css_select(selector) => array
29
35
  # css_select(element, selector) => array
@@ -53,8 +59,8 @@ module ActionController
53
59
  # end
54
60
  #
55
61
  # # Selects all list items in unordered lists
56
- # items = css_select("ul>li")
57
- #
62
+ # items = css_select("ul>li")
63
+ #
58
64
  # # Selects all form tags and then all inputs inside the form
59
65
  # forms = css_select("form")
60
66
  # forms.each do |form|
@@ -212,7 +218,7 @@ module ActionController
212
218
  # Otherwise just operate on the response document.
213
219
  root = response_from_page_or_rjs
214
220
  end
215
-
221
+
216
222
  # First or second argument is the selector: string and we pass
217
223
  # all remaining arguments. Array and we pass the argument. Also
218
224
  # accepts selector itself.
@@ -225,7 +231,7 @@ module ActionController
225
231
  selector = arg
226
232
  else raise ArgumentError, "Expecting a selector as the first argument"
227
233
  end
228
-
234
+
229
235
  # Next argument is used for equality tests.
230
236
  equals = {}
231
237
  case arg = args.shift
@@ -315,10 +321,10 @@ module ActionController
315
321
  # Returns all matches elements.
316
322
  matches
317
323
  end
318
-
324
+
319
325
  def count_description(min, max) #:nodoc:
320
326
  pluralize = lambda {|word, quantity| word << (quantity == 1 ? '' : 's')}
321
-
327
+
322
328
  if min && max && (max != min)
323
329
  "between #{min} and #{max} elements"
324
330
  elsif min && !(min == 1 && max == 1)
@@ -327,7 +333,7 @@ module ActionController
327
333
  "at most #{max} #{pluralize['element', max]}"
328
334
  end
329
335
  end
330
-
336
+
331
337
  # :call-seq:
332
338
  # assert_select_rjs(id?) { |elements| ... }
333
339
  # assert_select_rjs(statement, id?) { |elements| ... }
@@ -344,7 +350,7 @@ module ActionController
344
350
  # that update or insert an element with that identifier.
345
351
  #
346
352
  # Use the first argument to narrow down assertions to only statements
347
- # of that type. Possible values are <tt>:replace</tt>, <tt>:replace_html</tt>,
353
+ # of that type. Possible values are <tt>:replace</tt>, <tt>:replace_html</tt>,
348
354
  # <tt>:show</tt>, <tt>:hide</tt>, <tt>:toggle</tt>, <tt>:remove</tt> and
349
355
  # <tt>:insert_html</tt>.
350
356
  #
@@ -488,7 +494,7 @@ module ActionController
488
494
  # end
489
495
  # end
490
496
  # end
491
- #
497
+ #
492
498
  #
493
499
  # # Selects all paragraph tags from within the description of an RSS feed
494
500
  # assert_select_feed :rss, 2.0 do
@@ -502,7 +502,7 @@ module ActionController #:nodoc:
502
502
  end
503
503
  elsif block_given?
504
504
  key = key.dup
505
- value = value.dup if value
505
+ value = value.dup if value.duplicable?
506
506
  yield key, value
507
507
  filtered_parameters[key] = value
508
508
  else
@@ -22,6 +22,7 @@ module ActionController #:nodoc:
22
22
  # ActionController::Base.cache_store = :file_store, "/path/to/cache/directory"
23
23
  # ActionController::Base.cache_store = :drb_store, "druby://localhost:9192"
24
24
  # ActionController::Base.cache_store = :mem_cache_store, "localhost"
25
+ # ActionController::Base.cache_store = :mem_cache_store, Memcached::Rails.new("localhost:11211")
25
26
  # ActionController::Base.cache_store = MyOwnStore.new("parameter")
26
27
  module Caching
27
28
  autoload :Actions, 'action_controller/caching/actions'
@@ -87,8 +87,9 @@ module ActionController #:nodoc:
87
87
  def delete(key, options = {})
88
88
  options.symbolize_keys!
89
89
  options[:path] = "/" unless options.has_key?(:path)
90
- super(key.to_s)
90
+ value = super(key.to_s)
91
91
  @controller.response.delete_cookie(key, options)
92
+ value
92
93
  end
93
94
  end
94
95
  end
@@ -227,9 +227,9 @@ module ActionController
227
227
  end
228
228
 
229
229
  def decode_credentials(header)
230
- header.to_s.gsub(/^Digest\s+/,'').split(',').inject({}) do |hash, pair|
230
+ header.to_s.gsub(/^Digest\s+/,'').split(',').inject({}.with_indifferent_access) do |hash, pair|
231
231
  key, value = pair.split('=', 2)
232
- hash[key.strip.to_sym] = value.to_s.gsub(/^"|"$/,'').gsub(/'/, '')
232
+ hash[key.strip] = value.to_s.gsub(/^"|"$/,'').gsub(/'/, '')
233
233
  hash
234
234
  end
235
235
  end
@@ -289,6 +289,7 @@ module ActionController
289
289
  # allow a user to use new nonce without prompting user again for their
290
290
  # username and password.
291
291
  def validate_nonce(request, value, seconds_to_timeout=5*60)
292
+ return false if value.nil?
292
293
  t = Base64.decode64(value).split(":").first.to_i
293
294
  nonce(t) == value && (t - Time.now.to_i).abs <= seconds_to_timeout
294
295
  end
@@ -1,6 +1,7 @@
1
1
  require 'stringio'
2
2
  require 'uri'
3
3
  require 'active_support/test_case'
4
+ require 'action_controller/rack_lint_patch'
4
5
 
5
6
  module ActionController
6
7
  module Integration #:nodoc:
@@ -268,7 +269,9 @@ module ActionController
268
269
 
269
270
  env["QUERY_STRING"] ||= ""
270
271
 
271
- data = data.is_a?(IO) ? data : StringIO.new(data || '')
272
+ data ||= ''
273
+ data.force_encoding(Encoding::ASCII_8BIT) if data.respond_to?(:force_encoding)
274
+ data = data.is_a?(IO) ? data : StringIO.new(data)
272
275
 
273
276
  env.update(
274
277
  "REQUEST_METHOD" => method.to_s.upcase,
@@ -476,6 +479,11 @@ EOF
476
479
  end
477
480
 
478
481
  module Runner
482
+ def initialize(*args)
483
+ super
484
+ @integration_session = nil
485
+ end
486
+
479
487
  # Reset the current session. This is useful for testing multiple sessions
480
488
  # in a single test case.
481
489
  def reset!
@@ -543,8 +551,12 @@ EOF
543
551
  # Delegate unhandled messages to the current session instance.
544
552
  def method_missing(sym, *args, &block)
545
553
  reset! unless @integration_session
546
- returning @integration_session.__send__(sym, *args, &block) do
547
- copy_session_variables!
554
+ if @integration_session.respond_to?(sym)
555
+ returning @integration_session.__send__(sym, *args, &block) do
556
+ copy_session_variables!
557
+ end
558
+ else
559
+ super
548
560
  end
549
561
  end
550
562
  end
@@ -194,6 +194,11 @@ module ActionController #:nodoc:
194
194
  end
195
195
  end
196
196
 
197
+ def initialize(*args)
198
+ super
199
+ @real_format = nil
200
+ end
201
+
197
202
  # Returns the name of the active layout. If the layout was specified as a method reference (through a symbol), this method
198
203
  # is called and the return value is used. Likewise if the layout was specified as an inline method (through a proc or method
199
204
  # object). If the layout was defined without a directory, layouts is assumed. So <tt>layout "weblog/standard"</tt> will return
@@ -221,7 +226,7 @@ module ActionController #:nodoc:
221
226
  end
222
227
 
223
228
  def find_layout(layout, format, html_fallback=false) #:nodoc:
224
- view_paths.find_template(layout.to_s =~ /layouts\// ? layout : "layouts/#{layout}", format, html_fallback)
229
+ view_paths.find_template(layout.to_s =~ /\A\/|layouts\// ? layout : "layouts/#{layout}", format, html_fallback)
225
230
  rescue ActionView::MissingTemplate
226
231
  raise if Mime::Type.lookup_by_extension(format.to_s).html?
227
232
  end
@@ -10,3 +10,5 @@ use lambda { ActionController::Base.session_store },
10
10
  use "ActionController::ParamsParser"
11
11
  use "Rack::MethodOverride"
12
12
  use "Rack::Head"
13
+
14
+ use "ActionController::StringCoercion"
@@ -76,8 +76,7 @@ module ActionController
76
76
  record_or_hash_or_array = record_or_hash_or_array[0] if record_or_hash_or_array.size == 1
77
77
  end
78
78
 
79
- record = extract_record(record_or_hash_or_array)
80
- namespace = extract_namespace(record_or_hash_or_array)
79
+ record = extract_record(record_or_hash_or_array)
81
80
 
82
81
  args = case record_or_hash_or_array
83
82
  when Hash; [ record_or_hash_or_array ]
@@ -98,8 +97,7 @@ module ActionController
98
97
  end
99
98
 
100
99
  args.delete_if {|arg| arg.is_a?(Symbol) || arg.is_a?(String)}
101
-
102
- named_route = build_named_route_call(record_or_hash_or_array, namespace, inflection, options)
100
+ named_route = build_named_route_call(record_or_hash_or_array, inflection, options)
103
101
 
104
102
  url_options = options.except(:action, :routing_type)
105
103
  unless url_options.empty?
@@ -153,7 +151,7 @@ module ActionController
153
151
  options[:routing_type] || :url
154
152
  end
155
153
 
156
- def build_named_route_call(records, namespace, inflection, options = {})
154
+ def build_named_route_call(records, inflection, options = {})
157
155
  unless records.is_a?(Array)
158
156
  record = extract_record(records)
159
157
  route = ''
@@ -163,7 +161,7 @@ module ActionController
163
161
  if parent.is_a?(Symbol) || parent.is_a?(String)
164
162
  string << "#{parent}_"
165
163
  else
166
- string << "#{RecordIdentifier.__send__("plural_class_name", parent)}".singularize
164
+ string << RecordIdentifier.__send__("plural_class_name", parent).singularize
167
165
  string << "_"
168
166
  end
169
167
  end
@@ -172,12 +170,12 @@ module ActionController
172
170
  if record.is_a?(Symbol) || record.is_a?(String)
173
171
  route << "#{record}_"
174
172
  else
175
- route << "#{RecordIdentifier.__send__("plural_class_name", record)}"
173
+ route << RecordIdentifier.__send__("plural_class_name", record)
176
174
  route = route.singularize if inflection == :singular
177
175
  route << "_"
178
176
  end
179
177
 
180
- action_prefix(options) + namespace + route + routing_type(options).to_s
178
+ action_prefix(options) + route + routing_type(options).to_s
181
179
  end
182
180
 
183
181
  def extract_record(record_or_hash_or_array)
@@ -187,18 +185,5 @@ module ActionController
187
185
  else record_or_hash_or_array
188
186
  end
189
187
  end
190
-
191
- # Remove the first symbols from the array and return the url prefix
192
- # implied by those symbols.
193
- def extract_namespace(record_or_hash_or_array)
194
- return "" unless record_or_hash_or_array.is_a?(Array)
195
-
196
- namespace_keys = []
197
- while (key = record_or_hash_or_array.first) && key.is_a?(String) || key.is_a?(Symbol)
198
- namespace_keys << record_or_hash_or_array.shift
199
- end
200
-
201
- namespace_keys.map {|k| "#{k}_"}.join
202
- end
203
188
  end
204
189
  end
@@ -0,0 +1,36 @@
1
+ # Rack 1.0 does not allow string subclass body. This does not play well with our ActionView::SafeBuffer.
2
+ # The next release of Rack will be allowing string subclass body - http://github.com/rack/rack/commit/de668df02802a0335376a81ba709270e43ba9d55
3
+ # TODO : Remove this monkey patch after the next release of Rack
4
+
5
+ module RackLintPatch
6
+ module AllowStringSubclass
7
+ def self.included(base)
8
+ base.send :alias_method, :each, :each_with_hack
9
+ end
10
+
11
+ def each_with_hack
12
+ @closed = false
13
+
14
+ @body.each { |part|
15
+ assert("Body yielded non-string value #{part.inspect}") {
16
+ part.kind_of?(String)
17
+ }
18
+ yield part
19
+ }
20
+
21
+ if @body.respond_to?(:to_path)
22
+ assert("The file identified by body.to_path does not exist") {
23
+ ::File.exist? @body.to_path
24
+ }
25
+ end
26
+ end
27
+ end
28
+
29
+ begin
30
+ app = proc {|env| [200, {"Content-Type" => "text/plain", "Content-Length" => "12"}, [Class.new(String).new("Hello World!")]] }
31
+ response = Rack::MockRequest.new(Rack::Lint.new(app)).get('/')
32
+ rescue Rack::Lint::LintError => e
33
+ raise(e) unless e.message =~ /Body yielded non-string value/
34
+ Rack::Lint.send :include, AllowStringSubclass
35
+ end
36
+ end
@@ -89,9 +89,13 @@ module ActionController #:nodoc:
89
89
  request.method == :get ||
90
90
  request.xhr? ||
91
91
  !verifiable_request_format? ||
92
- form_authenticity_token == params[request_forgery_protection_token]
92
+ form_authenticity_token == form_authenticity_param
93
93
  end
94
-
94
+
95
+ def form_authenticity_param
96
+ params[request_forgery_protection_token]
97
+ end
98
+
95
99
  def verifiable_request_format?
96
100
  !request.content_type.nil? && request.content_type.verify_request?
97
101
  end
@@ -47,7 +47,8 @@ module ActionController # :nodoc:
47
47
  @block = nil
48
48
 
49
49
  @body = "",
50
- @session, @assigns = [], []
50
+ @session = []
51
+ @assigns = []
51
52
  end
52
53
 
53
54
  def location; headers['Location'] end
@@ -0,0 +1,29 @@
1
+ module ActionController
2
+ class StringCoercion
3
+ class UglyBody < ActiveSupport::BasicObject
4
+ def initialize(body)
5
+ @body = body
6
+ end
7
+
8
+ def each
9
+ @body.each do |part|
10
+ yield part.to_s
11
+ end
12
+ end
13
+
14
+ private
15
+ def method_missing(*args, &block)
16
+ @body.__send__(*args, &block)
17
+ end
18
+ end
19
+
20
+ def initialize(app)
21
+ @app = app
22
+ end
23
+
24
+ def call(env)
25
+ status, headers, body = @app.call(env)
26
+ [status, headers, UglyBody.new(body)]
27
+ end
28
+ end
29
+ end