actionpack 7.1.5.1 → 7.2.0.beta1

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 (157) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +61 -642
  3. data/lib/abstract_controller/asset_paths.rb +2 -0
  4. data/lib/abstract_controller/base.rb +102 -98
  5. data/lib/abstract_controller/caching/fragments.rb +50 -53
  6. data/lib/abstract_controller/caching.rb +2 -0
  7. data/lib/abstract_controller/callbacks.rb +66 -64
  8. data/lib/abstract_controller/collector.rb +6 -6
  9. data/lib/abstract_controller/deprecator.rb +2 -0
  10. data/lib/abstract_controller/error.rb +2 -0
  11. data/lib/abstract_controller/helpers.rb +70 -85
  12. data/lib/abstract_controller/logger.rb +2 -0
  13. data/lib/abstract_controller/railties/routes_helpers.rb +2 -0
  14. data/lib/abstract_controller/rendering.rb +13 -12
  15. data/lib/abstract_controller/translation.rb +12 -13
  16. data/lib/abstract_controller/url_for.rb +8 -6
  17. data/lib/abstract_controller.rb +2 -0
  18. data/lib/action_controller/api/api_rendering.rb +2 -0
  19. data/lib/action_controller/api.rb +74 -72
  20. data/lib/action_controller/base.rb +155 -117
  21. data/lib/action_controller/caching.rb +15 -12
  22. data/lib/action_controller/deprecator.rb +2 -0
  23. data/lib/action_controller/form_builder.rb +20 -17
  24. data/lib/action_controller/log_subscriber.rb +3 -1
  25. data/lib/action_controller/metal/allow_browser.rb +119 -0
  26. data/lib/action_controller/metal/basic_implicit_render.rb +2 -0
  27. data/lib/action_controller/metal/conditional_get.rb +188 -174
  28. data/lib/action_controller/metal/content_security_policy.rb +25 -24
  29. data/lib/action_controller/metal/cookies.rb +4 -2
  30. data/lib/action_controller/metal/data_streaming.rb +64 -55
  31. data/lib/action_controller/metal/default_headers.rb +5 -3
  32. data/lib/action_controller/metal/etag_with_flash.rb +3 -1
  33. data/lib/action_controller/metal/etag_with_template_digest.rb +17 -15
  34. data/lib/action_controller/metal/exceptions.rb +11 -9
  35. data/lib/action_controller/metal/flash.rb +12 -10
  36. data/lib/action_controller/metal/head.rb +12 -10
  37. data/lib/action_controller/metal/helpers.rb +63 -55
  38. data/lib/action_controller/metal/http_authentication.rb +214 -203
  39. data/lib/action_controller/metal/implicit_render.rb +17 -15
  40. data/lib/action_controller/metal/instrumentation.rb +15 -12
  41. data/lib/action_controller/metal/live.rb +113 -107
  42. data/lib/action_controller/metal/logging.rb +6 -4
  43. data/lib/action_controller/metal/mime_responds.rb +151 -142
  44. data/lib/action_controller/metal/parameter_encoding.rb +34 -32
  45. data/lib/action_controller/metal/params_wrapper.rb +57 -59
  46. data/lib/action_controller/metal/permissions_policy.rb +13 -12
  47. data/lib/action_controller/metal/rate_limiting.rb +62 -0
  48. data/lib/action_controller/metal/redirecting.rb +108 -82
  49. data/lib/action_controller/metal/renderers.rb +50 -49
  50. data/lib/action_controller/metal/rendering.rb +103 -75
  51. data/lib/action_controller/metal/request_forgery_protection.rb +162 -133
  52. data/lib/action_controller/metal/rescue.rb +11 -9
  53. data/lib/action_controller/metal/streaming.rb +138 -136
  54. data/lib/action_controller/metal/strong_parameters.rb +483 -478
  55. data/lib/action_controller/metal/testing.rb +2 -0
  56. data/lib/action_controller/metal/url_for.rb +17 -15
  57. data/lib/action_controller/metal.rb +58 -57
  58. data/lib/action_controller/railtie.rb +3 -0
  59. data/lib/action_controller/railties/helpers.rb +2 -0
  60. data/lib/action_controller/renderer.rb +42 -36
  61. data/lib/action_controller/template_assertions.rb +4 -2
  62. data/lib/action_controller/test_case.rb +146 -126
  63. data/lib/action_controller.rb +5 -1
  64. data/lib/action_dispatch/constants.rb +2 -0
  65. data/lib/action_dispatch/deprecator.rb +2 -0
  66. data/lib/action_dispatch/http/cache.rb +27 -26
  67. data/lib/action_dispatch/http/content_disposition.rb +2 -0
  68. data/lib/action_dispatch/http/content_security_policy.rb +48 -59
  69. data/lib/action_dispatch/http/filter_parameters.rb +13 -14
  70. data/lib/action_dispatch/http/filter_redirect.rb +15 -1
  71. data/lib/action_dispatch/http/headers.rb +22 -22
  72. data/lib/action_dispatch/http/mime_negotiation.rb +30 -41
  73. data/lib/action_dispatch/http/mime_type.rb +25 -21
  74. data/lib/action_dispatch/http/mime_types.rb +2 -0
  75. data/lib/action_dispatch/http/parameters.rb +11 -9
  76. data/lib/action_dispatch/http/permissions_policy.rb +26 -36
  77. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  78. data/lib/action_dispatch/http/request.rb +75 -95
  79. data/lib/action_dispatch/http/response.rb +61 -61
  80. data/lib/action_dispatch/http/upload.rb +18 -16
  81. data/lib/action_dispatch/http/url.rb +75 -73
  82. data/lib/action_dispatch/journey/formatter.rb +13 -6
  83. data/lib/action_dispatch/journey/gtg/builder.rb +4 -3
  84. data/lib/action_dispatch/journey/gtg/simulator.rb +2 -0
  85. data/lib/action_dispatch/journey/gtg/transition_table.rb +10 -8
  86. data/lib/action_dispatch/journey/nfa/dot.rb +2 -0
  87. data/lib/action_dispatch/journey/nodes/node.rb +6 -5
  88. data/lib/action_dispatch/journey/parser.rb +4 -3
  89. data/lib/action_dispatch/journey/parser_extras.rb +2 -0
  90. data/lib/action_dispatch/journey/path/pattern.rb +4 -1
  91. data/lib/action_dispatch/journey/route.rb +9 -7
  92. data/lib/action_dispatch/journey/router/utils.rb +16 -15
  93. data/lib/action_dispatch/journey/router.rb +4 -2
  94. data/lib/action_dispatch/journey/routes.rb +4 -2
  95. data/lib/action_dispatch/journey/scanner.rb +4 -2
  96. data/lib/action_dispatch/journey/visitors.rb +2 -0
  97. data/lib/action_dispatch/journey.rb +2 -0
  98. data/lib/action_dispatch/log_subscriber.rb +2 -0
  99. data/lib/action_dispatch/middleware/actionable_exceptions.rb +2 -0
  100. data/lib/action_dispatch/middleware/assume_ssl.rb +8 -5
  101. data/lib/action_dispatch/middleware/callbacks.rb +3 -1
  102. data/lib/action_dispatch/middleware/cookies.rb +119 -104
  103. data/lib/action_dispatch/middleware/debug_exceptions.rb +13 -5
  104. data/lib/action_dispatch/middleware/debug_locks.rb +15 -13
  105. data/lib/action_dispatch/middleware/debug_view.rb +2 -0
  106. data/lib/action_dispatch/middleware/exception_wrapper.rb +6 -11
  107. data/lib/action_dispatch/middleware/executor.rb +2 -0
  108. data/lib/action_dispatch/middleware/flash.rb +63 -51
  109. data/lib/action_dispatch/middleware/host_authorization.rb +17 -15
  110. data/lib/action_dispatch/middleware/public_exceptions.rb +8 -6
  111. data/lib/action_dispatch/middleware/reloader.rb +5 -3
  112. data/lib/action_dispatch/middleware/remote_ip.rb +77 -72
  113. data/lib/action_dispatch/middleware/request_id.rb +14 -9
  114. data/lib/action_dispatch/middleware/server_timing.rb +4 -2
  115. data/lib/action_dispatch/middleware/session/abstract_store.rb +2 -0
  116. data/lib/action_dispatch/middleware/session/cache_store.rb +13 -8
  117. data/lib/action_dispatch/middleware/session/cookie_store.rb +27 -26
  118. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +7 -3
  119. data/lib/action_dispatch/middleware/show_exceptions.rb +16 -16
  120. data/lib/action_dispatch/middleware/ssl.rb +43 -40
  121. data/lib/action_dispatch/middleware/stack.rb +11 -10
  122. data/lib/action_dispatch/middleware/static.rb +33 -31
  123. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +1 -1
  124. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +1 -1
  125. data/lib/action_dispatch/railtie.rb +2 -3
  126. data/lib/action_dispatch/request/session.rb +23 -21
  127. data/lib/action_dispatch/request/utils.rb +2 -0
  128. data/lib/action_dispatch/routing/endpoint.rb +2 -0
  129. data/lib/action_dispatch/routing/inspector.rb +6 -4
  130. data/lib/action_dispatch/routing/mapper.rb +623 -625
  131. data/lib/action_dispatch/routing/polymorphic_routes.rb +69 -62
  132. data/lib/action_dispatch/routing/redirection.rb +37 -32
  133. data/lib/action_dispatch/routing/route_set.rb +60 -46
  134. data/lib/action_dispatch/routing/routes_proxy.rb +6 -4
  135. data/lib/action_dispatch/routing/url_for.rb +130 -125
  136. data/lib/action_dispatch/routing.rb +150 -148
  137. data/lib/action_dispatch/system_test_case.rb +91 -81
  138. data/lib/action_dispatch/system_testing/browser.rb +4 -2
  139. data/lib/action_dispatch/system_testing/driver.rb +2 -0
  140. data/lib/action_dispatch/system_testing/server.rb +2 -0
  141. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +32 -21
  142. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +2 -0
  143. data/lib/action_dispatch/testing/assertion_response.rb +8 -6
  144. data/lib/action_dispatch/testing/assertions/response.rb +26 -23
  145. data/lib/action_dispatch/testing/assertions/routing.rb +153 -84
  146. data/lib/action_dispatch/testing/assertions.rb +2 -0
  147. data/lib/action_dispatch/testing/integration.rb +223 -222
  148. data/lib/action_dispatch/testing/request_encoder.rb +2 -0
  149. data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
  150. data/lib/action_dispatch/testing/test_process.rb +12 -8
  151. data/lib/action_dispatch/testing/test_request.rb +3 -1
  152. data/lib/action_dispatch/testing/test_response.rb +27 -26
  153. data/lib/action_dispatch.rb +22 -32
  154. data/lib/action_pack/gem_version.rb +6 -4
  155. data/lib/action_pack/version.rb +3 -1
  156. data/lib/action_pack.rb +17 -16
  157. metadata +33 -16
