actionpack 7.0.8.1 → 7.2.2.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +94 -500
- data/MIT-LICENSE +1 -1
- data/README.rdoc +2 -2
- data/lib/abstract_controller/asset_paths.rb +2 -0
- data/lib/abstract_controller/base.rb +119 -106
- data/lib/abstract_controller/caching/fragments.rb +51 -52
- data/lib/abstract_controller/caching.rb +2 -0
- data/lib/abstract_controller/callbacks.rb +94 -67
- data/lib/abstract_controller/collector.rb +6 -6
- data/lib/abstract_controller/deprecator.rb +9 -0
- data/lib/abstract_controller/error.rb +2 -0
- data/lib/abstract_controller/helpers.rb +121 -91
- data/lib/abstract_controller/logger.rb +2 -0
- data/lib/abstract_controller/railties/routes_helpers.rb +3 -16
- data/lib/abstract_controller/rendering.rb +14 -13
- data/lib/abstract_controller/translation.rb +12 -30
- data/lib/abstract_controller/url_for.rb +9 -5
- data/lib/abstract_controller.rb +8 -0
- data/lib/action_controller/api/api_rendering.rb +2 -0
- data/lib/action_controller/api.rb +78 -73
- data/lib/action_controller/base.rb +199 -141
- data/lib/action_controller/caching.rb +16 -11
- data/lib/action_controller/deprecator.rb +9 -0
- data/lib/action_controller/form_builder.rb +21 -16
- data/lib/action_controller/log_subscriber.rb +19 -5
- data/lib/action_controller/metal/allow_browser.rb +123 -0
- data/lib/action_controller/metal/basic_implicit_render.rb +2 -0
- data/lib/action_controller/metal/conditional_get.rb +187 -174
- data/lib/action_controller/metal/content_security_policy.rb +26 -25
- data/lib/action_controller/metal/cookies.rb +4 -2
- data/lib/action_controller/metal/data_streaming.rb +65 -54
- data/lib/action_controller/metal/default_headers.rb +6 -2
- data/lib/action_controller/metal/etag_with_flash.rb +4 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +18 -14
- data/lib/action_controller/metal/exceptions.rb +19 -9
- data/lib/action_controller/metal/flash.rb +12 -10
- data/lib/action_controller/metal/head.rb +20 -16
- data/lib/action_controller/metal/helpers.rb +64 -67
- data/lib/action_controller/metal/http_authentication.rb +214 -200
- data/lib/action_controller/metal/implicit_render.rb +21 -17
- data/lib/action_controller/metal/instrumentation.rb +22 -12
- data/lib/action_controller/metal/live.rb +125 -92
- data/lib/action_controller/metal/logging.rb +6 -4
- data/lib/action_controller/metal/mime_responds.rb +151 -142
- data/lib/action_controller/metal/parameter_encoding.rb +34 -32
- data/lib/action_controller/metal/params_wrapper.rb +58 -58
- data/lib/action_controller/metal/permissions_policy.rb +14 -13
- data/lib/action_controller/metal/rate_limiting.rb +62 -0
- data/lib/action_controller/metal/redirecting.rb +110 -84
- data/lib/action_controller/metal/renderers.rb +50 -49
- data/lib/action_controller/metal/rendering.rb +103 -82
- data/lib/action_controller/metal/request_forgery_protection.rb +279 -161
- data/lib/action_controller/metal/rescue.rb +12 -8
- data/lib/action_controller/metal/streaming.rb +174 -132
- data/lib/action_controller/metal/strong_parameters.rb +598 -473
- data/lib/action_controller/metal/testing.rb +2 -0
- data/lib/action_controller/metal/url_for.rb +23 -14
- data/lib/action_controller/metal.rb +145 -61
- data/lib/action_controller/railtie.rb +25 -9
- data/lib/action_controller/railties/helpers.rb +2 -0
- data/lib/action_controller/renderer.rb +105 -66
- data/lib/action_controller/template_assertions.rb +4 -2
- data/lib/action_controller/test_case.rb +157 -128
- data/lib/action_controller.rb +17 -3
- data/lib/action_dispatch/constants.rb +34 -0
- data/lib/action_dispatch/deprecator.rb +9 -0
- data/lib/action_dispatch/http/cache.rb +28 -29
- data/lib/action_dispatch/http/content_disposition.rb +2 -0
- data/lib/action_dispatch/http/content_security_policy.rb +69 -49
- data/lib/action_dispatch/http/filter_parameters.rb +27 -12
- data/lib/action_dispatch/http/filter_redirect.rb +22 -1
- data/lib/action_dispatch/http/headers.rb +23 -21
- data/lib/action_dispatch/http/mime_negotiation.rb +37 -48
- data/lib/action_dispatch/http/mime_type.rb +60 -30
- data/lib/action_dispatch/http/mime_types.rb +5 -1
- data/lib/action_dispatch/http/parameters.rb +12 -10
- data/lib/action_dispatch/http/permissions_policy.rb +32 -34
- data/lib/action_dispatch/http/rack_cache.rb +4 -0
- data/lib/action_dispatch/http/request.rb +132 -79
- data/lib/action_dispatch/http/response.rb +136 -103
- data/lib/action_dispatch/http/upload.rb +19 -15
- data/lib/action_dispatch/http/url.rb +75 -73
- data/lib/action_dispatch/journey/formatter.rb +19 -6
- data/lib/action_dispatch/journey/gtg/builder.rb +4 -3
- data/lib/action_dispatch/journey/gtg/simulator.rb +2 -0
- data/lib/action_dispatch/journey/gtg/transition_table.rb +10 -8
- data/lib/action_dispatch/journey/nfa/dot.rb +2 -0
- data/lib/action_dispatch/journey/nodes/node.rb +6 -5
- data/lib/action_dispatch/journey/parser.rb +4 -3
- data/lib/action_dispatch/journey/parser_extras.rb +2 -0
- data/lib/action_dispatch/journey/path/pattern.rb +18 -15
- data/lib/action_dispatch/journey/route.rb +12 -9
- data/lib/action_dispatch/journey/router/utils.rb +16 -15
- data/lib/action_dispatch/journey/router.rb +13 -10
- data/lib/action_dispatch/journey/routes.rb +6 -4
- data/lib/action_dispatch/journey/scanner.rb +4 -2
- data/lib/action_dispatch/journey/visitors.rb +2 -0
- data/lib/action_dispatch/journey.rb +2 -0
- data/lib/action_dispatch/log_subscriber.rb +25 -0
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +7 -6
- data/lib/action_dispatch/middleware/assume_ssl.rb +27 -0
- data/lib/action_dispatch/middleware/callbacks.rb +4 -0
- data/lib/action_dispatch/middleware/cookies.rb +192 -194
- data/lib/action_dispatch/middleware/debug_exceptions.rb +36 -27
- data/lib/action_dispatch/middleware/debug_locks.rb +18 -13
- data/lib/action_dispatch/middleware/debug_view.rb +9 -2
- data/lib/action_dispatch/middleware/exception_wrapper.rb +181 -27
- data/lib/action_dispatch/middleware/executor.rb +9 -1
- data/lib/action_dispatch/middleware/flash.rb +65 -46
- data/lib/action_dispatch/middleware/host_authorization.rb +22 -17
- data/lib/action_dispatch/middleware/public_exceptions.rb +12 -8
- data/lib/action_dispatch/middleware/reloader.rb +9 -5
- data/lib/action_dispatch/middleware/remote_ip.rb +88 -83
- data/lib/action_dispatch/middleware/request_id.rb +15 -8
- data/lib/action_dispatch/middleware/server_timing.rb +8 -6
- data/lib/action_dispatch/middleware/session/abstract_store.rb +7 -0
- data/lib/action_dispatch/middleware/session/cache_store.rb +14 -7
- data/lib/action_dispatch/middleware/session/cookie_store.rb +32 -25
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +9 -3
- data/lib/action_dispatch/middleware/show_exceptions.rb +42 -28
- data/lib/action_dispatch/middleware/ssl.rb +60 -45
- data/lib/action_dispatch/middleware/stack.rb +15 -9
- data/lib/action_dispatch/middleware/static.rb +40 -34
- data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +8 -1
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +7 -7
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +17 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +16 -12
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +3 -0
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +47 -38
- data/lib/action_dispatch/railtie.rb +12 -4
- data/lib/action_dispatch/request/session.rb +39 -27
- data/lib/action_dispatch/request/utils.rb +10 -3
- data/lib/action_dispatch/routing/endpoint.rb +2 -0
- data/lib/action_dispatch/routing/inspector.rb +59 -9
- data/lib/action_dispatch/routing/mapper.rb +686 -639
- data/lib/action_dispatch/routing/polymorphic_routes.rb +70 -61
- data/lib/action_dispatch/routing/redirection.rb +52 -38
- data/lib/action_dispatch/routing/route_set.rb +106 -62
- data/lib/action_dispatch/routing/routes_proxy.rb +16 -19
- data/lib/action_dispatch/routing/url_for.rb +131 -122
- data/lib/action_dispatch/routing.rb +152 -150
- data/lib/action_dispatch/system_test_case.rb +91 -81
- data/lib/action_dispatch/system_testing/browser.rb +27 -19
- data/lib/action_dispatch/system_testing/driver.rb +16 -22
- data/lib/action_dispatch/system_testing/server.rb +2 -0
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +53 -31
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +2 -0
- data/lib/action_dispatch/testing/assertion_response.rb +9 -7
- data/lib/action_dispatch/testing/assertions/response.rb +36 -26
- data/lib/action_dispatch/testing/assertions/routing.rb +203 -95
- data/lib/action_dispatch/testing/assertions.rb +5 -1
- data/lib/action_dispatch/testing/integration.rb +240 -229
- data/lib/action_dispatch/testing/request_encoder.rb +6 -1
- data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
- data/lib/action_dispatch/testing/test_process.rb +14 -9
- data/lib/action_dispatch/testing/test_request.rb +4 -2
- data/lib/action_dispatch/testing/test_response.rb +34 -19
- data/lib/action_dispatch.rb +52 -21
- data/lib/action_pack/gem_version.rb +5 -3
- data/lib/action_pack/version.rb +3 -1
- data/lib/action_pack.rb +18 -17
- metadata +91 -32
@@ -1,46 +1,50 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
require "active_support/core_ext/object/deep_dup"
|
4
6
|
require "active_support/core_ext/array/wrap"
|
5
7
|
|
6
8
|
module ActionDispatch # :nodoc:
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
9
|
+
# # Action Dispatch Content Security Policy
|
10
|
+
#
|
11
|
+
# Configures the HTTP [Content-Security-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy)
|
12
|
+
# response header to help protect against XSS and
|
13
|
+
# injection attacks.
|
10
14
|
#
|
11
15
|
# Example global policy:
|
12
16
|
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
17
|
+
# Rails.application.config.content_security_policy do |policy|
|
18
|
+
# policy.default_src :self, :https
|
19
|
+
# policy.font_src :self, :https, :data
|
20
|
+
# policy.img_src :self, :https, :data
|
21
|
+
# policy.object_src :none
|
22
|
+
# policy.script_src :self, :https
|
23
|
+
# policy.style_src :self, :https
|
20
24
|
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
25
|
+
# # Specify URI for violation reports
|
26
|
+
# policy.report_uri "/csp-violation-report-endpoint"
|
27
|
+
# end
|
24
28
|
class ContentSecurityPolicy
|
25
|
-
class
|
26
|
-
|
27
|
-
POLICY = "Content-Security-Policy"
|
28
|
-
POLICY_REPORT_ONLY = "Content-Security-Policy-Report-Only"
|
29
|
+
class InvalidDirectiveError < StandardError
|
30
|
+
end
|
29
31
|
|
32
|
+
class Middleware
|
30
33
|
def initialize(app)
|
31
34
|
@app = app
|
32
35
|
end
|
33
36
|
|
34
37
|
def call(env)
|
35
|
-
request = ActionDispatch::Request.new env
|
36
38
|
status, headers, _ = response = @app.call(env)
|
37
39
|
|
38
|
-
# Returning CSP headers with a 304 Not Modified is harmful, since nonces in the
|
39
|
-
# CSP headers might not match nonces in the cached HTML.
|
40
|
+
# Returning CSP headers with a 304 Not Modified is harmful, since nonces in the
|
41
|
+
# new CSP headers might not match nonces in the cached HTML.
|
40
42
|
return response if status == 304
|
41
43
|
|
42
44
|
return response if policy_present?(headers)
|
43
45
|
|
46
|
+
request = ActionDispatch::Request.new env
|
47
|
+
|
44
48
|
if policy = request.content_security_policy
|
45
49
|
nonce = request.content_security_policy_nonce
|
46
50
|
nonce_directives = request.content_security_policy_nonce_directives
|
@@ -54,14 +58,15 @@ module ActionDispatch # :nodoc:
|
|
54
58
|
private
|
55
59
|
def header_name(request)
|
56
60
|
if request.content_security_policy_report_only
|
57
|
-
|
61
|
+
ActionDispatch::Constants::CONTENT_SECURITY_POLICY_REPORT_ONLY
|
58
62
|
else
|
59
|
-
|
63
|
+
ActionDispatch::Constants::CONTENT_SECURITY_POLICY
|
60
64
|
end
|
61
65
|
end
|
62
66
|
|
63
67
|
def policy_present?(headers)
|
64
|
-
headers[
|
68
|
+
headers[ActionDispatch::Constants::CONTENT_SECURITY_POLICY] ||
|
69
|
+
headers[ActionDispatch::Constants::CONTENT_SECURITY_POLICY_REPORT_ONLY]
|
65
70
|
end
|
66
71
|
end
|
67
72
|
|
@@ -123,6 +128,7 @@ module ActionDispatch # :nodoc:
|
|
123
128
|
MAPPINGS = {
|
124
129
|
self: "'self'",
|
125
130
|
unsafe_eval: "'unsafe-eval'",
|
131
|
+
unsafe_hashes: "'unsafe-hashes'",
|
126
132
|
unsafe_inline: "'unsafe-inline'",
|
127
133
|
none: "'none'",
|
128
134
|
http: "http:",
|
@@ -189,14 +195,14 @@ module ActionDispatch # :nodoc:
|
|
189
195
|
end
|
190
196
|
end
|
191
197
|
|
192
|
-
# Specify whether to prevent the user agent from loading any assets over
|
193
|
-
#
|
198
|
+
# Specify whether to prevent the user agent from loading any assets over HTTP
|
199
|
+
# when the page uses HTTPS:
|
194
200
|
#
|
195
|
-
#
|
201
|
+
# policy.block_all_mixed_content
|
196
202
|
#
|
197
|
-
# Pass
|
203
|
+
# Pass `false` to allow it again:
|
198
204
|
#
|
199
|
-
#
|
205
|
+
# policy.block_all_mixed_content false
|
200
206
|
#
|
201
207
|
def block_all_mixed_content(enabled = true)
|
202
208
|
if enabled
|
@@ -208,11 +214,11 @@ module ActionDispatch # :nodoc:
|
|
208
214
|
|
209
215
|
# Restricts the set of plugins that can be embedded:
|
210
216
|
#
|
211
|
-
#
|
217
|
+
# policy.plugin_types "application/x-shockwave-flash"
|
212
218
|
#
|
213
219
|
# Leave empty to allow all plugins:
|
214
220
|
#
|
215
|
-
#
|
221
|
+
# policy.plugin_types
|
216
222
|
#
|
217
223
|
def plugin_types(*types)
|
218
224
|
if types.first
|
@@ -222,23 +228,23 @@ module ActionDispatch # :nodoc:
|
|
222
228
|
end
|
223
229
|
end
|
224
230
|
|
225
|
-
# Enable the
|
226
|
-
# directive. Violation reports will be sent to the
|
231
|
+
# Enable the [report-uri](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/report-uri)
|
232
|
+
# directive. Violation reports will be sent to the
|
233
|
+
# specified URI:
|
227
234
|
#
|
228
|
-
#
|
235
|
+
# policy.report_uri "/csp-violation-report-endpoint"
|
229
236
|
#
|
230
237
|
def report_uri(uri)
|
231
238
|
@directives["report-uri"] = [uri]
|
232
239
|
end
|
233
240
|
|
234
|
-
# Specify asset types for which
|
235
|
-
# is required:
|
241
|
+
# Specify asset types for which [Subresource Integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity) is required:
|
236
242
|
#
|
237
|
-
#
|
243
|
+
# policy.require_sri_for :script, :style
|
238
244
|
#
|
239
245
|
# Leave empty to not require Subresource Integrity:
|
240
246
|
#
|
241
|
-
#
|
247
|
+
# policy.require_sri_for
|
242
248
|
#
|
243
249
|
def require_sri_for(*types)
|
244
250
|
if types.first
|
@@ -248,18 +254,18 @@ module ActionDispatch # :nodoc:
|
|
248
254
|
end
|
249
255
|
end
|
250
256
|
|
251
|
-
# Specify whether a
|
257
|
+
# Specify whether a [sandbox](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/sandbox)
|
252
258
|
# should be enabled for the requested resource:
|
253
259
|
#
|
254
|
-
#
|
260
|
+
# policy.sandbox
|
255
261
|
#
|
256
262
|
# Values can be passed as arguments:
|
257
263
|
#
|
258
|
-
#
|
264
|
+
# policy.sandbox "allow-scripts", "allow-modals"
|
259
265
|
#
|
260
|
-
# Pass
|
266
|
+
# Pass `false` to disable the sandbox:
|
261
267
|
#
|
262
|
-
#
|
268
|
+
# policy.sandbox false
|
263
269
|
#
|
264
270
|
def sandbox(*values)
|
265
271
|
if values.empty?
|
@@ -273,11 +279,11 @@ module ActionDispatch # :nodoc:
|
|
273
279
|
|
274
280
|
# Specify whether user agents should treat any assets over HTTP as HTTPS:
|
275
281
|
#
|
276
|
-
#
|
282
|
+
# policy.upgrade_insecure_requests
|
277
283
|
#
|
278
|
-
# Pass
|
284
|
+
# Pass `false` to disable it:
|
279
285
|
#
|
280
|
-
#
|
286
|
+
# policy.upgrade_insecure_requests false
|
281
287
|
#
|
282
288
|
def upgrade_insecure_requests(enabled = true)
|
283
289
|
if enabled
|
@@ -316,9 +322,9 @@ module ActionDispatch # :nodoc:
|
|
316
322
|
@directives.map do |directive, sources|
|
317
323
|
if sources.is_a?(Array)
|
318
324
|
if nonce && nonce_directive?(directive, nonce_directives)
|
319
|
-
"#{directive} #{build_directive(sources, context).join(' ')} 'nonce-#{nonce}'"
|
325
|
+
"#{directive} #{build_directive(directive, sources, context).join(' ')} 'nonce-#{nonce}'"
|
320
326
|
else
|
321
|
-
"#{directive} #{build_directive(sources, context).join(' ')}"
|
327
|
+
"#{directive} #{build_directive(directive, sources, context).join(' ')}"
|
322
328
|
end
|
323
329
|
elsif sources
|
324
330
|
directive
|
@@ -328,8 +334,22 @@ module ActionDispatch # :nodoc:
|
|
328
334
|
end
|
329
335
|
end
|
330
336
|
|
331
|
-
def
|
332
|
-
sources.
|
337
|
+
def validate(directive, sources)
|
338
|
+
sources.flatten.each do |source|
|
339
|
+
if source.include?(";") || source != source.gsub(/[[:space:]]/, "")
|
340
|
+
raise InvalidDirectiveError, <<~MSG.squish
|
341
|
+
Invalid Content Security Policy #{directive}: "#{source}".
|
342
|
+
Directive values must not contain whitespace or semicolons.
|
343
|
+
Please use multiple arguments or other directive methods instead.
|
344
|
+
MSG
|
345
|
+
end
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
def build_directive(directive, sources, context)
|
350
|
+
resolved_sources = sources.map { |source| resolve_source(source, context) }
|
351
|
+
|
352
|
+
validate(directive, resolved_sources)
|
333
353
|
end
|
334
354
|
|
335
355
|
def resolve_source(source, context)
|
@@ -1,16 +1,21 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
require "active_support/parameter_filter"
|
4
6
|
|
5
7
|
module ActionDispatch
|
6
8
|
module Http
|
9
|
+
# # Action Dispatch HTTP Filter Parameters
|
10
|
+
#
|
7
11
|
# Allows you to specify sensitive query string and POST parameters to filter
|
8
12
|
# from the request log.
|
9
13
|
#
|
10
|
-
#
|
11
|
-
#
|
14
|
+
# # Replaces values with "[FILTERED]" for keys that match /foo|bar/i.
|
15
|
+
# env["action_dispatch.parameter_filter"] = [:foo, "bar"]
|
12
16
|
#
|
13
|
-
# For more information about filter behavior, see
|
17
|
+
# For more information about filter behavior, see
|
18
|
+
# ActiveSupport::ParameterFilter.
|
14
19
|
module FilterParameters
|
15
20
|
ENV_MATCH = [/RAW_POST_DATA/, "rack.request.form_vars"] # :nodoc:
|
16
21
|
NULL_PARAM_FILTER = ActiveSupport::ParameterFilter.new # :nodoc:
|
@@ -21,6 +26,7 @@ module ActionDispatch
|
|
21
26
|
@filtered_parameters = nil
|
22
27
|
@filtered_env = nil
|
23
28
|
@filtered_path = nil
|
29
|
+
@parameter_filter = nil
|
24
30
|
end
|
25
31
|
|
26
32
|
# Returns a hash of parameters with all sensitive data replaced.
|
@@ -40,13 +46,17 @@ module ActionDispatch
|
|
40
46
|
@filtered_path ||= query_string.empty? ? path : "#{path}?#{filtered_query_string}"
|
41
47
|
end
|
42
48
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
49
|
+
# Returns the `ActiveSupport::ParameterFilter` object used to filter in this
|
50
|
+
# request.
|
51
|
+
def parameter_filter
|
52
|
+
@parameter_filter ||= if has_header?("action_dispatch.parameter_filter")
|
53
|
+
parameter_filter_for get_header("action_dispatch.parameter_filter")
|
54
|
+
else
|
55
|
+
NULL_PARAM_FILTER
|
56
|
+
end
|
48
57
|
end
|
49
58
|
|
59
|
+
private
|
50
60
|
def env_filter # :doc:
|
51
61
|
user_key = fetch_header("action_dispatch.parameter_filter") {
|
52
62
|
return NULL_ENV_FILTER
|
@@ -58,12 +68,17 @@ module ActionDispatch
|
|
58
68
|
ActiveSupport::ParameterFilter.new(filters)
|
59
69
|
end
|
60
70
|
|
61
|
-
KV_RE = "[^&;=]+"
|
62
|
-
PAIR_RE = %r{(#{KV_RE})=(#{KV_RE})}
|
63
71
|
def filtered_query_string # :doc:
|
64
|
-
query_string.
|
65
|
-
|
72
|
+
parts = query_string.split(/([&;])/)
|
73
|
+
filtered_parts = parts.map do |part|
|
74
|
+
if part.include?("=")
|
75
|
+
key, value = part.split("=", 2)
|
76
|
+
parameter_filter.filter(key => value).first.join("=")
|
77
|
+
else
|
78
|
+
part
|
79
|
+
end
|
66
80
|
end
|
81
|
+
filtered_parts.join("")
|
67
82
|
end
|
68
83
|
end
|
69
84
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
module ActionDispatch
|
4
6
|
module Http
|
5
7
|
module FilterRedirect
|
@@ -9,7 +11,7 @@ module ActionDispatch
|
|
9
11
|
if location_filter_match?
|
10
12
|
FILTERED
|
11
13
|
else
|
12
|
-
|
14
|
+
parameter_filtered_location
|
13
15
|
end
|
14
16
|
end
|
15
17
|
|
@@ -31,6 +33,25 @@ module ActionDispatch
|
|
31
33
|
end
|
32
34
|
end
|
33
35
|
end
|
36
|
+
|
37
|
+
def parameter_filtered_location
|
38
|
+
uri = URI.parse(location)
|
39
|
+
unless uri.query.nil? || uri.query.empty?
|
40
|
+
parts = uri.query.split(/([&;])/)
|
41
|
+
filtered_parts = parts.map do |part|
|
42
|
+
if part.include?("=")
|
43
|
+
key, value = part.split("=", 2)
|
44
|
+
request.parameter_filter.filter(key => value).first.join("=")
|
45
|
+
else
|
46
|
+
part
|
47
|
+
end
|
48
|
+
end
|
49
|
+
uri.query = filtered_parts.join("")
|
50
|
+
end
|
51
|
+
uri.to_s
|
52
|
+
rescue URI::Error
|
53
|
+
FILTERED
|
54
|
+
end
|
34
55
|
end
|
35
56
|
end
|
36
57
|
end
|
@@ -1,26 +1,30 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
module ActionDispatch
|
4
6
|
module Http
|
7
|
+
# # Action Dispatch HTTP Headers
|
8
|
+
#
|
5
9
|
# Provides access to the request's HTTP headers from the environment.
|
6
10
|
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
+
# env = { "CONTENT_TYPE" => "text/plain", "HTTP_USER_AGENT" => "curl/7.43.0" }
|
12
|
+
# headers = ActionDispatch::Http::Headers.from_hash(env)
|
13
|
+
# headers["Content-Type"] # => "text/plain"
|
14
|
+
# headers["User-Agent"] # => "curl/7.43.0"
|
11
15
|
#
|
12
16
|
# Also note that when headers are mapped to CGI-like variables by the Rack
|
13
17
|
# server, both dashes and underscores are converted to underscores. This
|
14
18
|
# ambiguity cannot be resolved at this stage anymore. Both underscores and
|
15
19
|
# dashes have to be interpreted as if they were originally sent as dashes.
|
16
20
|
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
+
# # GET / HTTP/1.1
|
22
|
+
# # ...
|
23
|
+
# # User-Agent: curl/7.43.0
|
24
|
+
# # X_Custom_Header: token
|
21
25
|
#
|
22
|
-
#
|
23
|
-
#
|
26
|
+
# headers["X_Custom_Header"] # => nil
|
27
|
+
# headers["X-Custom-Header"] # => "token"
|
24
28
|
class Headers
|
25
29
|
CGI_VARIABLES = Set.new(%W[
|
26
30
|
AUTH_TYPE
|
@@ -65,7 +69,7 @@ module ActionDispatch
|
|
65
69
|
@req.set_header env_name(key), value
|
66
70
|
end
|
67
71
|
|
68
|
-
# Add a value to a multivalued header like
|
72
|
+
# Add a value to a multivalued header like `Vary` or `Accept-Encoding`.
|
69
73
|
def add(key, value)
|
70
74
|
@req.add_header env_name(key), value
|
71
75
|
end
|
@@ -79,11 +83,10 @@ module ActionDispatch
|
|
79
83
|
|
80
84
|
# Returns the value for the given key mapped to @env.
|
81
85
|
#
|
82
|
-
# If the key is not found and an optional code block is not provided,
|
83
|
-
#
|
86
|
+
# If the key is not found and an optional code block is not provided, raises a
|
87
|
+
# `KeyError` exception.
|
84
88
|
#
|
85
|
-
# If the code block is provided, then it will be run and
|
86
|
-
# its result returned.
|
89
|
+
# If the code block is provided, then it will be run and its result returned.
|
87
90
|
def fetch(key, default = DEFAULT)
|
88
91
|
@req.fetch_header(env_name(key)) do
|
89
92
|
return default unless default == DEFAULT
|
@@ -97,16 +100,15 @@ module ActionDispatch
|
|
97
100
|
end
|
98
101
|
|
99
102
|
# Returns a new Http::Headers instance containing the contents of
|
100
|
-
#
|
103
|
+
# `headers_or_env` and the original instance.
|
101
104
|
def merge(headers_or_env)
|
102
105
|
headers = @req.dup.headers
|
103
106
|
headers.merge!(headers_or_env)
|
104
107
|
headers
|
105
108
|
end
|
106
109
|
|
107
|
-
# Adds the contents of
|
108
|
-
#
|
109
|
-
# <tt>headers_or_env</tt>.
|
110
|
+
# Adds the contents of `headers_or_env` to original instance entries; duplicate
|
111
|
+
# keys are overwritten with the values from `headers_or_env`.
|
110
112
|
def merge!(headers_or_env)
|
111
113
|
headers_or_env.each do |key, value|
|
112
114
|
@req.set_header env_name(key), value
|
@@ -116,8 +118,8 @@ module ActionDispatch
|
|
116
118
|
def env; @req.env.dup; end
|
117
119
|
|
118
120
|
private
|
119
|
-
# Converts an HTTP header name to an environment variable name if it is
|
120
|
-
#
|
121
|
+
# Converts an HTTP header name to an environment variable name if it is not
|
122
|
+
# contained within the headers hash.
|
121
123
|
def env_name(key)
|
122
124
|
key = key.to_s
|
123
125
|
if HTTP_HEADER.match?(key)
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
require "active_support/core_ext/module/attribute_accessors"
|
4
6
|
|
5
7
|
module ActionDispatch
|
@@ -16,10 +18,9 @@ module ActionDispatch
|
|
16
18
|
|
17
19
|
included do
|
18
20
|
mattr_accessor :ignore_accept_header, default: false
|
19
|
-
cattr_accessor :return_only_media_type_on_content_type, default: false
|
20
21
|
end
|
21
22
|
|
22
|
-
# The MIME type of the HTTP request, such as Mime
|
23
|
+
# The MIME type of the HTTP request, such as [Mime](:xml).
|
23
24
|
def content_mime_type
|
24
25
|
fetch_header("action_dispatch.request.content_type") do |k|
|
25
26
|
v = if get_header("CONTENT_TYPE") =~ /^([^,;]*)/
|
@@ -33,19 +34,6 @@ module ActionDispatch
|
|
33
34
|
end
|
34
35
|
end
|
35
36
|
|
36
|
-
def content_type
|
37
|
-
if self.class.return_only_media_type_on_content_type
|
38
|
-
ActiveSupport::Deprecation.warn(
|
39
|
-
"Rails 7.1 will return Content-Type header without modification." \
|
40
|
-
" If you want just the MIME type, please use `#media_type` instead."
|
41
|
-
)
|
42
|
-
|
43
|
-
content_mime_type&.to_s
|
44
|
-
else
|
45
|
-
super
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
37
|
def has_content_type? # :nodoc:
|
50
38
|
get_header "CONTENT_TYPE"
|
51
39
|
end
|
@@ -66,13 +54,13 @@ module ActionDispatch
|
|
66
54
|
end
|
67
55
|
end
|
68
56
|
|
69
|
-
# Returns the MIME type for the
|
57
|
+
# Returns the MIME type for the format used in the request.
|
70
58
|
#
|
71
|
-
#
|
72
|
-
#
|
73
|
-
#
|
59
|
+
# GET /posts/5.xml | request.format => Mime[:xml]
|
60
|
+
# GET /posts/5.xhtml | request.format => Mime[:html]
|
61
|
+
# GET /posts/5 | request.format => Mime[:html] or Mime[:js], or request.accepts.first
|
74
62
|
#
|
75
|
-
def format(
|
63
|
+
def format(_view_path = nil)
|
76
64
|
formats.first || Mime::NullType.instance
|
77
65
|
end
|
78
66
|
|
@@ -81,7 +69,7 @@ module ActionDispatch
|
|
81
69
|
v = if params_readable?
|
82
70
|
Array(Mime[parameters[:format]])
|
83
71
|
elsif use_accept_header && valid_accept_header
|
84
|
-
accepts
|
72
|
+
accepts.dup
|
85
73
|
elsif extension_format = format_from_path_extension
|
86
74
|
[extension_format]
|
87
75
|
elsif xhr?
|
@@ -90,7 +78,7 @@ module ActionDispatch
|
|
90
78
|
[Mime[:html]]
|
91
79
|
end
|
92
80
|
|
93
|
-
v
|
81
|
+
v.select! do |format|
|
94
82
|
format.symbol || format.ref == "*/*"
|
95
83
|
end
|
96
84
|
|
@@ -98,7 +86,7 @@ module ActionDispatch
|
|
98
86
|
end
|
99
87
|
end
|
100
88
|
|
101
|
-
# Sets the
|
89
|
+
# Sets the variant for template.
|
102
90
|
def variant=(variant)
|
103
91
|
variant = Array(variant)
|
104
92
|
|
@@ -113,36 +101,37 @@ module ActionDispatch
|
|
113
101
|
@variant ||= ActiveSupport::ArrayInquirer.new
|
114
102
|
end
|
115
103
|
|
116
|
-
# Sets the
|
104
|
+
# Sets the format by string extension, which can be used to force custom formats
|
117
105
|
# that are not controlled by the extension.
|
118
106
|
#
|
119
|
-
#
|
120
|
-
#
|
107
|
+
# class ApplicationController < ActionController::Base
|
108
|
+
# before_action :adjust_format_for_iphone
|
121
109
|
#
|
122
|
-
#
|
123
|
-
#
|
124
|
-
#
|
125
|
-
#
|
126
|
-
#
|
110
|
+
# private
|
111
|
+
# def adjust_format_for_iphone
|
112
|
+
# request.format = :iphone if request.env["HTTP_USER_AGENT"][/iPhone/]
|
113
|
+
# end
|
114
|
+
# end
|
127
115
|
def format=(extension)
|
128
116
|
parameters[:format] = extension.to_s
|
129
117
|
set_header "action_dispatch.request.formats", [Mime::Type.lookup_by_extension(parameters[:format])]
|
130
118
|
end
|
131
119
|
|
132
|
-
# Sets the
|
133
|
-
# to set multiple, ordered formats, which is useful when you want to have a
|
120
|
+
# Sets the formats by string extensions. This differs from #format= by allowing
|
121
|
+
# you to set multiple, ordered formats, which is useful when you want to have a
|
122
|
+
# fallback.
|
134
123
|
#
|
135
|
-
# In this example, the
|
136
|
-
# to the
|
124
|
+
# In this example, the `:iphone` format will be used if it's available,
|
125
|
+
# otherwise it'll fall back to the `:html` format.
|
137
126
|
#
|
138
|
-
#
|
139
|
-
#
|
127
|
+
# class ApplicationController < ActionController::Base
|
128
|
+
# before_action :adjust_format_for_iphone_with_html_fallback
|
140
129
|
#
|
141
|
-
#
|
142
|
-
#
|
143
|
-
#
|
144
|
-
#
|
145
|
-
#
|
130
|
+
# private
|
131
|
+
# def adjust_format_for_iphone_with_html_fallback
|
132
|
+
# request.formats = [ :iphone, :html ] if request.env["HTTP_USER_AGENT"][/iPhone/]
|
133
|
+
# end
|
134
|
+
# end
|
146
135
|
def formats=(extensions)
|
147
136
|
parameters[:format] = extensions.first.to_s
|
148
137
|
set_header "action_dispatch.request.formats", extensions.collect { |extension|
|
@@ -168,26 +157,26 @@ module ActionDispatch
|
|
168
157
|
end
|
169
158
|
|
170
159
|
private
|
171
|
-
# We use normal content negotiation unless you include
|
172
|
-
#
|
160
|
+
# We use normal content negotiation unless you include **/** in your list, in
|
161
|
+
# which case we assume you're a browser and send HTML.
|
173
162
|
BROWSER_LIKE_ACCEPTS = /,\s*\*\/\*|\*\/\*\s*,/
|
174
163
|
|
175
|
-
def params_readable?
|
164
|
+
def params_readable?
|
176
165
|
parameters[:format]
|
177
166
|
rescue *RESCUABLE_MIME_FORMAT_ERRORS
|
178
167
|
false
|
179
168
|
end
|
180
169
|
|
181
|
-
def valid_accept_header
|
170
|
+
def valid_accept_header
|
182
171
|
(xhr? && (accept.present? || content_mime_type)) ||
|
183
172
|
(accept.present? && !accept.match?(BROWSER_LIKE_ACCEPTS))
|
184
173
|
end
|
185
174
|
|
186
|
-
def use_accept_header
|
175
|
+
def use_accept_header
|
187
176
|
!self.class.ignore_accept_header
|
188
177
|
end
|
189
178
|
|
190
|
-
def format_from_path_extension
|
179
|
+
def format_from_path_extension
|
191
180
|
path = get_header("action_dispatch.original_path") || get_header("PATH_INFO")
|
192
181
|
if match = path && path.match(/\.(\w+)\z/)
|
193
182
|
Mime[match.captures.first]
|