actionpack 7.1.6 → 7.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +117 -607
  3. data/README.rdoc +1 -1
  4. data/lib/abstract_controller/asset_paths.rb +2 -0
  5. data/lib/abstract_controller/base.rb +93 -100
  6. data/lib/abstract_controller/caching/fragments.rb +50 -53
  7. data/lib/abstract_controller/caching.rb +2 -0
  8. data/lib/abstract_controller/callbacks.rb +66 -64
  9. data/lib/abstract_controller/collector.rb +7 -7
  10. data/lib/abstract_controller/deprecator.rb +2 -0
  11. data/lib/abstract_controller/error.rb +2 -0
  12. data/lib/abstract_controller/helpers.rb +71 -84
  13. data/lib/abstract_controller/logger.rb +2 -0
  14. data/lib/abstract_controller/railties/routes_helpers.rb +2 -0
  15. data/lib/abstract_controller/rendering.rb +13 -12
  16. data/lib/abstract_controller/translation.rb +12 -13
  17. data/lib/abstract_controller/url_for.rb +8 -6
  18. data/lib/abstract_controller.rb +2 -0
  19. data/lib/action_controller/api/api_rendering.rb +2 -0
  20. data/lib/action_controller/api.rb +75 -72
  21. data/lib/action_controller/base.rb +198 -126
  22. data/lib/action_controller/caching.rb +15 -12
  23. data/lib/action_controller/deprecator.rb +2 -0
  24. data/lib/action_controller/form_builder.rb +20 -17
  25. data/lib/action_controller/log_subscriber.rb +3 -1
  26. data/lib/action_controller/metal/allow_browser.rb +123 -0
  27. data/lib/action_controller/metal/basic_implicit_render.rb +2 -0
  28. data/lib/action_controller/metal/conditional_get.rb +187 -174
  29. data/lib/action_controller/metal/content_security_policy.rb +25 -24
  30. data/lib/action_controller/metal/cookies.rb +4 -2
  31. data/lib/action_controller/metal/data_streaming.rb +69 -62
  32. data/lib/action_controller/metal/default_headers.rb +5 -3
  33. data/lib/action_controller/metal/etag_with_flash.rb +3 -1
  34. data/lib/action_controller/metal/etag_with_template_digest.rb +17 -15
  35. data/lib/action_controller/metal/exceptions.rb +11 -9
  36. data/lib/action_controller/metal/flash.rb +12 -10
  37. data/lib/action_controller/metal/head.rb +12 -10
  38. data/lib/action_controller/metal/helpers.rb +63 -55
  39. data/lib/action_controller/metal/http_authentication.rb +209 -201
  40. data/lib/action_controller/metal/implicit_render.rb +17 -15
  41. data/lib/action_controller/metal/instrumentation.rb +15 -12
  42. data/lib/action_controller/metal/live.rb +116 -108
  43. data/lib/action_controller/metal/logging.rb +6 -4
  44. data/lib/action_controller/metal/mime_responds.rb +151 -142
  45. data/lib/action_controller/metal/parameter_encoding.rb +34 -32
  46. data/lib/action_controller/metal/params_wrapper.rb +57 -59
  47. data/lib/action_controller/metal/permissions_policy.rb +13 -12
  48. data/lib/action_controller/metal/rate_limiting.rb +62 -0
  49. data/lib/action_controller/metal/redirecting.rb +112 -85
  50. data/lib/action_controller/metal/renderers.rb +50 -49
  51. data/lib/action_controller/metal/rendering.rb +104 -76
  52. data/lib/action_controller/metal/request_forgery_protection.rb +165 -134
  53. data/lib/action_controller/metal/rescue.rb +11 -9
  54. data/lib/action_controller/metal/streaming.rb +138 -136
  55. data/lib/action_controller/metal/strong_parameters.rb +483 -481
  56. data/lib/action_controller/metal/testing.rb +2 -0
  57. data/lib/action_controller/metal/url_for.rb +17 -15
  58. data/lib/action_controller/metal.rb +86 -60
  59. data/lib/action_controller/railtie.rb +3 -0
  60. data/lib/action_controller/railties/helpers.rb +2 -0
  61. data/lib/action_controller/renderer.rb +41 -36
  62. data/lib/action_controller/template_assertions.rb +4 -2
  63. data/lib/action_controller/test_case.rb +148 -129
  64. data/lib/action_controller.rb +5 -1
  65. data/lib/action_dispatch/constants.rb +8 -0
  66. data/lib/action_dispatch/deprecator.rb +2 -0
  67. data/lib/action_dispatch/http/cache.rb +27 -26
  68. data/lib/action_dispatch/http/content_disposition.rb +2 -0
  69. data/lib/action_dispatch/http/content_security_policy.rb +40 -38
  70. data/lib/action_dispatch/http/filter_parameters.rb +9 -5
  71. data/lib/action_dispatch/http/filter_redirect.rb +22 -1
  72. data/lib/action_dispatch/http/headers.rb +22 -22
  73. data/lib/action_dispatch/http/mime_negotiation.rb +35 -41
  74. data/lib/action_dispatch/http/mime_type.rb +25 -21
  75. data/lib/action_dispatch/http/mime_types.rb +2 -0
  76. data/lib/action_dispatch/http/parameters.rb +11 -9
  77. data/lib/action_dispatch/http/permissions_policy.rb +19 -36
  78. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  79. data/lib/action_dispatch/http/request.rb +72 -72
  80. data/lib/action_dispatch/http/response.rb +88 -62
  81. data/lib/action_dispatch/http/upload.rb +18 -16
  82. data/lib/action_dispatch/http/url.rb +77 -75
  83. data/lib/action_dispatch/journey/formatter.rb +21 -9
  84. data/lib/action_dispatch/journey/gtg/builder.rb +4 -3
  85. data/lib/action_dispatch/journey/gtg/simulator.rb +2 -0
  86. data/lib/action_dispatch/journey/gtg/transition_table.rb +14 -12
  87. data/lib/action_dispatch/journey/nfa/dot.rb +2 -0
  88. data/lib/action_dispatch/journey/nodes/node.rb +6 -5
  89. data/lib/action_dispatch/journey/parser.rb +4 -3
  90. data/lib/action_dispatch/journey/parser_extras.rb +2 -0
  91. data/lib/action_dispatch/journey/path/pattern.rb +4 -1
  92. data/lib/action_dispatch/journey/route.rb +9 -7
  93. data/lib/action_dispatch/journey/router/utils.rb +16 -15
  94. data/lib/action_dispatch/journey/router.rb +4 -2
  95. data/lib/action_dispatch/journey/routes.rb +4 -2
  96. data/lib/action_dispatch/journey/scanner.rb +4 -2
  97. data/lib/action_dispatch/journey/visitors.rb +2 -0
  98. data/lib/action_dispatch/journey.rb +2 -0
  99. data/lib/action_dispatch/log_subscriber.rb +2 -0
  100. data/lib/action_dispatch/middleware/actionable_exceptions.rb +2 -0
  101. data/lib/action_dispatch/middleware/assume_ssl.rb +8 -5
  102. data/lib/action_dispatch/middleware/callbacks.rb +3 -1
  103. data/lib/action_dispatch/middleware/cookies.rb +119 -104
  104. data/lib/action_dispatch/middleware/debug_exceptions.rb +16 -6
  105. data/lib/action_dispatch/middleware/debug_locks.rb +15 -13
  106. data/lib/action_dispatch/middleware/debug_view.rb +2 -0
  107. data/lib/action_dispatch/middleware/exception_wrapper.rb +9 -14
  108. data/lib/action_dispatch/middleware/executor.rb +7 -2
  109. data/lib/action_dispatch/middleware/flash.rb +63 -51
  110. data/lib/action_dispatch/middleware/host_authorization.rb +17 -15
  111. data/lib/action_dispatch/middleware/public_exceptions.rb +13 -7
  112. data/lib/action_dispatch/middleware/reloader.rb +5 -3
  113. data/lib/action_dispatch/middleware/remote_ip.rb +76 -72
  114. data/lib/action_dispatch/middleware/request_id.rb +14 -9
  115. data/lib/action_dispatch/middleware/server_timing.rb +4 -2
  116. data/lib/action_dispatch/middleware/session/abstract_store.rb +2 -0
  117. data/lib/action_dispatch/middleware/session/cache_store.rb +13 -8
  118. data/lib/action_dispatch/middleware/session/cookie_store.rb +27 -26
  119. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +7 -3
  120. data/lib/action_dispatch/middleware/show_exceptions.rb +16 -16
  121. data/lib/action_dispatch/middleware/ssl.rb +43 -40
  122. data/lib/action_dispatch/middleware/stack.rb +11 -10
  123. data/lib/action_dispatch/middleware/static.rb +33 -31
  124. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +1 -1
  125. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +1 -1
  126. data/lib/action_dispatch/railtie.rb +2 -3
  127. data/lib/action_dispatch/request/session.rb +23 -21
  128. data/lib/action_dispatch/request/utils.rb +2 -0
  129. data/lib/action_dispatch/routing/endpoint.rb +2 -0
  130. data/lib/action_dispatch/routing/inspector.rb +5 -3
  131. data/lib/action_dispatch/routing/mapper.rb +622 -623
  132. data/lib/action_dispatch/routing/polymorphic_routes.rb +69 -62
  133. data/lib/action_dispatch/routing/redirection.rb +37 -32
  134. data/lib/action_dispatch/routing/route_set.rb +59 -45
  135. data/lib/action_dispatch/routing/routes_proxy.rb +6 -4
  136. data/lib/action_dispatch/routing/url_for.rb +130 -125
  137. data/lib/action_dispatch/routing.rb +150 -148
  138. data/lib/action_dispatch/system_test_case.rb +91 -81
  139. data/lib/action_dispatch/system_testing/browser.rb +4 -2
  140. data/lib/action_dispatch/system_testing/driver.rb +2 -0
  141. data/lib/action_dispatch/system_testing/server.rb +2 -0
  142. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +32 -21
  143. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +2 -0
  144. data/lib/action_dispatch/testing/assertion_response.rb +9 -7
  145. data/lib/action_dispatch/testing/assertions/response.rb +26 -23
  146. data/lib/action_dispatch/testing/assertions/routing.rb +153 -84
  147. data/lib/action_dispatch/testing/assertions.rb +2 -0
  148. data/lib/action_dispatch/testing/integration.rb +222 -222
  149. data/lib/action_dispatch/testing/request_encoder.rb +11 -9
  150. data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
  151. data/lib/action_dispatch/testing/test_process.rb +11 -8
  152. data/lib/action_dispatch/testing/test_request.rb +3 -1
  153. data/lib/action_dispatch/testing/test_response.rb +27 -26
  154. data/lib/action_dispatch.rb +22 -28
  155. data/lib/action_pack/gem_version.rb +5 -3
  156. data/lib/action_pack/version.rb +3 -1
  157. data/lib/action_pack.rb +17 -16
  158. metadata +34 -11
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  require "rack/session/abstract/id"
4
6
  require "action_controller/metal/exceptions"