@@ -1,41 +1,48 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  require "active_support/core_ext/hash/keys"
4
6
 
5
7
  module ActionDispatch
6
- # = Action Dispatch \Flash
8
+ # # Action Dispatch Flash
7
9
  #
8
- # The flash provides a way to pass temporary primitive-types (String, Array, Hash) between actions. Anything you place in the flash will be exposed
9
- # to the very next action and then cleared out. This is a great way of doing notices and alerts, such as a create
10
- # action that sets <tt>flash[:notice] = "Post successfully created"</tt> before redirecting to a display action that can
11
- # then expose the flash to its template. Actually, that exposure is automatically done.
10
+ # The flash provides a way to pass temporary primitive-types (String, Array,
11
+ # Hash) between actions. Anything you place in the flash will be exposed to the
12
+ # very next action and then cleared out. This is a great way of doing notices
13
+ # and alerts, such as a create action that sets `flash[:notice] = "Post
14
+ # successfully created"` before redirecting to a display action that can then
15
+ # expose the flash to its template. Actually, that exposure is automatically
16
+ # done.
12
17
  #
13
- # class PostsController < ActionController::Base
14
- # def create
15
- # # save post
16
- # flash[:notice] = "Post successfully created"
17
- # redirect_to @post
18
- # end
18
+ # class PostsController < ActionController::Base
19
+ # def create
20
+ # # save post
21
+ # flash[:notice] = "Post successfully created"
22
+ # redirect_to @post
23
+ # end
19
24
  #
