actionpack 5.1.7 → 5.2.4.3

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 (148) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +282 -362
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +5 -5
  5. data/lib/abstract_controller.rb +3 -0
  6. data/lib/abstract_controller/asset_paths.rb +2 -0
  7. data/lib/abstract_controller/base.rb +10 -2
  8. data/lib/abstract_controller/caching.rb +3 -2
  9. data/lib/abstract_controller/caching/fragments.rb +30 -7
  10. data/lib/abstract_controller/callbacks.rb +25 -3
  11. data/lib/abstract_controller/collector.rb +2 -0
  12. data/lib/abstract_controller/error.rb +2 -0
  13. data/lib/abstract_controller/helpers.rb +4 -5
  14. data/lib/abstract_controller/logger.rb +2 -0
  15. data/lib/abstract_controller/railties/routes_helpers.rb +2 -0
  16. data/lib/abstract_controller/rendering.rb +9 -16
  17. data/lib/abstract_controller/translation.rb +2 -0
  18. data/lib/abstract_controller/url_for.rb +2 -0
  19. data/lib/action_controller.rb +3 -0
  20. data/lib/action_controller/api.rb +2 -0
  21. data/lib/action_controller/api/api_rendering.rb +2 -0
  22. data/lib/action_controller/base.rb +3 -0
  23. data/lib/action_controller/caching.rb +2 -0
  24. data/lib/action_controller/form_builder.rb +2 -0
  25. data/lib/action_controller/log_subscriber.rb +5 -3
  26. data/lib/action_controller/metal.rb +13 -14
  27. data/lib/action_controller/metal/basic_implicit_render.rb +2 -0
  28. data/lib/action_controller/metal/conditional_get.rb +4 -3
  29. data/lib/action_controller/metal/content_security_policy.rb +52 -0
  30. data/lib/action_controller/metal/cookies.rb +2 -0
  31. data/lib/action_controller/metal/data_streaming.rb +7 -5
  32. data/lib/action_controller/metal/etag_with_flash.rb +2 -0
  33. data/lib/action_controller/metal/etag_with_template_digest.rb +3 -2
  34. data/lib/action_controller/metal/exceptions.rb +2 -3
  35. data/lib/action_controller/metal/flash.rb +3 -2
  36. data/lib/action_controller/metal/force_ssl.rb +4 -2
  37. data/lib/action_controller/metal/head.rb +2 -0
  38. data/lib/action_controller/metal/helpers.rb +4 -3
  39. data/lib/action_controller/metal/http_authentication.rb +8 -9
  40. data/lib/action_controller/metal/implicit_render.rb +2 -0
  41. data/lib/action_controller/metal/instrumentation.rb +4 -6
  42. data/lib/action_controller/metal/live.rb +3 -1
  43. data/lib/action_controller/metal/mime_responds.rb +3 -1
  44. data/lib/action_controller/metal/parameter_encoding.rb +2 -0
  45. data/lib/action_controller/metal/params_wrapper.rb +14 -10
  46. data/lib/action_controller/metal/redirecting.rb +22 -11
  47. data/lib/action_controller/metal/renderers.rb +4 -3
  48. data/lib/action_controller/metal/rendering.rb +2 -2
  49. data/lib/action_controller/metal/request_forgery_protection.rb +62 -10
  50. data/lib/action_controller/metal/rescue.rb +5 -3
  51. data/lib/action_controller/metal/streaming.rb +3 -1
  52. data/lib/action_controller/metal/strong_parameters.rb +36 -25
  53. data/lib/action_controller/metal/testing.rb +2 -6
  54. data/lib/action_controller/metal/url_for.rb +2 -0
  55. data/lib/action_controller/railtie.rb +16 -4
  56. data/lib/action_controller/railties/helpers.rb +2 -0
  57. data/lib/action_controller/renderer.rb +2 -0
  58. data/lib/action_controller/template_assertions.rb +2 -0
  59. data/lib/action_controller/test_case.rb +16 -10
  60. data/lib/action_dispatch.rb +9 -5
  61. data/lib/action_dispatch/http/cache.rb +22 -14
  62. data/lib/action_dispatch/http/content_security_policy.rb +272 -0
  63. data/lib/action_dispatch/http/filter_parameters.rb +4 -2
  64. data/lib/action_dispatch/http/filter_redirect.rb +2 -0
  65. data/lib/action_dispatch/http/headers.rb +2 -0
  66. data/lib/action_dispatch/http/mime_negotiation.rb +4 -8
  67. data/lib/action_dispatch/http/mime_type.rb +15 -13
  68. data/lib/action_dispatch/http/mime_types.rb +17 -2
  69. data/lib/action_dispatch/http/parameter_filter.rb +2 -0
  70. data/lib/action_dispatch/http/parameters.rb +6 -9
  71. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  72. data/lib/action_dispatch/http/request.rb +36 -16
  73. data/lib/action_dispatch/http/response.rb +11 -9
  74. data/lib/action_dispatch/http/upload.rb +2 -0
  75. data/lib/action_dispatch/http/url.rb +5 -6
  76. data/lib/action_dispatch/journey.rb +2 -0
  77. data/lib/action_dispatch/journey/formatter.rb +4 -2
  78. data/lib/action_dispatch/journey/gtg/builder.rb +2 -0
  79. data/lib/action_dispatch/journey/gtg/simulator.rb +2 -8
  80. data/lib/action_dispatch/journey/gtg/transition_table.rb +3 -2
  81. data/lib/action_dispatch/journey/nfa/builder.rb +2 -0
  82. data/lib/action_dispatch/journey/nfa/dot.rb +12 -10
  83. data/lib/action_dispatch/journey/nfa/simulator.rb +2 -0
  84. data/lib/action_dispatch/journey/nfa/transition_table.rb +2 -0
  85. data/lib/action_dispatch/journey/nodes/node.rb +2 -0
  86. data/lib/action_dispatch/journey/parser_extras.rb +2 -0
  87. data/lib/action_dispatch/journey/path/pattern.rb +4 -1
  88. data/lib/action_dispatch/journey/route.rb +15 -6
  89. data/lib/action_dispatch/journey/router.rb +3 -1
  90. data/lib/action_dispatch/journey/router/utils.rb +14 -7
  91. data/lib/action_dispatch/journey/routes.rb +3 -1
  92. data/lib/action_dispatch/journey/scanner.rb +1 -0
  93. data/lib/action_dispatch/journey/visitors.rb +5 -3
  94. data/lib/action_dispatch/middleware/callbacks.rb +2 -0
  95. data/lib/action_dispatch/middleware/cookies.rb +148 -91
  96. data/lib/action_dispatch/middleware/debug_exceptions.rb +4 -2
  97. data/lib/action_dispatch/middleware/debug_locks.rb +9 -7
  98. data/lib/action_dispatch/middleware/exception_wrapper.rb +5 -6
  99. data/lib/action_dispatch/middleware/executor.rb +2 -0
  100. data/lib/action_dispatch/middleware/flash.rb +4 -2
  101. data/lib/action_dispatch/middleware/public_exceptions.rb +6 -4
  102. data/lib/action_dispatch/middleware/reloader.rb +2 -0
  103. data/lib/action_dispatch/middleware/remote_ip.rb +7 -5
  104. data/lib/action_dispatch/middleware/request_id.rb +3 -1
  105. data/lib/action_dispatch/middleware/session/abstract_store.rb +17 -1
  106. data/lib/action_dispatch/middleware/session/cache_store.rb +13 -6
  107. data/lib/action_dispatch/middleware/session/cookie_store.rb +31 -32
  108. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +2 -0
  109. data/lib/action_dispatch/middleware/show_exceptions.rb +3 -1
  110. data/lib/action_dispatch/middleware/ssl.rb +44 -38
  111. data/lib/action_dispatch/middleware/stack.rb +4 -2
  112. data/lib/action_dispatch/middleware/static.rb +14 -12
  113. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +21 -0
  114. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +13 -0
  115. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +1 -0
  116. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +6 -2
  117. data/lib/action_dispatch/railtie.rb +11 -1
  118. data/lib/action_dispatch/request/session.rb +16 -5
  119. data/lib/action_dispatch/request/utils.rb +6 -4
  120. data/lib/action_dispatch/routing.rb +3 -1
  121. data/lib/action_dispatch/routing/endpoint.rb +9 -2
  122. data/lib/action_dispatch/routing/inspector.rb +6 -4
  123. data/lib/action_dispatch/routing/mapper.rb +64 -52
  124. data/lib/action_dispatch/routing/polymorphic_routes.rb +2 -0
  125. data/lib/action_dispatch/routing/redirection.rb +7 -5
  126. data/lib/action_dispatch/routing/route_set.rb +29 -24
  127. data/lib/action_dispatch/routing/routes_proxy.rb +5 -2
  128. data/lib/action_dispatch/routing/url_for.rb +25 -5
  129. data/lib/action_dispatch/system_test_case.rb +22 -6
  130. data/lib/action_dispatch/system_testing/browser.rb +49 -0
  131. data/lib/action_dispatch/system_testing/driver.rb +9 -3
  132. data/lib/action_dispatch/system_testing/server.rb +2 -16
  133. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +12 -14
  134. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +8 -2
  135. data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +26 -0
  136. data/lib/action_dispatch/testing/assertion_response.rb +2 -0
  137. data/lib/action_dispatch/testing/assertions.rb +2 -0
  138. data/lib/action_dispatch/testing/assertions/response.rb +4 -2
  139. data/lib/action_dispatch/testing/assertions/routing.rb +5 -5
  140. data/lib/action_dispatch/testing/integration.rb +24 -21
  141. data/lib/action_dispatch/testing/request_encoder.rb +3 -1
  142. data/lib/action_dispatch/testing/test_process.rb +2 -0
  143. data/lib/action_dispatch/testing/test_request.rb +3 -1
  144. data/lib/action_dispatch/testing/test_response.rb +23 -3
  145. data/lib/action_pack.rb +3 -1
  146. data/lib/action_pack/gem_version.rb +5 -3
  147. data/lib/action_pack/version.rb +2 -0
  148. metadata +23 -11
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "action_dispatch/http/request"
2
4
  require "action_dispatch/middleware/exception_wrapper"