5
7
  require "active_support/security_utils"
@@ -11,51 +13,53 @@ module ActionController # :nodoc:
11
13
  class InvalidCrossOriginRequest < ActionControllerError # :nodoc:
12
14
  end
13
15
 
14
- # = Action Controller Request Forgery Protection
16
+ # # Action Controller Request Forgery Protection
15
17
  #
16
- # Controller actions are protected from Cross-Site Request Forgery (CSRF) attacks
17
- # by including a token in the rendered HTML for your application. This token is
18
- # stored as a random string in the session, to which an attacker does not have
19
- # access. When a request reaches your application, \Rails verifies the received
20
- # token with the token in the session. All requests are checked except GET requests
21
- # as these should be idempotent. Keep in mind that all session-oriented requests
22
- # are CSRF protected by default, including JavaScript and HTML requests.
18
+ # Controller actions are protected from Cross-Site Request Forgery (CSRF)
19
+ # attacks by including a token in the rendered HTML for your application. This
20
+ # token is stored as a random string in the session, to which an attacker does
21
+ # not have access. When a request reaches your application, Rails verifies the
22
+ # received token with the token in the session. All requests are checked except
23
+ # GET requests as these should be idempotent. Keep in mind that all
24
+ # session-oriented requests are CSRF protected by default, including JavaScript
25
+ # and HTML requests.
23
26
  #