20
- # def show
21
- # # doesn't need to assign the flash notice to the template, that's done automatically
25
+ # def show
26
+ # # doesn't need to assign the flash notice to the template, that's done automatically
27
+ # end
22
28
  # end
23
- # end
24
29
  #
25
- # Then in +show.html.erb+:
30
+ # Then in `show.html.erb`:
26
31
  #
27
- # <% if flash[:notice] %>
28
- # <div class="notice"><%= flash[:notice] %></div>
29
- # <% end %>
32
+ # <% if flash[:notice] %>
33
+ # <div class="notice"><%= flash[:notice] %></div>
34
+ # <% end %>
30
35
  #
31
- # Since the +notice+ and +alert+ keys are a common idiom, convenience accessors are available:
36
+ # Since the `notice` and `alert` keys are a common idiom, convenience accessors
37
+ # are available:
32
38
  #
33
- # flash.alert = "You must be logged in"
34
- # flash.notice = "Post successfully created"
39
+ # flash.alert = "You must be logged in"
40
+ # flash.notice = "Post successfully created"
35
41
  #
36
- # This example places a string in the flash. And of course, you can put as many as you like at a time too. If you want to pass
37
- # non-primitive types, you will have to handle that in your application. Example: To show messages with links, you will have to
38
- # use sanitize helper.
42
+ # This example places a string in the flash. And of course, you can put as many
43
+ # as you like at a time too. If you want to pass non-primitive types, you will
44
+ # have to handle that in your application. Example: To show messages with links,
45
+ # you will have to use sanitize helper.
39
46
  #
40
47
  # Just remember: They'll be gone by the time the next action has been performed.
41
48
  #
@@ -98,12 +105,12 @@ module ActionDispatch
98
105
  @flash[k.to_s]
99
106
  end
100
107
 
101
- # Convenience accessor for <tt>flash.now[:alert]=</tt>.
108
+ # Convenience accessor for `flash.now[:alert]=`.
102
109
  def alert=(message)
103
110
  self[:alert] = message
104
111
  end
105
112
 
106
- # Convenience accessor for <tt>flash.now[:notice]=</tt>.
113
+ # Convenience accessor for `flash.now[:notice]=`.
107
114
  def notice=(message)
108
115
  self[:notice] = message
109
116
  end
@@ -131,8 +138,8 @@ module ActionDispatch
131
138
  end
132
139
  end
133
140
 
134
- # Builds a hash containing the flashes to keep for the next request.
135
- # If there are none to keep, returns +nil+.
141
+ # Builds a hash containing the flashes to keep for the next request. If there
142
+ # are none to keep, returns `nil`.
136
143
  def to_session_value # :nodoc:
137
144
  flashes_to_keep = @flashes.except(*@discard)
138
145
  return nil if flashes_to_keep.empty?
@@ -177,8 +184,8 @@ module ActionDispatch
177
184
  @flashes.key? name.to_s
178
185
  end
179
186
 
180
- # Immediately deletes the single flash entry. Use this method when you
181
- # want remove the message within the current action. See also #discard.
187
+ # Immediately deletes the single flash entry. Use this method when you want
188
+ # remove the message within the current action. See also #discard.
182
189
  def delete(key)
