actionpack 5.0.0.beta3 → 5.0.0.beta4

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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +101 -16
  3. data/lib/abstract_controller/base.rb +2 -4
  4. data/lib/abstract_controller/error.rb +4 -0
  5. data/lib/abstract_controller/helpers.rb +2 -1
  6. data/lib/abstract_controller/rendering.rb +1 -0
  7. data/lib/action_controller/api.rb +20 -19
  8. data/lib/action_controller/metal/basic_implicit_render.rb +1 -1
  9. data/lib/action_controller/metal/conditional_get.rb +52 -21
  10. data/lib/action_controller/metal/cookies.rb +1 -1
  11. data/lib/action_controller/metal/data_streaming.rb +9 -10
  12. data/lib/action_controller/metal/force_ssl.rb +4 -4
  13. data/lib/action_controller/metal/http_authentication.rb +8 -3
  14. data/lib/action_controller/metal/implicit_render.rb +55 -17
  15. data/lib/action_controller/metal/instrumentation.rb +3 -2
  16. data/lib/action_controller/metal/live.rb +2 -2
  17. data/lib/action_controller/metal/mime_responds.rb +1 -1
  18. data/lib/action_controller/metal/redirecting.rb +1 -1
  19. data/lib/action_controller/metal/request_forgery_protection.rb +3 -2
  20. data/lib/action_controller/metal/rescue.rb +6 -2
  21. data/lib/action_controller/metal/strong_parameters.rb +30 -3
  22. data/lib/action_controller/renderer.rb +1 -1
  23. data/lib/action_controller/test_case.rb +2 -2
  24. data/lib/action_dispatch.rb +1 -1
  25. data/lib/action_dispatch/http/cache.rb +49 -15
  26. data/lib/action_dispatch/http/filter_parameters.rb +9 -3
  27. data/lib/action_dispatch/http/headers.rb +2 -2
  28. data/lib/action_dispatch/http/mime_types.rb +1 -1
  29. data/lib/action_dispatch/http/request.rb +0 -1
  30. data/lib/action_dispatch/journey/formatter.rb +7 -2
  31. data/lib/action_dispatch/journey/route.rb +1 -1
  32. data/lib/action_dispatch/middleware/callbacks.rb +10 -1
  33. data/lib/action_dispatch/middleware/exception_wrapper.rb +0 -1
  34. data/lib/action_dispatch/middleware/executor.rb +19 -0
  35. data/lib/action_dispatch/middleware/flash.rb +5 -0
  36. data/lib/action_dispatch/middleware/params_parser.rb +1 -0
  37. data/lib/action_dispatch/middleware/reloader.rb +12 -54
  38. data/lib/action_dispatch/middleware/ssl.rb +19 -3
  39. data/lib/action_dispatch/railtie.rb +2 -0
  40. data/lib/action_dispatch/request/session.rb +16 -10
  41. data/lib/action_dispatch/routing.rb +12 -3
  42. data/lib/action_dispatch/routing/inspector.rb +3 -3
  43. data/lib/action_dispatch/routing/mapper.rb +6 -3
  44. data/lib/action_dispatch/routing/route_set.rb +16 -15
  45. data/lib/action_dispatch/routing/url_for.rb +1 -1
  46. data/lib/action_dispatch/testing/assertions/routing.rb +1 -1
  47. data/lib/action_dispatch/testing/integration.rb +43 -27
  48. data/lib/action_pack/gem_version.rb +1 -1
  49. metadata +12 -11
  50. data/lib/action_dispatch/middleware/load_interlock.rb +0 -21
@@ -4,9 +4,11 @@ module ActionDispatch
4
4
  module Http
5
5
  # Allows you to specify sensitive parameters which will be replaced from
6
6
  # the request log by looking in the query string of the request and all