24
27
  # Since HTML and JavaScript requests are typically made from the browser, we
25
- # need to ensure to verify request authenticity for the web browser. We can
26
- # use session-oriented authentication for these types of requests, by using
27
- # the <tt>protect_from_forgery</tt> method in our controllers.
28
+ # need to ensure to verify request authenticity for the web browser. We can use
29
+ # session-oriented authentication for these types of requests, by using the
30
+ # `protect_from_forgery` method in our controllers.
28
31
  #
29
32
  # GET requests are not protected since they don't have side effects like writing
30
33
  # to the database and don't leak sensitive information. JavaScript requests are
31
- # an exception: a third-party site can use a <script> tag to reference a JavaScript
32
- # URL on your site. When your JavaScript response loads on their site, it executes.
33
- # With carefully crafted JavaScript on their end, sensitive data in your JavaScript
34
- # response may be extracted. To prevent this, only XmlHttpRequest (known as XHR or
35
- # Ajax) requests are allowed to make requests for JavaScript responses.
34
+ # an exception: a third-party site can use a <script> tag to reference a
35
+ # JavaScript URL on your site. When your JavaScript response loads on their
36
+ # site, it executes. With carefully crafted JavaScript on their end, sensitive
37
+ # data in your JavaScript response may be extracted. To prevent this, only
38
+ # XmlHttpRequest (known as XHR or Ajax) requests are allowed to make requests
39
+ # for JavaScript responses.
36
40
  #
37
41
  # Subclasses of ActionController::Base are protected by default with the
38
- # <tt>:exception</tt> strategy, which raises an
42
+ # `:exception` strategy, which raises an
39
43
  # ActionController::InvalidAuthenticityToken error on unverified requests.