183
190
  key = key.to_s
184
191
  @discard.delete key
@@ -211,45 +218,49 @@ module ActionDispatch
211
218
  self
212
219
  end
213
220
 
214
- # Sets a flash that will not be available to the next action, only to the current.
221
+ # Sets a flash that will not be available to the next action, only to the
222
+ # current.
215
223
  #
216
224
  # flash.now[:message] = "Hello current action"
217
225
  #
218
- # This method enables you to use the flash as a central messaging system in your app.
219
- # When you need to pass an object to the next action, you use the standard flash assign (<tt>[]=</tt>).
220
- # When you need to pass an object to the current action, you use <tt>now</tt>, and your object will
221
- # vanish when the current action is done.
226
+ # This method enables you to use the flash as a central messaging system in your
227
+ # app. When you need to pass an object to the next action, you use the standard
228
+ # flash assign (`[]=`). When you need to pass an object to the current action,
229
+ # you use `now`, and your object will vanish when the current action is done.
222
230
  #
223
- # Entries set via <tt>now</tt> are accessed the same way as standard entries: <tt>flash['my-key']</tt>.
231
+ # Entries set via `now` are accessed the same way as standard entries:
232
+ # `flash['my-key']`.
224
233
  #
225
234
  # Also, brings two convenience accessors:
226
235
  #
227
- # flash.now.alert = "Beware now!"
228
- # # Equivalent to flash.now[:alert] = "Beware now!"
236
+ # flash.now.alert = "Beware now!"
237
+ # # Equivalent to flash.now[:alert] = "Beware now!"
229
238
  #
230
- # flash.now.notice = "Good luck now!"
231
- # # Equivalent to flash.now[:notice] = "Good luck now!"
239
+ # flash.now.notice = "Good luck now!"
240
+ # # Equivalent to flash.now[:notice] = "Good luck now!"
232
241
  def now
233
242
  @now ||= FlashNow.new(self)
234
243
  end
235
244
 
236
- # Keeps either the entire current flash or a specific flash entry available for the next action:
245
+ # Keeps either the entire current flash or a specific flash entry available for
246
+ # the next action:
237
247
  #
238
- # flash.keep # keeps the entire flash
239
- # flash.keep(:notice) # keeps only the "notice" entry, the rest of the flash is discarded
248
+ # flash.keep # keeps the entire flash
249
+ # flash.keep(:notice) # keeps only the "notice" entry, the rest of the flash is discarded
240
250
  def keep(k = nil)
241
251
  k = k.to_s if k
242
252
  @discard.subtract Array(k || keys)
243
253
  k ? self[k] : self
244
254
  end
245
255
 
246
- # Marks the entire flash or a single flash entry to be discarded by the end of the current action:
256
+ # Marks the entire flash or a single flash entry to be discarded by the end of
257
+ # the current action:
247
258
  #
248
259
  # flash.discard # discard the entire flash at the end of the current action
249
260
  # flash.discard(:warning) # discard only the "warning" entry at the end of the current action
250
261
  #
251
- # Use this method when you want to display the message in the current
252
- # action but not in the next one. See also #delete.
262
+ # Use this method when you want to display the message in the current action but
263
+ # not in the next one. See also #delete.
253
264
  def discard(k = nil)
254
265
  k = k.to_s if k
255
266
  @discard.merge Array(k || keys)
@@ -258,28 +269,29 @@ module ActionDispatch
258
269
 
259
270
  # Mark for removal entries that were kept, and delete unkept ones.
260
271
  #
261
- # This method is called automatically by filters, so you generally don't need to care about it.
272
+ # This method is called automatically by filters, so you generally don't need to
273
+ # care about it.
262
274
  def sweep # :nodoc:
263
275
  @discard.each { |k| @flashes.delete k }
264
276
  @discard.replace @flashes.keys
265
277
  end
266
278
 
267
- # Convenience accessor for <tt>flash[:alert]</tt>.
279
+ # Convenience accessor for `flash[:alert]`.
268
280
  def alert
269
281
  self[:alert]
270
282
  end
271
283
 
272
- # Convenience accessor for <tt>flash[:alert]=</tt>.
284
+ # Convenience accessor for `flash[:alert]=`.
273
285
  def alert=(message)
274
286
  self[:alert] = message
275
287
  end
276
288
 
277
- # Convenience accessor for <tt>flash[:notice]</tt>.
289
+ # Convenience accessor for `flash[:notice]`.
278
290
  def notice
279
291
  self[:notice]
280
292
  end
281
293
 
282
- # Convenience accessor for <tt>flash[:notice]=</tt>.
294
+ # Convenience accessor for `flash[:notice]=`.
283
295
  def notice=(message)
284
296
  self[:notice] = message
285
297
  end
@@ -1,24 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActionDispatch
4
- # = Action Dispatch \HostAuthorization
6
+ # # Action Dispatch HostAuthorization
5
7
  #
6
- # This middleware guards from DNS rebinding attacks by explicitly permitting
7
- # the hosts a request can be sent to, and is passed the options set in
8
- # +config.host_authorization+.
8
+ # This middleware guards from DNS rebinding attacks by explicitly permitting the
9
+ # hosts a request can be sent to, and is passed the options set in
10
+ # `config.host_authorization`.
9
11
  #
