actionpack 7.1.5.1 → 8.1.2

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 (177) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +308 -523
  3. data/README.rdoc +1 -1
  4. data/lib/abstract_controller/asset_paths.rb +6 -2
  5. data/lib/abstract_controller/base.rb +104 -105
  6. data/lib/abstract_controller/caching/fragments.rb +50 -53
  7. data/lib/abstract_controller/caching.rb +8 -3
  8. data/lib/abstract_controller/callbacks.rb +70 -62
  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 +4 -1
  14. data/lib/abstract_controller/railties/routes_helpers.rb +2 -0
  15. data/lib/abstract_controller/rendering.rb +13 -13
  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 +76 -72
  21. data/lib/action_controller/base.rb +199 -126
  22. data/lib/action_controller/caching.rb +16 -14
  23. data/lib/action_controller/deprecator.rb +2 -0
  24. data/lib/action_controller/form_builder.rb +21 -18
  25. data/lib/action_controller/log_subscriber.rb +23 -2
  26. data/lib/action_controller/metal/allow_browser.rb +133 -0
  27. data/lib/action_controller/metal/basic_implicit_render.rb +2 -0
  28. data/lib/action_controller/metal/conditional_get.rb +217 -175
  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 +72 -63
  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 +16 -9
  36. data/lib/action_controller/metal/flash.rb +13 -14
  37. data/lib/action_controller/metal/head.rb +15 -11
  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 +16 -14
  42. data/lib/action_controller/metal/live.rb +177 -128
  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 +22 -12
  48. data/lib/action_controller/metal/rate_limiting.rb +92 -0
  49. data/lib/action_controller/metal/redirecting.rb +213 -94
  50. data/lib/action_controller/metal/renderers.rb +78 -57
  51. data/lib/action_controller/metal/rendering.rb +111 -77
  52. data/lib/action_controller/metal/request_forgery_protection.rb +182 -143
  53. data/lib/action_controller/metal/rescue.rb +20 -9
  54. data/lib/action_controller/metal/streaming.rb +118 -195
  55. data/lib/action_controller/metal/strong_parameters.rb +720 -530
  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 +36 -15
  60. data/lib/action_controller/railties/helpers.rb +2 -0
  61. data/lib/action_controller/renderer.rb +41 -36
  62. data/lib/action_controller/structured_event_subscriber.rb +116 -0
  63. data/lib/action_controller/template_assertions.rb +4 -2
  64. data/lib/action_controller/test_case.rb +160 -131
  65. data/lib/action_controller.rb +5 -1
  66. data/lib/action_dispatch/constants.rb +8 -0
  67. data/lib/action_dispatch/deprecator.rb +2 -0
  68. data/lib/action_dispatch/http/cache.rb +163 -35
  69. data/lib/action_dispatch/http/content_disposition.rb +2 -0
  70. data/lib/action_dispatch/http/content_security_policy.rb +54 -39
  71. data/lib/action_dispatch/http/filter_parameters.rb +14 -8
  72. data/lib/action_dispatch/http/filter_redirect.rb +22 -1
  73. data/lib/action_dispatch/http/headers.rb +22 -22
  74. data/lib/action_dispatch/http/mime_negotiation.rb +89 -41
  75. data/lib/action_dispatch/http/mime_type.rb +25 -21
  76. data/lib/action_dispatch/http/mime_types.rb +3 -0
  77. data/lib/action_dispatch/http/param_builder.rb +187 -0
  78. data/lib/action_dispatch/http/param_error.rb +26 -0
  79. data/lib/action_dispatch/http/parameters.rb +14 -12
  80. data/lib/action_dispatch/http/permissions_policy.rb +25 -36
  81. data/lib/action_dispatch/http/query_parser.rb +55 -0
  82. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  83. data/lib/action_dispatch/http/request.rb +141 -92
  84. data/lib/action_dispatch/http/response.rb +137 -77
  85. data/lib/action_dispatch/http/upload.rb +18 -16
  86. data/lib/action_dispatch/http/url.rb +187 -89
  87. data/lib/action_dispatch/journey/formatter.rb +21 -9
  88. data/lib/action_dispatch/journey/gtg/builder.rb +4 -3
  89. data/lib/action_dispatch/journey/gtg/simulator.rb +34 -11
  90. data/lib/action_dispatch/journey/gtg/transition_table.rb +47 -53
  91. data/lib/action_dispatch/journey/nfa/dot.rb +2 -0
  92. data/lib/action_dispatch/journey/nodes/node.rb +8 -6
  93. data/lib/action_dispatch/journey/parser.rb +99 -195
  94. data/lib/action_dispatch/journey/path/pattern.rb +4 -1
  95. data/lib/action_dispatch/journey/route.rb +54 -38
  96. data/lib/action_dispatch/journey/router/utils.rb +22 -27
  97. data/lib/action_dispatch/journey/router.rb +63 -83
  98. data/lib/action_dispatch/journey/routes.rb +11 -2
  99. data/lib/action_dispatch/journey/scanner.rb +46 -42
  100. data/lib/action_dispatch/journey/visitors.rb +57 -23
  101. data/lib/action_dispatch/journey/visualizer/fsm.js +4 -6
  102. data/lib/action_dispatch/journey.rb +2 -0
  103. data/lib/action_dispatch/log_subscriber.rb +7 -1
  104. data/lib/action_dispatch/middleware/actionable_exceptions.rb +2 -0
  105. data/lib/action_dispatch/middleware/assume_ssl.rb +8 -5
  106. data/lib/action_dispatch/middleware/callbacks.rb +3 -1
  107. data/lib/action_dispatch/middleware/cookies.rb +125 -106
  108. data/lib/action_dispatch/middleware/debug_exceptions.rb +37 -8
  109. data/lib/action_dispatch/middleware/debug_locks.rb +15 -13
  110. data/lib/action_dispatch/middleware/debug_view.rb +13 -5
  111. data/lib/action_dispatch/middleware/exception_wrapper.rb +18 -23
  112. data/lib/action_dispatch/middleware/executor.rb +19 -4
  113. data/lib/action_dispatch/middleware/flash.rb +63 -51
  114. data/lib/action_dispatch/middleware/host_authorization.rb +17 -15
  115. data/lib/action_dispatch/middleware/public_exceptions.rb +14 -12
  116. data/lib/action_dispatch/middleware/reloader.rb +5 -3
  117. data/lib/action_dispatch/middleware/remote_ip.rb +87 -77
  118. data/lib/action_dispatch/middleware/request_id.rb +16 -10
  119. data/lib/action_dispatch/middleware/server_timing.rb +4 -2
  120. data/lib/action_dispatch/middleware/session/abstract_store.rb +2 -0
  121. data/lib/action_dispatch/middleware/session/cache_store.rb +30 -8
  122. data/lib/action_dispatch/middleware/session/cookie_store.rb +27 -26
  123. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +7 -3
  124. data/lib/action_dispatch/middleware/show_exceptions.rb +16 -16
  125. data/lib/action_dispatch/middleware/ssl.rb +53 -40
  126. data/lib/action_dispatch/middleware/stack.rb +11 -10
  127. data/lib/action_dispatch/middleware/static.rb +33 -31
  128. data/lib/action_dispatch/middleware/templates/rescues/_copy_button.html.erb +1 -0
  129. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +3 -5
  130. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +9 -5
  131. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +1 -0
  132. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +1 -0
  133. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +4 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +3 -0
  135. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +50 -0
  136. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +1 -0
  137. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +1 -0
  138. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -0
  139. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -0
  140. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -0
  141. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +1 -1
  142. data/lib/action_dispatch/railtie.rb +23 -3
  143. data/lib/action_dispatch/request/session.rb +24 -21
  144. data/lib/action_dispatch/request/utils.rb +11 -3
  145. data/lib/action_dispatch/routing/endpoint.rb +2 -0
  146. data/lib/action_dispatch/routing/inspector.rb +85 -60
  147. data/lib/action_dispatch/routing/mapper.rb +1031 -851
  148. data/lib/action_dispatch/routing/polymorphic_routes.rb +69 -62
  149. data/lib/action_dispatch/routing/redirection.rb +47 -39
  150. data/lib/action_dispatch/routing/route_set.rb +79 -56
  151. data/lib/action_dispatch/routing/routes_proxy.rb +7 -4
  152. data/lib/action_dispatch/routing/url_for.rb +130 -125
  153. data/lib/action_dispatch/routing.rb +150 -148
  154. data/lib/action_dispatch/structured_event_subscriber.rb +20 -0
  155. data/lib/action_dispatch/system_test_case.rb +91 -81
  156. data/lib/action_dispatch/system_testing/browser.rb +16 -23
  157. data/lib/action_dispatch/system_testing/driver.rb +2 -0
  158. data/lib/action_dispatch/system_testing/server.rb +2 -0
  159. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +34 -23
  160. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +2 -0
  161. data/lib/action_dispatch/testing/assertion_response.rb +9 -7
  162. data/lib/action_dispatch/testing/assertions/response.rb +52 -25
  163. data/lib/action_dispatch/testing/assertions/routing.rb +168 -87
  164. data/lib/action_dispatch/testing/assertions.rb +2 -0
  165. data/lib/action_dispatch/testing/integration.rb +233 -223
  166. data/lib/action_dispatch/testing/request_encoder.rb +11 -9
  167. data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
  168. data/lib/action_dispatch/testing/test_process.rb +11 -8
  169. data/lib/action_dispatch/testing/test_request.rb +3 -1
  170. data/lib/action_dispatch/testing/test_response.rb +27 -26
  171. data/lib/action_dispatch.rb +36 -32
  172. data/lib/action_pack/gem_version.rb +6 -4
  173. data/lib/action_pack/version.rb +3 -1
  174. data/lib/action_pack.rb +17 -16
  175. metadata +36 -32
  176. data/lib/action_dispatch/journey/parser.y +0 -50
  177. data/lib/action_dispatch/journey/parser_extras.rb +0 -31