40
44
  #
41
45
  # APIs may want to disable this behavior since they are typically designed to be
42
- # state-less: that is, the request API client handles the session instead of \Rails.
43
- # One way to achieve this is to use the <tt>:null_session</tt> strategy instead,
46
+ # state-less: that is, the request API client handles the session instead of
47
+ # Rails. One way to achieve this is to use the `:null_session` strategy instead,
44
48
  # which allows unverified requests to be handled, but with an empty session:
45
49
  #
46
- # class ApplicationController < ActionController::Base
47
- # protect_from_forgery with: :null_session
48
- # end
50
+ # class ApplicationController < ActionController::Base
51
+ # protect_from_forgery with: :null_session
52
+ # end
49
53
  #
50
- # Note that API only applications don't include this module or a session middleware
51
- # by default, and so don't require CSRF protection to be configured.
54
+ # Note that API only applications don't include this module or a session
55
+ # middleware by default, and so don't require CSRF protection to be configured.
52
56
  #
53
- # The token parameter is named <tt>authenticity_token</tt> by default. The name and
54
- # value of this token must be added to every layout that renders forms by including
55
- # <tt>csrf_meta_tags</tt> in the HTML +head+.
57
+ # The token parameter is named `authenticity_token` by default. The name and
58
+ # value of this token must be added to every layout that renders forms by
59
+ # including `csrf_meta_tags` in the HTML `head`.
56
60
  #
57
- # Learn more about CSRF attacks and securing your application in the
58
- # {Ruby on Rails Security Guide}[https://guides.rubyonrails.org/security.html].
61
+ # Learn more about CSRF attacks and securing your application in the [Ruby on
62
+ # Rails Security Guide](https://guides.rubyonrails.org/security.html).
59
63
  module RequestForgeryProtection
60
64
  CSRF_TOKEN = "action_controller.csrf_token"
61
65
 
@@ -65,8 +69,8 @@ module ActionController # :nodoc:
65
69
  include AbstractController::Callbacks
66
70
 
67
71
  included do
68
- # Sets the token parameter name for RequestForgery. Calling +protect_from_forgery+
69
- # sets it to <tt>:authenticity_token</tt> by default.
72
+ # Sets the token parameter name for RequestForgery. Calling
73
+ # `protect_from_forgery` sets it to `:authenticity_token` by default.
70
74
  config_accessor :request_forgery_protection_token
71
75
  self.request_forgery_protection_token ||= :authenticity_token
72
76
 
@@ -74,7 +78,8 @@ module ActionController # :nodoc:
74
78
  config_accessor :forgery_protection_strategy
75
79
  self.forgery_protection_strategy = nil
76
80
 
77
- # Controls whether request forgery protection is turned on or not. Turned off by default only in test mode.
81
+ # Controls whether request forgery protection is turned on or not. Turned off by
82
+ # default only in test mode.
78
83
  config_accessor :allow_forgery_protection
79
84
  self.allow_forgery_protection = true if allow_forgery_protection.nil?
80
85
 
@@ -90,10 +95,6 @@ module ActionController # :nodoc:
90
95
  config_accessor :per_form_csrf_tokens
91
96
  self.per_form_csrf_tokens = false
92
97
 
93
- # Controls whether forgery protection is enabled by default.
94
- config_accessor :default_protect_from_forgery
95
- self.default_protect_from_forgery = false
96
-
97
98
  # The strategy to use for storing and retrieving CSRF tokens.
98
99
  config_accessor :csrf_token_storage_strategy
99
100
  self.csrf_token_storage_strategy = SessionStore.new
@@ -103,79 +104,98 @@ module ActionController # :nodoc:
103
104
  end
104
105
 
105
106
  module ClassMethods
106
- # Turn on request forgery protection. Bear in mind that GET and HEAD requests are not checked.
107
+ # Turn on request forgery protection. Bear in mind that GET and HEAD requests
108
+ # are not checked.
107
109
  #
108
- # class ApplicationController < ActionController::Base
109
- # protect_from_forgery
110
- # end
110
+ # class ApplicationController < ActionController::Base
111
+ # protect_from_forgery
112
+ # end
111
113
  #
112
- # class FooController < ApplicationController
113
- # protect_from_forgery except: :index
114
- # end
114
+ # class FooController < ApplicationController
115
+ # protect_from_forgery except: :index
116
+ # end
115
117
  #
116
- # You can disable forgery protection on a controller using skip_forgery_protection:
118
+ # You can disable forgery protection on a controller using
119
+ # skip_forgery_protection:
117
120
  #
118
- # class BarController < ApplicationController
119
- # skip_forgery_protection
120
- # end
121
+ # class BarController < ApplicationController
122
+ # skip_forgery_protection
123
+ # end
121
124
  #