7
- # sub-hashes of the params hash to filter. If a block is given, each key and
8
- # value of the params hash and all sub-hashes is passed to it, the value
9
- # or key can be replaced using String#replace or similar method.
7
+ # sub-hashes of the params hash to filter. Filtering only certain sub-keys
8
+ # from a hash is possible by using the dot notation: 'credit_card.number'.
9
+ # If a block is given, each key and value of the params hash and all
10
+ # sub-hashes is passed to it, the value or key can be replaced using
11
+ # String#replace or similar method.
10
12
  #
11
13
  # env["action_dispatch.parameter_filter"] = [:password]
12
14
  # => replaces the value to all keys matching /password/i with "[FILTERED]"
@@ -14,6 +16,10 @@ module ActionDispatch
14
16
  # env["action_dispatch.parameter_filter"] = [:foo, "bar"]
15
17
  # => replaces the value to all keys matching /foo|bar/i with "[FILTERED]"
16
18
  #
19
+ # env["action_dispatch.parameter_filter"] = [ "credit_card.code" ]
20
+ # => replaces { credit_card: {code: "xxxx"} } with "[FILTERED]", does not
21
+ # change { file: { code: "xxxx"} }
22
+ #
17
23
  # env["action_dispatch.parameter_filter"] = -> (k, v) do
18
24
  # v.reverse! if k =~ /secret/i
19
25
  # end
@@ -5,7 +5,7 @@ module ActionDispatch
5
5
  # env = { "CONTENT_TYPE" => "text/plain", "HTTP_USER_AGENT" => "curl/7.43.0" }
6
6
  # headers = ActionDispatch::Http::Headers.new(env)
7
7
  # headers["Content-Type"] # => "text/plain"
8
- # headers["User-Agent"] # => "curl/7/43/0"
8
+ # headers["User-Agent"] # => "curl/7.43.0"
9
9
  #
10
10
  # Also note that when headers are mapped to CGI-like variables by the Rack
11
11
  # server, both dashes and underscores are converted to underscores. This
@@ -115,7 +115,7 @@ module ActionDispatch
115
115
 
116
116
  private
117
117
 
118
- # Converts a HTTP header name to an environment variable name if it is
118
+ # Converts an HTTP header name to an environment variable name if it is
119
119
  # not contained within the headers hash.
120
120
  def env_name(key)
121
121
  key = key.to_s
@@ -21,7 +21,7 @@ Mime::Type.register "video/mpeg", :mpeg, [], %w(mpg mpeg mpe)
21
21
  Mime::Type.register "application/xml", :xml, %w( text/xml application/x-xml )
22
22
  Mime::Type.register "application/rss+xml", :rss
23
23
  Mime::Type.register "application/atom+xml", :atom
24
- Mime::Type.register "application/x-yaml", :yaml, %w( text/yaml )
24
+ Mime::Type.register "application/x-yaml", :yaml, %w( text/yaml ), %w(yml yaml)
25
25
 
26
26
  Mime::Type.register "multipart/form-data", :multipart_form
27
27
  Mime::Type.register "application/x-www-form-urlencoded", :url_encoded_form
@@ -337,7 +337,6 @@ module ActionDispatch
337
337
  else
338
338
  self.session = {}
339
339
  end
340
- self.flash = nil
341
340
  end
342
341
 
343
342
  def session=(session) #:nodoc:
@@ -32,8 +32,13 @@ module ActionDispatch
32
32
 
33
33
  defaults = route.defaults
34
34
  required_parts = route.required_parts
35
- parameterized_parts.keep_if do |key, value|
36
- (defaults[key].nil? && value.present?) || value.to_s != defaults[key].to_s || required_parts.include?(key)
35
+
36
+ route.parts.reverse_each do |key|
37
+ break if defaults[key].nil? && parameterized_parts[key].present?
38
+ break if parameterized_parts[key].to_s != defaults[key].to_s
39
+ break if required_parts.include?(key)
40
+
41
+ parameterized_parts.delete(key)
37
42
  end
38
43
 