3
5
  require "action_dispatch/routing/inspector"
@@ -10,7 +12,7 @@ module ActionDispatch
10
12
  # This middleware is responsible for logging exceptions and
11
13
  # showing a debugging page in case the request is local.
12
14
  class DebugExceptions
13
- RESCUES_TEMPLATE_PATH = File.expand_path("../templates", __FILE__)
15
+ RESCUES_TEMPLATE_PATH = File.expand_path("templates", __dir__)
14
16
 
15
17
  class DebugView < ActionView::Base
16
18
  def debug_params(params)
@@ -21,7 +23,7 @@ module ActionDispatch
21
23
  if clean_params.empty?
22
24
  "None"
23
25
  else
24
- PP.pp(clean_params, "", 200)
26
+ PP.pp(clean_params, "".dup, 200)
25
27
  end
26
28
  end
27
29
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActionDispatch
2
4
  # This middleware can be used to diagnose deadlocks in the autoload interlock.
3
5
  #
@@ -41,7 +43,7 @@ module ActionDispatch
41
43
 
42
44
  private
43
45
  def render_details(req)
44
- threads = ActiveSupport::Dependencies.interlock.raw_state do |threads|
46
+ threads = ActiveSupport::Dependencies.interlock.raw_state do |raw_threads|
45
47
  # The Interlock itself comes to a complete halt as long as this block