@@ -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,37 +69,41 @@ 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.
70
- config_accessor :request_forgery_protection_token
72
+ # Sets the token parameter name for RequestForgery. Calling
73
+ # `protect_from_forgery` sets it to `:authenticity_token` by default.
74
+ singleton_class.delegate :request_forgery_protection_token, :request_forgery_protection_token=, to: :config
75
+ delegate :request_forgery_protection_token, :request_forgery_protection_token=, to: :config
71
76
  self.request_forgery_protection_token ||= :authenticity_token
72
77
 
73
78
  # Holds the class which implements the request forgery protection.
74
- config_accessor :forgery_protection_strategy
79
+ singleton_class.delegate :forgery_protection_strategy, :forgery_protection_strategy=, to: :config
80
+ delegate :forgery_protection_strategy, :forgery_protection_strategy=, to: :config
75
81
  self.forgery_protection_strategy = nil
76
82
 
77
- # Controls whether request forgery protection is turned on or not. Turned off by default only in test mode.
78
- config_accessor :allow_forgery_protection
83
+ # Controls whether request forgery protection is turned on or not. Turned off by
84
+ # default only in test mode.
85
+ singleton_class.delegate :allow_forgery_protection, :allow_forgery_protection=, to: :config
86
+ delegate :allow_forgery_protection, :allow_forgery_protection=, to: :config
79
87
  self.allow_forgery_protection = true if allow_forgery_protection.nil?
