actionpack 6.1.4.4 → 7.0.0.alpha1
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 +101 -450
- data/MIT-LICENSE +1 -1
- data/README.rdoc +2 -3
- data/lib/abstract_controller/asset_paths.rb +1 -1
- data/lib/abstract_controller/base.rb +7 -21
- data/lib/abstract_controller/caching/fragments.rb +2 -2
- data/lib/abstract_controller/caching.rb +1 -1
- data/lib/abstract_controller/callbacks.rb +9 -8
- data/lib/abstract_controller/collector.rb +4 -2
- data/lib/abstract_controller/error.rb +1 -1
- data/lib/abstract_controller/helpers.rb +3 -2
- data/lib/abstract_controller/logger.rb +1 -1
- data/lib/abstract_controller/railties/routes_helpers.rb +2 -0
- data/lib/abstract_controller/translation.rb +0 -2
- data/lib/abstract_controller/url_for.rb +4 -6
- data/lib/action_controller/api.rb +1 -1
- data/lib/action_controller/log_subscriber.rb +3 -1
- data/lib/action_controller/metal/conditional_get.rb +38 -1
- data/lib/action_controller/metal/content_security_policy.rb +1 -1
- data/lib/action_controller/metal/cookies.rb +1 -1
- data/lib/action_controller/metal/data_streaming.rb +5 -13
- data/lib/action_controller/metal/exceptions.rb +19 -30
- data/lib/action_controller/metal/flash.rb +6 -2
- data/lib/action_controller/metal/http_authentication.rb +15 -15
- data/lib/action_controller/metal/instrumentation.rb +55 -52
- data/lib/action_controller/metal/live.rb +42 -2
- data/lib/action_controller/metal/mime_responds.rb +3 -3
- data/lib/action_controller/metal/params_wrapper.rb +7 -7
- data/lib/action_controller/metal/permissions_policy.rb +1 -1
- data/lib/action_controller/metal/query_tags.rb +16 -0
- data/lib/action_controller/metal/redirecting.rb +50 -16
- data/lib/action_controller/metal/rendering.rb +7 -7
- data/lib/action_controller/metal/request_forgery_protection.rb +64 -20
- data/lib/action_controller/metal/rescue.rb +1 -1
- data/lib/action_controller/metal/streaming.rb +1 -3
- data/lib/action_controller/metal/strong_parameters.rb +24 -28
- data/lib/action_controller/metal/testing.rb +0 -2
- data/lib/action_controller/metal.rb +7 -10
- data/lib/action_controller/railtie.rb +42 -5
- data/lib/action_controller/test_case.rb +6 -2
- data/lib/action_controller.rb +2 -5
- data/lib/action_dispatch/http/cache.rb +13 -6
- data/lib/action_dispatch/http/content_security_policy.rb +39 -35
- data/lib/action_dispatch/http/filter_parameters.rb +5 -0
- data/lib/action_dispatch/http/mime_negotiation.rb +13 -3
- data/lib/action_dispatch/http/mime_type.rb +9 -11
- data/lib/action_dispatch/http/parameters.rb +4 -4
- data/lib/action_dispatch/http/permissions_policy.rb +1 -1
- data/lib/action_dispatch/http/request.rb +10 -19
- data/lib/action_dispatch/http/response.rb +3 -3
- data/lib/action_dispatch/http/url.rb +9 -10
- 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 +22 -13
- data/lib/action_dispatch/journey/route.rb +5 -12
- data/lib/action_dispatch/journey/router/utils.rb +2 -2
- data/lib/action_dispatch/journey/router.rb +1 -1
- data/lib/action_dispatch/journey/routes.rb +3 -3
- 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/middleware/actionable_exceptions.rb +0 -1
- data/lib/action_dispatch/middleware/cookies.rb +7 -3
- data/lib/action_dispatch/middleware/debug_exceptions.rb +6 -4
- data/lib/action_dispatch/middleware/debug_locks.rb +3 -3
- data/lib/action_dispatch/middleware/exception_wrapper.rb +4 -0
- data/lib/action_dispatch/middleware/flash.rb +9 -11
- data/lib/action_dispatch/middleware/host_authorization.rb +15 -34
- data/lib/action_dispatch/middleware/remote_ip.rb +16 -4
- data/lib/action_dispatch/middleware/session/abstract_store.rb +1 -1
- data/lib/action_dispatch/middleware/show_exceptions.rb +7 -9
- data/lib/action_dispatch/middleware/stack.rb +50 -9
- data/lib/action_dispatch/middleware/static.rb +2 -5
- data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +4 -11
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +28 -18
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +5 -14
- data/lib/action_dispatch/railtie.rb +8 -2
- data/lib/action_dispatch/request/session.rb +43 -13
- data/lib/action_dispatch/routing/mapper.rb +44 -72
- data/lib/action_dispatch/routing/redirection.rb +0 -2
- data/lib/action_dispatch/routing/route_set.rb +9 -6
- data/lib/action_dispatch/routing/routes_proxy.rb +1 -1
- data/lib/action_dispatch/routing/url_for.rb +1 -2
- data/lib/action_dispatch/routing.rb +2 -2
- data/lib/action_dispatch/system_test_case.rb +5 -5
- data/lib/action_dispatch/system_testing/driver.rb +24 -4
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +10 -6
- data/lib/action_dispatch/testing/assertions.rb +2 -5
- data/lib/action_dispatch/testing/integration.rb +6 -8
- data/lib/action_dispatch/testing/test_process.rb +12 -9
- data/lib/action_dispatch.rb +1 -1
- data/lib/action_pack/gem_version.rb +4 -4
- data/lib/action_pack.rb +1 -1
- metadata +18 -17
@@ -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">
|
@@ -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)
|
@@ -88,6 +88,10 @@ module ActionDispatch
|
|
88
88
|
# :startdoc:
|
89
89
|
end
|
90
90
|
|
91
|
+
ActiveSupport.on_load(:action_dispatch_request) do
|
92
|
+
include RequestCookieMethods
|
93
|
+
end
|
94
|
+
|
91
95
|
# Read and write data to cookies through ActionController#cookies.
|
92
96
|
#
|
93
97
|
# When reading cookie data, the data is read from the HTTP request header, Cookie.
|
@@ -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.
|
@@ -280,7 +284,7 @@ module ActionDispatch
|
|
280
284
|
end
|
281
285
|
end
|
282
286
|
|
283
|
-
class CookieJar
|
287
|
+
class CookieJar # :nodoc:
|
284
288
|
include Enumerable, ChainedCookieJars
|
285
289
|
|
286
290
|
# This regular expression is used to split the levels of a domain.
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "action_dispatch/http/request"
|
4
3
|
require "action_dispatch/middleware/exception_wrapper"
|
5
4
|
require "action_dispatch/routing/inspector"
|
6
5
|
|
@@ -135,6 +134,7 @@ module ActionDispatch
|
|
135
134
|
logger = logger(request)
|
136
135
|
|
137
136
|
return unless logger
|
137
|
+
return if !log_rescued_responses?(request) && wrapper.rescue_response?
|
138
138
|
|
139
139
|
exception = wrapper.exception
|
140
140
|
trace = wrapper.exception_trace
|
@@ -149,9 +149,7 @@ module ActionDispatch
|
|
149
149
|
log_array(logger, message)
|
150
150
|
end
|
151
151
|
|
152
|
-
def log_array(logger,
|
153
|
-
lines = Array(array)
|
154
|
-
|
152
|
+
def log_array(logger, lines)
|
155
153
|
return if lines.empty?
|
156
154
|
|
157
155
|
if logger.formatter && logger.formatter.respond_to?(:tags_text)
|
@@ -178,5 +176,9 @@ module ActionDispatch
|
|
178
176
|
def api_request?(content_type)
|
179
177
|
@response_format == :api && !content_type.html?
|
180
178
|
end
|
179
|
+
|
180
|
+
def log_rescued_responses?(request)
|
181
|
+
request.get_header("action_dispatch.log_rescued_responses")
|
182
|
+
end
|
181
183
|
end
|
182
184
|
end
|
@@ -9,9 +9,9 @@ module ActionDispatch
|
|
9
9
|
# config.middleware.insert_before Rack::Sendfile, ActionDispatch::DebugLocks
|
10
10
|
#
|
11
11
|
# After restarting the application and re-triggering the deadlock condition,
|
12
|
-
# <tt>/rails/locks</tt> will show a summary of all threads currently
|
13
|
-
# the interlock, which lock level they are holding or awaiting, and
|
14
|
-
# current backtrace.
|
12
|
+
# the route <tt>/rails/locks</tt> will show a summary of all threads currently
|
13
|
+
# known to the interlock, which lock level they are holding or awaiting, and
|
14
|
+
# their current backtrace.
|
15
15
|
#
|
16
16
|
# Generally a deadlock will be caused by the interlock conflicting with some
|
17
17
|
# other external lock or blocking I/O call. These cannot be automatically
|
@@ -118,6 +118,10 @@ module ActionDispatch
|
|
118
118
|
Rack::Utils.status_code(@@rescue_responses[class_name])
|
119
119
|
end
|
120
120
|
|
121
|
+
def rescue_response?
|
122
|
+
@@rescue_responses.key?(exception.class.name)
|
123
|
+
end
|
124
|
+
|
121
125
|
def source_extracts
|
122
126
|
backtrace.map do |trace|
|
123
127
|
file, line_number = extract_file_and_line_number(trace)
|
@@ -59,16 +59,14 @@ module ActionDispatch
|
|
59
59
|
end
|
60
60
|
|
61
61
|
def commit_flash # :nodoc:
|
62
|
-
session
|
63
|
-
flash_hash = self.flash_hash
|
62
|
+
return unless session.enabled?
|
64
63
|
|
65
64
|
if flash_hash && (flash_hash.present? || session.key?("flash"))
|
66
65
|
session["flash"] = flash_hash.to_session_value
|
67
66
|
self.flash = flash_hash.dup
|
68
67
|
end
|
69
68
|
|
70
|
-
if
|
71
|
-
session.key?("flash") && session["flash"].nil?
|
69
|
+
if session.loaded? && session.key?("flash") && session["flash"].nil?
|
72
70
|
session.delete("flash")
|
73
71
|
end
|
74
72
|
end
|
@@ -79,7 +77,7 @@ module ActionDispatch
|
|
79
77
|
end
|
80
78
|
end
|
81
79
|
|
82
|
-
class FlashNow
|
80
|
+
class FlashNow # :nodoc:
|
83
81
|
attr_accessor :flash
|
84
82
|
|
85
83
|
def initialize(flash)
|
@@ -111,7 +109,7 @@ module ActionDispatch
|
|
111
109
|
class FlashHash
|
112
110
|
include Enumerable
|
113
111
|
|
114
|
-
def self.from_session_value(value)
|
112
|
+
def self.from_session_value(value) # :nodoc:
|
115
113
|
case value
|
116
114
|
when FlashHash # Rails 3.1, 3.2
|
117
115
|
flashes = value.instance_variable_get(:@flashes)
|
@@ -132,13 +130,13 @@ module ActionDispatch
|
|
132
130
|
|
133
131
|
# Builds a hash containing the flashes to keep for the next request.
|
134
132
|
# If there are none to keep, returns +nil+.
|
135
|
-
def to_session_value
|
133
|
+
def to_session_value # :nodoc:
|
136
134
|
flashes_to_keep = @flashes.except(*@discard)
|
137
135
|
return nil if flashes_to_keep.empty?
|
138
136
|
{ "discard" => [], "flashes" => flashes_to_keep }
|
139
137
|
end
|
140
138
|
|
141
|
-
def initialize(flashes = {}, discard = [])
|
139
|
+
def initialize(flashes = {}, discard = []) # :nodoc:
|
142
140
|
@discard = Set.new(stringify_array(discard))
|
143
141
|
@flashes = flashes.stringify_keys
|
144
142
|
@now = nil
|
@@ -162,7 +160,7 @@ module ActionDispatch
|
|
162
160
|
@flashes[k.to_s]
|
163
161
|
end
|
164
162
|
|
165
|
-
def update(h)
|
163
|
+
def update(h) # :nodoc:
|
166
164
|
@discard.subtract stringify_array(h.keys)
|
167
165
|
@flashes.update h.stringify_keys
|
168
166
|
self
|
@@ -202,7 +200,7 @@ module ActionDispatch
|
|
202
200
|
|
203
201
|
alias :merge! :update
|
204
202
|
|
205
|
-
def replace(h)
|
203
|
+
def replace(h) # :nodoc:
|
206
204
|
@discard.clear
|
207
205
|
@flashes.replace h.stringify_keys
|
208
206
|
self
|
@@ -253,7 +251,7 @@ module ActionDispatch
|
|
253
251
|
# Mark for removal entries that were kept, and delete unkept ones.
|
254
252
|
#
|
255
253
|
# This method is called automatically by filters, so you generally don't need to care about it.
|
256
|
-
def sweep
|
254
|
+
def sweep # :nodoc:
|
257
255
|
@discard.each { |k| @flashes.delete k }
|
258
256
|
@discard.replace @flashes.keys
|
259
257
|
end
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "action_dispatch/http/request"
|
4
|
-
|
5
3
|
module ActionDispatch
|
6
4
|
# This middleware guards from DNS rebinding attacks by explicitly permitting
|
7
5
|
# the hosts a request can be sent to, and is passed the options set in
|
@@ -15,17 +13,6 @@ module ActionDispatch
|
|
15
13
|
# application will be executed and rendered. If no +response_app+ is given, a
|
16
14
|
# default one will run, which responds with <tt>403 Forbidden</tt>.
|
17
15
|
class HostAuthorization
|
18
|
-
ALLOWED_HOSTS_IN_DEVELOPMENT = [".localhost", IPAddr.new("0.0.0.0/0"), IPAddr.new("::/0")]
|
19
|
-
PORT_REGEX = /(?::\d+)/ # :nodoc:
|
20
|
-
IPV4_HOSTNAME = /(?<host>\d+\.\d+\.\d+\.\d+)#{PORT_REGEX}?/ # :nodoc:
|
21
|
-
IPV6_HOSTNAME = /(?<host>[a-f0-9]*:[a-f0-9.:]+)/i # :nodoc:
|
22
|
-
IPV6_HOSTNAME_WITH_PORT = /\[#{IPV6_HOSTNAME}\]#{PORT_REGEX}/i # :nodoc:
|
23
|
-
VALID_IP_HOSTNAME = Regexp.union( # :nodoc:
|
24
|
-
/\A#{IPV4_HOSTNAME}\z/,
|
25
|
-
/\A#{IPV6_HOSTNAME}\z/,
|
26
|
-
/\A#{IPV6_HOSTNAME_WITH_PORT}\z/,
|
27
|
-
)
|
28
|
-
|
29
16
|
class Permissions # :nodoc:
|
30
17
|
def initialize(hosts)
|
31
18
|
@hosts = sanitize_hosts(hosts)
|
@@ -37,17 +24,11 @@ module ActionDispatch
|
|
37
24
|
|
38
25
|
def allows?(host)
|
39
26
|
@hosts.any? do |allowed|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
# IP. Treat similar errors as blocked access.
|
46
|
-
false
|
47
|
-
end
|
48
|
-
else
|
49
|
-
allowed === host
|
50
|
-
end
|
27
|
+
allowed === host
|
28
|
+
rescue
|
29
|
+
# IPAddr#=== raises an error if you give it a hostname instead of
|
30
|
+
# IP. Treat similar errors as blocked access.
|
31
|
+
false
|
51
32
|
end
|
52
33
|
end
|
53
34
|
|
@@ -63,20 +44,16 @@ module ActionDispatch
|
|
63
44
|
end
|
64
45
|
|
65
46
|
def sanitize_regexp(host)
|
66
|
-
/\A#{host}
|
47
|
+
/\A#{host}\z/
|
67
48
|
end
|
68
49
|
|
69
50
|
def sanitize_string(host)
|
70
51
|
if host.start_with?(".")
|
71
|
-
/\A(
|
52
|
+
/\A(.+\.)?#{Regexp.escape(host[1..-1])}\z/i
|
72
53
|
else
|
73
|
-
/\A#{Regexp.escape host}
|
54
|
+
/\A#{Regexp.escape host}\z/i
|
74
55
|
end
|
75
56
|
end
|
76
|
-
|
77
|
-
def extract_hostname(host)
|
78
|
-
host.slice(VALID_IP_HOSTNAME, "host") || host
|
79
|
-
end
|
80
57
|
end
|
81
58
|
|
82
59
|
DEFAULT_RESPONSE_APP = -> env do
|
@@ -99,7 +76,7 @@ module ActionDispatch
|
|
99
76
|
|
100
77
|
unless deprecated_response_app.nil?
|
101
78
|
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
102
|
-
`action_dispatch.hosts_response_app` is deprecated and will be ignored in Rails
|
79
|
+
`action_dispatch.hosts_response_app` is deprecated and will be ignored in Rails 7.0.
|
103
80
|
Use the Host Authorization `response_app` setting instead.
|
104
81
|
MSG
|
105
82
|
|
@@ -123,9 +100,13 @@ module ActionDispatch
|
|
123
100
|
end
|
124
101
|
|
125
102
|
private
|
103
|
+
HOSTNAME = /[a-z0-9.-]+|\[[a-f0-9]*:[a-f0-9.:]+\]/i
|
104
|
+
VALID_ORIGIN_HOST = /\A(#{HOSTNAME})(?::\d+)?\z/
|
105
|
+
VALID_FORWARDED_HOST = /(?:\A|,[ ]?)(#{HOSTNAME})(?::\d+)?\z/
|
106
|
+
|
126
107
|
def authorized?(request)
|
127
|
-
origin_host = request.get_header("HTTP_HOST")
|
128
|
-
forwarded_host = request.x_forwarded_host&.
|
108
|
+
origin_host = request.get_header("HTTP_HOST")&.slice(VALID_ORIGIN_HOST, 1) || ""
|
109
|
+
forwarded_host = request.x_forwarded_host&.slice(VALID_FORWARDED_HOST, 1) || ""
|
129
110
|
|
130
111
|
@permissions.allows?(origin_host) && (forwarded_host.blank? || @permissions.allows?(forwarded_host))
|
131
112
|
end
|
@@ -51,10 +51,8 @@ module ActionDispatch
|
|
51
51
|
# clients (like WAP devices), or behind proxies that set headers in an
|
52
52
|
# incorrect or confusing way (like AWS ELB).
|
53
53
|
#
|
54
|
-
# The +custom_proxies+ argument can take an
|
55
|
-
#
|
56
|
-
# single string, IPAddr, or Regexp object is provided, it will be used in
|
57
|
-
# addition to +TRUSTED_PROXIES+. Any proxy setup will put the value you
|
54
|
+
# The +custom_proxies+ argument can take an enumerable which will be used
|
55
|
+
# instead of +TRUSTED_PROXIES+. Any proxy setup will put the value you
|
58
56
|
# want in the middle (or at the beginning) of the X-Forwarded-For list,
|
59
57
|
# with your proxy servers after it. If your proxies aren't removed, pass
|
60
58
|
# them in via the +custom_proxies+ parameter. That way, the middleware will
|
@@ -67,6 +65,20 @@ module ActionDispatch
|
|
67
65
|
elsif custom_proxies.respond_to?(:any?)
|
68
66
|
custom_proxies
|
69
67
|
else
|
68
|
+
ActiveSupport::Deprecation.warn(<<~EOM)
|
69
|
+
Setting config.action_dispatch.trusted_proxies to a single value has
|
70
|
+
been deprecated. Please set this to an enumerable instead. For
|
71
|
+
example, instead of:
|
72
|
+
|
73
|
+
config.action_dispatch.trusted_proxies = IPAddr.new("10.0.0.0/8")
|
74
|
+
|
75
|
+
Wrap the value in an Array:
|
76
|
+
|
77
|
+
config.action_dispatch.trusted_proxies = [IPAddr.new("10.0.0.0/8")]
|
78
|
+
|
79
|
+
Note that unlike passing a single argument, passing an enumerable
|
80
|
+
will *replace* the default set of trusted proxies.
|
81
|
+
EOM
|
70
82
|
Array(custom_proxies) + TRUSTED_PROXIES
|
71
83
|
end
|
72
84
|
end
|
@@ -8,7 +8,7 @@ require "action_dispatch/request/session"
|
|
8
8
|
|
9
9
|
module ActionDispatch
|
10
10
|
module Session
|
11
|
-
class SessionRestoreError < StandardError
|
11
|
+
class SessionRestoreError < StandardError # :nodoc:
|
12
12
|
def initialize
|
13
13
|
super("Session contains objects whose class definition isn't available.\n" \
|
14
14
|
"Remember to require the classes for all objects kept in the session.\n" \
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "action_dispatch/http/request"
|
4
3
|
require "action_dispatch/middleware/exception_wrapper"
|
5
4
|
|
6
5
|
module ActionDispatch
|
@@ -15,14 +14,8 @@ module ActionDispatch
|
|
15
14
|
# If the application returns a "X-Cascade" pass response, this middleware
|
16
15
|
# will send an empty response as result with the correct status code.
|
17
16
|
# If any exception happens inside the exceptions app, this middleware
|
18
|
-
# catches the exceptions and returns a
|
17
|
+
# catches the exceptions and returns a failsafe response.
|
19
18
|
class ShowExceptions
|
20
|
-
FAILSAFE_RESPONSE = [500, { "Content-Type" => "text/plain" },
|
21
|
-
["500 Internal Server Error\n" \
|
22
|
-
"If you are the administrator of this website, then please read this web " \
|
23
|
-
"application's log file and/or the web server's log file to find out what " \
|
24
|
-
"went wrong."]]
|
25
|
-
|
26
19
|
def initialize(app, exceptions_app)
|
27
20
|
@app = app
|
28
21
|
@exceptions_app = exceptions_app
|
@@ -53,7 +46,12 @@ module ActionDispatch
|
|
53
46
|
response[1]["X-Cascade"] == "pass" ? pass_response(status) : response
|
54
47
|
rescue Exception => failsafe_error
|
55
48
|
$stderr.puts "Error during failsafe response: #{failsafe_error}\n #{failsafe_error.backtrace * "\n "}"
|
56
|
-
|
49
|
+
|
50
|
+
[500, { "Content-Type" => "text/plain" },
|
51
|
+
["500 Internal Server Error\n" \
|
52
|
+
"If you are the administrator of this website, then please read this web " \
|
53
|
+
"application's log file and/or the web server's log file to find out what " \
|
54
|
+
"went wrong."]]
|
57
55
|
end
|
58
56
|
|
59
57
|
def pass_response(status)
|
@@ -5,6 +5,16 @@ require "active_support/dependencies"
|
|
5
5
|
|
6
6
|
module ActionDispatch
|
7
7
|
class MiddlewareStack
|
8
|
+
class FakeRuntime # :nodoc:
|
9
|
+
def initialize(app)
|
10
|
+
@app = app
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(env)
|
14
|
+
@app.call(env)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
8
18
|
class Middleware
|
9
19
|
attr_reader :args, :block, :klass
|
10
20
|
|
@@ -69,11 +79,12 @@ module ActionDispatch
|
|
69
79
|
|
70
80
|
def initialize(*args)
|
71
81
|
@middlewares = []
|
82
|
+
@rack_runtime_deprecated = true
|
72
83
|
yield(self) if block_given?
|
73
84
|
end
|
74
85
|
|
75
|
-
def each
|
76
|
-
@middlewares.each
|
86
|
+
def each(&block)
|
87
|
+
@middlewares.each(&block)
|
77
88
|
end
|
78
89
|
|
79
90
|
def size
|
@@ -91,7 +102,7 @@ module ActionDispatch
|
|
91
102
|
def unshift(klass, *args, &block)
|
92
103
|
middlewares.unshift(build_middleware(klass, args, block))
|
93
104
|
end
|
94
|
-
ruby2_keywords(:unshift)
|
105
|
+
ruby2_keywords(:unshift)
|
95
106
|
|
96
107
|
def initialize_copy(other)
|
97
108
|
self.middlewares = other.middlewares.dup
|
@@ -101,7 +112,7 @@ module ActionDispatch
|
|
101
112
|
index = assert_index(index, :before)
|
102
113
|
middlewares.insert(index, build_middleware(klass, args, block))
|
103
114
|
end
|
104
|
-
ruby2_keywords(:insert)
|
115
|
+
ruby2_keywords(:insert)
|
105
116
|
|
106
117
|
alias_method :insert_before, :insert
|
107
118
|
|
@@ -109,17 +120,29 @@ module ActionDispatch
|
|
109
120
|
index = assert_index(index, :after)
|
110
121
|
insert(index + 1, *args, &block)
|
111
122
|
end
|
112
|
-
ruby2_keywords(:insert_after)
|
123
|
+
ruby2_keywords(:insert_after)
|
113
124
|
|
114
125
|
def swap(target, *args, &block)
|
115
126
|
index = assert_index(target, :before)
|
116
127
|
insert(index, *args, &block)
|
117
128
|
middlewares.delete_at(index + 1)
|
118
129
|
end
|
119
|
-
ruby2_keywords(:swap)
|
130
|
+
ruby2_keywords(:swap)
|
120
131
|
|
132
|
+
# Deletes a middleware from the middleware stack.
|
133
|
+
#
|
134
|
+
# Returns the array of middlewares not including the deleted item, or
|
135
|
+
# returns nil if the target is not found.
|
121
136
|
def delete(target)
|
122
|
-
middlewares.
|
137
|
+
middlewares.reject! { |m| m.name == target.name }
|
138
|
+
end
|
139
|
+
|
140
|
+
# Deletes a middleware from the middleware stack.
|
141
|
+
#
|
142
|
+
# Returns the array of middlewares not including the deleted item, or
|
143
|
+
# raises +RuntimeError+ if the target is not found.
|
144
|
+
def delete!(target)
|
145
|
+
delete(target) || (raise "No such middleware to remove: #{target.inspect}")
|
123
146
|
end
|
124
147
|
|
125
148
|
def move(target, source)
|
@@ -143,7 +166,7 @@ module ActionDispatch
|
|
143
166
|
def use(klass, *args, &block)
|
144
167
|
middlewares.push(build_middleware(klass, args, block))
|
145
168
|
end
|
146
|
-
ruby2_keywords(:use)
|
169
|
+
ruby2_keywords(:use)
|
147
170
|
|
148
171
|
def build(app = nil, &block)
|
149
172
|
instrumenting = ActiveSupport::Notifications.notifier.listening?(InstrumentationProxy::EVENT_NAME)
|
@@ -158,13 +181,31 @@ module ActionDispatch
|
|
158
181
|
|
159
182
|
private
|
160
183
|
def assert_index(index, where)
|
161
|
-
i = index.is_a?(Integer) ? index :
|
184
|
+
i = index.is_a?(Integer) ? index : index_of(index)
|
162
185
|
raise "No such middleware to insert #{where}: #{index.inspect}" unless i
|
163
186
|
i
|
164
187
|
end
|
165
188
|
|
166
189
|
def build_middleware(klass, args, block)
|
190
|
+
@rack_runtime_deprecated = false if klass == Rack::Runtime
|
191
|
+
|
167
192
|
Middleware.new(klass, args, block)
|
168
193
|
end
|
194
|
+
|
195
|
+
def index_of(klass)
|
196
|
+
raise "ActionDispatch::MiddlewareStack::FakeRuntime can not be referenced in middleware operations" if klass == FakeRuntime
|
197
|
+
|
198
|
+
if klass == Rack::Runtime && @rack_runtime_deprecated
|
199
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
200
|
+
Rack::Runtime is removed from the default middleware stack in Rails
|
201
|
+
and referencing it in middleware operations without adding it back
|
202
|
+
is deprecated and will throw an error in Rails 7.1
|
203
|
+
MSG
|
204
|
+
end
|
205
|
+
|
206
|
+
middlewares.index do |m|
|
207
|
+
m.name == klass.name || (@rack_runtime_deprecated && m.klass == FakeRuntime && klass == Rack::Runtime)
|
208
|
+
end
|
209
|
+
end
|
169
210
|
end
|
170
211
|
end
|
@@ -137,11 +137,8 @@ module ActionDispatch
|
|
137
137
|
end
|
138
138
|
|
139
139
|
def file_readable?(path)
|
140
|
-
|
141
|
-
|
142
|
-
false
|
143
|
-
else
|
144
|
-
file_stat.file? && file_stat.readable?
|
140
|
+
file_path = File.join(@root, path.b)
|
141
|
+
File.file?(file_path) && File.readable?(file_path)
|
145
142
|
end
|
146
143
|
|
147
144
|
def compressible?(content_type)
|
@@ -1,24 +1,17 @@
|
|
1
|
-
|
2
|
-
<% if (hide = @exception.blamed_files.length > 8) %>
|
3
|
-
<a href="#" onclick="return toggleTrace()">Toggle blamed files</a>
|
4
|
-
<% end %>
|
5
|
-
<pre id="blame_trace" <%='style="display:none"' if hide %>><code><%= @exception.describe_blame %></code></pre>
|
6
|
-
<% end %>
|
7
|
-
|
8
|
-
<h2 style="margin-top: 30px">Request</h2>
|
1
|
+
<h2 class="request-heading">Request</h2>
|
9
2
|
<% if params_valid? %>
|
10
3
|
<p><b>Parameters</b>:</p> <pre><%= debug_params(@request.filtered_parameters) %></pre>
|
11
4
|
<% end %>
|
12
5
|
|
13
6
|
<div class="details">
|
14
7
|
<div class="summary"><a href="#" onclick="return toggleSessionDump()">Toggle session dump</a></div>
|
15
|
-
<div id="session_dump"
|
8
|
+
<div id="session_dump" class="hidden"><pre><%= debug_hash @request.session %></pre></div>
|
16
9
|
</div>
|
17
10
|
|
18
11
|
<div class="details">
|
19
12
|
<div class="summary"><a href="#" onclick="return toggleEnvDump()">Toggle env dump</a></div>
|
20
|
-
<div id="env_dump"
|
13
|
+
<div id="env_dump" class="hidden"><pre><%= debug_hash @request.env.slice(*@request.class::ENV_METHODS) %></pre></div>
|
21
14
|
</div>
|
22
15
|
|
23
|
-
<h2
|
16
|
+
<h2 class="response-heading">Response</h2>
|
24
17
|
<p><b>Headers</b>:</p> <pre><%= debug_headers(defined?(@response) ? @response.headers : {}) %></pre>
|