46
48
  # is executing. That gives us a more consistent picture of everything,
47
49
  # but creates a pretty strong Observer Effect.
@@ -51,29 +53,29 @@ module ActionDispatch
51
53
  # strictly diagnostic tool (to be used when something has gone wrong),
52
54
  # and not for any sort of general monitoring.
53
55
 
54
- threads.each.with_index do |(thread, info), idx|
56
+ raw_threads.each.with_index do |(thread, info), idx|
55
57
  info[:index] = idx
56
58
  info[:backtrace] = thread.backtrace
57
59
  end
58
60
 
59
- threads
61
+ raw_threads
60
62
  end
61
63
 
62
64
  str = threads.map do |thread, info|
63
65
  if info[:exclusive]
64
- lock_state = "Exclusive"
66
+ lock_state = "Exclusive".dup
65
67
  elsif info[:sharing] > 0
66
- lock_state = "Sharing"
68
+ lock_state = "Sharing".dup
67
69
  lock_state << " x#{info[:sharing]}" if info[:sharing] > 1
68
70
  else
69
- lock_state = "No lock"
71
+ lock_state = "No lock".dup
70
72
  end
71
73
 
72
74
  if info[:waiting]
73
75
  lock_state << " (yielded share)"
74
76
  end
75
77
 
76
- msg = "Thread #{info[:index]} [0x#{thread.__id__.to_s(16)} #{thread.status || 'dead'}] #{lock_state}\n"
78
+ msg = "Thread #{info[:index]} [0x#{thread.__id__.to_s(16)} #{thread.status || 'dead'}] #{lock_state}\n".dup
77
79
 
78
80
  if info[:sleeper]
79
81
  msg << " Waiting in #{info[:sleeper]}"
@@ -1,11 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/core_ext/module/attribute_accessors"
2
4
  require "rack/utils"
3
5
 
4
6
  module ActionDispatch
5
7
  class ExceptionWrapper