80
88
 
81
89
  # Controls whether a CSRF failure logs a warning. On by default.
82
- config_accessor :log_warning_on_csrf_failure
90
+ singleton_class.delegate :log_warning_on_csrf_failure, :log_warning_on_csrf_failure=, to: :config
91
+ delegate :log_warning_on_csrf_failure, :log_warning_on_csrf_failure=, to: :config
83
92
  self.log_warning_on_csrf_failure = true
84
93
 
85
94
  # Controls whether the Origin header is checked in addition to the CSRF token.
86
- config_accessor :forgery_protection_origin_check
95
+ singleton_class.delegate :forgery_protection_origin_check, :forgery_protection_origin_check=, to: :config
96
+ delegate :forgery_protection_origin_check, :forgery_protection_origin_check=, to: :config
87
97
  self.forgery_protection_origin_check = false
88
98
 
89
99
  # Controls whether form-action/method specific CSRF tokens are used.
90
- config_accessor :per_form_csrf_tokens
100
+ singleton_class.delegate :per_form_csrf_tokens, :per_form_csrf_tokens=, to: :config
101
+ delegate :per_form_csrf_tokens, :per_form_csrf_tokens=, to: :config
91
102
  self.per_form_csrf_tokens = false
92
103
 
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
104
  # The strategy to use for storing and retrieving CSRF tokens.