122
125
  # Valid Options:
123
126
  #
124
- # * <tt>:only</tt> / <tt>:except</tt> - Only apply forgery protection to a subset of actions. For example <tt>only: [ :create, :create_all ]</tt>.
125
- # * <tt>:if</tt> / <tt>:unless</tt> - Turn off the forgery protection entirely depending on the passed Proc or method reference.
126
- # * <tt>:prepend</tt> - By default, the verification of the authentication token will be added at the position of the
127
- # protect_from_forgery call in your application. This means any callbacks added before are run first. This is useful
128
- # when you want your forgery protection to depend on other callbacks, like authentication methods (Oauth vs Cookie auth).
127
+ # * `:only` / `:except` - Only apply forgery protection to a subset of
128
+ # actions. For example `only: [ :create, :create_all ]`.
129
+ # * `:if` / `:unless` - Turn off the forgery protection entirely depending on
130
+ # the passed Proc or method reference.
131
+ # * `:prepend` - By default, the verification of the authentication token will
132
+ # be added at the position of the protect_from_forgery call in your
133
+ # application. This means any callbacks added before are run first. This is
134
+ # useful when you want your forgery protection to depend on other callbacks,
135
+ # like authentication methods (Oauth vs Cookie auth).
136
+ #
137
+ # If you need to add verification to the beginning of the callback chain,
138
+ # use `prepend: true`.
139
+ # * `:with` - Set the method to handle unverified request. Note if
140
+ # `default_protect_from_forgery` is true, Rails call protect_from_forgery
141
+ # with `with :exception`.
129
142
  #
130
- # If you need to add verification to the beginning of the callback chain, use <tt>prepend: true</tt>.
131
- # * <tt>:with</tt> - Set the method to handle unverified request.
132
- # Note if <tt>default_protect_from_forgery</tt> is true, Rails call protect_from_forgery with <tt>with :exception</tt>.
133
143
  #
134
144
  # Built-in unverified request handling methods are:
135
- # * <tt>:exception</tt> - Raises ActionController::InvalidAuthenticityToken exception.
136
- # * <tt>:reset_session</tt> - Resets the session.
137
- # * <tt>:null_session</tt> - Provides an empty session during request but doesn't reset it completely. Used as default if <tt>:with</tt> option is not specified.
138
145
  #
139
- # You can also implement custom strategy classes for unverified request handling:
146
+ # * `:exception` - Raises ActionController::InvalidAuthenticityToken
147
+ # exception.
148
+ # * `:reset_session` - Resets the session.
149
+ # * `:null_session` - Provides an empty session during request but doesn't
150
+ # reset it completely. Used as default if `:with` option is not specified.
140
151
  #
141
- # class CustomStrategy
142
- # def initialize(controller)
143
- # @controller = controller
144
- # end
145
152
  #
146
- # def handle_unverified_request
147
- # # Custom behavior for unverfied request
148
- # end
149
- # end
153
+ # You can also implement custom strategy classes for unverified request
154
+ # handling:
155
+ #
156
+ # class CustomStrategy
157
+ # def initialize(controller)
158
+ # @controller = controller
159
+ # end
160
+ #
161
+ # def handle_unverified_request
162
+ # # Custom behavior for unverfied request
163
+ # end
164
+ # end
165
+ #
166
+ # class ApplicationController < ActionController::Base
167
+ # protect_from_forgery with: CustomStrategy
168
+ # end
169
+ #
170
+ # * `:store` - Set the strategy to store and retrieve CSRF tokens.
150
171
  #
151
- # class ApplicationController < ActionController::Base
152
- # protect_from_forgery with: CustomStrategy
153
- # end
154
- # * <tt>:store</tt> - Set the strategy to store and retrieve CSRF tokens.
155
172
  #
156
173
  # Built-in session token strategies are:
157
- # * <tt>:session</tt> - Store the CSRF token in the session. Used as default if <tt>:store</tt> option is not specified.
158
- # * <tt>:cookie</tt> - Store the CSRF token in an encrypted cookie.
174
+ #
175
+ # * `:session` - Store the CSRF token in the session. Used as default if
176
+ # `:store` option is not specified.
177
+ # * `:cookie` - Store the CSRF token in an encrypted cookie.
178
+ #
159
179
  #
160
180
  # You can also implement custom strategy classes for CSRF token storage:
161
181
  #
162
- # class CustomStore
163
- # def fetch(request)
164
- # # Return the token from a custom location
165
- # end
182
+ # class CustomStore
183
+ # def fetch(request)
184
+ # # Return the token from a custom location
185
+ # end
166
186
  #
