actionpack 6.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionpack might be problematic. Click here for more details.

Files changed (181) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +311 -0
  3. data/MIT-LICENSE +21 -0
  4. data/README.rdoc +58 -0
  5. data/lib/abstract_controller.rb +27 -0
  6. data/lib/abstract_controller/asset_paths.rb +12 -0
  7. data/lib/abstract_controller/base.rb +267 -0
  8. data/lib/abstract_controller/caching.rb +66 -0
  9. data/lib/abstract_controller/caching/fragments.rb +150 -0
  10. data/lib/abstract_controller/callbacks.rb +224 -0
  11. data/lib/abstract_controller/collector.rb +43 -0
  12. data/lib/abstract_controller/error.rb +6 -0
  13. data/lib/abstract_controller/helpers.rb +194 -0
  14. data/lib/abstract_controller/logger.rb +14 -0
  15. data/lib/abstract_controller/railties/routes_helpers.rb +20 -0
  16. data/lib/abstract_controller/rendering.rb +127 -0
  17. data/lib/abstract_controller/translation.rb +32 -0
  18. data/lib/abstract_controller/url_for.rb +35 -0
  19. data/lib/action_controller.rb +67 -0
  20. data/lib/action_controller/api.rb +150 -0
  21. data/lib/action_controller/api/api_rendering.rb +16 -0
  22. data/lib/action_controller/base.rb +271 -0
  23. data/lib/action_controller/caching.rb +46 -0
  24. data/lib/action_controller/form_builder.rb +50 -0
  25. data/lib/action_controller/log_subscriber.rb +81 -0
  26. data/lib/action_controller/metal.rb +256 -0
  27. data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
  28. data/lib/action_controller/metal/conditional_get.rb +280 -0
  29. data/lib/action_controller/metal/content_security_policy.rb +52 -0
  30. data/lib/action_controller/metal/cookies.rb +16 -0
  31. data/lib/action_controller/metal/data_streaming.rb +151 -0
  32. data/lib/action_controller/metal/default_headers.rb +17 -0
  33. data/lib/action_controller/metal/etag_with_flash.rb +18 -0
  34. data/lib/action_controller/metal/etag_with_template_digest.rb +57 -0
  35. data/lib/action_controller/metal/exceptions.rb +74 -0
  36. data/lib/action_controller/metal/flash.rb +61 -0
  37. data/lib/action_controller/metal/force_ssl.rb +58 -0
  38. data/lib/action_controller/metal/head.rb +60 -0
  39. data/lib/action_controller/metal/helpers.rb +122 -0
  40. data/lib/action_controller/metal/http_authentication.rb +518 -0
  41. data/lib/action_controller/metal/implicit_render.rb +63 -0
  42. data/lib/action_controller/metal/instrumentation.rb +105 -0
  43. data/lib/action_controller/metal/live.rb +314 -0
  44. data/lib/action_controller/metal/mime_responds.rb +324 -0
  45. data/lib/action_controller/metal/parameter_encoding.rb +51 -0
  46. data/lib/action_controller/metal/params_wrapper.rb +297 -0
  47. data/lib/action_controller/metal/redirecting.rb +133 -0
  48. data/lib/action_controller/metal/renderers.rb +181 -0
  49. data/lib/action_controller/metal/rendering.rb +122 -0
  50. data/lib/action_controller/metal/request_forgery_protection.rb +456 -0
  51. data/lib/action_controller/metal/rescue.rb +28 -0
  52. data/lib/action_controller/metal/streaming.rb +223 -0
  53. data/lib/action_controller/metal/strong_parameters.rb +1105 -0
  54. data/lib/action_controller/metal/testing.rb +16 -0
  55. data/lib/action_controller/metal/url_for.rb +58 -0
  56. data/lib/action_controller/railtie.rb +89 -0
  57. data/lib/action_controller/railties/helpers.rb +24 -0
  58. data/lib/action_controller/renderer.rb +130 -0
  59. data/lib/action_controller/template_assertions.rb +11 -0
  60. data/lib/action_controller/test_case.rb +626 -0
  61. data/lib/action_dispatch.rb +114 -0
  62. data/lib/action_dispatch/http/cache.rb +226 -0
  63. data/lib/action_dispatch/http/content_disposition.rb +45 -0
  64. data/lib/action_dispatch/http/content_security_policy.rb +284 -0
  65. data/lib/action_dispatch/http/filter_parameters.rb +86 -0
  66. data/lib/action_dispatch/http/filter_redirect.rb +37 -0
  67. data/lib/action_dispatch/http/headers.rb +132 -0
  68. data/lib/action_dispatch/http/mime_negotiation.rb +177 -0
  69. data/lib/action_dispatch/http/mime_type.rb +350 -0
  70. data/lib/action_dispatch/http/mime_types.rb +50 -0
  71. data/lib/action_dispatch/http/parameter_filter.rb +12 -0
  72. data/lib/action_dispatch/http/parameters.rb +136 -0
  73. data/lib/action_dispatch/http/rack_cache.rb +63 -0
  74. data/lib/action_dispatch/http/request.rb +427 -0
  75. data/lib/action_dispatch/http/response.rb +534 -0
  76. data/lib/action_dispatch/http/upload.rb +92 -0
  77. data/lib/action_dispatch/http/url.rb +350 -0
  78. data/lib/action_dispatch/journey.rb +7 -0
  79. data/lib/action_dispatch/journey/formatter.rb +189 -0
  80. data/lib/action_dispatch/journey/gtg/builder.rb +164 -0
  81. data/lib/action_dispatch/journey/gtg/simulator.rb +41 -0
  82. data/lib/action_dispatch/journey/gtg/transition_table.rb +158 -0
  83. data/lib/action_dispatch/journey/nfa/builder.rb +78 -0
  84. data/lib/action_dispatch/journey/nfa/dot.rb +36 -0
  85. data/lib/action_dispatch/journey/nfa/simulator.rb +47 -0
  86. data/lib/action_dispatch/journey/nfa/transition_table.rb +120 -0
  87. data/lib/action_dispatch/journey/nodes/node.rb +141 -0
  88. data/lib/action_dispatch/journey/parser.rb +199 -0
  89. data/lib/action_dispatch/journey/parser.y +50 -0
  90. data/lib/action_dispatch/journey/parser_extras.rb +31 -0
  91. data/lib/action_dispatch/journey/path/pattern.rb +203 -0
  92. data/lib/action_dispatch/journey/route.rb +204 -0
  93. data/lib/action_dispatch/journey/router.rb +153 -0
  94. data/lib/action_dispatch/journey/router/utils.rb +102 -0
  95. data/lib/action_dispatch/journey/routes.rb +81 -0
  96. data/lib/action_dispatch/journey/scanner.rb +71 -0
  97. data/lib/action_dispatch/journey/visitors.rb +268 -0
  98. data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
  99. data/lib/action_dispatch/journey/visualizer/fsm.js +134 -0
  100. data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
  101. data/lib/action_dispatch/middleware/actionable_exceptions.rb +39 -0
  102. data/lib/action_dispatch/middleware/callbacks.rb +34 -0
  103. data/lib/action_dispatch/middleware/cookies.rb +663 -0
  104. data/lib/action_dispatch/middleware/debug_exceptions.rb +185 -0
  105. data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
  106. data/lib/action_dispatch/middleware/debug_view.rb +68 -0
  107. data/lib/action_dispatch/middleware/exception_wrapper.rb +181 -0
  108. data/lib/action_dispatch/middleware/executor.rb +21 -0
  109. data/lib/action_dispatch/middleware/flash.rb +300 -0
  110. data/lib/action_dispatch/middleware/host_authorization.rb +103 -0
  111. data/lib/action_dispatch/middleware/public_exceptions.rb +61 -0
  112. data/lib/action_dispatch/middleware/reloader.rb +12 -0
  113. data/lib/action_dispatch/middleware/remote_ip.rb +181 -0
  114. data/lib/action_dispatch/middleware/request_id.rb +43 -0
  115. data/lib/action_dispatch/middleware/session/abstract_store.rb +92 -0
  116. data/lib/action_dispatch/middleware/session/cache_store.rb +54 -0
  117. data/lib/action_dispatch/middleware/session/cookie_store.rb +113 -0
  118. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +28 -0
  119. data/lib/action_dispatch/middleware/show_exceptions.rb +62 -0
  120. data/lib/action_dispatch/middleware/ssl.rb +150 -0
  121. data/lib/action_dispatch/middleware/stack.rb +148 -0
  122. data/lib/action_dispatch/middleware/static.rb +129 -0
  123. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
  124. data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
  125. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +24 -0
  126. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
  127. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +29 -0
  128. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  129. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +62 -0
  130. data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -0
  132. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -0
  133. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +38 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
  135. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +24 -0
  136. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +15 -0
  137. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +165 -0
  138. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -0
  139. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
  140. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +11 -0
  141. data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
  142. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +32 -0
  143. data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
  144. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
  145. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
  146. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +6 -0
  147. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
  148. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +16 -0
  149. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +203 -0
  150. data/lib/action_dispatch/railtie.rb +58 -0
  151. data/lib/action_dispatch/request/session.rb +242 -0
  152. data/lib/action_dispatch/request/utils.rb +78 -0
  153. data/lib/action_dispatch/routing.rb +261 -0
  154. data/lib/action_dispatch/routing/endpoint.rb +17 -0
  155. data/lib/action_dispatch/routing/inspector.rb +274 -0
  156. data/lib/action_dispatch/routing/mapper.rb +2289 -0
  157. data/lib/action_dispatch/routing/polymorphic_routes.rb +351 -0
  158. data/lib/action_dispatch/routing/redirection.rb +201 -0
  159. data/lib/action_dispatch/routing/route_set.rb +887 -0
  160. data/lib/action_dispatch/routing/routes_proxy.rb +69 -0
  161. data/lib/action_dispatch/routing/url_for.rb +237 -0
  162. data/lib/action_dispatch/system_test_case.rb +168 -0
  163. data/lib/action_dispatch/system_testing/browser.rb +80 -0
  164. data/lib/action_dispatch/system_testing/driver.rb +68 -0
  165. data/lib/action_dispatch/system_testing/server.rb +31 -0
  166. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +97 -0
  167. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +33 -0
  168. data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +26 -0
  169. data/lib/action_dispatch/testing/assertion_response.rb +47 -0
  170. data/lib/action_dispatch/testing/assertions.rb +24 -0
  171. data/lib/action_dispatch/testing/assertions/response.rb +106 -0
  172. data/lib/action_dispatch/testing/assertions/routing.rb +234 -0
  173. data/lib/action_dispatch/testing/integration.rb +659 -0
  174. data/lib/action_dispatch/testing/request_encoder.rb +55 -0
  175. data/lib/action_dispatch/testing/test_process.rb +50 -0
  176. data/lib/action_dispatch/testing/test_request.rb +71 -0
  177. data/lib/action_dispatch/testing/test_response.rb +25 -0
  178. data/lib/action_pack.rb +26 -0
  179. data/lib/action_pack/gem_version.rb +17 -0
  180. data/lib/action_pack/version.rb +10 -0
  181. metadata +329 -0
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionController
4
+ module Redirecting
5
+ extend ActiveSupport::Concern
6
+
7
+ include AbstractController::Logger
8
+ include ActionController::UrlFor
9
+
10
+ # Redirects the browser to the target specified in +options+. This parameter can be any one of:
11
+ #
12
+ # * <tt>Hash</tt> - The URL will be generated by calling url_for with the +options+.
13
+ # * <tt>Record</tt> - The URL will be generated by calling url_for with the +options+, which will reference a named URL for that record.
14
+ # * <tt>String</tt> starting with <tt>protocol://</tt> (like <tt>http://</tt>) or a protocol relative reference (like <tt>//</tt>) - Is passed straight through as the target for redirection.
15
+ # * <tt>String</tt> not containing a protocol - The current protocol and host is prepended to the string.
16
+ # * <tt>Proc</tt> - A block that will be executed in the controller's context. Should return any option accepted by +redirect_to+.
17
+ #
18
+ # === Examples:
19
+ #
20
+ # redirect_to action: "show", id: 5
21
+ # redirect_to @post
22
+ # redirect_to "http://www.rubyonrails.org"
23
+ # redirect_to "/images/screenshot.jpg"
24
+ # redirect_to posts_url
25
+ # redirect_to proc { edit_post_url(@post) }
26
+ #
27
+ # The redirection happens as a <tt>302 Found</tt> header unless otherwise specified using the <tt>:status</tt> option:
28
+ #
29
+ # redirect_to post_url(@post), status: :found
30
+ # redirect_to action: 'atom', status: :moved_permanently
31
+ # redirect_to post_url(@post), status: 301
32
+ # redirect_to action: 'atom', status: 302
33
+ #
34
+ # The status code can either be a standard {HTTP Status code}[https://www.iana.org/assignments/http-status-codes] as an
35
+ # integer, or a symbol representing the downcased, underscored and symbolized description.
36
+ # Note that the status code must be a 3xx HTTP code, or redirection will not occur.
37
+ #
38
+ # If you are using XHR requests other than GET or POST and redirecting after the
39
+ # request then some browsers will follow the redirect using the original request
40
+ # method. This may lead to undesirable behavior such as a double DELETE. To work
41
+ # around this you can return a <tt>303 See Other</tt> status code which will be
42
+ # followed using a GET request.
43
+ #
44
+ # redirect_to posts_url, status: :see_other
45
+ # redirect_to action: 'index', status: 303
46
+ #
47
+ # It is also possible to assign a flash message as part of the redirection. There are two special accessors for the commonly used flash names
48
+ # +alert+ and +notice+ as well as a general purpose +flash+ bucket.
49
+ #
50
+ # redirect_to post_url(@post), alert: "Watch it, mister!"
51
+ # redirect_to post_url(@post), status: :found, notice: "Pay attention to the road"
52
+ # redirect_to post_url(@post), status: 301, flash: { updated_post_id: @post.id }
53
+ # redirect_to({ action: 'atom' }, alert: "Something serious happened")
54
+ #
55
+ # Statements after +redirect_to+ in our controller get executed, so +redirect_to+ doesn't stop the execution of the function.
56
+ # To terminate the execution of the function immediately after the +redirect_to+, use return.
57
+ # redirect_to post_url(@post) and return
58
+ def redirect_to(options = {}, response_options = {})
59
+ raise ActionControllerError.new("Cannot redirect to nil!") unless options
60
+ raise AbstractController::DoubleRenderError if response_body
61
+
62
+ self.status = _extract_redirect_to_status(options, response_options)
63
+ self.location = _compute_redirect_to_location(request, options)
64
+ self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.unwrapped_html_escape(response.location)}\">redirected</a>.</body></html>"
65
+ end
66
+
67
+ # Redirects the browser to the page that issued the request (the referrer)
68
+ # if possible, otherwise redirects to the provided default fallback
69
+ # location.
70
+ #
71
+ # The referrer information is pulled from the HTTP +Referer+ (sic) header on
72
+ # the request. This is an optional header and its presence on the request is
73
+ # subject to browser security settings and user preferences. If the request
74
+ # is missing this header, the <tt>fallback_location</tt> will be used.
75
+ #
76
+ # redirect_back fallback_location: { action: "show", id: 5 }
77
+ # redirect_back fallback_location: @post
78
+ # redirect_back fallback_location: "http://www.rubyonrails.org"
79
+ # redirect_back fallback_location: "/images/screenshot.jpg"
80
+ # redirect_back fallback_location: posts_url
81
+ # redirect_back fallback_location: proc { edit_post_url(@post) }
82
+ # redirect_back fallback_location: '/', allow_other_host: false
83
+ #
84
+ # ==== Options
85
+ # * <tt>:fallback_location</tt> - The default fallback location that will be used on missing +Referer+ header.
86
+ # * <tt>:allow_other_host</tt> - Allow or disallow redirection to the host that is different to the current host, defaults to true.
87
+ #
88
+ # All other options that can be passed to <tt>redirect_to</tt> are accepted as
89
+ # options and the behavior is identical.
90
+ def redirect_back(fallback_location:, allow_other_host: true, **args)
91
+ referer = request.headers["Referer"]
92
+ redirect_to_referer = referer && (allow_other_host || _url_host_allowed?(referer))
93
+ redirect_to redirect_to_referer ? referer : fallback_location, **args
94
+ end
95
+
96
+ def _compute_redirect_to_location(request, options) #:nodoc:
97
+ case options
98
+ # The scheme name consist of a letter followed by any combination of
99
+ # letters, digits, and the plus ("+"), period ("."), or hyphen ("-")
100
+ # characters; and is terminated by a colon (":").
101
+ # See https://tools.ietf.org/html/rfc3986#section-3.1
102
+ # The protocol relative scheme starts with a double slash "//".
103
+ when /\A([a-z][a-z\d\-+\.]*:|\/\/).*/i
104
+ options
105
+ when String
106
+ request.protocol + request.host_with_port + options
107
+ when Proc
108
+ _compute_redirect_to_location request, instance_eval(&options)
109
+ else
110
+ url_for(options)
111
+ end.delete("\0\r\n")
112
+ end
113
+ module_function :_compute_redirect_to_location
114
+ public :_compute_redirect_to_location
115
+
116
+ private
117
+ def _extract_redirect_to_status(options, response_options)
118
+ if options.is_a?(Hash) && options.key?(:status)
119
+ Rack::Utils.status_code(options.delete(:status))
120
+ elsif response_options.key?(:status)
121
+ Rack::Utils.status_code(response_options[:status])
122
+ else
123
+ 302
124
+ end
125
+ end
126
+
127
+ def _url_host_allowed?(url)
128
+ URI(url.to_s).host == request.host
129
+ rescue ArgumentError, URI::Error
130
+ false
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,181 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+
5
+ module ActionController
6
+ # See <tt>Renderers.add</tt>
7
+ def self.add_renderer(key, &block)
8
+ Renderers.add(key, &block)
9
+ end
10
+
11
+ # See <tt>Renderers.remove</tt>
12
+ def self.remove_renderer(key)
13
+ Renderers.remove(key)
14
+ end
15
+
16
+ # See <tt>Responder#api_behavior</tt>
17
+ class MissingRenderer < LoadError
18
+ def initialize(format)
19
+ super "No renderer defined for format: #{format}"
20
+ end
21
+ end
22
+
23
+ module Renderers
24
+ extend ActiveSupport::Concern
25
+
26
+ # A Set containing renderer names that correspond to available renderer procs.
27
+ # Default values are <tt>:json</tt>, <tt>:js</tt>, <tt>:xml</tt>.
28
+ RENDERERS = Set.new
29
+
30
+ included do
31
+ class_attribute :_renderers, default: Set.new.freeze
32
+ end
33
+
34
+ # Used in <tt>ActionController::Base</tt>
35
+ # and <tt>ActionController::API</tt> to include all
36
+ # renderers by default.
37
+ module All
38
+ extend ActiveSupport::Concern
39
+ include Renderers
40
+
41
+ included do
42
+ self._renderers = RENDERERS
43
+ end
44
+ end
45
+
46
+ # Adds a new renderer to call within controller actions.
47
+ # A renderer is invoked by passing its name as an option to
48
+ # <tt>AbstractController::Rendering#render</tt>. To create a renderer
49
+ # pass it a name and a block. The block takes two arguments, the first
50
+ # is the value paired with its key and the second is the remaining
51
+ # hash of options passed to +render+.
52
+ #
53
+ # Create a csv renderer:
54
+ #
55
+ # ActionController::Renderers.add :csv do |obj, options|
56
+ # filename = options[:filename] || 'data'
57
+ # str = obj.respond_to?(:to_csv) ? obj.to_csv : obj.to_s
58
+ # send_data str, type: Mime[:csv],
59
+ # disposition: "attachment; filename=#{filename}.csv"
60
+ # end
61
+ #
62
+ # Note that we used Mime[:csv] for the csv mime type as it comes with Rails.
63
+ # For a custom renderer, you'll need to register a mime type with
64
+ # <tt>Mime::Type.register</tt>.
65
+ #
66
+ # To use the csv renderer in a controller action:
67
+ #
68
+ # def show
69
+ # @csvable = Csvable.find(params[:id])
70
+ # respond_to do |format|
71
+ # format.html
72
+ # format.csv { render csv: @csvable, filename: @csvable.name }
73
+ # end
74
+ # end
75
+ def self.add(key, &block)
76
+ define_method(_render_with_renderer_method_name(key), &block)
77
+ RENDERERS << key.to_sym
78
+ end
79
+
80
+ # This method is the opposite of add method.
81
+ #
82
+ # To remove a csv renderer:
83
+ #
84
+ # ActionController::Renderers.remove(:csv)
85
+ def self.remove(key)
86
+ RENDERERS.delete(key.to_sym)
87
+ method_name = _render_with_renderer_method_name(key)
88
+ remove_possible_method(method_name)
89
+ end
90
+
91
+ def self._render_with_renderer_method_name(key)
92
+ "_render_with_renderer_#{key}"
93
+ end
94
+
95
+ module ClassMethods
96
+ # Adds, by name, a renderer or renderers to the +_renderers+ available
97
+ # to call within controller actions.
98
+ #
99
+ # It is useful when rendering from an <tt>ActionController::Metal</tt> controller or
100
+ # otherwise to add an available renderer proc to a specific controller.
101
+ #
102
+ # Both <tt>ActionController::Base</tt> and <tt>ActionController::API</tt>
103
+ # include <tt>ActionController::Renderers::All</tt>, making all renderers
104
+ # available in the controller. See <tt>Renderers::RENDERERS</tt> and <tt>Renderers.add</tt>.
105
+ #
106
+ # Since <tt>ActionController::Metal</tt> controllers cannot render, the controller
107
+ # must include <tt>AbstractController::Rendering</tt>, <tt>ActionController::Rendering</tt>,
108
+ # and <tt>ActionController::Renderers</tt>, and have at least one renderer.
109
+ #
110
+ # Rather than including <tt>ActionController::Renderers::All</tt> and including all renderers,
111
+ # you may specify which renderers to include by passing the renderer name or names to
112
+ # +use_renderers+. For example, a controller that includes only the <tt>:json</tt> renderer
113
+ # (+_render_with_renderer_json+) might look like:
114
+ #
115
+ # class MetalRenderingController < ActionController::Metal
116
+ # include AbstractController::Rendering
117
+ # include ActionController::Rendering
118
+ # include ActionController::Renderers
119
+ #
120
+ # use_renderers :json
121
+ #
122
+ # def show
123
+ # render json: record
124
+ # end
125
+ # end
126
+ #
127
+ # You must specify a +use_renderer+, else the +controller.renderer+ and
128
+ # +controller._renderers+ will be <tt>nil</tt>, and the action will fail.
129
+ def use_renderers(*args)
130
+ renderers = _renderers + args
131
+ self._renderers = renderers.freeze
132
+ end
133
+ alias use_renderer use_renderers
134
+ end
135
+
136
+ # Called by +render+ in <tt>AbstractController::Rendering</tt>
137
+ # which sets the return value as the +response_body+.
138
+ #
139
+ # If no renderer is found, +super+ returns control to
140
+ # <tt>ActionView::Rendering.render_to_body</tt>, if present.
141
+ def render_to_body(options)
142
+ _render_to_body_with_renderer(options) || super
143
+ end
144
+
145
+ def _render_to_body_with_renderer(options)
146
+ _renderers.each do |name|
147
+ if options.key?(name)
148
+ _process_options(options)
149
+ method_name = Renderers._render_with_renderer_method_name(name)
150
+ return send(method_name, options.delete(name), options)
151
+ end
152
+ end
153
+ nil
154
+ end
155
+
156
+ add :json do |json, options|
157
+ json = json.to_json(options) unless json.kind_of?(String)
158
+
159
+ if options[:callback].present?
160
+ if media_type.nil? || media_type == Mime[:json]
161
+ self.content_type = Mime[:js]
162
+ end
163
+
164
+ "/**/#{options[:callback]}(#{json})"
165
+ else
166
+ self.content_type = Mime[:json] if media_type.nil?
167
+ json
168
+ end
169
+ end
170
+
171
+ add :js do |js, options|
172
+ self.content_type = Mime[:js] if media_type.nil?
173
+ js.respond_to?(:to_js) ? js.to_js(options) : js
174
+ end
175
+
176
+ add :xml do |xml, options|
177
+ self.content_type = Mime[:xml] if media_type.nil?
178
+ xml.respond_to?(:to_xml) ? xml.to_xml(options) : xml
179
+ end
180
+ end
181
+ end
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionController
4
+ module Rendering
5
+ extend ActiveSupport::Concern
6
+
7
+ RENDER_FORMATS_IN_PRIORITY = [:body, :plain, :html]
8
+
9
+ module ClassMethods
10
+ # Documentation at ActionController::Renderer#render
11
+ delegate :render, to: :renderer
12
+
13
+ # Returns a renderer instance (inherited from ActionController::Renderer)
14
+ # for the controller.
15
+ attr_reader :renderer
16
+
17
+ def setup_renderer! # :nodoc:
18
+ @renderer = Renderer.for(self)
19
+ end
20
+
21
+ def inherited(klass)
22
+ klass.setup_renderer!
23
+ super
24
+ end
25
+ end
26
+
27
+ # Before processing, set the request formats in current controller formats.
28
+ def process_action(*) #:nodoc:
29
+ self.formats = request.formats.map(&:ref).compact
30
+ super
31
+ end
32
+
33
+ # Check for double render errors and set the content_type after rendering.
34
+ def render(*args) #:nodoc:
35
+ raise ::AbstractController::DoubleRenderError if response_body
36
+ super
37
+ end
38
+
39
+ # Overwrite render_to_string because body can now be set to a Rack body.
40
+ def render_to_string(*)
41
+ result = super
42
+ if result.respond_to?(:each)
43
+ string = +""
44
+ result.each { |r| string << r }
45
+ string
46
+ else
47
+ result
48
+ end
49
+ end
50
+
51
+ def render_to_body(options = {})
52
+ super || _render_in_priorities(options) || " "
53
+ end
54
+
55
+ private
56
+
57
+ def _process_variant(options)
58
+ if defined?(request) && !request.nil? && request.variant.present?
59
+ options[:variant] = request.variant
60
+ end
61
+ end
62
+
63
+ def _render_in_priorities(options)
64
+ RENDER_FORMATS_IN_PRIORITY.each do |format|
65
+ return options[format] if options.key?(format)
66
+ end
67
+
68
+ nil
69
+ end
70
+
71
+ def _set_html_content_type
72
+ self.content_type = Mime[:html].to_s
73
+ end
74
+
75
+ def _set_rendered_content_type(format)
76
+ if format && !response.media_type
77
+ self.content_type = format.to_s
78
+ end
79
+ end
80
+
81
+ # Normalize arguments by catching blocks and setting them on :update.
82
+ def _normalize_args(action = nil, options = {}, &blk)
83
+ options = super
84
+ options[:update] = blk if block_given?
85
+ options
86
+ end
87
+
88
+ # Normalize both text and status options.
89
+ def _normalize_options(options)
90
+ _normalize_text(options)
91
+
92
+ if options[:html]
93
+ options[:html] = ERB::Util.html_escape(options[:html])
94
+ end
95
+
96
+ if options[:status]
97
+ options[:status] = Rack::Utils.status_code(options[:status])
98
+ end
99
+
100
+ super
101
+ end
102
+
103
+ def _normalize_text(options)
104
+ RENDER_FORMATS_IN_PRIORITY.each do |format|
105
+ if options.key?(format) && options[format].respond_to?(:to_text)
106
+ options[format] = options[format].to_text
107
+ end
108
+ end
109
+ end
110
+
111
+ # Process controller specific options, as status, content-type and location.
112
+ def _process_options(options)
113
+ status, content_type, location = options.values_at(:status, :content_type, :location)
114
+
115
+ self.status = status if status
116
+ self.content_type = content_type if content_type
117
+ headers["Location"] = url_for(location) if location
118
+
119
+ super
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,456 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rack/session/abstract/id"
4
+ require "action_controller/metal/exceptions"
5
+ require "active_support/security_utils"
6
+
7
+ module ActionController #:nodoc:
8
+ class InvalidAuthenticityToken < ActionControllerError #:nodoc:
9
+ end
10
+
11
+ class InvalidCrossOriginRequest < ActionControllerError #:nodoc:
12
+ end
13
+
14
+ # Controller actions are protected from Cross-Site Request Forgery (CSRF) attacks
15
+ # by including a token in the rendered HTML for your application. This token is
16
+ # stored as a random string in the session, to which an attacker does not have
17
+ # access. When a request reaches your application, \Rails verifies the received
18
+ # token with the token in the session. All requests are checked except GET requests
19
+ # as these should be idempotent. Keep in mind that all session-oriented requests
20
+ # are CSRF protected by default, including JavaScript and HTML requests.
21
+ #
22
+ # Since HTML and JavaScript requests are typically made from the browser, we
23
+ # need to ensure to verify request authenticity for the web browser. We can
24
+ # use session-oriented authentication for these types of requests, by using
25
+ # the <tt>protect_from_forgery</tt> method in our controllers.
26
+ #
27
+ # GET requests are not protected since they don't have side effects like writing
28
+ # to the database and don't leak sensitive information. JavaScript requests are
29
+ # an exception: a third-party site can use a <script> tag to reference a JavaScript
30
+ # URL on your site. When your JavaScript response loads on their site, it executes.
31
+ # With carefully crafted JavaScript on their end, sensitive data in your JavaScript
32
+ # response may be extracted. To prevent this, only XmlHttpRequest (known as XHR or
33
+ # Ajax) requests are allowed to make requests for JavaScript responses.
34
+ #
35
+ # It's important to remember that XML or JSON requests are also checked by default. If
36
+ # you're building an API or an SPA you could change forgery protection method in
37
+ # <tt>ApplicationController</tt> (by default: <tt>:exception</tt>):
38
+ #
39
+ # class ApplicationController < ActionController::Base
40
+ # protect_from_forgery unless: -> { request.format.json? }
41
+ # end
42
+ #
43
+ # It is generally safe to exclude XHR requests from CSRF protection
44
+ # (like the code snippet above does), because XHR requests can only be made from
45
+ # the same origin. Note however that any cross-origin third party domain
46
+ # allowed via {CORS}[https://en.wikipedia.org/wiki/Cross-origin_resource_sharing]
47
+ # will also be able to create XHR requests. Be sure to check your
48
+ # CORS configuration before disabling forgery protection for XHR.
49
+ #
50
+ # CSRF protection is turned on with the <tt>protect_from_forgery</tt> method.
51
+ # By default <tt>protect_from_forgery</tt> protects your session with
52
+ # <tt>:null_session</tt> method, which provides an empty session
53
+ # during request.
54
+ #
55
+ # We may want to disable CSRF protection for APIs since they are typically
56
+ # designed to be state-less. That is, the request API client will handle
57
+ # the session for you instead of Rails.
58
+ #
59
+ # The token parameter is named <tt>authenticity_token</tt> by default. The name and
60
+ # value of this token must be added to every layout that renders forms by including
61
+ # <tt>csrf_meta_tags</tt> in the HTML +head+.
62
+ #
63
+ # Learn more about CSRF attacks and securing your application in the
64
+ # {Ruby on Rails Security Guide}[https://guides.rubyonrails.org/security.html].
65
+ module RequestForgeryProtection
66
+ extend ActiveSupport::Concern
67
+
68
+ include AbstractController::Helpers
69
+ include AbstractController::Callbacks
70
+
71
+ included do
72
+ # Sets the token parameter name for RequestForgery. Calling +protect_from_forgery+
73
+ # sets it to <tt>:authenticity_token</tt> by default.
74
+ config_accessor :request_forgery_protection_token
75
+ self.request_forgery_protection_token ||= :authenticity_token
76
+
77
+ # Holds the class which implements the request forgery protection.
78
+ config_accessor :forgery_protection_strategy
79
+ self.forgery_protection_strategy = nil
80
+
81
+ # Controls whether request forgery protection is turned on or not. Turned off by default only in test mode.
82
+ config_accessor :allow_forgery_protection
83
+ self.allow_forgery_protection = true if allow_forgery_protection.nil?
84
+
85
+ # Controls whether a CSRF failure logs a warning. On by default.
86
+ config_accessor :log_warning_on_csrf_failure
87
+ self.log_warning_on_csrf_failure = true
88
+
89
+ # Controls whether the Origin header is checked in addition to the CSRF token.
90
+ config_accessor :forgery_protection_origin_check
91
+ self.forgery_protection_origin_check = false
92
+
93
+ # Controls whether form-action/method specific CSRF tokens are used.
94
+ config_accessor :per_form_csrf_tokens
95
+ self.per_form_csrf_tokens = false
96
+
97
+ # Controls whether forgery protection is enabled by default.
98
+ config_accessor :default_protect_from_forgery
99
+ self.default_protect_from_forgery = false
100
+
101
+ helper_method :form_authenticity_token
102
+ helper_method :protect_against_forgery?
103
+ end
104
+
105
+ module ClassMethods
106
+ # Turn on request forgery protection. Bear in mind that GET and HEAD requests are not checked.
107
+ #
108
+ # class ApplicationController < ActionController::Base
109
+ # protect_from_forgery
110
+ # end
111
+ #
112
+ # class FooController < ApplicationController
113
+ # protect_from_forgery except: :index
114
+ # end
115
+ #
116
+ # You can disable forgery protection on controller by skipping the verification before_action:
117
+ #
118
+ # skip_before_action :verify_authenticity_token
119
+ #
120
+ # Valid Options:
121
+ #
122
+ # * <tt>:only/:except</tt> - Only apply forgery protection to a subset of actions. For example <tt>only: [ :create, :create_all ]</tt>.
123
+ # * <tt>:if/:unless</tt> - Turn off the forgery protection entirely depending on the passed Proc or method reference.
124
+ # * <tt>:prepend</tt> - By default, the verification of the authentication token will be added at the position of the
125
+ # protect_from_forgery call in your application. This means any callbacks added before are run first. This is useful
126
+ # when you want your forgery protection to depend on other callbacks, like authentication methods (Oauth vs Cookie auth).
127
+ #
128
+ # If you need to add verification to the beginning of the callback chain, use <tt>prepend: true</tt>.
129
+ # * <tt>:with</tt> - Set the method to handle unverified request.
130
+ #
131
+ # Valid unverified request handling methods are:
132
+ # * <tt>:exception</tt> - Raises ActionController::InvalidAuthenticityToken exception.
133
+ # * <tt>:reset_session</tt> - Resets the session.
134
+ # * <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.
135
+ def protect_from_forgery(options = {})
136
+ options = options.reverse_merge(prepend: false)
137
+
138
+ self.forgery_protection_strategy = protection_method_class(options[:with] || :null_session)
139
+ self.request_forgery_protection_token ||= :authenticity_token
140
+ before_action :verify_authenticity_token, options
141
+ append_after_action :verify_same_origin_request
142
+ end
143
+
144
+ # Turn off request forgery protection. This is a wrapper for:
145
+ #
146
+ # skip_before_action :verify_authenticity_token
147
+ #
148
+ # See +skip_before_action+ for allowed options.
149
+ def skip_forgery_protection(options = {})
150
+ skip_before_action :verify_authenticity_token, options
151
+ end
152
+
153
+ private
154
+
155
+ def protection_method_class(name)
156
+ ActionController::RequestForgeryProtection::ProtectionMethods.const_get(name.to_s.classify)
157
+ rescue NameError
158
+ raise ArgumentError, "Invalid request forgery protection method, use :null_session, :exception, or :reset_session"
159
+ end
160
+ end
161
+
162
+ module ProtectionMethods
163
+ class NullSession
164
+ def initialize(controller)
165
+ @controller = controller
166
+ end
167
+
168
+ # This is the method that defines the application behavior when a request is found to be unverified.
169
+ def handle_unverified_request
170
+ request = @controller.request
171
+ request.session = NullSessionHash.new(request)
172
+ request.flash = nil
173
+ request.session_options = { skip: true }
174
+ request.cookie_jar = NullCookieJar.build(request, {})
175
+ end
176
+
177
+ private
178
+
179
+ class NullSessionHash < Rack::Session::Abstract::SessionHash #:nodoc:
180
+ def initialize(req)
181
+ super(nil, req)
182
+ @data = {}
183
+ @loaded = true
184
+ end
185
+
186
+ # no-op
187
+ def destroy; end
188
+
189
+ def exists?
190
+ true
191
+ end
192
+ end
193
+
194
+ class NullCookieJar < ActionDispatch::Cookies::CookieJar #:nodoc:
195
+ def write(*)
196
+ # nothing
197
+ end
198
+ end
199
+ end
200
+
201
+ class ResetSession
202
+ def initialize(controller)
203
+ @controller = controller
204
+ end
205
+
206
+ def handle_unverified_request
207
+ @controller.reset_session
208
+ end
209
+ end
210
+
211
+ class Exception
212
+ def initialize(controller)
213
+ @controller = controller
214
+ end
215
+
216
+ def handle_unverified_request
217
+ raise ActionController::InvalidAuthenticityToken
218
+ end
219
+ end
220
+ end
221
+
222
+ private
223
+ # The actual before_action that is used to verify the CSRF token.
224
+ # Don't override this directly. Provide your own forgery protection
225
+ # strategy instead. If you override, you'll disable same-origin
226
+ # <tt><script></tt> verification.
227
+ #
228
+ # Lean on the protect_from_forgery declaration to mark which actions are
229
+ # due for same-origin request verification. If protect_from_forgery is
230
+ # enabled on an action, this before_action flags its after_action to
231
+ # verify that JavaScript responses are for XHR requests, ensuring they
232
+ # follow the browser's same-origin policy.
233
+ def verify_authenticity_token # :doc:
234
+ mark_for_same_origin_verification!
235
+
236
+ if !verified_request?
237
+ if logger && log_warning_on_csrf_failure
238
+ if valid_request_origin?
239
+ logger.warn "Can't verify CSRF token authenticity."
240
+ else
241
+ logger.warn "HTTP Origin header (#{request.origin}) didn't match request.base_url (#{request.base_url})"
242
+ end
243
+ end
244
+ handle_unverified_request
245
+ end
246
+ end
247
+
248
+ def handle_unverified_request # :doc:
249
+ forgery_protection_strategy.new(self).handle_unverified_request
250
+ end
251
+
252
+ #:nodoc:
253
+ CROSS_ORIGIN_JAVASCRIPT_WARNING = "Security warning: an embedded " \
254
+ "<script> tag on another site requested protected JavaScript. " \
255
+ "If you know what you're doing, go ahead and disable forgery " \
256
+ "protection on this action to permit cross-origin JavaScript embedding."
257
+ private_constant :CROSS_ORIGIN_JAVASCRIPT_WARNING
258
+ # :startdoc:
259
+
260
+ # If +verify_authenticity_token+ was run (indicating that we have
261
+ # forgery protection enabled for this request) then also verify that
262
+ # we aren't serving an unauthorized cross-origin response.
263
+ def verify_same_origin_request # :doc:
264
+ if marked_for_same_origin_verification? && non_xhr_javascript_response?
265
+ if logger && log_warning_on_csrf_failure
266
+ logger.warn CROSS_ORIGIN_JAVASCRIPT_WARNING
267
+ end
268
+ raise ActionController::InvalidCrossOriginRequest, CROSS_ORIGIN_JAVASCRIPT_WARNING
269
+ end
270
+ end
271
+
272
+ # GET requests are checked for cross-origin JavaScript after rendering.
273
+ def mark_for_same_origin_verification! # :doc:
274
+ @marked_for_same_origin_verification = request.get?
275
+ end
276
+
277
+ # If the +verify_authenticity_token+ before_action ran, verify that
278
+ # JavaScript responses are only served to same-origin GET requests.
279
+ def marked_for_same_origin_verification? # :doc:
280
+ @marked_for_same_origin_verification ||= false
281
+ end
282
+
283
+ # Check for cross-origin JavaScript responses.
284
+ def non_xhr_javascript_response? # :doc:
285
+ %r(\A(?:text|application)/javascript).match?(media_type) && !request.xhr?
286
+ end
287
+
288
+ AUTHENTICITY_TOKEN_LENGTH = 32
289
+
290
+ # Returns true or false if a request is verified. Checks:
291
+ #
292
+ # * Is it a GET or HEAD request? GETs should be safe and idempotent
293
+ # * Does the form_authenticity_token match the given token value from the params?
294
+ # * Does the X-CSRF-Token header match the form_authenticity_token?
295
+ def verified_request? # :doc:
296
+ !protect_against_forgery? || request.get? || request.head? ||
297
+ (valid_request_origin? && any_authenticity_token_valid?)
298
+ end
299
+
300
+ # Checks if any of the authenticity tokens from the request are valid.
301
+ def any_authenticity_token_valid? # :doc:
302
+ request_authenticity_tokens.any? do |token|
303
+ valid_authenticity_token?(session, token)
304
+ end
305
+ end
306
+
307
+ # Possible authenticity tokens sent in the request.
308
+ def request_authenticity_tokens # :doc:
309
+ [form_authenticity_param, request.x_csrf_token]
310
+ end
311
+
312
+ # Sets the token value for the current session.
313
+ def form_authenticity_token(form_options: {})
314
+ masked_authenticity_token(session, form_options: form_options)
315
+ end
316
+
317
+ # Creates a masked version of the authenticity token that varies
318
+ # on each request. The masking is used to mitigate SSL attacks
319
+ # like BREACH.
320
+ def masked_authenticity_token(session, form_options: {}) # :doc:
321
+ action, method = form_options.values_at(:action, :method)
322
+
323
+ raw_token = if per_form_csrf_tokens && action && method
324
+ action_path = normalize_action_path(action)
325
+ per_form_csrf_token(session, action_path, method)
326
+ else
327
+ real_csrf_token(session)
328
+ end
329
+
330
+ one_time_pad = SecureRandom.random_bytes(AUTHENTICITY_TOKEN_LENGTH)
331
+ encrypted_csrf_token = xor_byte_strings(one_time_pad, raw_token)
332
+ masked_token = one_time_pad + encrypted_csrf_token
333
+ Base64.strict_encode64(masked_token)
334
+ end
335
+
336
+ # Checks the client's masked token to see if it matches the
337
+ # session token. Essentially the inverse of
338
+ # +masked_authenticity_token+.
339
+ def valid_authenticity_token?(session, encoded_masked_token) # :doc:
340
+ if encoded_masked_token.nil? || encoded_masked_token.empty? || !encoded_masked_token.is_a?(String)
341
+ return false
342
+ end
343
+
344
+ begin
345
+ masked_token = Base64.strict_decode64(encoded_masked_token)
346
+ rescue ArgumentError # encoded_masked_token is invalid Base64
347
+ return false
348
+ end
349
+
350
+ # See if it's actually a masked token or not. In order to
351
+ # deploy this code, we should be able to handle any unmasked
352
+ # tokens that we've issued without error.
353
+
354
+ if masked_token.length == AUTHENTICITY_TOKEN_LENGTH
355
+ # This is actually an unmasked token. This is expected if
356
+ # you have just upgraded to masked tokens, but should stop
357
+ # happening shortly after installing this gem.
358
+ compare_with_real_token masked_token, session
359
+
360
+ elsif masked_token.length == AUTHENTICITY_TOKEN_LENGTH * 2
361
+ csrf_token = unmask_token(masked_token)
362
+
363
+ compare_with_real_token(csrf_token, session) ||
364
+ valid_per_form_csrf_token?(csrf_token, session)
365
+ else
366
+ false # Token is malformed.
367
+ end
368
+ end
369
+
370
+ def unmask_token(masked_token) # :doc:
371
+ # Split the token into the one-time pad and the encrypted
372
+ # value and decrypt it.
373
+ one_time_pad = masked_token[0...AUTHENTICITY_TOKEN_LENGTH]
374
+ encrypted_csrf_token = masked_token[AUTHENTICITY_TOKEN_LENGTH..-1]
375
+ xor_byte_strings(one_time_pad, encrypted_csrf_token)
376
+ end
377
+
378
+ def compare_with_real_token(token, session) # :doc:
379
+ ActiveSupport::SecurityUtils.fixed_length_secure_compare(token, real_csrf_token(session))
380
+ end
381
+
382
+ def valid_per_form_csrf_token?(token, session) # :doc:
383
+ if per_form_csrf_tokens
384
+ correct_token = per_form_csrf_token(
385
+ session,
386
+ normalize_action_path(request.fullpath),
387
+ request.request_method
388
+ )
389
+
390
+ ActiveSupport::SecurityUtils.fixed_length_secure_compare(token, correct_token)
391
+ else
392
+ false
393
+ end
394
+ end
395
+
396
+ def real_csrf_token(session) # :doc:
397
+ session[:_csrf_token] ||= SecureRandom.base64(AUTHENTICITY_TOKEN_LENGTH)
398
+ Base64.strict_decode64(session[:_csrf_token])
399
+ end
400
+
401
+ def per_form_csrf_token(session, action_path, method) # :doc:
402
+ OpenSSL::HMAC.digest(
403
+ OpenSSL::Digest::SHA256.new,
404
+ real_csrf_token(session),
405
+ [action_path, method.downcase].join("#")
406
+ )
407
+ end
408
+
409
+ def xor_byte_strings(s1, s2) # :doc:
410
+ s2 = s2.dup
411
+ size = s1.bytesize
412
+ i = 0
413
+ while i < size
414
+ s2.setbyte(i, s1.getbyte(i) ^ s2.getbyte(i))
415
+ i += 1
416
+ end
417
+ s2
418
+ end
419
+
420
+ # The form's authenticity parameter. Override to provide your own.
421
+ def form_authenticity_param # :doc:
422
+ params[request_forgery_protection_token]
423
+ end
424
+
425
+ # Checks if the controller allows forgery protection.
426
+ def protect_against_forgery? # :doc:
427
+ allow_forgery_protection
428
+ end
429
+
430
+ NULL_ORIGIN_MESSAGE = <<~MSG
431
+ The browser returned a 'null' origin for a request with origin-based forgery protection turned on. This usually
432
+ means you have the 'no-referrer' Referrer-Policy header enabled, or that the request came from a site that
433
+ refused to give its origin. This makes it impossible for Rails to verify the source of the requests. Likely the
434
+ best solution is to change your referrer policy to something less strict like same-origin or strict-origin.
435
+ If you cannot change the referrer policy, you can disable origin checking with the
436
+ Rails.application.config.action_controller.forgery_protection_origin_check setting.
437
+ MSG
438
+
439
+ # Checks if the request originated from the same origin by looking at the
440
+ # Origin header.
441
+ def valid_request_origin? # :doc:
442
+ if forgery_protection_origin_check
443
+ # We accept blank origin headers because some user agents don't send it.
444
+ raise InvalidAuthenticityToken, NULL_ORIGIN_MESSAGE if request.origin == "null"
445
+ request.origin.nil? || request.origin == request.base_url
446
+ else
447
+ true
448
+ end
449
+ end
450
+
451
+ def normalize_action_path(action_path) # :doc:
452
+ uri = URI.parse(action_path)
453
+ uri.path.chomp("/")
454
+ end
455
+ end
456
+ end