actionpack 6.1.7.5 → 7.1.3.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 +355 -435
- data/MIT-LICENSE +2 -1
- data/README.rdoc +6 -7
- data/lib/abstract_controller/asset_paths.rb +1 -1
- data/lib/abstract_controller/base.rb +33 -37
- data/lib/abstract_controller/caching/fragments.rb +4 -2
- data/lib/abstract_controller/caching.rb +1 -1
- data/lib/abstract_controller/callbacks.rb +50 -11
- data/lib/abstract_controller/collector.rb +2 -2
- data/lib/abstract_controller/deprecator.rb +7 -0
- data/lib/abstract_controller/error.rb +1 -1
- data/lib/abstract_controller/helpers.rb +78 -30
- data/lib/abstract_controller/logger.rb +1 -1
- data/lib/abstract_controller/railties/routes_helpers.rb +3 -16
- data/lib/abstract_controller/rendering.rb +12 -14
- data/lib/abstract_controller/translation.rb +26 -7
- data/lib/abstract_controller/url_for.rb +6 -6
- data/lib/abstract_controller.rb +6 -0
- data/lib/action_controller/api.rb +12 -10
- data/lib/action_controller/base.rb +8 -21
- data/lib/action_controller/caching.rb +2 -0
- data/lib/action_controller/deprecator.rb +7 -0
- data/lib/action_controller/form_builder.rb +4 -2
- data/lib/action_controller/log_subscriber.rb +20 -7
- data/lib/action_controller/metal/basic_implicit_render.rb +3 -1
- data/lib/action_controller/metal/conditional_get.rb +137 -102
- data/lib/action_controller/metal/content_security_policy.rb +37 -3
- data/lib/action_controller/metal/cookies.rb +1 -1
- data/lib/action_controller/metal/data_streaming.rb +25 -31
- data/lib/action_controller/metal/default_headers.rb +2 -0
- data/lib/action_controller/metal/etag_with_flash.rb +3 -1
- data/lib/action_controller/metal/etag_with_template_digest.rb +2 -0
- data/lib/action_controller/metal/exceptions.rb +27 -30
- data/lib/action_controller/metal/flash.rb +6 -2
- data/lib/action_controller/metal/head.rb +9 -7
- data/lib/action_controller/metal/helpers.rb +5 -16
- data/lib/action_controller/metal/http_authentication.rb +78 -42
- data/lib/action_controller/metal/implicit_render.rb +5 -3
- data/lib/action_controller/metal/instrumentation.rb +62 -50
- data/lib/action_controller/metal/live.rb +67 -2
- data/lib/action_controller/metal/mime_responds.rb +5 -5
- data/lib/action_controller/metal/params_wrapper.rb +24 -13
- data/lib/action_controller/metal/permissions_policy.rb +20 -29
- data/lib/action_controller/metal/redirecting.rb +96 -23
- data/lib/action_controller/metal/renderers.rb +14 -15
- data/lib/action_controller/metal/rendering.rb +121 -16
- data/lib/action_controller/metal/request_forgery_protection.rb +208 -68
- data/lib/action_controller/metal/rescue.rb +7 -4
- data/lib/action_controller/metal/streaming.rb +74 -36
- data/lib/action_controller/metal/strong_parameters.rb +254 -151
- data/lib/action_controller/metal/testing.rb +9 -2
- data/lib/action_controller/metal/url_for.rb +10 -5
- data/lib/action_controller/metal.rb +89 -34
- data/lib/action_controller/railtie.rb +66 -9
- data/lib/action_controller/renderer.rb +99 -85
- data/lib/action_controller/test_case.rb +42 -11
- data/lib/action_controller.rb +10 -6
- data/lib/action_dispatch/constants.rb +32 -0
- data/lib/action_dispatch/deprecator.rb +7 -0
- data/lib/action_dispatch/http/cache.rb +21 -16
- data/lib/action_dispatch/http/content_security_policy.rb +122 -44
- data/lib/action_dispatch/http/filter_parameters.rb +14 -23
- data/lib/action_dispatch/http/headers.rb +3 -1
- data/lib/action_dispatch/http/mime_negotiation.rb +25 -15
- data/lib/action_dispatch/http/mime_type.rb +43 -22
- data/lib/action_dispatch/http/mime_types.rb +3 -1
- data/lib/action_dispatch/http/parameters.rb +6 -6
- data/lib/action_dispatch/http/permissions_policy.rb +57 -19
- data/lib/action_dispatch/http/rack_cache.rb +2 -0
- data/lib/action_dispatch/http/request.rb +75 -51
- data/lib/action_dispatch/http/response.rb +81 -77
- data/lib/action_dispatch/http/upload.rb +15 -2
- data/lib/action_dispatch/http/url.rb +11 -19
- data/lib/action_dispatch/journey/formatter.rb +8 -2
- data/lib/action_dispatch/journey/gtg/builder.rb +11 -12
- data/lib/action_dispatch/journey/gtg/simulator.rb +10 -4
- data/lib/action_dispatch/journey/gtg/transition_table.rb +77 -21
- data/lib/action_dispatch/journey/nodes/node.rb +70 -5
- data/lib/action_dispatch/journey/path/pattern.rb +36 -27
- data/lib/action_dispatch/journey/route.rb +8 -14
- data/lib/action_dispatch/journey/router/utils.rb +2 -2
- data/lib/action_dispatch/journey/router.rb +10 -9
- data/lib/action_dispatch/journey/routes.rb +5 -5
- data/lib/action_dispatch/journey/visualizer/fsm.js +49 -24
- data/lib/action_dispatch/journey/visualizer/index.html.erb +1 -1
- data/lib/action_dispatch/log_subscriber.rb +23 -0
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +5 -7
- data/lib/action_dispatch/middleware/assume_ssl.rb +24 -0
- data/lib/action_dispatch/middleware/callbacks.rb +2 -0
- data/lib/action_dispatch/middleware/cookies.rb +97 -107
- data/lib/action_dispatch/middleware/debug_exceptions.rb +31 -28
- data/lib/action_dispatch/middleware/debug_locks.rb +7 -4
- data/lib/action_dispatch/middleware/debug_view.rb +7 -2
- data/lib/action_dispatch/middleware/exception_wrapper.rb +190 -27
- data/lib/action_dispatch/middleware/executor.rb +3 -0
- data/lib/action_dispatch/middleware/flash.rb +24 -18
- data/lib/action_dispatch/middleware/host_authorization.rb +19 -20
- data/lib/action_dispatch/middleware/public_exceptions.rb +5 -3
- data/lib/action_dispatch/middleware/reloader.rb +7 -5
- data/lib/action_dispatch/middleware/remote_ip.rb +32 -19
- data/lib/action_dispatch/middleware/request_id.rb +5 -3
- data/lib/action_dispatch/middleware/server_timing.rb +76 -0
- data/lib/action_dispatch/middleware/session/abstract_store.rb +6 -1
- data/lib/action_dispatch/middleware/session/cache_store.rb +2 -0
- data/lib/action_dispatch/middleware/session/cookie_store.rb +19 -13
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +3 -1
- data/lib/action_dispatch/middleware/show_exceptions.rb +30 -25
- data/lib/action_dispatch/middleware/ssl.rb +18 -6
- data/lib/action_dispatch/middleware/stack.rb +34 -11
- data/lib/action_dispatch/middleware/static.rb +16 -16
- data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +5 -5
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +4 -11
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +8 -1
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +10 -5
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +7 -3
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +9 -9
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +45 -18
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -15
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +6 -6
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +7 -7
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +4 -4
- 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 +64 -55
- data/lib/action_dispatch/railtie.rb +20 -4
- data/lib/action_dispatch/request/session.rb +59 -19
- data/lib/action_dispatch/request/utils.rb +8 -3
- data/lib/action_dispatch/routing/inspector.rb +55 -7
- data/lib/action_dispatch/routing/mapper.rb +117 -107
- data/lib/action_dispatch/routing/polymorphic_routes.rb +2 -0
- data/lib/action_dispatch/routing/redirection.rb +20 -8
- data/lib/action_dispatch/routing/route_set.rb +67 -27
- data/lib/action_dispatch/routing/routes_proxy.rb +11 -16
- data/lib/action_dispatch/routing/url_for.rb +29 -26
- data/lib/action_dispatch/routing.rb +12 -13
- data/lib/action_dispatch/system_test_case.rb +8 -8
- data/lib/action_dispatch/system_testing/browser.rb +20 -29
- data/lib/action_dispatch/system_testing/driver.rb +34 -18
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +35 -20
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +0 -8
- data/lib/action_dispatch/testing/assertion_response.rb +1 -1
- data/lib/action_dispatch/testing/assertions/response.rb +14 -7
- data/lib/action_dispatch/testing/assertions/routing.rb +70 -30
- data/lib/action_dispatch/testing/assertions.rb +3 -4
- data/lib/action_dispatch/testing/integration.rb +33 -25
- data/lib/action_dispatch/testing/request_encoder.rb +4 -1
- data/lib/action_dispatch/testing/test_process.rb +5 -30
- data/lib/action_dispatch/testing/test_request.rb +1 -1
- data/lib/action_dispatch/testing/test_response.rb +34 -2
- data/lib/action_dispatch.rb +38 -4
- data/lib/action_pack/gem_version.rb +4 -4
- data/lib/action_pack/version.rb +1 -1
- data/lib/action_pack.rb +1 -1
- metadata +67 -30
@@ -68,7 +68,7 @@ function highlight_state(index, color) {
|
|
68
68
|
}
|
69
69
|
|
70
70
|
function highlight_finish(index) {
|
71
|
-
svg_nodes[index].select('
|
71
|
+
svg_nodes[index].select('ellipse')
|
72
72
|
.style("fill", "while")
|
73
73
|
.transition().duration(500)
|
74
74
|
.style("fill", "blue");
|
@@ -76,54 +76,79 @@ function highlight_finish(index) {
|
|
76
76
|
|
77
77
|
function match(input) {
|
78
78
|
reset_graph();
|
79
|
-
var table
|
80
|
-
var states
|
81
|
-
var regexp_states
|
82
|
-
var string_states
|
83
|
-
var
|
79
|
+
var table = tt();
|
80
|
+
var states = [[0, null]];
|
81
|
+
var regexp_states = table['regexp_states'];
|
82
|
+
var string_states = table['string_states'];
|
83
|
+
var stdparam_states = table['stdparam_states'];
|
84
|
+
var accepting = table['accepting'];
|
85
|
+
var default_re = new RegExp("^[^.\/?]+$");
|
86
|
+
var start_index = 0;
|
84
87
|
|
85
88
|
highlight_state(0);
|
86
89
|
|
87
90
|
tokenize(input, function(token) {
|
91
|
+
var end_index = start_index + token.length;
|
92
|
+
|
88
93
|
var new_states = [];
|
89
94
|
for(var key in states) {
|
90
|
-
var
|
95
|
+
var state_parts = states[key];
|
96
|
+
var state = state_parts[0];
|
97
|
+
var previous_start = state_parts[1];
|
98
|
+
|
99
|
+
if(previous_start == null) {
|
100
|
+
if(string_states[state] && string_states[state][token]) {
|
101
|
+
var new_state = string_states[state][token];
|
102
|
+
highlight_edge(state, new_state);
|
103
|
+
highlight_state(new_state);
|
104
|
+
new_states.push([new_state, null]);
|
105
|
+
}
|
91
106
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
107
|
+
if(stdparam_states[state] && default_re.test(token)) {
|
108
|
+
for(var key in stdparam_states[state]) {
|
109
|
+
var new_state = stdparam_states[state][key];
|
110
|
+
highlight_edge(state, new_state);
|
111
|
+
highlight_state(new_state);
|
112
|
+
new_states.push([new_state, null]);
|
113
|
+
}
|
114
|
+
}
|
97
115
|
}
|
98
116
|
|
99
117
|
if(regexp_states[state]) {
|
118
|
+
var slice_start = previous_start != null ? previous_start : start_index;
|
119
|
+
|
100
120
|
for(var key in regexp_states[state]) {
|
101
121
|
var re = new RegExp("^" + key + "$");
|
102
|
-
|
122
|
+
|
123
|
+
var accumulation = input.slice(slice_start, end_index);
|
124
|
+
|
125
|
+
if(re.test(accumulation)) {
|
103
126
|
var new_state = regexp_states[state][key];
|
104
127
|
highlight_edge(state, new_state);
|
105
128
|
highlight_state(new_state);
|
106
|
-
new_states.push(new_state);
|
129
|
+
new_states.push([new_state, null]);
|
107
130
|
}
|
131
|
+
|
132
|
+
// retry the same regexp with the accumulated data either way
|
133
|
+
new_states.push([state, slice_start]);
|
108
134
|
}
|
109
135
|
}
|
110
136
|
}
|
111
137
|
|
112
|
-
if(new_states.length == 0) {
|
113
|
-
return;
|
114
|
-
}
|
115
138
|
states = new_states;
|
139
|
+
start_index = end_index;
|
116
140
|
});
|
117
141
|
|
118
142
|
for(var key in states) {
|
119
|
-
var
|
143
|
+
var state_parts = states[key];
|
144
|
+
var state = state_parts[0];
|
145
|
+
var slice_start = state_parts[1];
|
146
|
+
|
147
|
+
// we must ignore ones that are still accepting more data
|
148
|
+
if (slice_start != null) continue;
|
149
|
+
|
120
150
|
if(accepting[state]) {
|
121
|
-
|
122
|
-
if(!regexp_states[mkey] && !string_states[mkey]) {
|
123
|
-
highlight_edge(state, mkey);
|
124
|
-
highlight_finish(mkey);
|
125
|
-
}
|
126
|
-
}
|
151
|
+
highlight_finish(state);
|
127
152
|
} else {
|
128
153
|
highlight_state(state, "red");
|
129
154
|
}
|
@@ -8,7 +8,7 @@
|
|
8
8
|
<%= style %>
|
9
9
|
<% end %>
|
10
10
|
</style>
|
11
|
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.8/d3.min.js"
|
11
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.8/d3.min.js"></script>
|
12
12
|
</head>
|
13
13
|
<body>
|
14
14
|
<div id="wrapper">
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionDispatch
|
4
|
+
class LogSubscriber < ActiveSupport::LogSubscriber
|
5
|
+
def redirect(event)
|
6
|
+
payload = event.payload
|
7
|
+
|
8
|
+
info { "Redirected to #{payload[:location]}" }
|
9
|
+
|
10
|
+
info do
|
11
|
+
status = payload[:status]
|
12
|
+
|
13
|
+
message = +"Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in #{event.duration.round}ms"
|
14
|
+
message << "\n\n" if defined?(Rails.env) && Rails.env.development?
|
15
|
+
|
16
|
+
message
|
17
|
+
end
|
18
|
+
end
|
19
|
+
subscribe_log_level :redirect, :info
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
ActionDispatch::LogSubscriber.attach_to :action_dispatch
|
@@ -1,8 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "erb"
|
4
3
|
require "uri"
|
5
|
-
require "action_dispatch/http/request"
|
6
4
|
require "active_support/actionable_error"
|
7
5
|
|
8
6
|
module ActionDispatch
|
@@ -31,15 +29,15 @@ module ActionDispatch
|
|
31
29
|
uri = URI.parse location
|
32
30
|
|
33
31
|
if uri.relative? || uri.scheme == "http" || uri.scheme == "https"
|
34
|
-
body = "
|
32
|
+
body = ""
|
35
33
|
else
|
36
|
-
return [400, {
|
34
|
+
return [400, { Rack::CONTENT_TYPE => "text/plain; charset=utf-8" }, ["Invalid redirection URI"]]
|
37
35
|
end
|
38
36
|
|
39
37
|
[302, {
|
40
|
-
|
41
|
-
|
42
|
-
|
38
|
+
Rack::CONTENT_TYPE => "text/html; charset=#{Response.default_charset}",
|
39
|
+
Rack::CONTENT_LENGTH => body.bytesize.to_s,
|
40
|
+
ActionDispatch::Constants::LOCATION => location,
|
43
41
|
}, [body]]
|
44
42
|
end
|
45
43
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionDispatch
|
4
|
+
# = Action Dispatch \AssumeSSL
|
5
|
+
#
|
6
|
+
# When proxying through a load balancer that terminates SSL, the forwarded request will appear
|
7
|
+
# as though it's HTTP instead of HTTPS to the application. This makes redirects and cookie
|
8
|
+
# security target HTTP instead of HTTPS. This middleware makes the server assume that the
|
9
|
+
# proxy already terminated SSL, and that the request really is HTTPS.
|
10
|
+
class AssumeSSL
|
11
|
+
def initialize(app)
|
12
|
+
@app = app
|
13
|
+
end
|
14
|
+
|
15
|
+
def call(env)
|
16
|
+
env["HTTPS"] = "on"
|
17
|
+
env["HTTP_X_FORWARDED_PORT"] = "443"
|
18
|
+
env["HTTP_X_FORWARDED_PROTO"] = "https"
|
19
|
+
env["rack.url_scheme"] = "https"
|
20
|
+
|
21
|
+
@app.call(env)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -7,7 +7,7 @@ require "active_support/json"
|
|
7
7
|
require "rack/utils"
|
8
8
|
|
9
9
|
module ActionDispatch
|
10
|
-
|
10
|
+
module RequestCookieMethods
|
11
11
|
def cookie_jar
|
12
12
|
fetch_header("action_dispatch.cookies") do
|
13
13
|
self.cookie_jar = Cookies::CookieJar.build(self, cookies)
|
@@ -70,7 +70,7 @@ module ActionDispatch
|
|
70
70
|
end
|
71
71
|
|
72
72
|
def cookies_same_site_protection
|
73
|
-
get_header(Cookies::COOKIES_SAME_SITE_PROTECTION)
|
73
|
+
get_header(Cookies::COOKIES_SAME_SITE_PROTECTION)&.call(self)
|
74
74
|
end
|
75
75
|
|
76
76
|
def cookies_digest
|
@@ -88,10 +88,14 @@ module ActionDispatch
|
|
88
88
|
# :startdoc:
|
89
89
|
end
|
90
90
|
|
91
|
-
|
91
|
+
ActiveSupport.on_load(:action_dispatch_request) do
|
92
|
+
include RequestCookieMethods
|
93
|
+
end
|
94
|
+
|
95
|
+
# Read and write data to cookies through ActionController::Cookies#cookies.
|
92
96
|
#
|
93
97
|
# 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
|
98
|
+
# When writing cookie data, the data is sent out in the HTTP response header, +Set-Cookie+.
|
95
99
|
#
|
96
100
|
# Examples of writing:
|
97
101
|
#
|
@@ -99,7 +103,7 @@ module ActionDispatch
|
|
99
103
|
# # This cookie will be deleted when the user's browser is closed.
|
100
104
|
# cookies[:user_name] = "david"
|
101
105
|
#
|
102
|
-
# # Cookie values are String
|
106
|
+
# # Cookie values are String-based. Other data types need to be serialized.
|
103
107
|
# cookies[:lat_lon] = JSON.generate([47.68, -122.37])
|
104
108
|
#
|
105
109
|
# # Sets a cookie that expires in 1 hour.
|
@@ -135,7 +139,7 @@ module ActionDispatch
|
|
135
139
|
#
|
136
140
|
# cookies.delete :user_name
|
137
141
|
#
|
138
|
-
# Please note that if you specify a
|
142
|
+
# Please note that if you specify a +:domain+ when setting a cookie, you must also specify the domain when deleting the cookie:
|
139
143
|
#
|
140
144
|
# cookies[:name] = {
|
141
145
|
# value: 'a yummy cookie',
|
@@ -156,13 +160,18 @@ module ActionDispatch
|
|
156
160
|
# to <tt>:all</tt>. To support multiple domains, provide an array, and
|
157
161
|
# the first domain matching <tt>request.host</tt> will be used. Make
|
158
162
|
# sure to specify the <tt>:domain</tt> option with <tt>:all</tt> or
|
159
|
-
# <tt>Array</tt> again when deleting cookies.
|
163
|
+
# <tt>Array</tt> again when deleting cookies. For more flexibility you
|
164
|
+
# can set the domain on a per-request basis by specifying <tt>:domain</tt>
|
165
|
+
# with a proc.
|
160
166
|
#
|
161
167
|
# domain: nil # Does not set cookie domain. (default)
|
162
168
|
# domain: :all # Allow the cookie for the top most level
|
163
169
|
# # domain and subdomains.
|
164
170
|
# domain: %w(.example.com .example.org) # Allow the cookie
|
165
171
|
# # for concrete domain names.
|
172
|
+
# domain: proc { Tenant.current.cookie_domain } # Set cookie domain dynamically
|
173
|
+
# domain: proc { |req| ".sub.#{req.host}" } # Set cookie domain dynamically based on request
|
174
|
+
#
|
166
175
|
#
|
167
176
|
# * <tt>:tld_length</tt> - When using <tt>:domain => :all</tt>, this option can be used to explicitly
|
168
177
|
# set the TLD length when using a short (<= 3 character) domain that is being interpreted as part of a TLD.
|
@@ -172,6 +181,10 @@ module ActionDispatch
|
|
172
181
|
# Default is +false+.
|
173
182
|
# * <tt>:httponly</tt> - Whether this cookie is accessible via scripting or
|
174
183
|
# only HTTP. Defaults to +false+.
|
184
|
+
# * <tt>:same_site</tt> - The value of the +SameSite+ cookie attribute, which
|
185
|
+
# determines how this cookie should be restricted in cross-site contexts.
|
186
|
+
# Possible values are +nil+, +:none+, +:lax+, and +:strict+. Defaults to
|
187
|
+
# +:lax+.
|
175
188
|
class Cookies
|
176
189
|
HTTP_HEADER = "Set-Cookie"
|
177
190
|
GENERATOR_KEY = "action_dispatch.key_generator"
|
@@ -195,7 +208,7 @@ module ActionDispatch
|
|
195
208
|
# Raised when storing more than 4K of session data.
|
196
209
|
CookieOverflow = Class.new StandardError
|
197
210
|
|
198
|
-
# Include in a cookie jar to allow chaining, e.g. cookies.permanent.signed
|
211
|
+
# Include in a cookie jar to allow chaining, e.g. +cookies.permanent.signed+.
|
199
212
|
module ChainedCookieJars
|
200
213
|
# Returns a jar that'll automatically set the assigned cookies to have an expiration date 20 years from now. Example:
|
201
214
|
#
|
@@ -280,7 +293,7 @@ module ActionDispatch
|
|
280
293
|
end
|
281
294
|
end
|
282
295
|
|
283
|
-
class CookieJar
|
296
|
+
class CookieJar # :nodoc:
|
284
297
|
include Enumerable, ChainedCookieJars
|
285
298
|
|
286
299
|
def self.build(req, cookies)
|
@@ -369,6 +382,8 @@ module ActionDispatch
|
|
369
382
|
# Removes the cookie on the client machine by setting the value to an empty string
|
370
383
|
# and the expiration date in the past. Like <tt>[]=</tt>, you can pass in
|
371
384
|
# an options hash to delete cookies with extra data such as a <tt>:path</tt>.
|
385
|
+
#
|
386
|
+
# Returns the value of the cookie, or +nil+ if the cookie does not exist.
|
372
387
|
def delete(name, options = {})
|
373
388
|
return unless @cookies.has_key? name.to_s
|
374
389
|
|
@@ -394,9 +409,15 @@ module ActionDispatch
|
|
394
409
|
@cookies.each_key { |k| delete(k, options) }
|
395
410
|
end
|
396
411
|
|
397
|
-
def write(
|
398
|
-
|
399
|
-
|
412
|
+
def write(response)
|
413
|
+
@set_cookies.each do |name, value|
|
414
|
+
if write_cookie?(value)
|
415
|
+
response.set_cookie(name, value)
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
@delete_cookies.each do |name, value|
|
420
|
+
response.delete_cookie(name, value)
|
400
421
|
end
|
401
422
|
end
|
402
423
|
|
@@ -407,21 +428,8 @@ module ActionDispatch
|
|
407
428
|
::Rack::Utils.escape(string)
|
408
429
|
end
|
409
430
|
|
410
|
-
def make_set_cookie_header(header)
|
411
|
-
header = @set_cookies.inject(header) { |m, (k, v)|
|
412
|
-
if write_cookie?(v)
|
413
|
-
::Rack::Utils.add_cookie_to_header(m, k, v)
|
414
|
-
else
|
415
|
-
m
|
416
|
-
end
|
417
|
-
}
|
418
|
-
@delete_cookies.inject(header) { |m, (k, v)|
|
419
|
-
::Rack::Utils.add_remove_cookie_to_header(m, k, v)
|
420
|
-
}
|
421
|
-
end
|
422
|
-
|
423
431
|
def write_cookie?(cookie)
|
424
|
-
request.ssl? || !cookie[:secure] || always_write_cookie
|
432
|
+
request.ssl? || !cookie[:secure] || always_write_cookie || request.host.end_with?(".onion")
|
425
433
|
end
|
426
434
|
|
427
435
|
def handle_options(options)
|
@@ -431,12 +439,13 @@ module ActionDispatch
|
|
431
439
|
|
432
440
|
options[:path] ||= "/"
|
433
441
|
|
434
|
-
|
435
|
-
|
442
|
+
unless options.key?(:same_site)
|
443
|
+
options[:same_site] = request.cookies_same_site_protection
|
444
|
+
end
|
436
445
|
|
437
446
|
if options[:domain] == :all || options[:domain] == "all"
|
438
447
|
cookie_domain = ""
|
439
|
-
dot_splitted_host = request.host.split(
|
448
|
+
dot_splitted_host = request.host.split(".", -1)
|
440
449
|
|
441
450
|
# Case where request.host is not an IP address or it's an invalid domain
|
442
451
|
# (ip confirms to the domain structure we expect so we explicitly check for ip)
|
@@ -446,10 +455,10 @@ module ActionDispatch
|
|
446
455
|
end
|
447
456
|
|
448
457
|
# If there is a provided tld length then we use it otherwise default domain.
|
449
|
-
if options[:tld_length].present?
|
458
|
+
if options[:tld_length].present?
|
450
459
|
# Case where the tld_length provided is valid
|
451
460
|
if dot_splitted_host.length >= options[:tld_length]
|
452
|
-
cookie_domain = dot_splitted_host.last(options[:tld_length]).join(
|
461
|
+
cookie_domain = dot_splitted_host.last(options[:tld_length]).join(".")
|
453
462
|
end
|
454
463
|
# Case where tld_length is not provided
|
455
464
|
else
|
@@ -458,12 +467,12 @@ module ActionDispatch
|
|
458
467
|
cookie_domain = dot_splitted_host.last(2).join(".")
|
459
468
|
# **.**, ***.** style TLDs like co.uk and com.au
|
460
469
|
else
|
461
|
-
cookie_domain = dot_splitted_host.last(3).join(
|
470
|
+
cookie_domain = dot_splitted_host.last(3).join(".")
|
462
471
|
end
|
463
472
|
end
|
464
473
|
|
465
474
|
options[:domain] = if cookie_domain.present?
|
466
|
-
|
475
|
+
cookie_domain
|
467
476
|
end
|
468
477
|
elsif options[:domain].is_a? Array
|
469
478
|
# If host matches one of the supplied domains.
|
@@ -471,6 +480,8 @@ module ActionDispatch
|
|
471
480
|
domain = domain.delete_prefix(".")
|
472
481
|
request.host == domain || request.host.end_with?(".#{domain}")
|
473
482
|
end
|
483
|
+
elsif options[:domain].respond_to?(:call)
|
484
|
+
options[:domain] = options[:domain].call(request)
|
474
485
|
end
|
475
486
|
end
|
476
487
|
end
|
@@ -534,75 +545,57 @@ module ActionDispatch
|
|
534
545
|
end
|
535
546
|
end
|
536
547
|
|
537
|
-
class MarshalWithJsonFallback # :nodoc:
|
538
|
-
def self.load(value)
|
539
|
-
Marshal.load(value)
|
540
|
-
rescue TypeError => e
|
541
|
-
ActiveSupport::JSON.decode(value) rescue raise e
|
542
|
-
end
|
543
|
-
|
544
|
-
def self.dump(value)
|
545
|
-
Marshal.dump(value)
|
546
|
-
end
|
547
|
-
end
|
548
|
-
|
549
|
-
class JsonSerializer # :nodoc:
|
550
|
-
def self.load(value)
|
551
|
-
ActiveSupport::JSON.decode(value)
|
552
|
-
end
|
553
|
-
|
554
|
-
def self.dump(value)
|
555
|
-
ActiveSupport::JSON.encode(value)
|
556
|
-
end
|
557
|
-
end
|
558
|
-
|
559
548
|
module SerializedCookieJars # :nodoc:
|
560
|
-
MARSHAL_SIGNATURE = "\x04\x08"
|
561
549
|
SERIALIZER = ActiveSupport::MessageEncryptor::NullSerializer
|
562
550
|
|
563
551
|
protected
|
564
|
-
def
|
565
|
-
request.
|
552
|
+
def digest
|
553
|
+
request.cookies_digest || "SHA1"
|
566
554
|
end
|
567
555
|
|
568
|
-
|
569
|
-
|
556
|
+
private
|
557
|
+
def serializer
|
558
|
+
@serializer ||=
|
559
|
+
case request.cookies_serializer
|
560
|
+
when nil
|
561
|
+
ActiveSupport::Messages::SerializerWithFallback[:marshal]
|
562
|
+
when :hybrid
|
563
|
+
ActiveSupport::Messages::SerializerWithFallback[:json_allow_marshal]
|
564
|
+
when Symbol
|
565
|
+
ActiveSupport::Messages::SerializerWithFallback[request.cookies_serializer]
|
566
|
+
else
|
567
|
+
request.cookies_serializer
|
568
|
+
end
|
570
569
|
end
|
571
570
|
|
572
|
-
def
|
573
|
-
|
574
|
-
|
571
|
+
def reserialize?(dumped)
|
572
|
+
serializer.is_a?(ActiveSupport::Messages::SerializerWithFallback) &&
|
573
|
+
serializer != ActiveSupport::Messages::SerializerWithFallback[:marshal] &&
|
574
|
+
!serializer.dumped?(dumped)
|
575
|
+
end
|
575
576
|
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
when rotate
|
583
|
-
serializer.load(value).tap do |v|
|
584
|
-
self[name] = { value: v }
|
585
|
-
end
|
586
|
-
else
|
587
|
-
serializer.load(value)
|
577
|
+
def parse(name, dumped, force_reserialize: false, **)
|
578
|
+
if dumped
|
579
|
+
begin
|
580
|
+
value = serializer.load(dumped)
|
581
|
+
rescue StandardError
|
582
|
+
return
|
588
583
|
end
|
584
|
+
|
585
|
+
self[name] = { value: value } if force_reserialize || reserialize?(dumped)
|
586
|
+
|
587
|
+
value
|
589
588
|
end
|
590
589
|
end
|
591
590
|
|
592
|
-
def
|
593
|
-
|
594
|
-
case serializer
|
595
|
-
when :marshal
|
596
|
-
MarshalWithJsonFallback
|
597
|
-
when :json, :hybrid
|
598
|
-
JsonSerializer
|
599
|
-
else
|
600
|
-
serializer
|
601
|
-
end
|
591
|
+
def commit(name, options)
|
592
|
+
options[:value] = serializer.dump(options[:value])
|
602
593
|
end
|
603
594
|
|
604
|
-
def
|
605
|
-
|
595
|
+
def check_for_overflow!(name, options)
|
596
|
+
if options[:value].bytesize > MAX_COOKIE_SIZE
|
597
|
+
raise CookieOverflow, "#{name} cookie overflowed with size #{options[:value].bytesize} bytes"
|
598
|
+
end
|
606
599
|
end
|
607
600
|
end
|
608
601
|
|
@@ -623,15 +616,15 @@ module ActionDispatch
|
|
623
616
|
|
624
617
|
private
|
625
618
|
def parse(name, signed_message, purpose: nil)
|
626
|
-
|
627
|
-
|
628
|
-
|
619
|
+
rotated = false
|
620
|
+
data = @verifier.verified(signed_message, purpose: purpose, on_rotation: -> { rotated = true })
|
621
|
+
super(name, data, force_reserialize: rotated)
|
629
622
|
end
|
630
623
|
|
631
624
|
def commit(name, options)
|
632
|
-
|
633
|
-
|
634
|
-
|
625
|
+
super
|
626
|
+
options[:value] = @verifier.generate(options[:value], **cookie_metadata(name, options))
|
627
|
+
check_for_overflow!(name, options)
|
635
628
|
end
|
636
629
|
end
|
637
630
|
|
@@ -673,17 +666,17 @@ module ActionDispatch
|
|
673
666
|
|
674
667
|
private
|
675
668
|
def parse(name, encrypted_message, purpose: nil)
|
676
|
-
|
677
|
-
|
678
|
-
|
669
|
+
rotated = false
|
670
|
+
data = @encryptor.decrypt_and_verify(encrypted_message, purpose: purpose, on_rotation: -> { rotated = true })
|
671
|
+
super(name, data, force_reserialize: rotated)
|
679
672
|
rescue ActiveSupport::MessageEncryptor::InvalidMessage, ActiveSupport::MessageVerifier::InvalidSignature
|
680
673
|
nil
|
681
674
|
end
|
682
675
|
|
683
676
|
def commit(name, options)
|
684
|
-
|
685
|
-
|
686
|
-
|
677
|
+
super
|
678
|
+
options[:value] = @encryptor.encrypt_and_sign(options[:value], **cookie_metadata(name, options))
|
679
|
+
check_for_overflow!(name, options)
|
687
680
|
end
|
688
681
|
end
|
689
682
|
|
@@ -692,21 +685,18 @@ module ActionDispatch
|
|
692
685
|
end
|
693
686
|
|
694
687
|
def call(env)
|
695
|
-
request = ActionDispatch::Request.new
|
696
|
-
|
697
|
-
status, headers, body = @app.call(env)
|
688
|
+
request = ActionDispatch::Request.new(env)
|
689
|
+
response = @app.call(env)
|
698
690
|
|
699
691
|
if request.have_cookie_jar?
|
700
692
|
cookie_jar = request.cookie_jar
|
701
693
|
unless cookie_jar.committed?
|
702
|
-
|
703
|
-
|
704
|
-
headers[HTTP_HEADER] = headers[HTTP_HEADER].join("\n")
|
705
|
-
end
|
694
|
+
response = Rack::Response[*response]
|
695
|
+
cookie_jar.write(response)
|
706
696
|
end
|
707
697
|
end
|
708
698
|
|
709
|
-
|
699
|
+
response.to_a
|
710
700
|
end
|
711
701
|
end
|
712
702
|
end
|