98
- config_accessor :csrf_token_storage_strategy
105
+ singleton_class.delegate :csrf_token_storage_strategy, :csrf_token_storage_strategy=, to: :config
106
+ delegate :csrf_token_storage_strategy, :csrf_token_storage_strategy=, to: :config
99
107
  self.csrf_token_storage_strategy = SessionStore.new
100
108
 
101
109
  helper_method :form_authenticity_token
@@ -103,79 +111,98 @@ module ActionController # :nodoc:
103
111
  end
104
112
 
105
113
  module ClassMethods
106
- # Turn on request forgery protection. Bear in mind that GET and HEAD requests are not checked.
114
+ # Turn on request forgery protection. Bear in mind that GET and HEAD requests
115
+ # are not checked.
107
116
  #
108
- # class ApplicationController < ActionController::Base
109
- # protect_from_forgery
110
- # end
117
+ # class ApplicationController < ActionController::Base
118
+ # protect_from_forgery
119
+ # end
111
120
  #
112
- # class FooController < ApplicationController
113
- # protect_from_forgery except: :index
114
- # end
121
+ # class FooController < ApplicationController
122
+ # protect_from_forgery except: :index
123
+ # end
115
124
  #
116
- # You can disable forgery protection on a controller using skip_forgery_protection:
125
+ # You can disable forgery protection on a controller using
126
+ # skip_forgery_protection:
117
127
  #
118
- # class BarController < ApplicationController
119
- # skip_forgery_protection
120
- # end
128
+ # class BarController < ApplicationController
129
+ # skip_forgery_protection
130
+ # end
121
131
  #
122
132
  # Valid Options:
123
133
  #
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).
134
+ # * `:only` / `:except` - Only apply forgery protection to a subset of
135
+ # actions. For example `only: [ :create, :create_all ]`.
136
+ # * `:if` / `:unless` - Turn off the forgery protection entirely depending on
137
+ # the passed Proc or method reference.
138
+ # * `:prepend` - By default, the verification of the authentication token will
139
+ # be added at the position of the protect_from_forgery call in your
140
+ # application. This means any callbacks added before are run first. This is
141
+ # useful when you want your forgery protection to depend on other callbacks,
142
+ # like authentication methods (Oauth vs Cookie auth).
143
+ #
144
+ # If you need to add verification to the beginning of the callback chain,
145
+ # use `prepend: true`.
146
+ # * `:with` - Set the method to handle unverified request. Note if
147
+ # `default_protect_from_forgery` is true, Rails call protect_from_forgery
148
+ # with `with :exception`.
129
149
  #
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
150
  #
134
151
  # 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
152
  #
139
- # You can also implement custom strategy classes for unverified request handling:
153
+ # * `:exception` - Raises ActionController::InvalidAuthenticityToken
154
+ # exception.
155
+ # * `:reset_session` - Resets the session.
156
+ # * `:null_session` - Provides an empty session during request but doesn't
157
+ # reset it completely. Used as default if `:with` option is not specified.
140
158
  #
141
- # class CustomStrategy
142
- # def initialize(controller)
143
- # @controller = controller
144
- # end
145
159
  #
146
- # def handle_unverified_request
147
- # # Custom behavior for unverfied request
148
- # end
149
- # end
160
+ # You can also implement custom strategy classes for unverified request
161
+ # handling:
162
+ #
163
+ # class CustomStrategy
164
+ # def initialize(controller)
165
+ # @controller = controller
166
+ # end
167
+ #
168
+ # def handle_unverified_request
169
+ # # Custom behavior for unverfied request
170
+ # end
171
+ # end
172
+ #
173
+ # class ApplicationController < ActionController::Base
174
+ # protect_from_forgery with: CustomStrategy
175
+ # end
176
+ #
177
+ # * `:store` - Set the strategy to store and retrieve CSRF tokens.
150
178
  #
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
179
  #
156
180
  # 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.
181
+ #
182
+ # * `:session` - Store the CSRF token in the session. Used as default if
183
+ # `:store` option is not specified.
184
+ # * `:cookie` - Store the CSRF token in an encrypted cookie.
185
+ #
159
186
  #
