actionpack 5.2.4.4 → 6.1.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.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +264 -322
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -3
- data/lib/abstract_controller.rb +1 -0
- data/lib/abstract_controller/base.rb +38 -4
- data/lib/abstract_controller/caching.rb +1 -1
- data/lib/abstract_controller/caching/fragments.rb +6 -22
- data/lib/abstract_controller/callbacks.rb +14 -2
- data/lib/abstract_controller/collector.rb +1 -2
- data/lib/abstract_controller/helpers.rb +106 -90
- data/lib/abstract_controller/railties/routes_helpers.rb +1 -1
- data/lib/abstract_controller/rendering.rb +9 -9
- data/lib/abstract_controller/translation.rb +11 -5
- data/lib/action_controller.rb +7 -4
- data/lib/action_controller/api.rb +4 -3
- data/lib/action_controller/base.rb +6 -9
- data/lib/action_controller/caching.rb +1 -3
- data/lib/action_controller/log_subscriber.rb +10 -7
- data/lib/action_controller/metal.rb +10 -8
- data/lib/action_controller/metal/basic_implicit_render.rb +1 -1
- data/lib/action_controller/metal/conditional_get.rb +19 -5
- data/lib/action_controller/metal/content_security_policy.rb +1 -2
- data/lib/action_controller/metal/cookies.rb +3 -1
- data/lib/action_controller/metal/data_streaming.rb +6 -7
- data/lib/action_controller/metal/default_headers.rb +17 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +3 -5
- data/lib/action_controller/metal/exceptions.rb +56 -2
- data/lib/action_controller/metal/flash.rb +5 -5
- data/lib/action_controller/metal/head.rb +7 -4
- data/lib/action_controller/metal/helpers.rb +14 -5
- data/lib/action_controller/metal/http_authentication.rb +24 -23
- data/lib/action_controller/metal/implicit_render.rb +5 -15
- data/lib/action_controller/metal/instrumentation.rb +13 -14
- data/lib/action_controller/metal/live.rb +30 -32
- data/lib/action_controller/metal/logging.rb +20 -0
- data/lib/action_controller/metal/mime_responds.rb +19 -4
- data/lib/action_controller/metal/parameter_encoding.rb +35 -4
- data/lib/action_controller/metal/params_wrapper.rb +31 -22
- data/lib/action_controller/metal/permissions_policy.rb +46 -0
- data/lib/action_controller/metal/redirecting.rb +6 -6
- data/lib/action_controller/metal/renderers.rb +4 -4
- data/lib/action_controller/metal/rendering.rb +8 -3
- data/lib/action_controller/metal/request_forgery_protection.rb +62 -34
- data/lib/action_controller/metal/rescue.rb +1 -1
- data/lib/action_controller/metal/streaming.rb +0 -1
- data/lib/action_controller/metal/strong_parameters.rb +167 -58
- data/lib/action_controller/metal/url_for.rb +1 -1
- data/lib/action_controller/railties/helpers.rb +1 -1
- data/lib/action_controller/renderer.rb +37 -13
- data/lib/action_controller/template_assertions.rb +1 -1
- data/lib/action_controller/test_case.rb +70 -65
- data/lib/action_dispatch.rb +9 -3
- data/lib/action_dispatch/http/cache.rb +26 -21
- data/lib/action_dispatch/http/content_disposition.rb +45 -0
- data/lib/action_dispatch/http/content_security_policy.rb +33 -19
- data/lib/action_dispatch/http/filter_parameters.rb +9 -8
- data/lib/action_dispatch/http/filter_redirect.rb +2 -3
- data/lib/action_dispatch/http/headers.rb +4 -4
- data/lib/action_dispatch/http/mime_negotiation.rb +26 -13
- data/lib/action_dispatch/http/mime_type.rb +42 -23
- data/lib/action_dispatch/http/parameters.rb +14 -23
- data/lib/action_dispatch/http/permissions_policy.rb +173 -0
- data/lib/action_dispatch/http/request.rb +45 -22
- data/lib/action_dispatch/http/response.rb +45 -25
- data/lib/action_dispatch/http/upload.rb +9 -1
- data/lib/action_dispatch/http/url.rb +82 -82
- data/lib/action_dispatch/journey.rb +0 -2
- data/lib/action_dispatch/journey/formatter.rb +54 -30
- data/lib/action_dispatch/journey/gtg/builder.rb +22 -37
- data/lib/action_dispatch/journey/gtg/simulator.rb +8 -7
- data/lib/action_dispatch/journey/gtg/transition_table.rb +6 -5
- data/lib/action_dispatch/journey/nfa/dot.rb +0 -11
- data/lib/action_dispatch/journey/nodes/node.rb +13 -11
- data/lib/action_dispatch/journey/parser.rb +13 -13
- data/lib/action_dispatch/journey/parser.y +1 -1
- data/lib/action_dispatch/journey/path/pattern.rb +19 -21
- data/lib/action_dispatch/journey/route.rb +10 -20
- data/lib/action_dispatch/journey/router.rb +26 -34
- data/lib/action_dispatch/journey/router/utils.rb +14 -12
- data/lib/action_dispatch/journey/routes.rb +0 -2
- data/lib/action_dispatch/journey/scanner.rb +10 -4
- data/lib/action_dispatch/journey/visitors.rb +1 -4
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
- data/lib/action_dispatch/middleware/callbacks.rb +2 -4
- data/lib/action_dispatch/middleware/cookies.rb +128 -109
- data/lib/action_dispatch/middleware/debug_exceptions.rb +43 -66
- data/lib/action_dispatch/middleware/debug_locks.rb +5 -5
- data/lib/action_dispatch/middleware/debug_view.rb +66 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +75 -30
- data/lib/action_dispatch/middleware/flash.rb +1 -1
- data/lib/action_dispatch/middleware/host_authorization.rb +121 -0
- data/lib/action_dispatch/middleware/public_exceptions.rb +6 -3
- data/lib/action_dispatch/middleware/remote_ip.rb +14 -16
- data/lib/action_dispatch/middleware/request_id.rb +5 -6
- data/lib/action_dispatch/middleware/session/abstract_store.rb +2 -3
- data/lib/action_dispatch/middleware/session/cookie_store.rb +3 -9
- data/lib/action_dispatch/middleware/show_exceptions.rb +3 -2
- data/lib/action_dispatch/middleware/ssl.rb +20 -15
- data/lib/action_dispatch/middleware/stack.rb +56 -2
- data/lib/action_dispatch/middleware/static.rb +153 -93
- data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
- data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
- data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +3 -1
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +4 -2
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +45 -35
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -0
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +23 -4
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +6 -3
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +3 -1
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +104 -8
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +24 -1
- data/lib/action_dispatch/railtie.rb +8 -2
- data/lib/action_dispatch/request/session.rb +10 -9
- data/lib/action_dispatch/request/utils.rb +26 -2
- data/lib/action_dispatch/routing.rb +21 -20
- data/lib/action_dispatch/routing/inspector.rb +100 -52
- data/lib/action_dispatch/routing/mapper.rb +155 -103
- data/lib/action_dispatch/routing/polymorphic_routes.rb +13 -15
- data/lib/action_dispatch/routing/redirection.rb +3 -3
- data/lib/action_dispatch/routing/route_set.rb +71 -69
- data/lib/action_dispatch/routing/url_for.rb +2 -2
- data/lib/action_dispatch/system_test_case.rb +54 -11
- data/lib/action_dispatch/system_testing/browser.rb +53 -16
- data/lib/action_dispatch/system_testing/driver.rb +11 -3
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +49 -7
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +8 -10
- data/lib/action_dispatch/testing/assertion_response.rb +0 -1
- data/lib/action_dispatch/testing/assertions.rb +1 -1
- data/lib/action_dispatch/testing/assertions/response.rb +4 -7
- data/lib/action_dispatch/testing/assertions/routing.rb +20 -8
- data/lib/action_dispatch/testing/integration.rb +61 -28
- data/lib/action_dispatch/testing/request_encoder.rb +2 -2
- data/lib/action_dispatch/testing/test_process.rb +29 -4
- data/lib/action_dispatch/testing/test_request.rb +3 -3
- data/lib/action_dispatch/testing/test_response.rb +4 -32
- data/lib/action_pack.rb +1 -1
- data/lib/action_pack/gem_version.rb +4 -4
- metadata +38 -26
- data/lib/action_controller/metal/force_ssl.rb +0 -99
- data/lib/action_dispatch/http/parameter_filter.rb +0 -86
- data/lib/action_dispatch/journey/nfa/builder.rb +0 -78
- data/lib/action_dispatch/journey/nfa/simulator.rb +0 -49
- data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -120
- data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +0 -26
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionDispatch
|
4
|
+
module Http
|
5
|
+
class ContentDisposition # :nodoc:
|
6
|
+
def self.format(disposition:, filename:)
|
7
|
+
new(disposition: disposition, filename: filename).to_s
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_reader :disposition, :filename
|
11
|
+
|
12
|
+
def initialize(disposition:, filename:)
|
13
|
+
@disposition = disposition
|
14
|
+
@filename = filename
|
15
|
+
end
|
16
|
+
|
17
|
+
TRADITIONAL_ESCAPED_CHAR = /[^ A-Za-z0-9!\#$+.^_`|~-]/
|
18
|
+
|
19
|
+
def ascii_filename
|
20
|
+
'filename="' + percent_escape(I18n.transliterate(filename), TRADITIONAL_ESCAPED_CHAR) + '"'
|
21
|
+
end
|
22
|
+
|
23
|
+
RFC_5987_ESCAPED_CHAR = /[^A-Za-z0-9!\#$&+.^_`|~-]/
|
24
|
+
|
25
|
+
def utf8_filename
|
26
|
+
"filename*=UTF-8''" + percent_escape(filename, RFC_5987_ESCAPED_CHAR)
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_s
|
30
|
+
if filename
|
31
|
+
"#{disposition}; #{ascii_filename}; #{utf8_filename}"
|
32
|
+
else
|
33
|
+
"#{disposition}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
def percent_escape(string, pattern)
|
39
|
+
string.gsub(pattern) do |char|
|
40
|
+
char.bytes.map { |byte| "%%%02X" % byte }.join
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -5,9 +5,9 @@ require "active_support/core_ext/object/deep_dup"
|
|
5
5
|
module ActionDispatch #:nodoc:
|
6
6
|
class ContentSecurityPolicy
|
7
7
|
class Middleware
|
8
|
-
CONTENT_TYPE = "Content-Type"
|
9
|
-
POLICY = "Content-Security-Policy"
|
10
|
-
POLICY_REPORT_ONLY = "Content-Security-Policy-Report-Only"
|
8
|
+
CONTENT_TYPE = "Content-Type"
|
9
|
+
POLICY = "Content-Security-Policy"
|
10
|
+
POLICY_REPORT_ONLY = "Content-Security-Policy-Report-Only"
|
11
11
|
|
12
12
|
def initialize(app)
|
13
13
|
@app = app
|
@@ -22,18 +22,18 @@ module ActionDispatch #:nodoc:
|
|
22
22
|
|
23
23
|
if policy = request.content_security_policy
|
24
24
|
nonce = request.content_security_policy_nonce
|
25
|
+
nonce_directives = request.content_security_policy_nonce_directives
|
25
26
|
context = request.controller_instance || request
|
26
|
-
headers[header_name(request)] = policy.build(context, nonce)
|
27
|
+
headers[header_name(request)] = policy.build(context, nonce, nonce_directives)
|
27
28
|
end
|
28
29
|
|
29
30
|
response
|
30
31
|
end
|
31
32
|
|
32
33
|
private
|
33
|
-
|
34
34
|
def html_response?(headers)
|
35
35
|
if content_type = headers[CONTENT_TYPE]
|
36
|
-
|
36
|
+
/html/.match?(content_type)
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
@@ -51,10 +51,11 @@ module ActionDispatch #:nodoc:
|
|
51
51
|
end
|
52
52
|
|
53
53
|
module Request
|
54
|
-
POLICY = "action_dispatch.content_security_policy"
|
55
|
-
POLICY_REPORT_ONLY = "action_dispatch.content_security_policy_report_only"
|
56
|
-
NONCE_GENERATOR = "action_dispatch.content_security_policy_nonce_generator"
|
57
|
-
NONCE = "action_dispatch.content_security_policy_nonce"
|
54
|
+
POLICY = "action_dispatch.content_security_policy"
|
55
|
+
POLICY_REPORT_ONLY = "action_dispatch.content_security_policy_report_only"
|
56
|
+
NONCE_GENERATOR = "action_dispatch.content_security_policy_nonce_generator"
|
57
|
+
NONCE = "action_dispatch.content_security_policy_nonce"
|
58
|
+
NONCE_DIRECTIVES = "action_dispatch.content_security_policy_nonce_directives"
|
58
59
|
|
59
60
|
def content_security_policy
|
60
61
|
get_header(POLICY)
|
@@ -80,6 +81,14 @@ module ActionDispatch #:nodoc:
|
|
80
81
|
set_header(NONCE_GENERATOR, generator)
|
81
82
|
end
|
82
83
|
|
84
|
+
def content_security_policy_nonce_directives
|
85
|
+
get_header(NONCE_DIRECTIVES)
|
86
|
+
end
|
87
|
+
|
88
|
+
def content_security_policy_nonce_directives=(generator)
|
89
|
+
set_header(NONCE_DIRECTIVES, generator)
|
90
|
+
end
|
91
|
+
|
83
92
|
def content_security_policy_nonce
|
84
93
|
if content_security_policy_nonce_generator
|
85
94
|
if nonce = get_header(NONCE)
|
@@ -91,7 +100,6 @@ module ActionDispatch #:nodoc:
|
|
91
100
|
end
|
92
101
|
|
93
102
|
private
|
94
|
-
|
95
103
|
def generate_content_security_policy_nonce
|
96
104
|
content_security_policy_nonce_generator.call(self)
|
97
105
|
end
|
@@ -127,14 +135,19 @@ module ActionDispatch #:nodoc:
|
|
127
135
|
manifest_src: "manifest-src",
|
128
136
|
media_src: "media-src",
|
129
137
|
object_src: "object-src",
|
138
|
+
prefetch_src: "prefetch-src",
|
130
139
|
script_src: "script-src",
|
140
|
+
script_src_attr: "script-src-attr",
|
141
|
+
script_src_elem: "script-src-elem",
|
131
142
|
style_src: "style-src",
|
143
|
+
style_src_attr: "style-src-attr",
|
144
|
+
style_src_elem: "style-src-elem",
|
132
145
|
worker_src: "worker-src"
|
133
146
|
}.freeze
|
134
147
|
|
135
|
-
|
148
|
+
DEFAULT_NONCE_DIRECTIVES = %w[script-src style-src].freeze
|
136
149
|
|
137
|
-
private_constant :MAPPINGS, :DIRECTIVES, :
|
150
|
+
private_constant :MAPPINGS, :DIRECTIVES, :DEFAULT_NONCE_DIRECTIVES
|
138
151
|
|
139
152
|
attr_reader :directives
|
140
153
|
|
@@ -203,8 +216,9 @@ module ActionDispatch #:nodoc:
|
|
203
216
|
end
|
204
217
|
end
|
205
218
|
|
206
|
-
def build(context = nil, nonce = nil)
|
207
|
-
|
219
|
+
def build(context = nil, nonce = nil, nonce_directives = nil)
|
220
|
+
nonce_directives = DEFAULT_NONCE_DIRECTIVES if nonce_directives.nil?
|
221
|
+
build_directives(context, nonce, nonce_directives).compact.join("; ")
|
208
222
|
end
|
209
223
|
|
210
224
|
private
|
@@ -227,10 +241,10 @@ module ActionDispatch #:nodoc:
|
|
227
241
|
end
|
228
242
|
end
|
229
243
|
|
230
|
-
def build_directives(context, nonce)
|
244
|
+
def build_directives(context, nonce, nonce_directives)
|
231
245
|
@directives.map do |directive, sources|
|
232
246
|
if sources.is_a?(Array)
|
233
|
-
if nonce && nonce_directive?(directive)
|
247
|
+
if nonce && nonce_directive?(directive, nonce_directives)
|
234
248
|
"#{directive} #{build_directive(sources, context).join(' ')} 'nonce-#{nonce}'"
|
235
249
|
else
|
236
250
|
"#{directive} #{build_directive(sources, context).join(' ')}"
|
@@ -265,8 +279,8 @@ module ActionDispatch #:nodoc:
|
|
265
279
|
end
|
266
280
|
end
|
267
281
|
|
268
|
-
def nonce_directive?(directive)
|
269
|
-
|
282
|
+
def nonce_directive?(directive, nonce_directives)
|
283
|
+
nonce_directives.include?(directive)
|
270
284
|
end
|
271
285
|
end
|
272
286
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "
|
3
|
+
require "active_support/parameter_filter"
|
4
4
|
|
5
5
|
module ActionDispatch
|
6
6
|
module Http
|
@@ -9,8 +9,8 @@ module ActionDispatch
|
|
9
9
|
# sub-hashes of the params hash to filter. Filtering only certain sub-keys
|
10
10
|
# from a hash is possible by using the dot notation: 'credit_card.number'.
|
11
11
|
# If a block is given, each key and value of the params hash and all
|
12
|
-
# sub-hashes
|
13
|
-
# String#replace or similar
|
12
|
+
# sub-hashes are passed to it, where the value or the key can be replaced using
|
13
|
+
# String#replace or similar methods.
|
14
14
|
#
|
15
15
|
# env["action_dispatch.parameter_filter"] = [:password]
|
16
16
|
# => replaces the value to all keys matching /password/i with "[FILTERED]"
|
@@ -23,13 +23,13 @@ module ActionDispatch
|
|
23
23
|
# change { file: { code: "xxxx"} }
|
24
24
|
#
|
25
25
|
# env["action_dispatch.parameter_filter"] = -> (k, v) do
|
26
|
-
# v.reverse! if k
|
26
|
+
# v.reverse! if k.match?(/secret/i)
|
27
27
|
# end
|
28
28
|
# => reverses the value to all keys matching /secret/i
|
29
29
|
module FilterParameters
|
30
30
|
ENV_MATCH = [/RAW_POST_DATA/, "rack.request.form_vars"] # :nodoc:
|
31
|
-
NULL_PARAM_FILTER = ParameterFilter.new # :nodoc:
|
32
|
-
NULL_ENV_FILTER = ParameterFilter.new ENV_MATCH # :nodoc:
|
31
|
+
NULL_PARAM_FILTER = ActiveSupport::ParameterFilter.new # :nodoc:
|
32
|
+
NULL_ENV_FILTER = ActiveSupport::ParameterFilter.new ENV_MATCH # :nodoc:
|
33
33
|
|
34
34
|
def initialize
|
35
35
|
super
|
@@ -41,6 +41,8 @@ module ActionDispatch
|
|
41
41
|
# Returns a hash of parameters with all sensitive data replaced.
|
42
42
|
def filtered_parameters
|
43
43
|
@filtered_parameters ||= parameter_filter.filter(parameters)
|
44
|
+
rescue ActionDispatch::Http::Parameters::ParseError
|
45
|
+
@filtered_parameters = {}
|
44
46
|
end
|
45
47
|
|
46
48
|
# Returns a hash of request.env with all sensitive data replaced.
|
@@ -54,7 +56,6 @@ module ActionDispatch
|
|
54
56
|
end
|
55
57
|
|
56
58
|
private
|
57
|
-
|
58
59
|
def parameter_filter # :doc:
|
59
60
|
parameter_filter_for fetch_header("action_dispatch.parameter_filter") {
|
60
61
|
return NULL_PARAM_FILTER
|
@@ -69,7 +70,7 @@ module ActionDispatch
|
|
69
70
|
end
|
70
71
|
|
71
72
|
def parameter_filter_for(filters) # :doc:
|
72
|
-
ParameterFilter.new(filters)
|
73
|
+
ActiveSupport::ParameterFilter.new(filters)
|
73
74
|
end
|
74
75
|
|
75
76
|
KV_RE = "[^&;=]+"
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module ActionDispatch
|
4
4
|
module Http
|
5
5
|
module FilterRedirect
|
6
|
-
FILTERED = "[FILTERED]"
|
6
|
+
FILTERED = "[FILTERED]" # :nodoc:
|
7
7
|
|
8
8
|
def filtered_location # :nodoc:
|
9
9
|
if location_filter_match?
|
@@ -14,7 +14,6 @@ module ActionDispatch
|
|
14
14
|
end
|
15
15
|
|
16
16
|
private
|
17
|
-
|
18
17
|
def location_filters
|
19
18
|
if request
|
20
19
|
request.get_header("action_dispatch.redirect_filter") || []
|
@@ -28,7 +27,7 @@ module ActionDispatch
|
|
28
27
|
if String === filter
|
29
28
|
location.include?(filter)
|
30
29
|
elsif Regexp === filter
|
31
|
-
location
|
30
|
+
location.match?(filter)
|
32
31
|
end
|
33
32
|
end
|
34
33
|
end
|
@@ -116,14 +116,14 @@ module ActionDispatch
|
|
116
116
|
def env; @req.env.dup; end
|
117
117
|
|
118
118
|
private
|
119
|
-
|
120
119
|
# Converts an HTTP header name to an environment variable name if it is
|
121
120
|
# not contained within the headers hash.
|
122
121
|
def env_name(key)
|
123
122
|
key = key.to_s
|
124
|
-
if key
|
125
|
-
key = key.upcase
|
126
|
-
key
|
123
|
+
if HTTP_HEADER.match?(key)
|
124
|
+
key = key.upcase
|
125
|
+
key.tr!("-", "_")
|
126
|
+
key.prepend("HTTP_") unless CGI_VARIABLES.include?(key)
|
127
127
|
end
|
128
128
|
key
|
129
129
|
end
|
@@ -7,6 +7,13 @@ module ActionDispatch
|
|
7
7
|
module MimeNegotiation
|
8
8
|
extend ActiveSupport::Concern
|
9
9
|
|
10
|
+
class InvalidType < ::Mime::Type::InvalidMimeType; end
|
11
|
+
|
12
|
+
RESCUABLE_MIME_FORMAT_ERRORS = [
|
13
|
+
ActionController::BadRequest,
|
14
|
+
ActionDispatch::Http::Parameters::ParseError,
|
15
|
+
]
|
16
|
+
|
10
17
|
included do
|
11
18
|
mattr_accessor :ignore_accept_header, default: false
|
12
19
|
end
|
@@ -20,6 +27,8 @@ module ActionDispatch
|
|
20
27
|
nil
|
21
28
|
end
|
22
29
|
set_header k, v
|
30
|
+
rescue ::Mime::Type::InvalidMimeType => e
|
31
|
+
raise InvalidType, e.message
|
23
32
|
end
|
24
33
|
end
|
25
34
|
|
@@ -42,6 +51,8 @@ module ActionDispatch
|
|
42
51
|
Mime::Type.parse(header)
|
43
52
|
end
|
44
53
|
set_header k, v
|
54
|
+
rescue ::Mime::Type::InvalidMimeType => e
|
55
|
+
raise InvalidType, e.message
|
45
56
|
end
|
46
57
|
end
|
47
58
|
|
@@ -57,13 +68,7 @@ module ActionDispatch
|
|
57
68
|
|
58
69
|
def formats
|
59
70
|
fetch_header("action_dispatch.request.formats") do |k|
|
60
|
-
|
61
|
-
parameters[:format]
|
62
|
-
rescue ActionController::BadRequest
|
63
|
-
false
|
64
|
-
end
|
65
|
-
|
66
|
-
v = if params_readable
|
71
|
+
v = if params_readable?
|
67
72
|
Array(Mime[parameters[:format]])
|
68
73
|
elsif use_accept_header && valid_accept_header
|
69
74
|
accepts
|
@@ -90,10 +95,7 @@ module ActionDispatch
|
|
90
95
|
if variant.all? { |v| v.is_a?(Symbol) }
|
91
96
|
@variant = ActiveSupport::ArrayInquirer.new(variant)
|
92
97
|
else
|
93
|
-
raise ArgumentError, "request.variant must be set to a Symbol or an Array of Symbols.
|
94
|
-
"For security reasons, never directly set the variant to a user-provided value, " \
|
95
|
-
"like params[:variant].to_sym. Check user-provided value against a whitelist first, " \
|
96
|
-
"then set the variant: request.variant = :tablet if params[:variant] == 'tablet'"
|
98
|
+
raise ArgumentError, "request.variant must be set to a Symbol or an Array of Symbols."
|
97
99
|
end
|
98
100
|
end
|
99
101
|
|
@@ -151,13 +153,24 @@ module ActionDispatch
|
|
151
153
|
order.include?(Mime::ALL) ? format : nil
|
152
154
|
end
|
153
155
|
|
154
|
-
|
156
|
+
def should_apply_vary_header?
|
157
|
+
!params_readable? && use_accept_header && valid_accept_header
|
158
|
+
end
|
155
159
|
|
160
|
+
private
|
161
|
+
# We use normal content negotiation unless you include */* in your list,
|
162
|
+
# in which case we assume you're a browser and send HTML.
|
156
163
|
BROWSER_LIKE_ACCEPTS = /,\s*\*\/\*|\*\/\*\s*,/
|
157
164
|
|
165
|
+
def params_readable? # :doc:
|
166
|
+
parameters[:format]
|
167
|
+
rescue *RESCUABLE_MIME_FORMAT_ERRORS
|
168
|
+
false
|
169
|
+
end
|
170
|
+
|
158
171
|
def valid_accept_header # :doc:
|
159
172
|
(xhr? && (accept.present? || content_mime_type)) ||
|
160
|
-
(accept.present? && accept
|
173
|
+
(accept.present? && !accept.match?(BROWSER_LIKE_ACCEPTS))
|
161
174
|
end
|
162
175
|
|
163
176
|
def use_accept_header # :doc:
|
@@ -1,17 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# -*- frozen-string-literal: true -*-
|
4
|
-
|
5
3
|
require "singleton"
|
6
|
-
require "active_support/core_ext/
|
4
|
+
require "active_support/core_ext/symbol/starts_ends_with"
|
7
5
|
|
8
6
|
module Mime
|
9
7
|
class Mimes
|
8
|
+
attr_reader :symbols
|
9
|
+
|
10
10
|
include Enumerable
|
11
11
|
|
12
12
|
def initialize
|
13
13
|
@mimes = []
|
14
|
-
@symbols =
|
14
|
+
@symbols = []
|
15
15
|
end
|
16
16
|
|
17
17
|
def each
|
@@ -20,15 +20,16 @@ module Mime
|
|
20
20
|
|
21
21
|
def <<(type)
|
22
22
|
@mimes << type
|
23
|
-
@symbols
|
23
|
+
@symbols << type.to_sym
|
24
24
|
end
|
25
25
|
|
26
26
|
def delete_if
|
27
|
-
@mimes.delete_if
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
27
|
+
@mimes.delete_if do |x|
|
28
|
+
if yield x
|
29
|
+
@symbols.delete(x.to_sym)
|
30
|
+
true
|
31
|
+
end
|
32
|
+
end
|
32
33
|
end
|
33
34
|
end
|
34
35
|
|
@@ -74,7 +75,7 @@ module Mime
|
|
74
75
|
def initialize(index, name, q = nil)
|
75
76
|
@index = index
|
76
77
|
@name = name
|
77
|
-
q ||= 0.0 if @name == "*/*"
|
78
|
+
q ||= 0.0 if @name == "*/*" # Default wildcard match to end of list.
|
78
79
|
@q = ((q || 1.0).to_f * 100).to_i
|
79
80
|
end
|
80
81
|
|
@@ -116,7 +117,7 @@ module Mime
|
|
116
117
|
type = list[idx]
|
117
118
|
break if type.q < app_xml.q
|
118
119
|
|
119
|
-
if type.name.
|
120
|
+
if type.name.end_with? "+xml"
|
120
121
|
list[app_xml_idx], list[idx] = list[idx], app_xml
|
121
122
|
app_xml_idx = idx
|
122
123
|
end
|
@@ -172,6 +173,7 @@ module Mime
|
|
172
173
|
def parse(accept_header)
|
173
174
|
if !accept_header.include?(",")
|
174
175
|
accept_header = accept_header.split(PARAMETER_SEPARATOR_REGEXP).first
|
176
|
+
return [] unless accept_header
|
175
177
|
parse_trailing_star(accept_header) || [Mime::Type.lookup(accept_header)].compact
|
176
178
|
else
|
177
179
|
list, index = [], 0
|
@@ -203,7 +205,7 @@ module Mime
|
|
203
205
|
# For an input of <tt>'application'</tt>, returns <tt>[Mime[:html], Mime[:js],
|
204
206
|
# Mime[:xml], Mime[:yaml], Mime[:atom], Mime[:json], Mime[:rss], Mime[:url_encoded_form]</tt>.
|
205
207
|
def parse_data_with_trailing_star(type)
|
206
|
-
Mime::SET.select { |m| m
|
208
|
+
Mime::SET.select { |m| m.match?(type) }
|
207
209
|
end
|
208
210
|
|
209
211
|
# This method is opposite of register method.
|
@@ -223,7 +225,18 @@ module Mime
|
|
223
225
|
|
224
226
|
attr_reader :hash
|
225
227
|
|
228
|
+
MIME_NAME = "[a-zA-Z0-9][a-zA-Z0-9#{Regexp.escape('!#$&-^_.+')}]{0,126}"
|
229
|
+
MIME_PARAMETER_KEY = "[a-zA-Z0-9][a-zA-Z0-9#{Regexp.escape('!#$&-^_.+')}]{0,126}"
|
230
|
+
MIME_PARAMETER_VALUE = "#{Regexp.escape('"')}?[a-zA-Z0-9][a-zA-Z0-9#{Regexp.escape('!#$&-^_.+')}]{0,126}#{Regexp.escape('"')}?"
|
231
|
+
MIME_PARAMETER = "\s*\;\s*#{MIME_PARAMETER_KEY}(?:\=#{MIME_PARAMETER_VALUE})?"
|
232
|
+
MIME_REGEXP = /\A(?:\*\/\*|#{MIME_NAME}\/(?:\*|#{MIME_NAME})(?:\s*#{MIME_PARAMETER}\s*)*)\z/
|
233
|
+
|
234
|
+
class InvalidMimeType < StandardError; end
|
235
|
+
|
226
236
|
def initialize(string, symbol = nil, synonyms = [])
|
237
|
+
unless MIME_REGEXP.match?(string)
|
238
|
+
raise InvalidMimeType, "#{string.inspect} is not a valid MIME type"
|
239
|
+
end
|
227
240
|
@symbol, @synonyms = symbol, synonyms
|
228
241
|
@string = string
|
229
242
|
@hash = [@string, @synonyms, @symbol].hash
|
@@ -273,25 +286,27 @@ module Mime
|
|
273
286
|
@synonyms.any? { |synonym| synonym.to_s =~ regexp } || @string =~ regexp
|
274
287
|
end
|
275
288
|
|
289
|
+
def match?(mime_type)
|
290
|
+
return false unless mime_type
|
291
|
+
regexp = Regexp.new(Regexp.quote(mime_type.to_s))
|
292
|
+
@synonyms.any? { |synonym| synonym.to_s.match?(regexp) } || @string.match?(regexp)
|
293
|
+
end
|
294
|
+
|
276
295
|
def html?
|
277
|
-
symbol == :html || @string
|
296
|
+
(symbol == :html) || /html/.match?(@string)
|
278
297
|
end
|
279
298
|
|
280
299
|
def all?; false; end
|
281
300
|
|
282
|
-
# TODO Change this to private once we've dropped Ruby 2.2 support.
|
283
|
-
# Workaround for Ruby 2.2 "private attribute?" warning.
|
284
301
|
protected
|
285
|
-
|
286
302
|
attr_reader :string, :synonyms
|
287
303
|
|
288
304
|
private
|
289
|
-
|
290
305
|
def to_ary; end
|
291
306
|
def to_a; end
|
292
307
|
|
293
308
|
def method_missing(method, *args)
|
294
|
-
if method.
|
309
|
+
if method.end_with?("?")
|
295
310
|
method[0..-2].downcase.to_sym == to_sym
|
296
311
|
else
|
297
312
|
super
|
@@ -299,7 +314,7 @@ module Mime
|
|
299
314
|
end
|
300
315
|
|
301
316
|
def respond_to_missing?(method, include_private = false)
|
302
|
-
|
317
|
+
method.end_with?("?") || super
|
303
318
|
end
|
304
319
|
end
|
305
320
|
|
@@ -307,7 +322,7 @@ module Mime
|
|
307
322
|
include Singleton
|
308
323
|
|
309
324
|
def initialize
|
310
|
-
super "*/*",
|
325
|
+
super "*/*", nil
|
311
326
|
end
|
312
327
|
|
313
328
|
def all?; true; end
|
@@ -326,15 +341,19 @@ module Mime
|
|
326
341
|
true
|
327
342
|
end
|
328
343
|
|
344
|
+
def to_s
|
345
|
+
""
|
346
|
+
end
|
347
|
+
|
329
348
|
def ref; end
|
330
349
|
|
331
350
|
private
|
332
351
|
def respond_to_missing?(method, _)
|
333
|
-
method.
|
352
|
+
method.end_with?("?")
|
334
353
|
end
|
335
354
|
|
336
355
|
def method_missing(method, *args)
|
337
|
-
false if method.
|
356
|
+
false if method.end_with?("?")
|
338
357
|
end
|
339
358
|
end
|
340
359
|
end
|