10
- # Requests can opt-out of Host Authorization with +exclude+:
12
+ # Requests can opt-out of Host Authorization with `exclude`:
11
13
  #
12
- # config.host_authorization = { exclude: ->(request) { request.path =~ /healthcheck/ } }
14
+ # config.host_authorization = { exclude: ->(request) { request.path =~ /healthcheck/ } }
13
15
  #
14
- # When a request comes to an unauthorized host, the +response_app+
15
- # application will be executed and rendered. If no +response_app+ is given, a
16
- # default one will run.
17
- # The default response app logs blocked host info with level 'error' and
18
- # responds with <tt>403 Forbidden</tt>. The body of the response contains debug info
19
- # if +config.consider_all_requests_local+ is set to true, otherwise the body is empty.
16
+ # When a request comes to an unauthorized host, the `response_app` application
17
+ # will be executed and rendered. If no `response_app` is given, a default one
18
+ # will run. The default response app logs blocked host info with level 'error'
19
+ # and responds with `403 Forbidden`. The body of the response contains debug
20
+ # info if `config.consider_all_requests_local` is set to true, otherwise the
21
+ # body is empty.
20
22
  class HostAuthorization
21
- ALLOWED_HOSTS_IN_DEVELOPMENT = [".localhost", IPAddr.new("0.0.0.0/0"), IPAddr.new("::/0")]
23
+ ALLOWED_HOSTS_IN_DEVELOPMENT = [".localhost", ".test", IPAddr.new("0.0.0.0/0"), IPAddr.new("::/0")]
22
24
  PORT_REGEX = /(?::\d+)/ # :nodoc:
23
25
  SUBDOMAIN_REGEX = /(?:[a-z0-9-]+\.)/i # :nodoc:
24
26
  IPV4_HOSTNAME = /(?<host>\d+\.\d+\.\d+\.\d+)#{PORT_REGEX}?/ # :nodoc:
@@ -45,8 +47,8 @@ module ActionDispatch
45
47
  begin
46
48
  allowed === extract_hostname(host)
47
49
  rescue
48
- # IPAddr#=== raises an error if you give it a hostname instead of
49
- # IP. Treat similar errors as blocked access.
50
+ # IPAddr#=== raises an error if you give it a hostname instead of IP. Treat
51
+ # similar errors as blocked access.
50
52
  false
51
53
  end
52
54
  else
@@ -1,15 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActionDispatch
4
- # = Action Dispatch \PublicExceptions
6
+ # # Action Dispatch PublicExceptions
5
7
  #
6
8
  # When called, this middleware renders an error page. By default if an HTML
7
- # response is expected it will render static error pages from the <tt>/public</tt>
9
+ # response is expected it will render static error pages from the `/public`
8
10
  # directory. For example when this middleware receives a 500 response it will
9
- # render the template found in <tt>/public/500.html</tt>.
10
- # If an internationalized locale is set, this middleware will attempt to render
11
- # the template in <tt>/public/500.<locale>.html</tt>. If an internationalized template
12
- # is not found it will fall back on <tt>/public/500.html</tt>.
11
+ # render the template found in `/public/500.html`. If an internationalized
12
+ # locale is set, this middleware will attempt to render the template in
13
+ # `/public/500.<locale>.html`. If an internationalized template is not found it
14
+ # will fall back on `/public/500.html`.
13
15
  #
14
16
  # When a request with a content type other than HTML is made, this middleware
15
17
  # will attempt to convert error information into the appropriate response type.
@@ -1,14 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActionDispatch
4
- # = Action Dispatch \Reloader
6
+ # # Action Dispatch Reloader
5
7
  #
6
8
  # ActionDispatch::Reloader wraps the request with callbacks provided by
7
9
  # ActiveSupport::Reloader, intended to assist with code reloading during
8
10
  # development.
9
11
  #
10
- # ActionDispatch::Reloader is included in the middleware stack only if
11
- # reloading is enabled, which it is by the default in +development+ mode.
12
+ # ActionDispatch::Reloader is included in the middleware stack only if reloading
13
+ # is enabled, which it is by the default in `development` mode.
12
14
  class Reloader < Executor
13
15
  end
14
16
  end
@@ -1,37 +1,41 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  require "ipaddr"
4
6
 
5
7
  module ActionDispatch
6
- # = Action Dispatch \RemoteIp
8
+ # # Action Dispatch RemoteIp
7
9
  #
8
- # This middleware calculates the IP address of the remote client that is
9
- # making the request. It does this by checking various headers that could
10
- # contain the address, and then picking the last-set address that is not
11
- # on the list of trusted IPs. This follows the precedent set by e.g.
12
- # {the Tomcat server}[https://issues.apache.org/bugzilla/show_bug.cgi?id=50453].
13
- # A more detailed explanation of the algorithm is given at GetIp#calculate_ip.
10
+ # This middleware calculates the IP address of the remote client that is making
11
+ # the request. It does this by checking various headers that could contain the
12
+ # address, and then picking the last-set address that is not on the list of
13
+ # trusted IPs. This follows the precedent set by e.g. [the Tomcat
14
+ # server](https://issues.apache.org/bugzilla/show_bug.cgi?id=50453). A more
15
+ # detailed explanation of the algorithm is given at GetIp#calculate_ip.
14
16
  #
