actionpack 7.1.3 → 7.2.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (158) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +82 -501
  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 +15 -7
  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 +198 -126
  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 +123 -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 +210 -205
  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 +525 -480
  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 +86 -60
  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 +10 -3
  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 +44 -38
  69. data/lib/action_dispatch/http/filter_parameters.rb +18 -9
  70. data/lib/action_dispatch/http/filter_redirect.rb +22 -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 +31 -24
  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 +20 -44
  77. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  78. data/lib/action_dispatch/http/request.rb +94 -75
  79. data/lib/action_dispatch/http/response.rb +73 -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 +8 -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 +31 -21
  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/rescues/missing_exact_template.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 -4
  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 +671 -636
  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 +10 -3
  140. data/lib/action_dispatch/system_testing/driver.rb +3 -1
  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 +8 -6
  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 +223 -222
  149. data/lib/action_dispatch/testing/request_encoder.rb +2 -0
  150. data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
  151. data/lib/action_dispatch/testing/test_process.rb +12 -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 +6 -4
  156. data/lib/action_pack/version.rb +3 -1
  157. data/lib/action_pack.rb +17 -16
  158. metadata +39 -16
@@ -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,96 @@ 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.
145
+ # * `:exception` - Raises ActionController::InvalidAuthenticityToken
146
+ # exception.
147
+ # * `:reset_session` - Resets the session.
148
+ # * `:null_session` - Provides an empty session during request but doesn't
149
+ # reset it completely. Used as default if `:with` option is not specified.
138
150
  #
139
- # You can also implement custom strategy classes for unverified request handling:
140
151
  #
141
- # class CustomStrategy
142
- # def initialize(controller)
143
- # @controller = controller
144
- # end
152
+ # You can also implement custom strategy classes for unverified request
153
+ # handling:
154
+ #
155
+ # class CustomStrategy
156
+ # def initialize(controller)
157
+ # @controller = controller
158
+ # end
159
+ #
160
+ # def handle_unverified_request
161
+ # # Custom behavior for unverfied request
162
+ # end
163
+ # end
164
+ #
165
+ # class ApplicationController < ActionController::Base
166
+ # protect_from_forgery with: CustomStrategy
167
+ # end
145
168
  #
146
- # def handle_unverified_request
147
- # # Custom behavior for unverfied request
148
- # end
149
- # end
169
+ # * `:store` - Set the strategy to store and retrieve CSRF tokens.
150
170
  #
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
171
  #
156
172
  # 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.
173
+ # * `:session` - Store the CSRF token in the session. Used as default if
174
+ # `:store` option is not specified.
175
+ # * `:cookie` - Store the CSRF token in an encrypted cookie.
176
+ #
159
177
  #
160
178
  # You can also implement custom strategy classes for CSRF token storage:
161
179
  #
162
- # class CustomStore
163
- # def fetch(request)
164
- # # Return the token from a custom location
165
- # end
180
+ # class CustomStore
181
+ # def fetch(request)
182
+ # # Return the token from a custom location
183
+ # end
166
184
  #
167
- # def store(request, csrf_token)
168
- # # Store the token in a custom location
169
- # end
185
+ # def store(request, csrf_token)
186
+ # # Store the token in a custom location
187
+ # end
170
188
  #
171
- # def reset(request)
172
- # # Delete the stored session token
189
+ # def reset(request)
190
+ # # Delete the stored session token
191
+ # end
173
192
  # end
174
- # end
175
193
  #
176
- # class ApplicationController < ActionController::Base
177
- # protect_from_forgery store: CustomStore.new
178
- # end
194
+ # class ApplicationController < ActionController::Base
195
+ # protect_from_forgery store: CustomStore.new
196
+ # end
179
197
  def protect_from_forgery(options = {})
180
198
  options = options.reverse_merge(prepend: false)
181
199
 
@@ -190,9 +208,9 @@ module ActionController # :nodoc:
190
208
 
191
209
  # Turn off request forgery protection. This is a wrapper for:
192
210
  #
193
- # skip_before_action :verify_authenticity_token
211
+ # skip_before_action :verify_authenticity_token
194
212
  #
195
- # See +skip_before_action+ for allowed options.
213
+ # See `skip_before_action` for allowed options.
196
214
  def skip_forgery_protection(options = {})
197
215
  skip_before_action :verify_authenticity_token, options.reverse_merge(raise: false)
198
216
  end
@@ -236,7 +254,8 @@ module ActionController # :nodoc:
236
254
  @controller = controller
237
255
  end
238
256
 
239
- # This is the method that defines the application behavior when a request is found to be unverified.
257
+ # This is the method that defines the application behavior when a request is
258
+ # found to be unverified.
240
259
  def handle_unverified_request
241
260
  request = @controller.request
242
261
  request.session = NullSessionHash.new(request)