167
- # def store(request, csrf_token)
168
- # # Store the token in a custom location
169
- # end
187
+ # def store(request, csrf_token)
188
+ # # Store the token in a custom location
189
+ # end
170
190
  #
171
- # def reset(request)
172
- # # Delete the stored session token
191
+ # def reset(request)
192
+ # # Delete the stored session token
193
+ # end
173
194
  # end
174
- # end
175
195
  #
176
- # class ApplicationController < ActionController::Base
177
- # protect_from_forgery store: CustomStore.new
178
- # end
196
+ # class ApplicationController < ActionController::Base
197
+ # protect_from_forgery store: CustomStore.new
198
+ # end
179
199
  def protect_from_forgery(options = {})
180
200
  options = options.reverse_merge(prepend: false)
181
201
 
@@ -190,9 +210,9 @@ module ActionController # :nodoc:
190
210
 
191
211
  # Turn off request forgery protection. This is a wrapper for:
192
212
  #
193
- # skip_before_action :verify_authenticity_token
213
+ # skip_before_action :verify_authenticity_token
194
214
  #
195
- # See +skip_before_action+ for allowed options.
215
+ # See `skip_before_action` for allowed options.
196
216
  def skip_forgery_protection(options = {})
197
217
  skip_before_action :verify_authenticity_token, options.reverse_merge(raise: false)
198
218
  end
@@ -236,7 +256,8 @@ module ActionController # :nodoc:
236
256
  @controller = controller
237
257
  end
238
258
 
239
- # This is the method that defines the application behavior when a request is found to be unverified.
259
+ # This is the method that defines the application behavior when a request is
260
+ # found to be unverified.
240
261
  def handle_unverified_request
241
262
  request = @controller.request
242
263
  request.session = NullSessionHash.new(request)
@@ -246,7 +267,7 @@ module ActionController # :nodoc:
246
267
  end
247
268
 
248
269
  private
249
- class NullSessionHash < Rack::Session::Abstract::SessionHash # :nodoc:
270
+ class NullSessionHash < Rack::Session::Abstract::SessionHash
250
271
  def initialize(req)
251
272
  super(nil, req)
252
273
  @data = {}
@@ -265,7 +286,7 @@ module ActionController # :nodoc:
265
286
  end
266
287
  end
267
288
 
268
- class NullCookieJar < ActionDispatch::Cookies::CookieJar # :nodoc:
289
+ class NullCookieJar < ActionDispatch::Cookies::CookieJar
269
290
  def write(*)
270
291
  # nothing
271
292
  end
@@ -344,7 +365,7 @@ module ActionController # :nodoc:
344
365
 
345
366
  def initialize(...)
346
367
  super
347
- @marked_for_same_origin_verification = nil
368
+ @_marked_for_same_origin_verification = nil
348
369
  end
349
370
 
350
371
  def reset_csrf_token(request) # :doc:
@@ -358,16 +379,15 @@ module ActionController # :nodoc:
358
379
  end
359
380
 
360
381
  private
361
- # The actual before_action that is used to verify the CSRF token.
362
- # Don't override this directly. Provide your own forgery protection
363
- # strategy instead. If you override, you'll disable same-origin
364
- # <tt><script></tt> verification.
382
+ # The actual before_action that is used to verify the CSRF token. Don't override
383
+ # this directly. Provide your own forgery protection strategy instead. If you
384
+ # override, you'll disable same-origin `<script>` verification.
365
385
  #
366
- # Lean on the protect_from_forgery declaration to mark which actions are
367
- # due for same-origin request verification. If protect_from_forgery is
368
- # enabled on an action, this before_action flags its after_action to
369
- # verify that JavaScript responses are for XHR requests, ensuring they
370
- # follow the browser's same-origin policy.
386
+ # Lean on the protect_from_forgery declaration to mark which actions are due for
387
+ # same-origin request verification. If protect_from_forgery is enabled on an
388
+ # action, this before_action flags its after_action to verify that JavaScript
389
+ # responses are for XHR requests, ensuring they follow the browser's same-origin
390
+ # policy.
371
391
  def verify_authenticity_token # :doc:
372
392
  mark_for_same_origin_verification!
373
393
 
@@ -378,7 +398,7 @@ module ActionController # :nodoc:
378
398
  end
379
399
  end
380
400
 
381
- def handle_unverified_request # :doc:
401
+ def handle_unverified_request
382
402
  protection_strategy = forgery_protection_strategy.new(self)
383
403
 
384
404
  if protection_strategy.respond_to?(:warning_message)
@@ -388,7 +408,7 @@ module ActionController # :nodoc:
388
408
  protection_strategy.handle_unverified_request
389
409
  end
390
410
 
391
- def unverified_request_warning_message # :nodoc:
411
+ def unverified_request_warning_message
392
412
  if valid_request_origin?