6
- cattr_accessor :rescue_responses
7
- @@rescue_responses = Hash.new(:internal_server_error)
8
- @@rescue_responses.merge!(
8
+ cattr_accessor :rescue_responses, default: Hash.new(:internal_server_error).merge!(
9
9
  "ActionController::RoutingError" => :not_found,
10
10
  "AbstractController::ActionNotFound" => :not_found,
11
11
  "ActionController::MethodNotAllowed" => :method_not_allowed,
@@ -21,12 +21,11 @@ module ActionDispatch
21
21
  "Rack::QueryParser::InvalidParameterError" => :bad_request
22
22
  )
23
23
 
24
- cattr_accessor :rescue_templates
25
- @@rescue_templates = Hash.new("diagnostics")
26
- @@rescue_templates.merge!(
24
+ cattr_accessor :rescue_templates, default: Hash.new("diagnostics").merge!(
27
25
  "ActionView::MissingTemplate" => "missing_template",
28
26
  "ActionController::RoutingError" => "routing_error",
29
27
  "AbstractController::ActionNotFound" => "unknown_action",
28
+ "ActiveRecord::StatementInvalid" => "invalid_statement",
30
29
  "ActionView::Template::Error" => "template_error"
31
30
  )
32
31
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "rack/body_proxy"
2
4
 
3
5
  module ActionDispatch
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/core_ext/hash/keys"
2
4
 
3
5
  module ActionDispatch
@@ -65,13 +67,13 @@ module ActionDispatch
65
67
  self.flash = flash_hash.dup
66
68
  end
67
69
 
68
- if (!session.respond_to?(:loaded?) || session.loaded?) && # (reset_session uses {}, which doesn't implement #loaded?)
70
+ if (!session.respond_to?(:loaded?) || session.loaded?) && # reset_session uses {}, which doesn't implement #loaded?
69
71
  session.key?("flash") && session["flash"].nil?
70
72
  session.delete("flash")
71
73
  end
72
74
  end
73
75
 
74
- def reset_session # :nodoc
76
+ def reset_session # :nodoc:
75
77
  super
76
78
  self.flash = nil
77
79
  end
@@ -1,11 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActionDispatch
2
4
  # When called, this middleware renders an error page. By default if an HTML
3
- # response is expected it will render static error pages from the `/public`
5
+ # response is expected it will render static error pages from the <tt>/public</tt>
4
6
  # directory. For example when this middleware receives a 500 response it will
5
- # render the template found in `/public/500.html`.
7
+ # render the template found in <tt>/public/500.html</tt>.
6
8
  # If an internationalized locale is set, this middleware will attempt to render
7
- # the template in `/public/500.<locale>.html`. If an internationalized template
8
- # is not found it will fall back on `/public/500.html`.
9
+ # the template in <tt>/public/500.<locale>.html</tt>. If an internationalized template
10
+ # is not found it will fall back on <tt>/public/500.html</tt>.
9
11
  #
10
12
  # When a request with a content type other than HTML is made, this middleware
11
13
  # will attempt to convert error information into the appropriate response type.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActionDispatch
2
4
  # ActionDispatch::Reloader wraps the request with callbacks provided by ActiveSupport::Reloader
3
5
  # callbacks, intended to assist with code reloading during development.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "ipaddr"
2
4
 
3
5
  module ActionDispatch
@@ -10,7 +12,7 @@ module ActionDispatch
10
12
  # by @gingerlime. A more detailed explanation of the algorithm is given
11
13
  # at GetIp#calculate_ip.
12
14
  #
13
- # Some Rack servers concatenate repeated headers, like {HTTP RFC 2616}[http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2]
15
+ # Some Rack servers concatenate repeated headers, like {HTTP RFC 2616}[https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2]
14
16
  # requires. Some Rack servers simply drop preceding headers, and only report
15
17
  # the value that was {given in the last header}[http://andre.arko.net/2011/12/26/repeated-headers-and-ruby-web-servers].
16
18
  # If you are behind multiple proxy servers (like NGINX to HAProxy to Unicorn)
@@ -29,7 +31,7 @@ module ActionDispatch
29
31
  # The default trusted IPs list simply includes IP addresses that are
30
32
  # guaranteed by the IP specification to be private addresses. Those will
31
33
  # not be the ultimate client IP in production, and so are discarded. See
32
- # http://en.wikipedia.org/wiki/Private_network for details.
34
+ # https://en.wikipedia.org/wiki/Private_network for details.
33
35
  TRUSTED_PROXIES = [
34
36
  "127.0.0.1", # localhost IPv4
35
37
  "::1", # localhost IPv6
@@ -157,13 +159,13 @@ module ActionDispatch
157
159
 
158
160
  def ips_from(header) # :doc:
159
161
  return [] unless header
160
- # Split the comma-separated list into an array of strings
162
+ # Split the comma-separated list into an array of strings.
161
163
  ips = header.strip.split(/[,\s]+/)
162
164
  ips.select do |ip|
163
165
  begin
164
- # Only return IPs that are valid according to the IPAddr#new method
166
+ # Only return IPs that are valid according to the IPAddr#new method.
165
167
  range = IPAddr.new(ip).to_range
166
- # we want to make sure nobody is sneaking a netmask in
168
+ # We want to make sure nobody is sneaking a netmask in.
167
169
  range.begin == range.end
168
170
  rescue ArgumentError
169
171
  nil
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "securerandom"
2
4
  require "active_support/core_ext/string/access"
3
5
 
@@ -28,7 +30,7 @@ module ActionDispatch
28
30
  private
29
31
  def make_request_id(request_id)
30
32
  if request_id.presence
31
- request_id.gsub(/[^\w\-]/, "".freeze).first(255)
33
+ request_id.gsub(/[^\w\-@]/, "".freeze).first(255)
32
34
  else
33
35
  internal_request_id
34
36
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "rack/utils"
2
4
  require "rack/request"
3
5
  require "rack/session/abstract/id"
@@ -53,7 +55,7 @@ module ActionDispatch
53
55
  rescue ArgumentError => argument_error
54
56
  if argument_error.message =~ %r{undefined class/module ([\w:]*\w)}
55
57
  begin
56
- # Note that the regexp does not allow $1 to end with a ':'
58
+ # Note that the regexp does not allow $1 to end with a ':'.
57
59
  $1.constantize
58
60
  rescue LoadError, NameError
59
61
  raise ActionDispatch::Session::SessionRestoreError
@@ -81,7 +83,21 @@ module ActionDispatch
81
83
  include SessionObject
82
84
 
83
85
  private
86
+ def set_cookie(request, session_id, cookie)
87
+ request.cookie_jar[key] = cookie
88
+ end
89
+ end
84
90
 
91
+ class AbstractSecureStore < Rack::Session::Abstract::PersistedSecure
92
+ include Compatibility
93
+ include StaleSessionCheck
94
+ include SessionObject
95
+
96
+ def generate_sid
97
+ Rack::Session::SessionId.new(super)
98
+ end
99
+
100
+ private
85
101
  def set_cookie(request, session_id, cookie)
86
102
  request.cookie_jar[key] = cookie
87
103
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "action_dispatch/middleware/session/abstract_store"
2
4
 
3
5
  module ActionDispatch
@@ -10,7 +12,7 @@ module ActionDispatch
10
12
  # * <tt>cache</tt> - The cache to use. If it is not specified, <tt>Rails.cache</tt> will be used.
11
13
  # * <tt>expire_after</tt> - The length of time a session will be stored before automatically expiring.
12
14
  # By default, the <tt>:expires_in</tt> option of the cache is used.
13
- class CacheStore < AbstractStore
15
+ class CacheStore < AbstractSecureStore
14
16
  def initialize(app, options = {})
15
17
  @cache = options[:cache] || Rails.cache
16
18
  options[:expire_after] ||= @cache.options[:expires_in]
@@ -19,7 +21,7 @@ module ActionDispatch
19
21
 
20
22
  # Get a session from the cache.
21
23
  def find_session(env, sid)
22
- unless sid && (session = @cache.read(cache_key(sid)))
24
+ unless sid && (session = get_session_with_fallback(sid))
23
25
  sid, session = generate_sid, {}
24
26
  end
25
27
  [sid, session]
@@ -27,7 +29,7 @@ module ActionDispatch
27
29
 
28
30
  # Set a session in the cache.
29
31
  def write_session(env, sid, session, options)
30
- key = cache_key(sid)
32
+ key = cache_key(sid.private_id)
31
33
  if session
32
34
  @cache.write(key, session, expires_in: options[:expire_after])
33
35
  else
@@ -38,14 +40,19 @@ module ActionDispatch
38
40
 
39
41
  # Remove a session from the cache.
40
42
  def delete_session(env, sid, options)
41
- @cache.delete(cache_key(sid))
43
+ @cache.delete(cache_key(sid.private_id))
44
+ @cache.delete(cache_key(sid.public_id))
42
45
  generate_sid
43
46
  end
44
47
 
45
48
  private
46
49
  # Turn the session id into a cache key.
47
- def cache_key(sid)
48
- "_session_id:#{sid}"
50
+ def cache_key(id)
51
+ "_session_id:#{id}"
52
+ end
53
+
54
+ def get_session_with_fallback(sid)
55
+ @cache.read(cache_key(sid.private_id)) || @cache.read(cache_key(sid.public_id))
49
56
  end
50
57
  end
51
58
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/core_ext/hash/keys"
2
4
  require "action_dispatch/middleware/session/abstract_store"
3
5
  require "rack/session/cookie"
@@ -19,39 +21,26 @@ module ActionDispatch
19
21
  # knowing your app's secret key, but can easily read their +user_id+. This
20
22
  # was the default for Rails 3 apps.
21
23
  #
22
- # If you have secret_key_base set, your cookies will be encrypted. This
24
+ # Your cookies will be encrypted using your apps secret_key_base. This
23
25
  # goes a step further than signed cookies in that encrypted cookies cannot
24
26
  # be altered or read by users. This is the default starting in Rails 4.
25
27
  #
26
- # If you have both secret_token and secret_key_base set, your cookies will
27
- # be encrypted, and signed cookies generated by Rails 3 will be
28
- # transparently read and encrypted to provide a smooth upgrade path.
29
- #
30
- # Configure your session store in config/initializers/session_store.rb:
28
+ # Configure your session store in <tt>config/initializers/session_store.rb</tt>:
31
29
  #
32
30
  # Rails.application.config.session_store :cookie_store, key: '_your_app_session'
33
31
  #
34
- # Configure your secret key in config/secrets.yml:
35
- #
36
- # development:
37
- # secret_key_base: 'secret key'
38
- #
39
- # To generate a secret key for an existing application, run `rails secret`.
32
+ # In the development and test environments your application's secret key base is
33
+ # generated by Rails and stored in a temporary file in <tt>tmp/development_secret.txt</tt>.
34
+ # In all other environments, it is stored encrypted in the
35
+ # <tt>config/credentials.yml.enc</tt> file.
40
36
  #
41
- # If you are upgrading an existing Rails 3 app, you should leave your
42
- # existing secret_token in place and simply add the new secret_key_base.
43
- # Note that you should wait to set secret_key_base until you have 100% of
44
- # your userbase on Rails 4 and are reasonably sure you will not need to
45
- # rollback to Rails 3. This is because cookies signed based on the new
46
- # secret_key_base in Rails 4 are not backwards compatible with Rails 3.
47
- # You are free to leave your existing secret_token in place, not set the
48
- # new secret_key_base, and ignore the deprecation warnings until you are
49
- # reasonably sure that your upgrade is otherwise complete. Additionally,
50
- # you should take care to make sure you are not relying on the ability to
51
- # decode signed cookies generated by your app in external applications or
52
- # JavaScript before upgrading.
37
+ # If your application was not updated to Rails 5.2 defaults, the secret_key_base
38
+ # will be found in the old <tt>config/secrets.yml</tt> file.
53
39
  #
54
- # Note that changing the secret key will invalidate all existing sessions!
40
+ # Note that changing your secret_key_base will invalidate all existing session.
41
+ # Additionally, you should take care to make sure you are not relying on the
42
+ # ability to decode signed cookies generated by your app in external
43
+ # applications or JavaScript before changing it.
55
44
  #
56
45
  # Because CookieStore extends Rack::Session::Abstract::Persisted, many of the
57
46
  # options described there can be used to customize the session cookie that
@@ -62,7 +51,16 @@ module ActionDispatch
62
51
  # would set the session cookie to expire automatically 14 days after creation.
63
52
  # Other useful options include <tt>:key</tt>, <tt>:secure</tt> and
64
53
  # <tt>:httponly</tt>.
65
- class CookieStore < AbstractStore
54
+ class CookieStore < AbstractSecureStore
55
+ class SessionId < DelegateClass(Rack::Session::SessionId)
56
+ attr_reader :cookie_value
57
+
58
+ def initialize(session_id, cookie_value = {})
59
+ super(session_id)
60
+ @cookie_value = cookie_value
61
+ end
62
+ end
63
+
66
64
  def initialize(app, options = {})
67
65
  super(app, options.merge!(cookie_only: true))
68
66
  end
@@ -70,7 +68,7 @@ module ActionDispatch
70
68
  def delete_session(req, session_id, options)
71
69
  new_sid = generate_sid unless options[:drop]
72
70
  # Reset hash and Assign the new session id
73
- req.set_header("action_dispatch.request.unsigned_session_cookie", new_sid ? { "session_id" => new_sid } : {})
71
+ req.set_header("action_dispatch.request.unsigned_session_cookie", new_sid ? { "session_id" => new_sid.public_id } : {})
74
72
  new_sid
75
73
  end
76
74
 
@@ -78,7 +76,7 @@ module ActionDispatch
78
76
  stale_session_check! do
79
77
  data = unpacked_cookie_data(req)
80
78
  data = persistent_session_id!(data)
81
- [data["session_id"], data]
79
+ [Rack::Session::SessionId.new(data["session_id"]), data]
82
80
  end
83
81
  end
84
82
 
@@ -86,7 +84,8 @@ module ActionDispatch
86
84
 
87
85
  def extract_session_id(req)
88
86
  stale_session_check! do
89
- unpacked_cookie_data(req)["session_id"]
87
+ sid = unpacked_cookie_data(req)["session_id"]
88
+ sid && Rack::Session::SessionId.new(sid)
90
89
  end
91
90
  end
92
91
 
@@ -104,13 +103,13 @@ module ActionDispatch
104
103
 
105
104
  def persistent_session_id!(data, sid = nil)
106
105
  data ||= {}
107
- data["session_id"] ||= sid || generate_sid
106
+ data["session_id"] ||= sid || generate_sid.public_id
108
107
  data
109
108
  end
110
109
 
111
110
  def write_session(req, sid, session_data, options)
112
- session_data["session_id"] = sid
113
- session_data
111
+ session_data["session_id"] = sid.public_id
112
+ SessionId.new(sid, session_data)
114
113
  end
115
114
 
116
115
  def set_cookie(request, session_id, cookie)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "action_dispatch/middleware/session/abstract_store"
2
4
  begin
3
5
  require "rack/session/dalli"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "action_dispatch/http/request"
2
4
  require "action_dispatch/middleware/exception_wrapper"
3
5
 
@@ -8,7 +10,7 @@ module ActionDispatch
8
10
  # The exceptions app should be passed as parameter on initialization
9
11
  # of ShowExceptions. Every time there is an exception, ShowExceptions will
10
12
  # store the exception in env["action_dispatch.exception"], rewrite the
11
- # PATH_INFO to the exception status code and call the rack app.
13
+ # PATH_INFO to the exception status code and call the Rack app.
12
14
  #
13
15
  # If the application returns a "X-Cascade" pass response, this middleware
14
16
  # will send an empty response as result with the correct status code.
@@ -1,50 +1,56 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActionDispatch
2
- # This middleware is added to the stack when `config.force_ssl = true`, and is passed
3
- # the options set in `config.ssl_options`. It does three jobs to enforce secure HTTP
4
+ # This middleware is added to the stack when <tt>config.force_ssl = true</tt>, and is passed
5
+ # the options set in +config.ssl_options+. It does three jobs to enforce secure HTTP
4
6
  # requests:
5
7
  #
6
- # 1. TLS redirect: Permanently redirects http:// requests to https://
7
- # with the same URL host, path, etc. Enabled by default. Set `config.ssl_options`
8
- # to modify the destination URL
9
- # (e.g. `redirect: { host: "secure.widgets.com", port: 8080 }`), or set
10
- # `redirect: false` to disable this feature.
8
+ # 1. <b>TLS redirect</b>: Permanently redirects +http://+ requests to +https://+
9
+ # with the same URL host, path, etc. Enabled by default. Set +config.ssl_options+
10
+ # to modify the destination URL
11
+ # (e.g. <tt>redirect: { host: "secure.widgets.com", port: 8080 }</tt>), or set
12
+ # <tt>redirect: false</tt> to disable this feature.
13
+ #
14
+ # Requests can opt-out of redirection with +exclude+:
15
+ #
16
+ # config.ssl_options = { redirect: { exclude: -> request { request.path =~ /healthcheck/ } } }
11
17
  #
12
18
  # Cookies will not be flagged as secure for excluded requests.
13
19
  #
14
- # 2. Secure cookies: Sets the `secure` flag on cookies to tell browsers they
15
- # mustn't be sent along with http:// requests. Enabled by default. Set
16
- # `config.ssl_options` with `secure_cookies: false` to disable this feature.
20
+ # 2. <b>Secure cookies</b>: Sets the +secure+ flag on cookies to tell browsers they
21
+ # must not be sent along with +http://+ requests. Enabled by default. Set
22
+ # +config.ssl_options+ with <tt>secure_cookies: false</tt> to disable this feature.
17
23
  #
18
- # 3. HTTP Strict Transport Security (HSTS): Tells the browser to remember
19
- # this site as TLS-only and automatically redirect non-TLS requests.
20
- # Enabled by default. Configure `config.ssl_options` with `hsts: false` to disable.
24
+ # 3. <b>HTTP Strict Transport Security (HSTS)</b>: Tells the browser to remember
25
+ # this site as TLS-only and automatically redirect non-TLS requests.
26
+ # Enabled by default. Configure +config.ssl_options+ with <tt>hsts: false</tt> to disable.
21
27
  #
22
- # Set `config.ssl_options` with `hsts: { }` to configure HSTS:
23
- # * `expires`: How long, in seconds, these settings will stick. The minimum
24
- # required to qualify for browser preload lists is `18.weeks`. Defaults to
25
- # `180.days` (recommended).
26
- # * `subdomains`: Set to `true` to tell the browser to apply these settings
27
- # to all subdomains. This protects your cookies from interception by a
28
- # vulnerable site on a subdomain. Defaults to `true`.
29
- # * `preload`: Advertise that this site may be included in browsers'
30
- # preloaded HSTS lists. HSTS protects your site on every visit *except the
31
- # first visit* since it hasn't seen your HSTS header yet. To close this
32
- # gap, browser vendors include a baked-in list of HSTS-enabled sites.
33
- # Go to https://hstspreload.appspot.com to submit your site for inclusion.
34
- # Defaults to `false`.
28
+ # Set +config.ssl_options+ with <tt>hsts: { ... }</tt> to configure HSTS:
35
29
  #
36
- # To turn off HSTS, omitting the header is not enough. Browsers will remember the
37
- # original HSTS directive until it expires. Instead, use the header to tell browsers to
38
- # expire HSTS immediately. Setting `hsts: false` is a shortcut for
39
- # `hsts: { expires: 0 }`.
30
+ # * +expires+: How long, in seconds, these settings will stick. The minimum
31
+ # required to qualify for browser preload lists is 1 year. Defaults to
32
+ # 1 year (recommended).
40
33
  #
41
- # Requests can opt-out of redirection with `exclude`:
34
+ # * +subdomains+: Set to +true+ to tell the browser to apply these settings
35
+ # to all subdomains. This protects your cookies from interception by a
36
+ # vulnerable site on a subdomain. Defaults to +true+.
42
37
  #
43
- # config.ssl_options = { redirect: { exclude: -> request { request.path =~ /healthcheck/ } } }
38
+ # * +preload+: Advertise that this site may be included in browsers'
39
+ # preloaded HSTS lists. HSTS protects your site on every visit <i>except the
40
+ # first visit</i> since it hasn't seen your HSTS header yet. To close this
41
+ # gap, browser vendors include a baked-in list of HSTS-enabled sites.
42
+ # Go to https://hstspreload.org to submit your site for inclusion.
43
+ # Defaults to +false+.
44
+ #
45
+ # To turn off HSTS, omitting the header is not enough. Browsers will remember the
46
+ # original HSTS directive until it expires. Instead, use the header to tell browsers to
47
+ # expire HSTS immediately. Setting <tt>hsts: false</tt> is a shortcut for
48
+ # <tt>hsts: { expires: 0 }</tt>.
44
49
  class SSL
45
- # Default to 180 days, the low end for https://www.ssllabs.com/ssltest/
46
- # and greater than the 18-week requirement for browser preload lists.
47
- HSTS_EXPIRES_IN = 15552000
50
+ # :stopdoc:
51
+
52
+ # Default to 1 year, the minimum for browser preload lists.
53
+ HSTS_EXPIRES_IN = 31536000
48
54
 
49
55
  def self.default_hsts_options
50
56
  { expires: HSTS_EXPIRES_IN, subdomains: true, preload: false }
@@ -94,9 +100,9 @@ module ActionDispatch
94
100
  end
95
101
  end
96
102
 
97
- # http://tools.ietf.org/html/rfc6797#section-6.1
103
+ # https://tools.ietf.org/html/rfc6797#section-6.1
98
104
  def build_hsts_header(hsts)
99
- value = "max-age=#{hsts[:expires].to_i}"
105
+ value = "max-age=#{hsts[:expires].to_i}".dup
100
106
  value << "; includeSubDomains" if hsts[:subdomains]
101
107
  value << "; preload" if hsts[:preload]
102
108
  value
@@ -135,7 +141,7 @@ module ActionDispatch
135
141
  host = @redirect[:host] || request.host
136
142
  port = @redirect[:port] || request.port
137
143
 
138
- location = "https://#{host}"
144
+ location = "https://#{host}".dup
139
145
  location << ":#{port}" if port != 80 && port != 443
140
146
  location << request.fullpath
141
147
  location