160
187
  # You can also implement custom strategy classes for CSRF token storage:
161
188
  #
162
- # class CustomStore
163
- # def fetch(request)
164
- # # Return the token from a custom location
165
- # end
189
+ # class CustomStore
190
+ # def fetch(request)
191
+ # # Return the token from a custom location
192
+ # end
166
193
  #
167
- # def store(request, csrf_token)
168
- # # Store the token in a custom location
169
- # end
194
+ # def store(request, csrf_token)
195
+ # # Store the token in a custom location
196
+ # end
170
197
  #
171
- # def reset(request)
172
- # # Delete the stored session token
198
+ # def reset(request)
199
+ # # Delete the stored session token
200
+ # end
173
201
  # end
174
- # end
175
202
  #
176
- # class ApplicationController < ActionController::Base
177
- # protect_from_forgery store: CustomStore.new
178
- # end
203
+ # class ApplicationController < ActionController::Base
204
+ # protect_from_forgery store: CustomStore.new
205
+ # end
179
206
  def protect_from_forgery(options = {})
180
207
  options = options.reverse_merge(prepend: false)
181
208
 
@@ -190,9 +217,9 @@ module ActionController # :nodoc:
190
217
 
191
218
  # Turn off request forgery protection. This is a wrapper for:
192
219
  #
193
- # skip_before_action :verify_authenticity_token
220
+ # skip_before_action :verify_authenticity_token
194
221
  #
195
- # See +skip_before_action+ for allowed options.
222
+ # See `skip_before_action` for allowed options.
196
223
  def skip_forgery_protection(options = {})
197
224
  skip_before_action :verify_authenticity_token, options.reverse_merge(raise: false)
198
225
  end
@@ -236,7 +263,8 @@ module ActionController # :nodoc:
236
263
  @controller = controller
237
264
  end
238
265
 
239
- # This is the method that defines the application behavior when a request is found to be unverified.
266
+ # This is the method that defines the application behavior when a request is
267
+ # found to be unverified.
240
268
  def handle_unverified_request
241
269
  request = @controller.request
242
270
  request.session = NullSessionHash.new(request)
@@ -246,7 +274,7 @@ module ActionController # :nodoc:
246
274
  end
247
275
 
248
276
  private
249
- class NullSessionHash < Rack::Session::Abstract::SessionHash # :nodoc:
277
+ class NullSessionHash < Rack::Session::Abstract::SessionHash
250
278
  def initialize(req)
251
279
  super(nil, req)
252
280
  @data = {}
@@ -265,7 +293,7 @@ module ActionController # :nodoc:
265
293
  end
266
294
  end
267
295
 
268
- class NullCookieJar < ActionDispatch::Cookies::CookieJar # :nodoc:
296
+ class NullCookieJar < ActionDispatch::Cookies::CookieJar
269
297
  def write(*)
270
298
  # nothing
271
299
  end
@@ -344,7 +372,7 @@ module ActionController # :nodoc:
344
372
 
345
373
  def initialize(...)
346
374
  super
347
- @marked_for_same_origin_verification = nil
375
+ @_marked_for_same_origin_verification = nil
348
376
  end
349
377
 
350
378
  def reset_csrf_token(request) # :doc:
@@ -358,16 +386,15 @@ module ActionController # :nodoc:
358
386
  end
359
387
 
360
388
  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.
389
+ # The actual before_action that is used to verify the CSRF token. Don't override
390
+ # this directly. Provide your own forgery protection strategy instead. If you
391
+ # override, you'll disable same-origin `<script>` verification.
365
392
  #
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.
393
+ # Lean on the protect_from_forgery declaration to mark which actions are due for
394
+ # same-origin request verification. If protect_from_forgery is enabled on an
395
+ # action, this before_action flags its after_action to verify that JavaScript
396
+ # responses are for XHR requests, ensuring they follow the browser's same-origin
397
+ # policy.
371
398
  def verify_authenticity_token # :doc:
372
399
  mark_for_same_origin_verification!
373
400
 
@@ -378,7 +405,7 @@ module ActionController # :nodoc:
378
405
  end
379
406
  end
380
407
 