393
413
  "Can't verify CSRF token authenticity."
394
414
  else
@@ -396,7 +416,6 @@ module ActionController # :nodoc:
396
416
  end
397
417
  end
398
418
 
399
- # :nodoc:
400
419
  CROSS_ORIGIN_JAVASCRIPT_WARNING = "Security warning: an embedded " \
401
420
  "<script> tag on another site requested protected JavaScript. " \
402
421
  "If you know what you're doing, go ahead and disable forgery " \
@@ -404,9 +423,9 @@ module ActionController # :nodoc:
404
423
  private_constant :CROSS_ORIGIN_JAVASCRIPT_WARNING
405
424
  # :startdoc:
406
425
 
407
- # If +verify_authenticity_token+ was run (indicating that we have
408
- # forgery protection enabled for this request) then also verify that
409
- # we aren't serving an unauthorized cross-origin response.
426
+ # If `verify_authenticity_token` was run (indicating that we have
427
+ # forgery protection enabled for this request) then also verify that we aren't
428
+ # serving an unauthorized cross-origin response.
410
429
  def verify_same_origin_request # :doc:
411
430
  if marked_for_same_origin_verification? && non_xhr_javascript_response?
412
431
  if logger && log_warning_on_csrf_failure
@@ -418,13 +437,13 @@ module ActionController # :nodoc:
418
437
 
419
438
  # GET requests are checked for cross-origin JavaScript after rendering.
420
439
  def mark_for_same_origin_verification! # :doc:
421
- @marked_for_same_origin_verification = request.get?
440
+ @_marked_for_same_origin_verification = request.get?
422
441
  end
423
442
 
424
- # If the +verify_authenticity_token+ before_action ran, verify that
425
- # JavaScript responses are only served to same-origin GET requests.
443
+ # If the `verify_authenticity_token` before_action ran, verify that JavaScript
444
+ # responses are only served to same-origin GET requests.
426
445
  def marked_for_same_origin_verification? # :doc:
427
- @marked_for_same_origin_verification ||= false
446
+ @_marked_for_same_origin_verification ||= false
428
447
  end
429
448
 
430
449
  # Check for cross-origin JavaScript responses.
@@ -436,9 +455,11 @@ module ActionController # :nodoc:
436
455
 
437
456
  # Returns true or false if a request is verified. Checks:
438
457
  #
439
- # * Is it a GET or HEAD request? GETs should be safe and idempotent
440
- # * Does the form_authenticity_token match the given token value from the params?
441
- # * Does the +X-CSRF-Token+ header match the form_authenticity_token?
458
+ # * Is it a GET or HEAD request? GETs should be safe and idempotent
459
+ # * Does the form_authenticity_token match the given token value from the
460
+ # params?
461
+ # * Does the `X-CSRF-Token` header match the form_authenticity_token?
462
+ #
442
463
  def verified_request? # :doc:
443
464
  !protect_against_forgery? || request.get? || request.head? ||
444
465
  (valid_request_origin? && any_authenticity_token_valid?)
@@ -461,9 +482,8 @@ module ActionController # :nodoc:
461
482
  masked_authenticity_token(form_options: form_options)
462
483
  end
463
484
 
464
- # Creates a masked version of the authenticity token that varies
465
- # on each request. The masking is used to mitigate SSL attacks
466
- # like BREACH.
485
+ # Creates a masked version of the authenticity token that varies on each
486
+ # request. The masking is used to mitigate SSL attacks like BREACH.
467
487
  def masked_authenticity_token(form_options: {})
468
488
  action, method = form_options.values_at(:action, :method)
469
489
 
@@ -477,11 +497,10 @@ module ActionController # :nodoc:
477
497
  mask_token(raw_token)
478
498
  end
479
499
 
480
- # Checks the client's masked token to see if it matches the
481
- # session token. Essentially the inverse of
482
- # +masked_authenticity_token+.
500
+ # Checks the client's masked token to see if it matches the session token.
501
+ # Essentially the inverse of `masked_authenticity_token`.
483
502
  def valid_authenticity_token?(session, encoded_masked_token) # :doc:
484
- if encoded_masked_token.nil? || encoded_masked_token.empty? || !encoded_masked_token.is_a?(String)
503
+ if !encoded_masked_token.is_a?(String) || encoded_masked_token.empty?
485
504
  return false
486
505
  end
487
506
 
@@ -491,14 +510,12 @@ module ActionController # :nodoc:
491
510
  return false
492
511
  end
493
512
 
494
- # See if it's actually a masked token or not. In order to
495
- # deploy this code, we should be able to handle any unmasked
496
- # tokens that we've issued without error.
513
+ # See if it's actually a masked token or not. In order to deploy this code, we
514
+ # should be able to handle any unmasked tokens that we've issued without error.
497
515
 
