actionpack 6.0.6.1 → 6.1.0.rc1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +232 -354
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/lib/abstract_controller/base.rb +35 -2
- data/lib/abstract_controller/callbacks.rb +2 -2
- data/lib/abstract_controller/helpers.rb +105 -90
- data/lib/abstract_controller/rendering.rb +9 -9
- data/lib/abstract_controller/translation.rb +8 -2
- data/lib/abstract_controller.rb +1 -0
- data/lib/action_controller/api.rb +2 -2
- data/lib/action_controller/base.rb +4 -2
- data/lib/action_controller/caching.rb +0 -1
- data/lib/action_controller/log_subscriber.rb +3 -3
- data/lib/action_controller/metal/conditional_get.rb +10 -2
- data/lib/action_controller/metal/content_security_policy.rb +1 -1
- data/lib/action_controller/metal/data_streaming.rb +1 -1
- data/lib/action_controller/metal/etag_with_template_digest.rb +2 -4
- data/lib/action_controller/metal/exceptions.rb +33 -0
- data/lib/action_controller/metal/feature_policy.rb +46 -0
- data/lib/action_controller/metal/head.rb +7 -4
- data/lib/action_controller/metal/helpers.rb +11 -1
- data/lib/action_controller/metal/http_authentication.rb +5 -3
- data/lib/action_controller/metal/implicit_render.rb +1 -1
- data/lib/action_controller/metal/instrumentation.rb +11 -9
- data/lib/action_controller/metal/live.rb +1 -1
- data/lib/action_controller/metal/logging.rb +20 -0
- data/lib/action_controller/metal/mime_responds.rb +6 -2
- data/lib/action_controller/metal/parameter_encoding.rb +35 -4
- data/lib/action_controller/metal/params_wrapper.rb +16 -11
- data/lib/action_controller/metal/redirecting.rb +1 -1
- data/lib/action_controller/metal/rendering.rb +6 -0
- data/lib/action_controller/metal/request_forgery_protection.rb +1 -1
- data/lib/action_controller/metal/rescue.rb +1 -1
- data/lib/action_controller/metal/strong_parameters.rb +103 -15
- data/lib/action_controller/metal.rb +2 -2
- data/lib/action_controller/renderer.rb +23 -13
- data/lib/action_controller/test_case.rb +62 -56
- data/lib/action_controller.rb +2 -3
- data/lib/action_dispatch/http/cache.rb +12 -10
- data/lib/action_dispatch/http/content_security_policy.rb +11 -0
- data/lib/action_dispatch/http/feature_policy.rb +168 -0
- data/lib/action_dispatch/http/filter_parameters.rb +1 -1
- data/lib/action_dispatch/http/filter_redirect.rb +1 -1
- data/lib/action_dispatch/http/headers.rb +3 -2
- data/lib/action_dispatch/http/mime_negotiation.rb +14 -8
- data/lib/action_dispatch/http/mime_type.rb +29 -16
- data/lib/action_dispatch/http/parameters.rb +1 -19
- data/lib/action_dispatch/http/request.rb +24 -8
- data/lib/action_dispatch/http/response.rb +17 -16
- data/lib/action_dispatch/http/url.rb +3 -2
- data/lib/action_dispatch/journey/formatter.rb +53 -28
- data/lib/action_dispatch/journey/gtg/builder.rb +22 -36
- data/lib/action_dispatch/journey/gtg/simulator.rb +8 -7
- data/lib/action_dispatch/journey/gtg/transition_table.rb +6 -4
- data/lib/action_dispatch/journey/nfa/dot.rb +0 -11
- data/lib/action_dispatch/journey/nodes/node.rb +4 -3
- 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 +13 -18
- data/lib/action_dispatch/journey/route.rb +7 -18
- data/lib/action_dispatch/journey/router/utils.rb +6 -4
- data/lib/action_dispatch/journey/router.rb +26 -30
- data/lib/action_dispatch/journey.rb +0 -2
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +1 -1
- data/lib/action_dispatch/middleware/cookies.rb +67 -32
- data/lib/action_dispatch/middleware/debug_exceptions.rb +8 -15
- data/lib/action_dispatch/middleware/debug_view.rb +1 -1
- data/lib/action_dispatch/middleware/exception_wrapper.rb +28 -16
- data/lib/action_dispatch/middleware/executor.rb +1 -1
- data/lib/action_dispatch/middleware/host_authorization.rb +35 -35
- data/lib/action_dispatch/middleware/remote_ip.rb +5 -4
- data/lib/action_dispatch/middleware/request_id.rb +4 -5
- data/lib/action_dispatch/middleware/session/abstract_store.rb +2 -2
- data/lib/action_dispatch/middleware/session/cookie_store.rb +2 -2
- data/lib/action_dispatch/middleware/ssl.rb +9 -6
- data/lib/action_dispatch/middleware/stack.rb +18 -0
- data/lib/action_dispatch/middleware/static.rb +154 -93
- data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +18 -0
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +2 -5
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +2 -3
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +88 -8
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +12 -1
- data/lib/action_dispatch/railtie.rb +3 -2
- data/lib/action_dispatch/request/session.rb +2 -8
- data/lib/action_dispatch/request/utils.rb +26 -2
- data/lib/action_dispatch/routing/inspector.rb +8 -7
- data/lib/action_dispatch/routing/mapper.rb +102 -71
- data/lib/action_dispatch/routing/polymorphic_routes.rb +16 -19
- data/lib/action_dispatch/routing/redirection.rb +3 -3
- data/lib/action_dispatch/routing/route_set.rb +49 -41
- data/lib/action_dispatch/system_test_case.rb +29 -24
- data/lib/action_dispatch/system_testing/browser.rb +33 -27
- data/lib/action_dispatch/system_testing/driver.rb +6 -7
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +47 -6
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +4 -7
- data/lib/action_dispatch/testing/assertions/response.rb +2 -4
- data/lib/action_dispatch/testing/assertions/routing.rb +5 -5
- data/lib/action_dispatch/testing/assertions.rb +1 -1
- data/lib/action_dispatch/testing/integration.rb +38 -27
- data/lib/action_dispatch/testing/test_process.rb +29 -4
- data/lib/action_dispatch/testing/test_request.rb +3 -3
- data/lib/action_dispatch.rb +3 -2
- data/lib/action_pack/gem_version.rb +3 -3
- data/lib/action_pack.rb +1 -1
- metadata +23 -25
- data/lib/action_controller/metal/force_ssl.rb +0 -58
- data/lib/action_dispatch/http/parameter_filter.rb +0 -12
- data/lib/action_dispatch/journey/nfa/builder.rb +0 -78
- data/lib/action_dispatch/journey/nfa/simulator.rb +0 -47
- data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -119
@@ -9,17 +9,6 @@ module ActionDispatch
|
|
9
9
|
" #{from} -> #{to} [label=\"#{sym || 'ε'}\"];"
|
10
10
|
}
|
11
11
|
|
12
|
-
# memo_nodes = memos.values.flatten.map { |n|
|
13
|
-
# label = n
|
14
|
-
# if Journey::Route === n
|
15
|
-
# label = "#{n.verb.source} #{n.path.spec}"
|
16
|
-
# end
|
17
|
-
# " #{n.object_id} [label=\"#{label}\", shape=box];"
|
18
|
-
# }
|
19
|
-
# memo_edges = memos.flat_map { |k, memos|
|
20
|
-
# (memos || []).map { |v| " #{k} -> #{v.object_id};" }
|
21
|
-
# }.uniq
|
22
|
-
|
23
12
|
<<-eodot
|
24
13
|
digraph nfa {
|
25
14
|
rankdir=LR;
|
@@ -79,9 +79,10 @@ module ActionDispatch
|
|
79
79
|
attr_reader :name
|
80
80
|
|
81
81
|
DEFAULT_EXP = /[^\.\/\?]+/
|
82
|
-
|
83
|
-
|
84
|
-
|
82
|
+
GREEDY_EXP = /(.+)/
|
83
|
+
def initialize(left, regexp = DEFAULT_EXP)
|
84
|
+
super(left)
|
85
|
+
@regexp = regexp
|
85
86
|
@name = -left.tr("*:", "")
|
86
87
|
end
|
87
88
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
#
|
2
2
|
# DO NOT MODIFY!!!!
|
3
|
-
# This file is automatically generated by Racc 1.4.
|
3
|
+
# This file is automatically generated by Racc 1.4.16
|
4
4
|
# from Racc grammar file "".
|
5
5
|
#
|
6
6
|
|
@@ -135,11 +135,11 @@ Racc_debug_parser = false
|
|
135
135
|
# reduce 0 omitted
|
136
136
|
|
137
137
|
def _reduce_1(val, _values)
|
138
|
-
Cat.new(val.first, val.last)
|
138
|
+
Cat.new(val.first, val.last)
|
139
139
|
end
|
140
140
|
|
141
141
|
def _reduce_2(val, _values)
|
142
|
-
val.first
|
142
|
+
val.first
|
143
143
|
end
|
144
144
|
|
145
145
|
# reduce 3 omitted
|
@@ -151,19 +151,19 @@ end
|
|
151
151
|
# reduce 6 omitted
|
152
152
|
|
153
153
|
def _reduce_7(val, _values)
|
154
|
-
Group.new(val[1])
|
154
|
+
Group.new(val[1])
|
155
155
|
end
|
156
156
|
|
157
157
|
def _reduce_8(val, _values)
|
158
|
-
Or.new([val.first, val.last])
|
158
|
+
Or.new([val.first, val.last])
|
159
159
|
end
|
160
160
|
|
161
161
|
def _reduce_9(val, _values)
|
162
|
-
Or.new([val.first, val.last])
|
162
|
+
Or.new([val.first, val.last])
|
163
163
|
end
|
164
164
|
|
165
165
|
def _reduce_10(val, _values)
|
166
|
-
Star.new(Symbol.new(val.last))
|
166
|
+
Star.new(Symbol.new(val.last, Symbol::GREEDY_EXP))
|
167
167
|
end
|
168
168
|
|
169
169
|
# reduce 11 omitted
|
@@ -175,19 +175,19 @@ end
|
|
175
175
|
# reduce 14 omitted
|
176
176
|
|
177
177
|
def _reduce_15(val, _values)
|
178
|
-
Slash.new(val.first)
|
178
|
+
Slash.new(val.first)
|
179
179
|
end
|
180
180
|
|
181
181
|
def _reduce_16(val, _values)
|
182
|
-
Symbol.new(val.first)
|
182
|
+
Symbol.new(val.first)
|
183
183
|
end
|
184
184
|
|
185
185
|
def _reduce_17(val, _values)
|
186
|
-
Literal.new(val.first)
|
186
|
+
Literal.new(val.first)
|
187
187
|
end
|
188
188
|
|
189
189
|
def _reduce_18(val, _values)
|
190
|
-
Dot.new(val.first)
|
190
|
+
Dot.new(val.first)
|
191
191
|
end
|
192
192
|
|
193
193
|
def _reduce_none(val, _values)
|
@@ -195,5 +195,5 @@ def _reduce_none(val, _values)
|
|
195
195
|
end
|
196
196
|
|
197
197
|
end # class Parser
|
198
|
-
|
199
|
-
|
198
|
+
end # module Journey
|
199
|
+
end # module ActionDispatch
|
@@ -6,16 +6,6 @@ module ActionDispatch
|
|
6
6
|
class Pattern # :nodoc:
|
7
7
|
attr_reader :spec, :requirements, :anchored
|
8
8
|
|
9
|
-
def self.from_string(string)
|
10
|
-
build(string, {}, "/.?", true)
|
11
|
-
end
|
12
|
-
|
13
|
-
def self.build(path, requirements, separators, anchored)
|
14
|
-
parser = Journey::Parser.new
|
15
|
-
ast = parser.parse path
|
16
|
-
new ast, requirements, separators, anchored
|
17
|
-
end
|
18
|
-
|
19
9
|
def initialize(ast, requirements, separators, anchored)
|
20
10
|
@spec = ast
|
21
11
|
@requirements = requirements
|
@@ -46,11 +36,6 @@ module ActionDispatch
|
|
46
36
|
node.regexp = re if re
|
47
37
|
end
|
48
38
|
|
49
|
-
@spec.find_all(&:star?).each do |node|
|
50
|
-
node = node.left
|
51
|
-
node.regexp = @requirements[node.to_sym] || /(.+)/
|
52
|
-
end
|
53
|
-
|
54
39
|
@spec
|
55
40
|
end
|
56
41
|
|
@@ -81,7 +66,7 @@ module ActionDispatch
|
|
81
66
|
end
|
82
67
|
|
83
68
|
def visit_CAT(node)
|
84
|
-
|
69
|
+
"#{visit(node.left)}#{visit(node.right)}"
|
85
70
|
end
|
86
71
|
|
87
72
|
def visit_SYMBOL(node)
|
@@ -107,8 +92,8 @@ module ActionDispatch
|
|
107
92
|
end
|
108
93
|
|
109
94
|
def visit_STAR(node)
|
110
|
-
re = @matchers[node.left.to_sym]
|
111
|
-
"(#{re})"
|
95
|
+
re = @matchers[node.left.to_sym]
|
96
|
+
re ? "(#{re})" : "(.+)"
|
112
97
|
end
|
113
98
|
|
114
99
|
def visit_OR(node)
|
@@ -165,6 +150,10 @@ module ActionDispatch
|
|
165
150
|
end
|
166
151
|
alias :=~ :match
|
167
152
|
|
153
|
+
def match?(other)
|
154
|
+
to_regexp.match?(other)
|
155
|
+
end
|
156
|
+
|
168
157
|
def source
|
169
158
|
to_regexp.source
|
170
159
|
end
|
@@ -173,6 +162,12 @@ module ActionDispatch
|
|
173
162
|
@re ||= regexp_visitor.new(@separators, @requirements).accept spec
|
174
163
|
end
|
175
164
|
|
165
|
+
def requirements_for_missing_keys_check
|
166
|
+
@requirements_for_missing_keys_check ||= requirements.transform_values do |regex|
|
167
|
+
/\A#{regex}\Z/
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
176
171
|
private
|
177
172
|
def regexp_visitor
|
178
173
|
@anchored ? AnchoredRegexp : UnanchoredRegexp
|
@@ -13,6 +13,7 @@ module ActionDispatch
|
|
13
13
|
VERBS = %w{ DELETE GET HEAD OPTIONS LINK PATCH POST PUT TRACE UNLINK }
|
14
14
|
VERBS.each do |v|
|
15
15
|
class_eval <<-eoc, __FILE__, __LINE__ + 1
|
16
|
+
# frozen_string_literal: true
|
16
17
|
class #{v}
|
17
18
|
def self.verb; name.split("::").last; end
|
18
19
|
def self.call(req); req.#{v.downcase}?; end
|
@@ -27,7 +28,7 @@ module ActionDispatch
|
|
27
28
|
@verb = verb
|
28
29
|
end
|
29
30
|
|
30
|
-
def call(request); @verb
|
31
|
+
def call(request); @verb == request.request_method; end
|
31
32
|
end
|
32
33
|
|
33
34
|
class All
|
@@ -49,15 +50,10 @@ module ActionDispatch
|
|
49
50
|
end
|
50
51
|
end
|
51
52
|
|
52
|
-
def self.build(name, app, path, constraints, required_defaults, defaults)
|
53
|
-
request_method_match = verb_matcher(constraints.delete(:request_method))
|
54
|
-
new name, app, path, constraints, required_defaults, defaults, request_method_match, 0, {}
|
55
|
-
end
|
56
|
-
|
57
53
|
##
|
58
54
|
# +path+ is a path constraint.
|
59
55
|
# +constraints+ is a hash of constraints to be applied to this route.
|
60
|
-
def initialize(name
|
56
|
+
def initialize(name:, app: nil, path:, constraints: {}, required_defaults: [], defaults: {}, request_method_match: nil, precedence: 0, scope_options: {}, internal: false)
|
61
57
|
@name = name
|
62
58
|
@app = app
|
63
59
|
@path = path
|
@@ -92,7 +88,7 @@ module ActionDispatch
|
|
92
88
|
end
|
93
89
|
end
|
94
90
|
|
95
|
-
# Needed for `rails routes`. Picks up succinctly defined requirements
|
91
|
+
# Needed for `bin/rails routes`. Picks up succinctly defined requirements
|
96
92
|
# for a route, for example route
|
97
93
|
#
|
98
94
|
# get 'photo/:id', :controller => 'photos', :action => 'show',
|
@@ -115,18 +111,11 @@ module ActionDispatch
|
|
115
111
|
end
|
116
112
|
|
117
113
|
def score(supplied_keys)
|
118
|
-
|
119
|
-
|
120
|
-
required_keys.each do |k|
|
114
|
+
path.required_names.each do |k|
|
121
115
|
return -1 unless supplied_keys.include?(k)
|
122
116
|
end
|
123
117
|
|
124
|
-
|
125
|
-
path.names.each do |k|
|
126
|
-
score += 1 if supplied_keys.include?(k)
|
127
|
-
end
|
128
|
-
|
129
|
-
score + (required_defaults.length * 2)
|
118
|
+
(required_defaults.length * 2) + path.names.count { |k| supplied_keys.include?(k) }
|
130
119
|
end
|
131
120
|
|
132
121
|
def parts
|
@@ -153,7 +142,7 @@ module ActionDispatch
|
|
153
142
|
end
|
154
143
|
|
155
144
|
def glob?
|
156
|
-
|
145
|
+
path.spec.any?(Nodes::Star)
|
157
146
|
end
|
158
147
|
|
159
148
|
def dispatcher?
|
@@ -19,11 +19,13 @@ module ActionDispatch
|
|
19
19
|
encoding = path.encoding
|
20
20
|
path = +"/#{path}"
|
21
21
|
path.squeeze!("/")
|
22
|
-
|
23
|
-
path
|
24
|
-
|
22
|
+
|
23
|
+
unless path == "/"
|
24
|
+
path.delete_suffix!("/")
|
25
|
+
path.gsub!(/(%[a-f0-9]{2})/) { $1.upcase }
|
26
|
+
end
|
27
|
+
|
25
28
|
path.force_encoding(encoding)
|
26
|
-
path
|
27
29
|
end
|
28
30
|
|
29
31
|
# URI path and fragment escaping
|
@@ -40,11 +40,12 @@ module ActionDispatch
|
|
40
40
|
req.path_info = "/" + req.path_info unless req.path_info.start_with? "/"
|
41
41
|
end
|
42
42
|
|
43
|
-
|
44
|
-
|
43
|
+
tmp_params = set_params.merge route.defaults
|
44
|
+
parameters.each_pair { |key, val|
|
45
|
+
tmp_params[key] = val.force_encoding(::Encoding::UTF_8)
|
45
46
|
}
|
46
47
|
|
47
|
-
req.path_parameters =
|
48
|
+
req.path_parameters = tmp_params
|
48
49
|
|
49
50
|
status, headers, body = route.app.serve(req)
|
50
51
|
|
@@ -65,7 +66,8 @@ module ActionDispatch
|
|
65
66
|
find_routes(rails_req).each do |match, parameters, route|
|
66
67
|
unless route.path.anchored
|
67
68
|
rails_req.script_name = match.to_s
|
68
|
-
rails_req.path_info = match.post_match
|
69
|
+
rails_req.path_info = match.post_match
|
70
|
+
rails_req.path_info = "/" + rails_req.path_info unless rails_req.path_info.start_with? "/"
|
69
71
|
end
|
70
72
|
|
71
73
|
parameters = route.defaults.merge parameters
|
@@ -105,23 +107,24 @@ module ActionDispatch
|
|
105
107
|
end
|
106
108
|
|
107
109
|
def find_routes(req)
|
108
|
-
|
109
|
-
|
110
|
+
path_info = req.path_info
|
111
|
+
routes = filter_routes(path_info).concat custom_routes.find_all { |r|
|
112
|
+
r.path.match?(path_info)
|
110
113
|
}
|
111
114
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
end
|
115
|
+
if req.head?
|
116
|
+
routes = match_head_routes(routes, req)
|
117
|
+
else
|
118
|
+
routes.select! { |r| r.matches?(req) }
|
119
|
+
end
|
118
120
|
|
119
121
|
routes.sort_by!(&:precedence)
|
120
122
|
|
121
123
|
routes.map! { |r|
|
122
|
-
match_data = r.path.match(
|
124
|
+
match_data = r.path.match(path_info)
|
123
125
|
path_parameters = {}
|
124
|
-
match_data.names.
|
126
|
+
match_data.names.each_with_index { |name, i|
|
127
|
+
val = match_data[i + 1]
|
125
128
|
path_parameters[name.to_sym] = Utils.unescape_uri(val) if val
|
126
129
|
}
|
127
130
|
[match_data, path_parameters, r]
|
@@ -129,24 +132,17 @@ module ActionDispatch
|
|
129
132
|
end
|
130
133
|
|
131
134
|
def match_head_routes(routes, req)
|
132
|
-
|
133
|
-
head_routes
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
end
|
142
|
-
else
|
143
|
-
head_routes
|
135
|
+
head_routes = routes.select { |r| r.requires_matching_verb? && r.matches?(req) }
|
136
|
+
return head_routes unless head_routes.empty?
|
137
|
+
|
138
|
+
begin
|
139
|
+
req.request_method = "GET"
|
140
|
+
routes.select! { |r| r.matches?(req) }
|
141
|
+
routes
|
142
|
+
ensure
|
143
|
+
req.request_method = "HEAD"
|
144
144
|
end
|
145
145
|
end
|
146
|
-
|
147
|
-
def match_routes(routes, req)
|
148
|
-
routes.select { |r| r.matches?(req) }
|
149
|
-
end
|
150
146
|
end
|
151
147
|
end
|
152
148
|
end
|
@@ -24,7 +24,7 @@ module ActionDispatch
|
|
24
24
|
|
25
25
|
private
|
26
26
|
def actionable_request?(request)
|
27
|
-
request.get_header("action_dispatch.show_detailed_exceptions")
|
27
|
+
request.get_header("action_dispatch.show_detailed_exceptions") && request.post? && request.path == endpoint
|
28
28
|
end
|
29
29
|
|
30
30
|
def redirect_to(location)
|
@@ -69,6 +69,10 @@ module ActionDispatch
|
|
69
69
|
get_header Cookies::COOKIES_SERIALIZER
|
70
70
|
end
|
71
71
|
|
72
|
+
def cookies_same_site_protection
|
73
|
+
get_header(Cookies::COOKIES_SAME_SITE_PROTECTION) || Proc.new { }
|
74
|
+
end
|
75
|
+
|
72
76
|
def cookies_digest
|
73
77
|
get_header Cookies::COOKIES_DIGEST
|
74
78
|
end
|
@@ -84,11 +88,10 @@ module ActionDispatch
|
|
84
88
|
# :startdoc:
|
85
89
|
end
|
86
90
|
|
87
|
-
#
|
91
|
+
# Read and write data to cookies through ActionController#cookies.
|
88
92
|
#
|
89
|
-
#
|
90
|
-
#
|
91
|
-
# the cookie object itself back, just the value it holds.
|
93
|
+
# When reading cookie data, the data is read from the HTTP request header, Cookie.
|
94
|
+
# When writing cookie data, the data is sent out in the HTTP response header, Set-Cookie.
|
92
95
|
#
|
93
96
|
# Examples of writing:
|
94
97
|
#
|
@@ -150,8 +153,10 @@ module ActionDispatch
|
|
150
153
|
# * <tt>:domain</tt> - The domain for which this cookie applies so you can
|
151
154
|
# restrict to the domain level. If you use a schema like www.example.com
|
152
155
|
# and want to share session with user.example.com set <tt>:domain</tt>
|
153
|
-
# to <tt>:all</tt>.
|
154
|
-
#
|
156
|
+
# to <tt>:all</tt>. To support multiple domains, provide an array, and
|
157
|
+
# the first domain matching <tt>request.host</tt> will be used. Make
|
158
|
+
# sure to specify the <tt>:domain</tt> option with <tt>:all</tt> or
|
159
|
+
# <tt>Array</tt> again when deleting cookies.
|
155
160
|
#
|
156
161
|
# domain: nil # Does not set cookie domain. (default)
|
157
162
|
# domain: :all # Allow the cookie for the top most level
|
@@ -181,6 +186,7 @@ module ActionDispatch
|
|
181
186
|
COOKIES_SERIALIZER = "action_dispatch.cookies_serializer"
|
182
187
|
COOKIES_DIGEST = "action_dispatch.cookies_digest"
|
183
188
|
COOKIES_ROTATIONS = "action_dispatch.cookies_rotations"
|
189
|
+
COOKIES_SAME_SITE_PROTECTION = "action_dispatch.cookies_same_site_protection"
|
184
190
|
USE_COOKIES_WITH_METADATA = "action_dispatch.use_cookies_with_metadata"
|
185
191
|
|
186
192
|
# Cookies can typically store 4096 bytes.
|
@@ -259,6 +265,12 @@ module ActionDispatch
|
|
259
265
|
request.use_authenticated_cookie_encryption
|
260
266
|
end
|
261
267
|
|
268
|
+
def prepare_upgrade_legacy_hmac_aes_cbc_cookies?
|
269
|
+
request.secret_key_base.present? &&
|
270
|
+
request.authenticated_encrypted_cookie_salt.present? &&
|
271
|
+
!request.use_authenticated_cookie_encryption
|
272
|
+
end
|
273
|
+
|
262
274
|
def encrypted_cookie_cipher
|
263
275
|
request.encrypted_cookie_cipher || "aes-256-gcm"
|
264
276
|
end
|
@@ -286,9 +298,9 @@ module ActionDispatch
|
|
286
298
|
DOMAIN_REGEXP = /[^.]*\.([^.]*|..\...|...\...)$/
|
287
299
|
|
288
300
|
def self.build(req, cookies)
|
289
|
-
new(req)
|
290
|
-
|
291
|
-
|
301
|
+
jar = new(req)
|
302
|
+
jar.update(cookies)
|
303
|
+
jar
|
292
304
|
end
|
293
305
|
|
294
306
|
attr_reader :request
|
@@ -346,28 +358,6 @@ module ActionDispatch
|
|
346
358
|
@cookies.map { |k, v| "#{escape(k)}=#{escape(v)}" }.join "; "
|
347
359
|
end
|
348
360
|
|
349
|
-
def handle_options(options) # :nodoc:
|
350
|
-
if options[:expires].respond_to?(:from_now)
|
351
|
-
options[:expires] = options[:expires].from_now
|
352
|
-
end
|
353
|
-
|
354
|
-
options[:path] ||= "/"
|
355
|
-
|
356
|
-
if options[:domain] == :all || options[:domain] == "all"
|
357
|
-
# If there is a provided tld length then we use it otherwise default domain regexp.
|
358
|
-
domain_regexp = options[:tld_length] ? /([^.]+\.?){#{options[:tld_length]}}$/ : DOMAIN_REGEXP
|
359
|
-
|
360
|
-
# If host is not ip and matches domain regexp.
|
361
|
-
# (ip confirms to domain regexp so we explicitly check for ip)
|
362
|
-
options[:domain] = if (request.host !~ /^[\d.]+$/) && (request.host =~ domain_regexp)
|
363
|
-
".#{$&}"
|
364
|
-
end
|
365
|
-
elsif options[:domain].is_a? Array
|
366
|
-
# If host matches one of the supplied domains without a dot in front of it.
|
367
|
-
options[:domain] = options[:domain].find { |domain| request.host.include? domain.sub(/^\./, "") }
|
368
|
-
end
|
369
|
-
end
|
370
|
-
|
371
361
|
# Sets the cookie named +name+. The second argument may be the cookie's
|
372
362
|
# value or a hash of options as documented above.
|
373
363
|
def []=(name, options)
|
@@ -447,6 +437,34 @@ module ActionDispatch
|
|
447
437
|
def write_cookie?(cookie)
|
448
438
|
request.ssl? || !cookie[:secure] || always_write_cookie
|
449
439
|
end
|
440
|
+
|
441
|
+
def handle_options(options)
|
442
|
+
if options[:expires].respond_to?(:from_now)
|
443
|
+
options[:expires] = options[:expires].from_now
|
444
|
+
end
|
445
|
+
|
446
|
+
options[:path] ||= "/"
|
447
|
+
|
448
|
+
cookies_same_site_protection = request.cookies_same_site_protection
|
449
|
+
options[:same_site] ||= cookies_same_site_protection.call(request)
|
450
|
+
|
451
|
+
if options[:domain] == :all || options[:domain] == "all"
|
452
|
+
# If there is a provided tld length then we use it otherwise default domain regexp.
|
453
|
+
domain_regexp = options[:tld_length] ? /([^.]+\.?){#{options[:tld_length]}}$/ : DOMAIN_REGEXP
|
454
|
+
|
455
|
+
# If host is not ip and matches domain regexp.
|
456
|
+
# (ip confirms to domain regexp so we explicitly check for ip)
|
457
|
+
options[:domain] = if !request.host.match?(/^[\d.]+$/) && (request.host =~ domain_regexp)
|
458
|
+
".#{$&}"
|
459
|
+
end
|
460
|
+
elsif options[:domain].is_a? Array
|
461
|
+
# If host matches one of the supplied domains.
|
462
|
+
options[:domain] = options[:domain].find do |domain|
|
463
|
+
domain = domain.delete_prefix(".")
|
464
|
+
request.host == domain || request.host.end_with?(".#{domain}")
|
465
|
+
end
|
466
|
+
end
|
467
|
+
end
|
450
468
|
end
|
451
469
|
|
452
470
|
class AbstractCookieJar # :nodoc:
|
@@ -508,6 +526,18 @@ module ActionDispatch
|
|
508
526
|
end
|
509
527
|
end
|
510
528
|
|
529
|
+
class MarshalWithJsonFallback # :nodoc:
|
530
|
+
def self.load(value)
|
531
|
+
Marshal.load(value)
|
532
|
+
rescue TypeError => e
|
533
|
+
ActiveSupport::JSON.decode(value) rescue raise e
|
534
|
+
end
|
535
|
+
|
536
|
+
def self.dump(value)
|
537
|
+
Marshal.dump(value)
|
538
|
+
end
|
539
|
+
end
|
540
|
+
|
511
541
|
class JsonSerializer # :nodoc:
|
512
542
|
def self.load(value)
|
513
543
|
ActiveSupport::JSON.decode(value)
|
@@ -555,7 +585,7 @@ module ActionDispatch
|
|
555
585
|
serializer = request.cookies_serializer || :marshal
|
556
586
|
case serializer
|
557
587
|
when :marshal
|
558
|
-
|
588
|
+
MarshalWithJsonFallback
|
559
589
|
when :json, :hybrid
|
560
590
|
JsonSerializer
|
561
591
|
else
|
@@ -625,6 +655,11 @@ module ActionDispatch
|
|
625
655
|
sign_secret = request.key_generator.generate_key(request.encrypted_signed_cookie_salt)
|
626
656
|
|
627
657
|
@encryptor.rotate(secret, sign_secret, cipher: legacy_cipher, digest: digest, serializer: SERIALIZER)
|
658
|
+
elsif prepare_upgrade_legacy_hmac_aes_cbc_cookies?
|
659
|
+
future_cipher = encrypted_cookie_cipher
|
660
|
+
secret = request.key_generator.generate_key(request.authenticated_encrypted_cookie_salt, ActiveSupport::MessageEncryptor.key_len(future_cipher))
|
661
|
+
|
662
|
+
@encryptor.rotate(secret, nil, cipher: future_cipher, serializer: SERIALIZER)
|
628
663
|
end
|
629
664
|
end
|
630
665
|
|
@@ -4,10 +4,7 @@ require "action_dispatch/http/request"
|
|
4
4
|
require "action_dispatch/middleware/exception_wrapper"
|
5
5
|
require "action_dispatch/routing/inspector"
|
6
6
|
|
7
|
-
require "active_support/actionable_error"
|
8
|
-
|
9
7
|
require "action_view"
|
10
|
-
require "action_view/base"
|
11
8
|
|
12
9
|
module ActionDispatch
|
13
10
|
# This middleware is responsible for logging exceptions and
|
@@ -140,20 +137,16 @@ module ActionDispatch
|
|
140
137
|
return unless logger
|
141
138
|
|
142
139
|
exception = wrapper.exception
|
140
|
+
trace = wrapper.exception_trace
|
143
141
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
message << "#{exception.class} (#{exception.message}):"
|
151
|
-
message.concat(exception.annotated_source_code) if exception.respond_to?(:annotated_source_code)
|
152
|
-
message << " "
|
153
|
-
message.concat(trace)
|
142
|
+
message = []
|
143
|
+
message << " "
|
144
|
+
message << "#{exception.class} (#{exception.message}):"
|
145
|
+
message.concat(exception.annotated_source_code) if exception.respond_to?(:annotated_source_code)
|
146
|
+
message << " "
|
147
|
+
message.concat(trace)
|
154
148
|
|
155
|
-
|
156
|
-
end
|
149
|
+
log_array(logger, message)
|
157
150
|
end
|
158
151
|
|
159
152
|
def log_array(logger, array)
|