39
44
  return [route.format(parameterized_parts), params]
@@ -82,7 +82,7 @@ module ActionDispatch
82
82
  end
83
83
 
84
84
  def requirements # :nodoc:
85
- # needed for rails `rake routes`
85
+ # needed for rails `rails routes`
86
86
  @defaults.merge(path.requirements).delete_if { |_,v|
87
87
  /.+?/ == v
88
88
  }
@@ -7,7 +7,16 @@ module ActionDispatch
7
7
  define_callbacks :call
8
8
 
9
9
  class << self
10
- delegate :to_prepare, :to_cleanup, :to => "ActionDispatch::Reloader"
10
+ def to_prepare(*args, &block)
11
+ ActiveSupport::Reloader.to_prepare(*args, &block)
12
+ end
13
+
14
+ def to_cleanup(*args, &block)
15
+ ActiveSupport::Reloader.to_complete(*args, &block)
16
+ end
17
+
18
+ deprecate to_prepare: 'use ActiveSupport::Reloader.to_prepare instead',
19
+ to_cleanup: 'use ActiveSupport::Reloader.to_complete instead'
11
20
 
12
21
  def before(*args, &block)
13
22
  set_callback(:call, :before, *args, &block)
@@ -1,4 +1,3 @@
1
- require 'action_controller/metal/exceptions'
2
1
  require 'active_support/core_ext/module/attribute_accessors'
3
2
  require 'rack/utils'
4
3
 
@@ -0,0 +1,19 @@
1
+ require 'rack/body_proxy'
2
+
3
+ module ActionDispatch
4
+ class Executor
5
+ def initialize(app, executor)
6
+ @app, @executor = app, executor
7
+ end
8
+
9
+ def call(env)
10
+ state = @executor.run!
11
+ begin
12
+ response = @app.call(env)
13
+ returned = response << ::Rack::BodyProxy.new(response.pop) { state.complete! }
14
+ ensure
15
+ state.complete! unless returned
16
+ end
17
+ end
18
+ end
19
+ end
@@ -70,6 +70,11 @@ module ActionDispatch
70
70
  session.delete('flash')
71
71
  end
72
72
  end
73
+
74
+ def reset_session # :nodoc
75
+ super
76
+ self.flash = nil
77
+ end
73
78
  end
74
79
 
75
80
  class FlashNow #:nodoc:
@@ -37,6 +37,7 @@ module ActionDispatch
37
37
  # The +parsers+ argument can take Hash of parsers where key is identifying
38
38
  # content mime type, and value is a lambda that is going to process data.
39
39
  def self.new(app, parsers = {})
40
+ ActiveSupport::Deprecation.warn('ActionDispatch::ParamsParser is deprecated and will be removed in Rails 5.1. Configure the parameter parsing in ActionDispatch::Request.parameter_parsers.')
40
41
  parsers = parsers.transform_keys { |key| key.respond_to?(:symbol) ? key.symbol : key }
41
42
  ActionDispatch::Request.parameter_parsers = ActionDispatch::Request::DEFAULT_PARSERS.merge(parsers)
42
43
  app
@@ -23,74 +23,32 @@ module ActionDispatch
23
23
  # middleware stack, but are executed only when <tt>ActionDispatch::Reloader.prepare!</tt>
24
24
  # or <tt>ActionDispatch::Reloader.cleanup!</tt> are called manually.
25
25
  #
26
- class Reloader
27
- include ActiveSupport::Callbacks
28
- include ActiveSupport::Deprecation::Reporting
29
-
30
- define_callbacks :prepare
31
- define_callbacks :cleanup
32
-
33
- # Add a prepare callback. Prepare callbacks are run before each request, prior
34
- # to ActionDispatch::Callback's before callbacks.
26
+ class Reloader < Executor
35
27
  def self.to_prepare(*args, &block)