@@ -246,7 +265,7 @@ module ActionController # :nodoc:
246
265
  end
247
266
 
248
267
  private
249
- class NullSessionHash < Rack::Session::Abstract::SessionHash # :nodoc:
268
+ class NullSessionHash < Rack::Session::Abstract::SessionHash
250
269
  def initialize(req)
251
270
  super(nil, req)
252
271
  @data = {}
@@ -265,7 +284,7 @@ module ActionController # :nodoc:
265
284
  end
266
285
  end
267
286
 
268
- class NullCookieJar < ActionDispatch::Cookies::CookieJar # :nodoc:
287
+ class NullCookieJar < ActionDispatch::Cookies::CookieJar
269
288
  def write(*)
270
289
  # nothing
271
290
  end
@@ -344,7 +363,7 @@ module ActionController # :nodoc:
344
363
 
345
364
  def initialize(...)
346
365
  super
347
- @marked_for_same_origin_verification = nil
366
+ @_marked_for_same_origin_verification = nil
348
367
  end
349
368
 
350
369
  def reset_csrf_token(request) # :doc:
@@ -358,16 +377,15 @@ module ActionController # :nodoc:
358
377
  end
359
378
 
360
379
  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.
380
+ # The actual before_action that is used to verify the CSRF token. Don't override
381
+ # this directly. Provide your own forgery protection strategy instead. If you
382
+ # override, you'll disable same-origin `<script>` verification.
365
383
  #
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.
384
+ # Lean on the protect_from_forgery declaration to mark which actions are due for
385
+ # same-origin request verification. If protect_from_forgery is enabled on an
386
+ # action, this before_action flags its after_action to verify that JavaScript
387
+ # responses are for XHR requests, ensuring they follow the browser's same-origin
388
+ # policy.
371
389
  def verify_authenticity_token # :doc:
372
390
  mark_for_same_origin_verification!
373
391
 
@@ -378,7 +396,7 @@ module ActionController # :nodoc:
378
396
  end
379
397
  end
380
398
 
381
- def handle_unverified_request # :doc:
399
+ def handle_unverified_request
382
400
  protection_strategy = forgery_protection_strategy.new(self)
383
401
 
384
402
  if protection_strategy.respond_to?(:warning_message)
@@ -388,7 +406,7 @@ module ActionController # :nodoc:
388
406
  protection_strategy.handle_unverified_request
389
407
  end
390
408
 
391
- def unverified_request_warning_message # :nodoc:
409
+ def unverified_request_warning_message
392
410
  if valid_request_origin?
393
411
  "Can't verify CSRF token authenticity."
394
412
  else
@@ -396,7 +414,6 @@ module ActionController # :nodoc:
396
414
  end
397
415
  end
398
416
 
399
- # :nodoc:
400
417
  CROSS_ORIGIN_JAVASCRIPT_WARNING = "Security warning: an embedded " \
401
418
  "<script> tag on another site requested protected JavaScript. " \
402
419
  "If you know what you're doing, go ahead and disable forgery " \
@@ -404,9 +421,9 @@ module ActionController # :nodoc:
404
421
  private_constant :CROSS_ORIGIN_JAVASCRIPT_WARNING
405
422
  # :startdoc:
406
423
 
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.
424
+ # If `verify_authenticity_token` was run (indicating that we have
425
+ # forgery protection enabled for this request) then also verify that we aren't
426
+ # serving an unauthorized cross-origin response.
410
427
  def verify_same_origin_request # :doc:
411
428
  if marked_for_same_origin_verification? && non_xhr_javascript_response?
412
429
  if logger && log_warning_on_csrf_failure
@@ -418,13 +435,13 @@ module ActionController # :nodoc:
418
435
 
419
436
  # GET requests are checked for cross-origin JavaScript after rendering.
420
437
  def mark_for_same_origin_verification! # :doc:
421
- @marked_for_same_origin_verification = request.get?
438
+ @_marked_for_same_origin_verification = request.get?
422
439
  end
423
440
 
424
- # If the +verify_authenticity_token+ before_action ran, verify that
425
- # JavaScript responses are only served to same-origin GET requests.
441
+ # If the `verify_authenticity_token` before_action ran, verify that JavaScript
442
+ # responses are only served to same-origin GET requests.
426
443
  def marked_for_same_origin_verification? # :doc:
427
- @marked_for_same_origin_verification ||= false
444
+ @_marked_for_same_origin_verification ||= false
428
445
  end
429
446
 
430
447
  # Check for cross-origin JavaScript responses.
@@ -436,9 +453,11 @@ module ActionController # :nodoc:
436
453
 
437
454
  # Returns true or false if a request is verified. Checks:
438
455
  #
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?
456
+ # * Is it a GET or HEAD request? GETs should be safe and idempotent
457
+ # * Does the form_authenticity_token match the given token value from the
458
+ # params?
459
+ # * Does the `X-CSRF-Token` header match the form_authenticity_token?
460
+ #
442
461
  def verified_request? # :doc:
443
462
  !protect_against_forgery? || request.get? || request.head? ||
444
463
  (valid_request_origin? && any_authenticity_token_valid?)
@@ -461,9 +480,8 @@ module ActionController # :nodoc:
461
480
  masked_authenticity_token(form_options: form_options)
462
481
  end
463
482
 
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.
483
+ # Creates a masked version of the authenticity token that varies on each
484
+ # request. The masking is used to mitigate SSL attacks like BREACH.
467
485
  def masked_authenticity_token(form_options: {})
468
486
  action, method = form_options.values_at(:action, :method)
469
487
 
@@ -477,9 +495,8 @@ module ActionController # :nodoc:
477
495
  mask_token(raw_token)
478
496
  end
479
497
 
480
- # Checks the client's masked token to see if it matches the
481
- # session token. Essentially the inverse of
482
- # +masked_authenticity_token+.
498
+ # Checks the client's masked token to see if it matches the session token.
499
+ # Essentially the inverse of `masked_authenticity_token`.
483
500
  def valid_authenticity_token?(session, encoded_masked_token) # :doc:
484
501
  if encoded_masked_token.nil? || encoded_masked_token.empty? || !encoded_masked_token.is_a?(String)
485
502
  return false
@@ -491,14 +508,12 @@ module ActionController # :nodoc:
491
508
  return false
492
509
  end
493
510
 
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.
511
+ # See if it's actually a masked token or not. In order to deploy this code, we
512
+ # should be able to handle any unmasked tokens that we've issued without error.
497
513
 
498
514
  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.
515
+ # This is actually an unmasked token. This is expected if you have just upgraded
516
+ # to masked tokens, but should stop happening shortly after installing this gem.
502
517
  compare_with_real_token masked_token
503
518
 
504
519
  elsif masked_token.length == AUTHENTICITY_TOKEN_LENGTH * 2
@@ -513,8 +528,7 @@ module ActionController # :nodoc:
513
528
  end
514
529
 
515
530
  def unmask_token(masked_token) # :doc:
516
- # Split the token into the one-time pad and the encrypted
517
- # value and decrypt it.
531
+ # Split the token into the one-time pad and the encrypted value and decrypt it.
518
532
  one_time_pad = masked_token[0...AUTHENTICITY_TOKEN_LENGTH]
519
533
  encrypted_csrf_token = masked_token[AUTHENTICITY_TOKEN_LENGTH..-1]
520
534
  xor_byte_strings(one_time_pad, encrypted_csrf_token)
@@ -606,8 +620,8 @@ module ActionController # :nodoc:
606
620
  Rails.application.config.action_controller.forgery_protection_origin_check setting.
607
621
  MSG
608
622
 
609
- # Checks if the request originated from the same origin by looking at the
610
- # Origin header.
623
+ # Checks if the request originated from the same origin by looking at the Origin
624
+ # header.
611
625
  def valid_request_origin? # :doc:
612
626
  if forgery_protection_origin_check
613
627
  # We accept blank origin headers because some user agents don't send it.
@@ -620,18 +634,33 @@ module ActionController # :nodoc:
620
634
 
621
635
  def normalize_action_path(action_path) # :doc:
622
636
  uri = URI.parse(action_path)
637
+
638
+ if uri.relative? && (action_path.blank? || !action_path.start_with?("/"))
639
+ normalize_relative_action_path(uri.path)
640
+ else
641
+ uri.path.chomp("/")
642
+ end
643
+ end
644
+
645
+ def normalize_relative_action_path(rel_action_path) # :doc:
646
+ uri = URI.parse(request.path)
647
+ # add the action path to the request.path
648
+ uri.path += "/#{rel_action_path}"
649
+ # relative path with "./path"
650
+ uri.path.gsub!("/./", "/")
651
+
623
652
  uri.path.chomp("/")
624
653
  end
625
654
 
626
- def generate_csrf_token # :nodoc:
655
+ def generate_csrf_token
627
656
  SecureRandom.urlsafe_base64(AUTHENTICITY_TOKEN_LENGTH)
628
657
  end
629
658
 
630
- def encode_csrf_token(csrf_token) # :nodoc:
659
+ def encode_csrf_token(csrf_token)
631
660
  Base64.urlsafe_encode64(csrf_token, padding: false)
632
661
  end
633
662
 
634
- def decode_csrf_token(encoded_csrf_token) # :nodoc:
663
+ def decode_csrf_token(encoded_csrf_token)
635
664
  Base64.urlsafe_decode64(encoded_csrf_token)
636
665
  end
637
666
  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