15
- # Some Rack servers concatenate repeated headers, like {HTTP RFC 2616}[https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2]
16
- # requires. Some Rack servers simply drop preceding headers, and only report
17
- # the value that was {given in the last header}[https://andre.arko.net/2011/12/26/repeated-headers-and-ruby-web-servers].
18
- # If you are behind multiple proxy servers (like NGINX to HAProxy to Unicorn)
19
- # then you should test your Rack server to make sure your data is good.
17
+ # Some Rack servers concatenate repeated headers, like [HTTP RFC
18
+ # 2616](https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2) requires.
19
+ # Some Rack servers simply drop preceding headers, and only report the value
20
+ # that was [given in the last
21
+ # header](https://andre.arko.net/2011/12/26/repeated-headers-and-ruby-web-server
22
+ # s). If you are behind multiple proxy servers (like NGINX to HAProxy to
23
+ # Unicorn) then you should test your Rack server to make sure your data is good.
20
24
  #
21
- # IF YOU DON'T USE A PROXY, THIS MAKES YOU VULNERABLE TO IP SPOOFING.
22
- # This middleware assumes that there is at least one proxy sitting around
23
- # and setting headers with the client's remote IP address. If you don't use
24
- # a proxy, because you are hosted on e.g. Heroku without SSL, any client can
25
- # claim to have any IP address by setting the +X-Forwarded-For+ header. If you
26
- # care about that, then you need to explicitly drop or ignore those headers
27
- # sometime before this middleware runs. Alternatively, remove this middleware
28
- # to avoid inadvertently relying on it.
25
+ # IF YOU DON'T USE A PROXY, THIS MAKES YOU VULNERABLE TO IP SPOOFING. This
26
+ # middleware assumes that there is at least one proxy sitting around and setting
27
+ # headers with the client's remote IP address. If you don't use a proxy, because
28
+ # you are hosted on e.g. Heroku without SSL, any client can claim to have any IP
29
+ # address by setting the `X-Forwarded-For` header. If you care about that, then
30
+ # you need to explicitly drop or ignore those headers sometime before this
31
+ # middleware runs. Alternatively, remove this middleware to avoid inadvertently
32
+ # relying on it.
29
33
  class RemoteIp
30
34
  class IpSpoofAttackError < StandardError; end
31
35
 
32
- # The default trusted IPs list simply includes IP addresses that are
33
- # guaranteed by the IP specification to be private addresses. Those will
34
- # not be the ultimate client IP in production, and so are discarded. See
36
+ # The default trusted IPs list simply includes IP addresses that are guaranteed
37
+ # by the IP specification to be private addresses. Those will not be the
38
+ # ultimate client IP in production, and so are discarded. See
35
39
  # https://en.wikipedia.org/wiki/Private_network for details.