36
- unless block_given?
37
- warn "to_prepare without a block is deprecated. Please use a block"
38
- end
39
- set_callback(:prepare, *args, &block)
28
+ ActiveSupport::Reloader.to_prepare(*args, &block)
40
29
  end
41
30
 
42
- # Add a cleanup callback. Cleanup callbacks are run after each request is
43
- # complete (after #close is called on the response body).
44
31
  def self.to_cleanup(*args, &block)
45
- unless block_given?
46
- warn "to_cleanup without a block is deprecated. Please use a block"
47
- end
48
- set_callback(:cleanup, *args, &block)
32
+ ActiveSupport::Reloader.to_complete(*args, &block)
49
33
  end
50
34
 
51
- # Execute all prepare callbacks.
52
35
  def self.prepare!
53
- new(nil).prepare!
36
+ default_reloader.prepare!
54
37
  end
55
38
 
56
- # Execute all cleanup callbacks.
57
39
  def self.cleanup!
58
- new(nil).cleanup!
59
- end
60
-
61
- def initialize(app, condition=nil)
62
- @app = app
63
- @condition = condition || lambda { true }
64
- @validated = true
40
+ default_reloader.reload!
65
41
  end
66
42
 
67
- def call(env)
68
- @validated = @condition.call
69
- prepare!
70
-
71
- response = @app.call(env)
72
- response[2] = ::Rack::BodyProxy.new(response[2]) { cleanup! }
43
+ class << self
44
+ attr_accessor :default_reloader # :nodoc:
73
45
 
74
- response
75
- rescue Exception
76
- cleanup!
77
- raise
46
+ deprecate to_prepare: 'use ActiveSupport::Reloader.to_prepare instead',
47
+ to_cleanup: 'use ActiveSupport::Reloader.to_complete instead',
48
+ prepare!: 'use Rails.application.reloader.prepare! instead',
49
+ cleanup!: 'use Rails.application.reloader.reload! instead of cleanup + prepare'
78
50
  end
79
51
 
80
- def prepare! #:nodoc:
81
- run_callbacks :prepare if validated?
82
- end
83
-
84
- def cleanup! #:nodoc:
85
- run_callbacks :cleanup if validated?
86
- ensure
87
- @validated = true
88
- end
89
-
90
- private
91
-
92
- def validated? #:nodoc:
93
- @validated
94
- end
52
+ self.default_reloader = ActiveSupport::Reloader
95
53
  end
96
54
  end
@@ -23,7 +23,7 @@ module ActionDispatch
23
23
  # preload lists is `18.weeks`.
24
24
  # * `subdomains`: Set to `true` to tell the browser to apply these settings
25
25
  # to all subdomains. This protects your cookies from interception by a
26
- # vulnerable site on a subdomain. Defaults to `false`.
26
+ # vulnerable site on a subdomain. Defaults to `true`.
27
27
  # * `preload`: Advertise that this site may be included in browsers'
28
28
  # preloaded HSTS lists. HSTS protects your site on every visit *except the
29
29
  # first visit* since it hasn't seen your HSTS header yet. To close this
@@ -34,6 +34,10 @@ module ActionDispatch
34
34
  # original HSTS directive until it expires. Instead, use the header to tell browsers to
35
35
  # expire HSTS immediately. Setting `hsts: false` is a shortcut for
36
36
  # `hsts: { expires: 0 }`.
37
+ #
38
+ # Requests can opt-out of redirection with `exclude`:
39
+ #
40
+ # config.ssl_options = { redirect: { exclude: -> request { request.path =~ /healthcheck/ } } }
37
41
  class SSL
38
42
  # Default to 180 days, the low end for https://www.ssllabs.com/ssltest/
39
43
  # and greater than the 18-week requirement for browser preload lists.
@@ -49,14 +53,26 @@ module ActionDispatch
49
53
  if options[:host] || options[:port]
50
54
  ActiveSupport::Deprecation.warn <<-end_warning.strip_heredoc