498
516
  if masked_token.length == AUTHENTICITY_TOKEN_LENGTH
499
- # This is actually an unmasked token. This is expected if
500
- # you have just upgraded to masked tokens, but should stop
501
- # happening shortly after installing this gem.
517
+ # This is actually an unmasked token. This is expected if you have just upgraded
518
+ # to masked tokens, but should stop happening shortly after installing this gem.
502
519
  compare_with_real_token masked_token
503
520
 
504
521
  elsif masked_token.length == AUTHENTICITY_TOKEN_LENGTH * 2
@@ -513,8 +530,7 @@ module ActionController # :nodoc:
513
530
  end
514
531
 
515
532
  def unmask_token(masked_token) # :doc:
516
- # Split the token into the one-time pad and the encrypted
517
- # value and decrypt it.
533
+ # Split the token into the one-time pad and the encrypted value and decrypt it.
518
534
  one_time_pad = masked_token[0...AUTHENTICITY_TOKEN_LENGTH]
519
535
  encrypted_csrf_token = masked_token[AUTHENTICITY_TOKEN_LENGTH..-1]
520
536
  xor_byte_strings(one_time_pad, encrypted_csrf_token)
@@ -606,8 +622,8 @@ module ActionController # :nodoc:
606
622
  Rails.application.config.action_controller.forgery_protection_origin_check setting.
607
623
  MSG
608
624
 
609
- # Checks if the request originated from the same origin by looking at the
610
- # Origin header.
625
+ # Checks if the request originated from the same origin by looking at the Origin
626
+ # header.
611
627
  def valid_request_origin? # :doc:
612
628
  if forgery_protection_origin_check
613
629
  # We accept blank origin headers because some user agents don't send it.
@@ -620,18 +636,33 @@ module ActionController # :nodoc:
620
636
 
621
637
  def normalize_action_path(action_path) # :doc:
622
638
  uri = URI.parse(action_path)
639
+
640
+ if uri.relative? && (action_path.blank? || !action_path.start_with?("/"))
641
+ normalize_relative_action_path(uri.path)
642
+ else
643
+ uri.path.chomp("/")
644
+ end
645
+ end
646
+
647
+ def normalize_relative_action_path(rel_action_path) # :doc:
648
+ uri = URI.parse(request.path)
649
+ # add the action path to the request.path
650
+ uri.path += "/#{rel_action_path}"
651
+ # relative path with "./path"
652
+ uri.path.gsub!("/./", "/")
653
+
623
654
  uri.path.chomp("/")
624
655
  end
625
656
 
626
- def generate_csrf_token # :nodoc:
657
+ def generate_csrf_token
627
658
  SecureRandom.urlsafe_base64(AUTHENTICITY_TOKEN_LENGTH)
628
659
  end
629
660
 
630
- def encode_csrf_token(csrf_token) # :nodoc:
661
+ def encode_csrf_token(csrf_token)
631
662
  Base64.urlsafe_encode64(csrf_token, padding: false)
632
663
  end
633
664
 
634
- def decode_csrf_token(encoded_csrf_token) # :nodoc:
665
+ def decode_csrf_token(encoded_csrf_token)
635
666
  Base64.urlsafe_decode64(encoded_csrf_token)
636
667
  end
637
668
  end
@@ -1,21 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActionController # :nodoc:
4
- # = Action Controller \Rescue
6
+ # # Action Controller Rescue
5
7
  #
6
8
  # This module is responsible for providing
7
- # {rescue_from}[rdoc-ref:ActiveSupport::Rescuable::ClassMethods#rescue_from]
8
- # to controllers, wrapping actions to handle configured errors, and
9
- # configuring when detailed exceptions must be shown.
9
+ # [rescue_from](rdoc-ref:ActiveSupport::Rescuable::ClassMethods#rescue_from) to
10
+ # controllers, wrapping actions to handle configured errors, and configuring
11
+ # when detailed exceptions must be shown.
10
12
  module Rescue
11
13
  extend ActiveSupport::Concern
12
14
  include ActiveSupport::Rescuable
13
15
 
14
- # Override this method if you want to customize when detailed
15
- # exceptions must be shown. This method is only called when
16
- # +consider_all_requests_local+ is +false+. By default, it returns
17
- # +false+, but someone may set it to <tt>request.local?</tt> so local
18
- # requests in production still show the detailed exception pages.
16
+ # Override this method if you want to customize when detailed exceptions must be
17
+ # shown. This method is only called when `consider_all_requests_local` is
18
+ # `false`. By default, it returns `false`, but someone may set it to
19
+ # `request.local?` so local requests in production still show the detailed
20
+ # exception pages.
19
21
  def show_detailed_exceptions?
20
22
  false
21
23
  end