381
- def handle_unverified_request # :doc:
408
+ def handle_unverified_request
382
409
  protection_strategy = forgery_protection_strategy.new(self)
383
410
 
384
411
  if protection_strategy.respond_to?(:warning_message)
@@ -388,7 +415,7 @@ module ActionController # :nodoc:
388
415
  protection_strategy.handle_unverified_request
389
416
  end
390
417
 
391
- def unverified_request_warning_message # :nodoc:
418
+ def unverified_request_warning_message
392
419
  if valid_request_origin?
393
420
  "Can't verify CSRF token authenticity."
394
421
  else
@@ -396,7 +423,6 @@ module ActionController # :nodoc:
396
423
  end
397
424
  end
398
425
 
399
- # :nodoc:
400
426
  CROSS_ORIGIN_JAVASCRIPT_WARNING = "Security warning: an embedded " \
401
427
  "<script> tag on another site requested protected JavaScript. " \
402
428
  "If you know what you're doing, go ahead and disable forgery " \
@@ -404,9 +430,9 @@ module ActionController # :nodoc:
404
430
  private_constant :CROSS_ORIGIN_JAVASCRIPT_WARNING
405
431
  # :startdoc:
406
432
 
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.
433
+ # If `verify_authenticity_token` was run (indicating that we have
434
+ # forgery protection enabled for this request) then also verify that we aren't
435
+ # serving an unauthorized cross-origin response.
410
436
  def verify_same_origin_request # :doc:
411
437
  if marked_for_same_origin_verification? && non_xhr_javascript_response?
412
438
  if logger && log_warning_on_csrf_failure
@@ -418,13 +444,13 @@ module ActionController # :nodoc:
418
444
 
419
445
  # GET requests are checked for cross-origin JavaScript after rendering.
420
446
  def mark_for_same_origin_verification! # :doc:
421
- @marked_for_same_origin_verification = request.get?
447
+ @_marked_for_same_origin_verification = request.get?
422
448
  end
423
449
 
424
- # If the +verify_authenticity_token+ before_action ran, verify that
425
- # JavaScript responses are only served to same-origin GET requests.
450
+ # If the `verify_authenticity_token` before_action ran, verify that JavaScript
451
+ # responses are only served to same-origin GET requests.
426
452
  def marked_for_same_origin_verification? # :doc:
427
- @marked_for_same_origin_verification ||= false
453
+ @_marked_for_same_origin_verification ||= false
428
454
  end
429
455
 
430
456
  # Check for cross-origin JavaScript responses.
@@ -436,11 +462,13 @@ module ActionController # :nodoc:
436
462
 
437
463
  # Returns true or false if a request is verified. Checks:
438
464
  #
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?
465
+ # * Is it a GET or HEAD request? GETs should be safe and idempotent
466
+ # * Does the form_authenticity_token match the given token value from the
467
+ # params?
468
+ # * Does the `X-CSRF-Token` header match the form_authenticity_token?
469
+ #
442
470
  def verified_request? # :doc:
443
- !protect_against_forgery? || request.get? || request.head? ||
471
+ request.get? || request.head? || !protect_against_forgery? ||
444
472
  (valid_request_origin? && any_authenticity_token_valid?)
445
473
  end
446
474
 
@@ -461,9 +489,8 @@ module ActionController # :nodoc:
461
489
  masked_authenticity_token(form_options: form_options)
462
490
  end
463
491
 
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.
492
+ # Creates a masked version of the authenticity token that varies on each
493
+ # request. The masking is used to mitigate SSL attacks like BREACH.
467
494
  def masked_authenticity_token(form_options: {})
468
495
  action, method = form_options.values_at(:action, :method)
469
496
 
@@ -477,11 +504,10 @@ module ActionController # :nodoc:
477
504
  mask_token(raw_token)
478
505
  end
479
506
 
480
- # Checks the client's masked token to see if it matches the
481
- # session token. Essentially the inverse of
482
- # +masked_authenticity_token+.
507
+ # Checks the client's masked token to see if it matches the session token.
508
+ # Essentially the inverse of `masked_authenticity_token`.
483
509
  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)
510
+ if !encoded_masked_token.is_a?(String) || encoded_masked_token.empty?
485
511
  return false
486
512
  end
487
513
 