36
40
  TRUSTED_PROXIES = [
37
41
  "127.0.0.0/8", # localhost IPv4 range, per RFC-3330
@@ -44,20 +48,20 @@ module ActionDispatch
44
48
 
45
49
  attr_reader :check_ip, :proxies
46
50
 
47
- # Create a new +RemoteIp+ middleware instance.
51
+ # Create a new `RemoteIp` middleware instance.
48
52
  #
49
- # The +ip_spoofing_check+ option is on by default. When on, an exception
50
- # is raised if it looks like the client is trying to lie about its own IP
51
- # address. It makes sense to turn off this check on sites aimed at non-IP
52
- # clients (like WAP devices), or behind proxies that set headers in an
53
- # incorrect or confusing way (like AWS ELB).
53
+ # The `ip_spoofing_check` option is on by default. When on, an exception is
54
+ # raised if it looks like the client is trying to lie about its own IP address.
55
+ # It makes sense to turn off this check on sites aimed at non-IP clients (like
56
+ # WAP devices), or behind proxies that set headers in an incorrect or confusing
57
+ # way (like AWS ELB).
54
58
  #
55
- # The +custom_proxies+ argument can take an enumerable which will be used
56
- # instead of +TRUSTED_PROXIES+. Any proxy setup will put the value you
57
- # want in the middle (or at the beginning) of the +X-Forwarded-For+ list,
58
- # with your proxy servers after it. If your proxies aren't removed, pass
59
- # them in via the +custom_proxies+ parameter. That way, the middleware will
60
- # ignore those IP addresses, and return the one that you want.
59
+ # The `custom_proxies` argument can take an enumerable which will be used
60
+ # instead of `TRUSTED_PROXIES`. Any proxy setup will put the value you want in
61
+ # the middle (or at the beginning) of the `X-Forwarded-For` list, with your
62
+ # proxy servers after it. If your proxies aren't removed, pass them in via the
63
+ # `custom_proxies` parameter. That way, the middleware will ignore those IP
64
+ # addresses, and return the one that you want.
61
65
  def initialize(app, ip_spoofing_check = true, custom_proxies = nil)
62
66
  @app = app
63
67
  @check_ip = ip_spoofing_check
@@ -82,19 +86,19 @@ module ActionDispatch
82
86
  end
83
87
  end
84
88
 
85
- # Since the IP address may not be needed, we store the object here
86
- # without calculating the IP to keep from slowing down the majority of
87
- # requests. For those requests that do need to know the IP, the
88
- # GetIp#calculate_ip method will calculate the memoized client IP address.
89
+ # Since the IP address may not be needed, we store the object here without
90
+ # calculating the IP to keep from slowing down the majority of requests. For
91
+ # those requests that do need to know the IP, the GetIp#calculate_ip method will
92
+ # calculate the memoized client IP address.
89
93
  def call(env)
90
94
  req = ActionDispatch::Request.new env
91
95
  req.remote_ip = GetIp.new(req, check_ip, proxies)
92
96
  @app.call(req.env)
93
97
  end
94
98
 
95
- # The GetIp class exists as a way to defer processing of the request data
96
- # into an actual IP address. If the ActionDispatch::Request#remote_ip method
97
- # is called, this class will calculate the value and then memoize it.
99
+ # The GetIp class exists as a way to defer processing of the request data into
100
+ # an actual IP address. If the ActionDispatch::Request#remote_ip method is
101
+ # called, this class will calculate the value and then memoize it.
98
102
  class GetIp
99
103
  def initialize(req, check_ip, proxies)
100
104
  @req = req
@@ -102,24 +106,25 @@ module ActionDispatch
102
106
  @proxies = proxies
103
107
  end
104
108
 
105
- # Sort through the various IP address headers, looking for the IP most
106
- # likely to be the address of the actual remote client making this
107
- # request.
109
+ # Sort through the various IP address headers, looking for the IP most likely to
110
+ # be the address of the actual remote client making this request.
108
111
  #
109
- # REMOTE_ADDR will be correct if the request is made directly against the
110
- # Ruby process, on e.g. Heroku. When the request is proxied by another
111
- # server like HAProxy or NGINX, the IP address that made the original
112
- # request will be put in an +X-Forwarded-For+ header. If there are multiple
113
- # proxies, that header may contain a list of IPs. Other proxy services
114
- # set the +Client-Ip+ header instead, so we check that too.
112
+ # REMOTE_ADDR will be correct if the request is made directly against the Ruby
113
+ # process, on e.g. Heroku. When the request is proxied by another server like
114
+ # HAProxy or NGINX, the IP address that made the original request will be put in
115
+ # an `X-Forwarded-For` header. If there are multiple proxies, that header may
116
+ # contain a list of IPs. Other proxy services set the `Client-Ip` header
117
+ # instead, so we check that too.
115
118
  #
116
- # As discussed in {this post about Rails IP Spoofing}[https://web.archive.org/web/20170626095448/https://blog.gingerlime.com/2012/rails-ip-spoofing-vulnerabilities-and-protection/],
117
- # while the first IP in the list is likely to be the "originating" IP,
118
- # it could also have been set by the client maliciously.
119
+ # As discussed in [this post about Rails IP
120
+ # Spoofing](https://web.archive.org/web/20170626095448/https://blog.gingerlime.c
121
+ # om/2012/rails-ip-spoofing-vulnerabilities-and-protection/), while the first IP
122
+ # in the list is likely to be the "originating" IP, it could also have been set
123
+ # by the client maliciously.
119
124
  #
120
- # In order to find the first address that is (probably) accurate, we
121
- # take the list of IPs, remove known and trusted proxies, and then take
122
- # the last address left, which was presumably set by one of those proxies.
125
+ # In order to find the first address that is (probably) accurate, we take the
126
+ # list of IPs, remove known and trusted proxies, and then take the last address
127
+ # left, which was presumably set by one of those proxies.
123
128
  def calculate_ip
124
129
  # Set by the Rack web server, this is a single value.
125
130
  remote_addr = ips_from(@req.remote_addr).last
@@ -128,19 +133,19 @@ module ActionDispatch
128
133
  client_ips = ips_from(@req.client_ip).reverse!
129
134
  forwarded_ips = ips_from(@req.x_forwarded_for).reverse!
130
135
 
131
- # +Client-Ip+ and +X-Forwarded-For+ should not, generally, both be set.
132
- # If they are both set, it means that either:
136
+ # `Client-Ip` and `X-Forwarded-For` should not, generally, both be set. If they
137
+ # are both set, it means that either:
133
138
  #
134
139
  # 1) This request passed through two proxies with incompatible IP header
135
- # conventions.
136
- # 2) The client passed one of +Client-Ip+ or +X-Forwarded-For+
137
- # (whichever the proxy servers weren't using) themselves.
140
+ # conventions.
141
+ #
142
+ # 2) The client passed one of `Client-Ip` or `X-Forwarded-For`
143
+ # (whichever the proxy servers weren't using) themselves.
138
144
  #
139
- # Either way, there is no way for us to determine which header is the
140
- # right one after the fact. Since we have no idea, if we are concerned
141
- # about IP spoofing we need to give up and explode. (If you're not
142
- # concerned about IP spoofing you can turn the +ip_spoofing_check+
143
- # option off.)
145
+ # Either way, there is no way for us to determine which header is the right one
146
+ # after the fact. Since we have no idea, if we are concerned about IP spoofing
147
+ # we need to give up and explode. (If you're not concerned about IP spoofing you
148
+ # can turn the `ip_spoofing_check` option off.)
144
149
  should_check_ip = @check_ip && client_ips.last && forwarded_ips.last
145
150
  if should_check_ip && !forwarded_ips.include?(client_ips.last)
146
151
  # We don't know which came from the proxy, and which from the user
@@ -151,14 +156,14 @@ module ActionDispatch
151
156
 
152
157
  # We assume these things about the IP headers:
153
158
  #
154
- # - X-Forwarded-For will be a list of IPs, one per proxy, or blank
155
- # - Client-Ip is propagated from the outermost proxy, or is blank
156
- # - REMOTE_ADDR will be the IP that made the request to Rack
159
+ # - X-Forwarded-For will be a list of IPs, one per proxy, or blank
160
+ # - Client-Ip is propagated from the outermost proxy, or is blank
161
+ # - REMOTE_ADDR will be the IP that made the request to Rack
157
162
  ips = forwarded_ips + client_ips
158
163
  ips.compact!
159
164
 
160
- # If every single IP option is in the trusted list, return the IP
161
- # that's furthest away
165
+ # If every single IP option is in the trusted list, return the IP that's
166
+ # furthest away
162
167
  filter_proxies(ips + [remote_addr]).first || ips.last || remote_addr
163
168
  end
164
169
 
@@ -1,21 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  require "securerandom"
4
6
  require "active_support/core_ext/string/access"
5
7
 
6
8
  module ActionDispatch
7
- # = Action Dispatch \RequestId
9
+ # # Action Dispatch RequestId
8
10
  #
9
- # Makes a unique request id available to the +action_dispatch.request_id+ env variable (which is then accessible
10
- # through ActionDispatch::Request#request_id or the alias ActionDispatch::Request#uuid) and sends
11
- # the same id to the client via the +X-Request-Id+ header.
11
+ # Makes a unique request id available to the `action_dispatch.request_id` env
12
+ # variable (which is then accessible through ActionDispatch::Request#request_id
13
+ # or the alias ActionDispatch::Request#uuid) and sends the same id to the client
14
+ # via the `X-Request-Id` header.
12
15
  #
13
- # The unique request id is either based on the +X-Request-Id+ header in the request, which would typically be generated
14
- # by a firewall, load balancer, or the web server, or, if this header is not available, a random uuid. If the
15
- # header is accepted from the outside world, we sanitize it to a max of 255 chars and alphanumeric and dashes only.
16
+ # The unique request id is either based on the `X-Request-Id` header in the
17
+ # request, which would typically be generated by a firewall, load balancer, or
18
+ # the web server, or, if this header is not available, a random uuid. If the
19
+ # header is accepted from the outside world, we sanitize it to a max of 255
20
+ # chars and alphanumeric and dashes only.
16
21
  #
17
- # The unique request id can be used to trace a request end-to-end and would typically end up being part of log files
18
- # from multiple pieces of the stack.
22
+ # The unique request id can be used to trace a request end-to-end and would
23
+ # typically end up being part of log files from multiple pieces of the stack.
19
24
  class RequestId
20
25
  def initialize(app, header:)
21
26
  @app = app
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  require "active_support/notifications"
4
6
 
5
7
  module ActionDispatch
@@ -29,8 +31,8 @@ module ActionDispatch
29
31
 
30
32
  def ensure_subscribed
31
33
  @mutex.synchronize do
32
- # Subscribe to all events, except those beginning with "!"
33
- # Ideally we would be more selective of what is being measured
34
+ # Subscribe to all events, except those beginning with "!" Ideally we would be
35
+ # more selective of what is being measured
34
36
  @subscriber ||= ActiveSupport::Notifications.subscribe(/\A[^!]/, self)
35
37
  end
36
38
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  require "rack/utils"
4
6
  require "rack/request"
5
7
  require "rack/session/abstract/id"
@@ -1,19 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  require "action_dispatch/middleware/session/abstract_store"
4
6
 
5
7
  module ActionDispatch
6
8
  module Session
7
- # = Action Dispatch Session \CacheStore
9
+ # # Action Dispatch Session CacheStore
10
+ #
11
+ # A session store that uses an ActiveSupport::Cache::Store to store the
12
+ # sessions. This store is most useful if you don't store critical data in your
13
+ # sessions and you don't need them to live for extended periods of time.
8
14
  #
9
- # A session store that uses an ActiveSupport::Cache::Store to store the sessions. This store is most useful
10
- # if you don't store critical data in your sessions and you don't need them to live for extended periods
11
- # of time.
15
+ # #### Options
16
+ # * `cache` - The cache to use. If it is not specified, `Rails.cache`
17
+ # will be used.
18
+ # * `expire_after` - The length of time a session will be stored before
19
+ # automatically expiring. By default, the `:expires_in` option of the cache
20
+ # is used.
12
21
  #
13
- # ==== Options
14
- # * <tt>cache</tt> - The cache to use. If it is not specified, <tt>Rails.cache</tt> will be used.
15
- # * <tt>expire_after</tt> - The length of time a session will be stored before automatically expiring.
16
- # By default, the <tt>:expires_in</tt> option of the cache is used.
17
22
  class CacheStore < AbstractSecureStore
18
23
  def initialize(app, options = {})
19
24
  @cache = options[:cache] || Rails.cache