51
55
  The `:host` and `:port` options are moving within `:redirect`:
52
- `config.ssl_options = { redirect: { host: …, port: … }}`.
56
+ `config.ssl_options = { redirect: { host: …, port: … } }`.
53
57
  end_warning
54
58
  @redirect = options.slice(:host, :port)
55
59
  else
56
60
  @redirect = redirect
57
61
  end
58
62
 
63
+ @exclude = @redirect && @redirect[:exclude] || proc { !@redirect }
59
64
  @secure_cookies = secure_cookies
65
+
66
+ if hsts != true && hsts != false && hsts[:subdomains].nil?
67
+ hsts[:subdomains] = false
68
+
69
+ ActiveSupport::Deprecation.warn <<-end_warning.strip_heredoc
70
+ In Rails 5.1, The `:subdomains` option of HSTS config will be treated as true if
71
+ unspecified. Set `config.ssl_options = { hsts: { subdomains: false } }` to opt out
72
+ of this behavior.
73
+ end_warning
74
+ end
75
+
60
76
  @hsts_header = build_hsts_header(normalize_hsts_options(hsts))
61
77
  end
62
78
 
@@ -69,7 +85,7 @@ module ActionDispatch
69
85
  flag_cookies_as_secure! headers if @secure_cookies
70
86
  end
71
87
  else
72
- return redirect_to_https request if @redirect
88
+ return redirect_to_https request unless @exclude.call(request)
73
89
  @app.call(env)
74
90
  end
75
91
  end
@@ -39,6 +39,8 @@ module ActionDispatch
39
39
  config.action_dispatch.always_write_cookie = Rails.env.development? if config.action_dispatch.always_write_cookie.nil?
40
40
  ActionDispatch::Cookies::CookieJar.always_write_cookie = config.action_dispatch.always_write_cookie
41
41
 
42
+ ActionDispatch::Reloader.default_reloader = app.reloader
43
+
42
44
  ActionDispatch.test_app = app
43
45
  end
44
46
  end
@@ -9,7 +9,7 @@ module ActionDispatch
9
9
 
10
10
  # Singleton object used to determine if an optional param wasn't specified
11
11
  Unspecified = Object.new
12
-
12
+
13
13
  # Creates a session hash, merging the properties of the previous session if any
14
14
  def self.create(store, req, default_options)
15
15
  session_was = find req
@@ -61,7 +61,7 @@ module ActionDispatch
61
61
  def initialize(by, req)
62
62
  @by = by
63
63
  @req = req
64
- @delegate = {}.with_indifferent_access
64
+ @delegate = {}
65
65
  @loaded = false
66
66
  @exists = nil # we haven't checked yet
67
67
  end
@@ -88,13 +88,13 @@ module ActionDispatch
88
88
  # nil if the given key is not found in the session.
89
89
  def [](key)
90
90
  load_for_read!
91
- @delegate[key]
91
+ @delegate[key.to_s]
92
92
  end
93
93
 
94
94
  # Returns true if the session has the given key or false.
95
95
  def has_key?(key)
96
96
  load_for_read!
97
- @delegate.key?(key)
97
+ @delegate.key?(key.to_s)
98
98
  end
99
99
  alias :key? :has_key?
100
100
  alias :include? :has_key?
@@ -112,7 +112,7 @@ module ActionDispatch
112
112
  # Writes given value to given key of the session.
113
113
  def []=(key, value)
114
114
  load_for_write!
115
- @delegate[key] = value
115
+ @delegate[key.to_s] = value
116
116
  end
117
117
 
118
118
  # Clears the session.
@@ -139,13 +139,13 @@ module ActionDispatch
139
139
  # # => {"session_id"=>"e29b9ea315edf98aad94cc78c34cc9b2", "foo" => "bar"}
140
140
  def update(hash)
141
141
  load_for_write!
142
- @delegate.update hash
142
+ @delegate.update stringify_keys(hash)
143
143
  end