@@ -491,14 +517,12 @@ module ActionController # :nodoc:
491
517
  return false
492
518
  end
493
519
 
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.
520
+ # See if it's actually a masked token or not. In order to deploy this code, we
521
+ # should be able to handle any unmasked tokens that we've issued without error.
497
522
 
498
523
  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.
524
+ # This is actually an unmasked token. This is expected if you have just upgraded
525
+ # to masked tokens, but should stop happening shortly after installing this gem.
502
526
  compare_with_real_token masked_token
503
527
 
504
528
  elsif masked_token.length == AUTHENTICITY_TOKEN_LENGTH * 2
@@ -513,8 +537,7 @@ module ActionController # :nodoc:
513
537
  end
514
538
 
515
539
  def unmask_token(masked_token) # :doc:
516
- # Split the token into the one-time pad and the encrypted
517
- # value and decrypt it.
540
+ # Split the token into the one-time pad and the encrypted value and decrypt it.
518
541
  one_time_pad = masked_token[0...AUTHENTICITY_TOKEN_LENGTH]
519
542
  encrypted_csrf_token = masked_token[AUTHENTICITY_TOKEN_LENGTH..-1]
520
543
  xor_byte_strings(one_time_pad, encrypted_csrf_token)
@@ -605,9 +628,10 @@ module ActionController # :nodoc:
605
628
  If you cannot change the referrer policy, you can disable origin checking with the
606
629
  Rails.application.config.action_controller.forgery_protection_origin_check setting.
607
630
  MSG
631
+ private_constant :NULL_ORIGIN_MESSAGE
608
632
 
609
- # Checks if the request originated from the same origin by looking at the
610
- # Origin header.
633
+ # Checks if the request originated from the same origin by looking at the Origin
634
+ # header.
611
635
  def valid_request_origin? # :doc:
612
636
  if forgery_protection_origin_check
613
637
  # We accept blank origin headers because some user agents don't send it.
@@ -618,20 +642,35 @@ module ActionController # :nodoc:
618
642
  end
619
643
  end
620
644
 
621
- def normalize_action_path(action_path) # :doc:
645
+ def normalize_action_path(action_path)
622
646
  uri = URI.parse(action_path)
647
+
648
+ if uri.relative? && (action_path.blank? || !action_path.start_with?("/"))
649
+ normalize_relative_action_path(uri.path)
650
+ else
651
+ uri.path.chomp("/")
652
+ end
653
+ end
654
+
655
+ def normalize_relative_action_path(rel_action_path)
656
+ uri = URI.parse(request.path)
657
+ # add the action path to the request.path
658
+ uri.path += "/#{rel_action_path}"
659
+ # relative path with "./path"
660
+ uri.path.gsub!("/./", "/")
661
+
623
662
  uri.path.chomp("/")
624
663
  end
625
664
 
626
- def generate_csrf_token # :nodoc:
665
+ def generate_csrf_token
627
666
  SecureRandom.urlsafe_base64(AUTHENTICITY_TOKEN_LENGTH)
628
667
  end
629
668
 
630
- def encode_csrf_token(csrf_token) # :nodoc:
669
+ def encode_csrf_token(csrf_token)
631
670
  Base64.urlsafe_encode64(csrf_token, padding: false)
632
671
  end
633
672
 
634
- def decode_csrf_token(encoded_csrf_token) # :nodoc:
673
+ def decode_csrf_token(encoded_csrf_token)
635
674
  Base64.urlsafe_decode64(encoded_csrf_token)
636
675
  end
637
676
  end
@@ -1,21 +1,32 @@
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
+ module ClassMethods
17
+ def handler_for_rescue(exception, ...) # :nodoc:
18
+ if handler = super
19
+ ActiveSupport::Notifications.instrument("rescue_from_callback.action_controller", exception: exception)
20
+ handler
21
+ end
22
+ end
23
+ end
24
+
25
+ # Override this method if you want to customize when detailed exceptions must be
26
+ # shown. This method is only called when `consider_all_requests_local` is
27
+ # `false`. By default, it returns `false`, but someone may set it to
28
+ # `request.local?` so local requests in production still show the detailed
29
+ # exception pages.
19
30
  def show_detailed_exceptions?
20
31
  false
21
32
  end