144
144
 
145
145
  # Deletes given key from the session.
146
146
  def delete(key)
147
147
  load_for_write!
148
- @delegate.delete key
148
+ @delegate.delete key.to_s
149
149
  end
150
150
 
151
151
  # Returns value of the given key from the session, or raises +KeyError+
@@ -165,9 +165,9 @@ module ActionDispatch
165
165
  def fetch(key, default=Unspecified, &block)
166
166
  load_for_read!
167
167
  if default == Unspecified
168
- @delegate.fetch(key, &block)
168
+ @delegate.fetch(key.to_s, &block)
169
169
  else
170
- @delegate.fetch(key, default, &block)
170
+ @delegate.fetch(key.to_s, default, &block)
171
171
  end
172
172
  end
173
173
 
@@ -211,9 +211,15 @@ module ActionDispatch
211
211
  def load!
212
212
  id, session = @by.load_session @req
213
213
  options[:id] = id
214
- @delegate.replace(session)
214
+ @delegate.replace(stringify_keys(session))
215
215
  @loaded = true
216
216
  end
217
+
218
+ def stringify_keys(other)
219
+ other.each_with_object({}) { |(key, value), hash|
220
+ hash[key.to_s] = value
221
+ }
222
+ end
217
223
  end
218
224
  end
219
225
  end
@@ -73,14 +73,14 @@ module ActionDispatch
73
73
  # get 'post/:id' => 'posts#show'
74
74
  # post 'post/:id' => 'posts#create_comment'
75
75
  #
76
+ # Now, if you POST to <tt>/posts/:id</tt>, it will route to the <tt>create_comment</tt> action. A GET on the same
77
+ # URL will route to the <tt>show</tt> action.
78
+ #
76
79
  # If your route needs to respond to more than one HTTP method (or all methods) then using the
77
80
  # <tt>:via</tt> option on <tt>match</tt> is preferable.
78
81
  #
79
82
  # match 'post/:id' => 'posts#show', via: [:get, :post]
80
83
  #
81
- # Now, if you POST to <tt>/posts/:id</tt>, it will route to the <tt>create_comment</tt> action. A GET on the same
82
- # URL will route to the <tt>show</tt> action.
83
- #
84
84
  # == Named routes
85
85
  #
86
86
  # Routes can be named by passing an <tt>:as</tt> option,
@@ -252,5 +252,14 @@ module ActionDispatch
252
252
 
253
253
  SEPARATORS = %w( / . ? ) #:nodoc:
254
254
  HTTP_METHODS = [:get, :head, :post, :patch, :put, :delete, :options] #:nodoc:
255
+
256
+ #:stopdoc:
257
+ INSECURE_URL_PARAMETERS_MESSAGE = <<-MSG.squish
258
+ Attempting to generate a URL from non-sanitized request parameters!
259
+
260
+ An attacker can inject malicious data into the generated URL, such as
261
+ changing the host. Whitelist and sanitize passed parameters to be secure.
262
+ MSG
263
+ #:startdoc:
255
264
  end
256
265
  end
@@ -33,11 +33,11 @@ module ActionDispatch
33
33
  end
34
34
 
35
35
  def controller
36
- requirements[:controller] || ':controller'
36
+ parts.include?(:controller) ? ':controller' : requirements[:controller]
37
37
  end
38
38
 
39
39
  def action
40
- requirements[:action] || ':action'
40
+ parts.include?(:action) ? ':action' : requirements[:action]
41
41
  end
42
42
 
43
43
  def internal?
@@ -51,7 +51,7 @@ module ActionDispatch
51
51
 
52
52
  ##
53
53
  # This class is just used for displaying route information when someone
54
- # executes `rake routes` or looks at the RoutingError page.
54
+ # executes `rails routes` or looks at the RoutingError page.
55
55
  # People should not use this class.
56
56
  class RoutesInspector # :nodoc:
57
57
  def initialize(routes)