actionpack 7.0.4 → 7.1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +495 -257
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -4
- data/lib/abstract_controller/base.rb +20 -11
- data/lib/abstract_controller/caching/fragments.rb +2 -0
- data/lib/abstract_controller/callbacks.rb +31 -6
- data/lib/abstract_controller/deprecator.rb +7 -0
- data/lib/abstract_controller/helpers.rb +75 -28
- data/lib/abstract_controller/railties/routes_helpers.rb +1 -16
- data/lib/abstract_controller/rendering.rb +12 -14
- data/lib/abstract_controller/translation.rb +11 -6
- data/lib/abstract_controller/url_for.rb +2 -0
- data/lib/abstract_controller.rb +6 -0
- data/lib/action_controller/api.rb +6 -4
- data/lib/action_controller/base.rb +3 -17
- data/lib/action_controller/caching.rb +2 -0
- data/lib/action_controller/deprecator.rb +7 -0
- data/lib/action_controller/form_builder.rb +2 -0
- data/lib/action_controller/log_subscriber.rb +16 -4
- data/lib/action_controller/metal/basic_implicit_render.rb +3 -1
- data/lib/action_controller/metal/conditional_get.rb +121 -123
- data/lib/action_controller/metal/content_security_policy.rb +5 -5
- data/lib/action_controller/metal/data_streaming.rb +20 -18
- 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 +8 -0
- data/lib/action_controller/metal/head.rb +9 -7
- data/lib/action_controller/metal/helpers.rb +3 -14
- data/lib/action_controller/metal/http_authentication.rb +15 -9
- data/lib/action_controller/metal/implicit_render.rb +5 -3
- data/lib/action_controller/metal/instrumentation.rb +8 -1
- data/lib/action_controller/metal/live.rb +25 -1
- data/lib/action_controller/metal/mime_responds.rb +2 -2
- data/lib/action_controller/metal/params_wrapper.rb +4 -2
- data/lib/action_controller/metal/permissions_policy.rb +2 -2
- data/lib/action_controller/metal/redirecting.rb +29 -8
- data/lib/action_controller/metal/renderers.rb +4 -4
- data/lib/action_controller/metal/rendering.rb +114 -9
- data/lib/action_controller/metal/request_forgery_protection.rb +144 -53
- data/lib/action_controller/metal/rescue.rb +6 -3
- data/lib/action_controller/metal/streaming.rb +71 -31
- data/lib/action_controller/metal/strong_parameters.rb +200 -103
- data/lib/action_controller/metal/url_for.rb +9 -4
- data/lib/action_controller/metal.rb +79 -21
- data/lib/action_controller/railtie.rb +24 -10
- data/lib/action_controller/renderer.rb +99 -85
- data/lib/action_controller/test_case.rb +18 -8
- data/lib/action_controller.rb +13 -3
- data/lib/action_dispatch/constants.rb +32 -0
- data/lib/action_dispatch/deprecator.rb +7 -0
- data/lib/action_dispatch/http/cache.rb +9 -11
- data/lib/action_dispatch/http/content_security_policy.rb +35 -13
- data/lib/action_dispatch/http/filter_parameters.rb +23 -32
- data/lib/action_dispatch/http/headers.rb +3 -1
- data/lib/action_dispatch/http/mime_negotiation.rb +22 -22
- data/lib/action_dispatch/http/mime_type.rb +37 -11
- data/lib/action_dispatch/http/mime_types.rb +3 -1
- data/lib/action_dispatch/http/parameters.rb +1 -1
- data/lib/action_dispatch/http/permissions_policy.rb +38 -23
- data/lib/action_dispatch/http/rack_cache.rb +2 -0
- data/lib/action_dispatch/http/request.rb +85 -32
- data/lib/action_dispatch/http/response.rb +80 -63
- data/lib/action_dispatch/http/upload.rb +15 -2
- data/lib/action_dispatch/journey/formatter.rb +8 -2
- data/lib/action_dispatch/journey/path/pattern.rb +14 -14
- data/lib/action_dispatch/journey/route.rb +3 -2
- data/lib/action_dispatch/journey/router.rb +9 -8
- data/lib/action_dispatch/journey/routes.rb +2 -2
- data/lib/action_dispatch/log_subscriber.rb +23 -0
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +5 -6
- 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 +108 -117
- data/lib/action_dispatch/middleware/debug_exceptions.rb +26 -25
- data/lib/action_dispatch/middleware/debug_locks.rb +4 -1
- data/lib/action_dispatch/middleware/debug_view.rb +7 -2
- data/lib/action_dispatch/middleware/exception_wrapper.rb +186 -27
- data/lib/action_dispatch/middleware/executor.rb +7 -1
- data/lib/action_dispatch/middleware/flash.rb +7 -0
- data/lib/action_dispatch/middleware/host_authorization.rb +18 -8
- 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 +21 -20
- data/lib/action_dispatch/middleware/request_id.rb +4 -2
- data/lib/action_dispatch/middleware/server_timing.rb +4 -4
- data/lib/action_dispatch/middleware/session/abstract_store.rb +5 -0
- data/lib/action_dispatch/middleware/session/cache_store.rb +2 -0
- data/lib/action_dispatch/middleware/session/cookie_store.rb +11 -5
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +3 -1
- data/lib/action_dispatch/middleware/show_exceptions.rb +39 -22
- data/lib/action_dispatch/middleware/ssl.rb +18 -6
- data/lib/action_dispatch/middleware/stack.rb +7 -2
- data/lib/action_dispatch/middleware/static.rb +14 -10
- data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +8 -1
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -3
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -3
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +7 -7
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +17 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +16 -12
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +3 -0
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +59 -41
- data/lib/action_dispatch/railtie.rb +13 -4
- data/lib/action_dispatch/request/session.rb +16 -6
- data/lib/action_dispatch/request/utils.rb +8 -3
- data/lib/action_dispatch/routing/inspector.rb +54 -6
- data/lib/action_dispatch/routing/mapper.rb +97 -26
- data/lib/action_dispatch/routing/polymorphic_routes.rb +2 -0
- data/lib/action_dispatch/routing/redirection.rb +15 -6
- data/lib/action_dispatch/routing/route_set.rb +53 -23
- data/lib/action_dispatch/routing/routes_proxy.rb +10 -15
- data/lib/action_dispatch/routing/url_for.rb +26 -22
- data/lib/action_dispatch/routing.rb +7 -7
- data/lib/action_dispatch/system_test_case.rb +3 -3
- data/lib/action_dispatch/system_testing/browser.rb +25 -19
- data/lib/action_dispatch/system_testing/driver.rb +15 -23
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +27 -16
- 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 +67 -28
- data/lib/action_dispatch/testing/assertions.rb +3 -1
- data/lib/action_dispatch/testing/integration.rb +27 -17
- data/lib/action_dispatch/testing/request_encoder.rb +4 -1
- data/lib/action_dispatch/testing/test_process.rb +4 -3
- data/lib/action_dispatch/testing/test_request.rb +1 -1
- data/lib/action_dispatch/testing/test_response.rb +23 -9
- data/lib/action_dispatch.rb +41 -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 +68 -32
@@ -6,6 +6,8 @@ require "action_dispatch/routing/inspector"
|
|
6
6
|
require "action_view"
|
7
7
|
|
8
8
|
module ActionDispatch
|
9
|
+
# = Action Dispatch \DebugExceptions
|
10
|
+
#
|
9
11
|
# This middleware is responsible for logging exceptions and
|
10
12
|
# showing a debugging page in case the request is local.
|
11
13
|
class DebugExceptions
|
@@ -24,26 +26,26 @@ module ActionDispatch
|
|
24
26
|
end
|
25
27
|
|
26
28
|
def call(env)
|
27
|
-
request = ActionDispatch::Request.new env
|
28
29
|
_, headers, body = response = @app.call(env)
|
29
30
|
|
30
|
-
if headers[
|
31
|
+
if headers[Constants::X_CASCADE] == "pass"
|
31
32
|
body.close if body.respond_to?(:close)
|
32
33
|
raise ActionController::RoutingError, "No route matches [#{env['REQUEST_METHOD']}] #{env['PATH_INFO'].inspect}"
|
33
34
|
end
|
34
35
|
|
35
36
|
response
|
36
37
|
rescue Exception => exception
|
37
|
-
|
38
|
-
|
39
|
-
|
38
|
+
request = ActionDispatch::Request.new env
|
39
|
+
backtrace_cleaner = request.get_header("action_dispatch.backtrace_cleaner")
|
40
|
+
wrapper = ExceptionWrapper.new(backtrace_cleaner, exception)
|
41
|
+
|
42
|
+
invoke_interceptors(request, exception, wrapper)
|
43
|
+
raise exception unless wrapper.show?(request)
|
44
|
+
render_exception(request, exception, wrapper)
|
40
45
|
end
|
41
46
|
|
42
47
|
private
|
43
|
-
def invoke_interceptors(request, exception)
|
44
|
-
backtrace_cleaner = request.get_header("action_dispatch.backtrace_cleaner")
|
45
|
-
wrapper = ExceptionWrapper.new(backtrace_cleaner, exception)
|
46
|
-
|
48
|
+
def invoke_interceptors(request, exception, wrapper)
|
47
49
|
@interceptors.each do |interceptor|
|
48
50
|
interceptor.call(request, exception)
|
49
51
|
rescue Exception
|
@@ -51,9 +53,7 @@ module ActionDispatch
|
|
51
53
|
end
|
52
54
|
end
|
53
55
|
|
54
|
-
def render_exception(request, exception)
|
55
|
-
backtrace_cleaner = request.get_header("action_dispatch.backtrace_cleaner")
|
56
|
-
wrapper = ExceptionWrapper.new(backtrace_cleaner, exception)
|
56
|
+
def render_exception(request, exception, wrapper)
|
57
57
|
log_error(request, wrapper)
|
58
58
|
|
59
59
|
if request.get_header("action_dispatch.show_detailed_exceptions")
|
@@ -94,7 +94,7 @@ module ActionDispatch
|
|
94
94
|
wrapper.status_code,
|
95
95
|
Rack::Utils::HTTP_STATUS_CODES[500]
|
96
96
|
),
|
97
|
-
exception: wrapper.
|
97
|
+
exception: wrapper.exception_inspect,
|
98
98
|
traces: wrapper.traces
|
99
99
|
}
|
100
100
|
|
@@ -115,19 +115,19 @@ module ActionDispatch
|
|
115
115
|
DebugView.new(
|
116
116
|
request: request,
|
117
117
|
exception_wrapper: wrapper,
|
118
|
+
# Everything should use the wrapper, but we need to pass
|
119
|
+
# `exception` for legacy code.
|
118
120
|
exception: wrapper.exception,
|
119
121
|
traces: wrapper.traces,
|
120
122
|
show_source_idx: wrapper.source_to_show_id,
|
121
123
|
trace_to_show: wrapper.trace_to_show,
|
122
|
-
routes_inspector: routes_inspector(wrapper
|
124
|
+
routes_inspector: routes_inspector(wrapper),
|
123
125
|
source_extracts: wrapper.source_extracts,
|
124
|
-
line_number: wrapper.line_number,
|
125
|
-
file: wrapper.file
|
126
126
|
)
|
127
127
|
end
|
128
128
|
|
129
129
|
def render(status, body, format)
|
130
|
-
[status, {
|
130
|
+
[status, { Rack::CONTENT_TYPE => "#{format}; charset=#{Response.default_charset}", Rack::CONTENT_LENGTH => body.bytesize.to_s }, [body]]
|
131
131
|
end
|
132
132
|
|
133
133
|
def log_error(request, wrapper)
|
@@ -136,26 +136,27 @@ module ActionDispatch
|
|
136
136
|
return unless logger
|
137
137
|
return if !log_rescued_responses?(request) && wrapper.rescue_response?
|
138
138
|
|
139
|
-
exception = wrapper.exception
|
140
139
|
trace = wrapper.exception_trace
|
141
140
|
|
142
141
|
message = []
|
143
142
|
message << " "
|
144
|
-
message << "#{
|
145
|
-
message.concat(
|
143
|
+
message << "#{wrapper.exception_class_name} (#{wrapper.message}):"
|
144
|
+
message.concat(wrapper.annotated_source_code)
|
146
145
|
message << " "
|
147
146
|
message.concat(trace)
|
148
147
|
|
149
|
-
log_array(logger, message)
|
148
|
+
log_array(logger, message, request)
|
150
149
|
end
|
151
150
|
|
152
|
-
def log_array(logger, lines)
|
151
|
+
def log_array(logger, lines, request)
|
153
152
|
return if lines.empty?
|
154
153
|
|
154
|
+
level = request.get_header("action_dispatch.debug_exception_log_level")
|
155
|
+
|
155
156
|
if logger.formatter && logger.formatter.respond_to?(:tags_text)
|
156
|
-
logger.
|
157
|
+
logger.add(level, lines.join("\n#{logger.formatter.tags_text}"))
|
157
158
|
else
|
158
|
-
logger.
|
159
|
+
logger.add(level, lines.join("\n"))
|
159
160
|
end
|
160
161
|
end
|
161
162
|
|
@@ -168,7 +169,7 @@ module ActionDispatch
|
|
168
169
|
end
|
169
170
|
|
170
171
|
def routes_inspector(exception)
|
171
|
-
if @routes_app.respond_to?(:routes) && (exception.
|
172
|
+
if @routes_app.respond_to?(:routes) && (exception.routing_error? || exception.template_error?)
|
172
173
|
ActionDispatch::Routing::RoutesInspector.new(@routes_app.routes.routes)
|
173
174
|
end
|
174
175
|
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActionDispatch
|
4
|
+
# = Action Dispatch \DebugLocks
|
5
|
+
#
|
4
6
|
# This middleware can be used to diagnose deadlocks in the autoload interlock.
|
5
7
|
#
|
6
8
|
# To use it, insert it near the top of the middleware stack, using
|
@@ -97,7 +99,8 @@ module ActionDispatch
|
|
97
99
|
msg << "\n#{info[:backtrace].join("\n")}\n" if info[:backtrace]
|
98
100
|
end.join("\n\n---\n\n\n")
|
99
101
|
|
100
|
-
[200, {
|
102
|
+
[200, { Rack::CONTENT_TYPE => "text/plain; charset=#{ActionDispatch::Response.default_charset}",
|
103
|
+
Rack::CONTENT_LENGTH => str.size.to_s }, [str]]
|
101
104
|
end
|
102
105
|
|
103
106
|
def blocked_by?(victim, blocker, all_threads)
|
@@ -7,18 +7,23 @@ require "action_view/base"
|
|
7
7
|
|
8
8
|
module ActionDispatch
|
9
9
|
class DebugView < ActionView::Base # :nodoc:
|
10
|
-
|
10
|
+
RESCUES_TEMPLATE_PATHS = [File.expand_path("templates", __dir__)]
|
11
11
|
|
12
12
|
def initialize(assigns)
|
13
|
-
paths =
|
13
|
+
paths = RESCUES_TEMPLATE_PATHS.dup
|
14
14
|
lookup_context = ActionView::LookupContext.new(paths)
|
15
15
|
super(lookup_context, assigns, nil)
|
16
|
+
@exception_wrapper = assigns[:exception_wrapper]
|
16
17
|
end
|
17
18
|
|
18
19
|
def compiled_method_container
|
19
20
|
self.class
|
20
21
|
end
|
21
22
|
|
23
|
+
def error_highlight_available?
|
24
|
+
@exception_wrapper.error_highlight_available?
|
25
|
+
end
|
26
|
+
|
22
27
|
def debug_params(params)
|
23
28
|
clean_params = params.clone
|
24
29
|
clean_params.delete("action")
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_support/core_ext/module/attribute_accessors"
|
4
|
+
require "active_support/syntax_error_proxy"
|
5
|
+
require "active_support/core_ext/thread/backtrace/location"
|
4
6
|
require "rack/utils"
|
5
7
|
|
6
8
|
module ActionDispatch
|
@@ -41,22 +43,76 @@ module ActionDispatch
|
|
41
43
|
"ActionDispatch::Http::MimeNegotiation::InvalidType"
|
42
44
|
]
|
43
45
|
|
44
|
-
attr_reader :backtrace_cleaner, :
|
46
|
+
attr_reader :backtrace_cleaner, :wrapped_causes, :exception_class_name, :exception
|
45
47
|
|
46
48
|
def initialize(backtrace_cleaner, exception)
|
47
49
|
@backtrace_cleaner = backtrace_cleaner
|
48
|
-
@
|
49
|
-
@exception_class_name = @exception.class.name
|
50
|
+
@exception_class_name = exception.class.name
|
50
51
|
@wrapped_causes = wrapped_causes_for(exception, backtrace_cleaner)
|
52
|
+
@exception = exception
|
53
|
+
if exception.is_a?(SyntaxError)
|
54
|
+
@exception = ActiveSupport::SyntaxErrorProxy.new(exception)
|
55
|
+
end
|
56
|
+
@backtrace = build_backtrace
|
57
|
+
end
|
58
|
+
|
59
|
+
def routing_error?
|
60
|
+
@exception.is_a?(ActionController::RoutingError)
|
61
|
+
end
|
51
62
|
|
52
|
-
|
63
|
+
def template_error?
|
64
|
+
@exception.is_a?(ActionView::Template::Error)
|
65
|
+
end
|
66
|
+
|
67
|
+
def sub_template_message
|
68
|
+
@exception.sub_template_message
|
69
|
+
end
|
70
|
+
|
71
|
+
def has_cause?
|
72
|
+
@exception.cause
|
73
|
+
end
|
74
|
+
|
75
|
+
def failures
|
76
|
+
@exception.failures
|
77
|
+
end
|
78
|
+
|
79
|
+
def has_corrections?
|
80
|
+
@exception.respond_to?(:original_message) && @exception.respond_to?(:corrections)
|
81
|
+
end
|
82
|
+
|
83
|
+
def original_message
|
84
|
+
@exception.original_message
|
85
|
+
end
|
86
|
+
|
87
|
+
def corrections
|
88
|
+
@exception.corrections
|
89
|
+
end
|
90
|
+
|
91
|
+
def file_name
|
92
|
+
@exception.file_name
|
93
|
+
end
|
94
|
+
|
95
|
+
def line_number
|
96
|
+
@exception.line_number
|
97
|
+
end
|
98
|
+
|
99
|
+
def actions
|
100
|
+
ActiveSupport::ActionableError.actions(@exception)
|
53
101
|
end
|
54
102
|
|
55
103
|
def unwrapped_exception
|
56
104
|
if wrapper_exceptions.include?(@exception_class_name)
|
57
|
-
exception.cause
|
105
|
+
@exception.cause
|
106
|
+
else
|
107
|
+
@exception
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def annotated_source_code
|
112
|
+
if exception.respond_to?(:annotated_source_code)
|
113
|
+
exception.annotated_source_code
|
58
114
|
else
|
59
|
-
|
115
|
+
[]
|
60
116
|
end
|
61
117
|
end
|
62
118
|
|
@@ -118,21 +174,44 @@ module ActionDispatch
|
|
118
174
|
Rack::Utils.status_code(@@rescue_responses[class_name])
|
119
175
|
end
|
120
176
|
|
177
|
+
def show?(request)
|
178
|
+
# We're treating `nil` as "unset", and we want the default setting to be
|
179
|
+
# `:all`. This logic should be extracted to `env_config` and calculated
|
180
|
+
# once.
|
181
|
+
config = request.get_header("action_dispatch.show_exceptions")
|
182
|
+
|
183
|
+
# Include true and false for backwards compatibility.
|
184
|
+
case config
|
185
|
+
when :none
|
186
|
+
false
|
187
|
+
when :rescuable
|
188
|
+
rescue_response?
|
189
|
+
when true
|
190
|
+
ActionDispatch.deprecator.warn("Setting action_dispatch.show_exceptions to true is deprecated. Set to :all instead.")
|
191
|
+
true
|
192
|
+
when false
|
193
|
+
ActionDispatch.deprecator.warn("Setting action_dispatch.show_exceptions to false is deprecated. Set to :none instead.")
|
194
|
+
false
|
195
|
+
else
|
196
|
+
true
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
121
200
|
def rescue_response?
|
122
201
|
@@rescue_responses.key?(exception.class.name)
|
123
202
|
end
|
124
203
|
|
125
204
|
def source_extracts
|
126
205
|
backtrace.map do |trace|
|
127
|
-
|
128
|
-
|
129
|
-
{
|
130
|
-
code: source_fragment(file, line_number),
|
131
|
-
line_number: line_number
|
132
|
-
}
|
206
|
+
extract_source(trace)
|
133
207
|
end
|
134
208
|
end
|
135
209
|
|
210
|
+
def error_highlight_available?
|
211
|
+
# ErrorHighlight.spot with backtrace_location keyword is available since error_highlight 0.4.0
|
212
|
+
defined?(ErrorHighlight) && Gem::Version.new(ErrorHighlight::VERSION) >= Gem::Version.new("0.4.0")
|
213
|
+
end
|
214
|
+
|
136
215
|
def trace_to_show
|
137
216
|
if traces["Application Trace"].empty? && rescue_template != "routing_error"
|
138
217
|
"Full Trace"
|
@@ -145,9 +224,65 @@ module ActionDispatch
|
|
145
224
|
(traces[trace_to_show].first || {})[:id]
|
146
225
|
end
|
147
226
|
|
227
|
+
def exception_name
|
228
|
+
exception.cause.class.to_s
|
229
|
+
end
|
230
|
+
|
231
|
+
def message
|
232
|
+
exception.message
|
233
|
+
end
|
234
|
+
|
235
|
+
def exception_inspect
|
236
|
+
exception.inspect
|
237
|
+
end
|
238
|
+
|
239
|
+
def exception_id
|
240
|
+
exception.object_id
|
241
|
+
end
|
242
|
+
|
148
243
|
private
|
149
|
-
|
150
|
-
|
244
|
+
class SourceMapLocation < DelegateClass(Thread::Backtrace::Location) # :nodoc:
|
245
|
+
def initialize(location, template)
|
246
|
+
super(location)
|
247
|
+
@template = template
|
248
|
+
end
|
249
|
+
|
250
|
+
def spot(exc)
|
251
|
+
if RubyVM::AbstractSyntaxTree.respond_to?(:node_id_for_backtrace_location) && __getobj__.is_a?(Thread::Backtrace::Location)
|
252
|
+
location = @template.spot(__getobj__)
|
253
|
+
else
|
254
|
+
location = super
|
255
|
+
end
|
256
|
+
|
257
|
+
if location
|
258
|
+
@template.translate_location(__getobj__, location)
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
attr_reader :backtrace
|
264
|
+
|
265
|
+
def build_backtrace
|
266
|
+
built_methods = {}
|
267
|
+
|
268
|
+
ActionView::PathRegistry.all_resolvers.each do |resolver|
|
269
|
+
resolver.built_templates.each do |template|
|
270
|
+
built_methods[template.method_name] = template
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
(@exception.backtrace_locations || []).map do |loc|
|
275
|
+
if built_methods.key?(loc.label.to_s)
|
276
|
+
thread_backtrace_location = if loc.respond_to?(:__getobj__)
|
277
|
+
loc.__getobj__
|
278
|
+
else
|
279
|
+
loc
|
280
|
+
end
|
281
|
+
SourceMapLocation.new(thread_backtrace_location, built_methods[loc.label.to_s])
|
282
|
+
else
|
283
|
+
loc
|
284
|
+
end
|
285
|
+
end
|
151
286
|
end
|
152
287
|
|
153
288
|
def causes_for(exception)
|
@@ -168,29 +303,53 @@ module ActionDispatch
|
|
168
303
|
end
|
169
304
|
end
|
170
305
|
|
306
|
+
def extract_source(trace)
|
307
|
+
spot = trace.spot(@exception)
|
308
|
+
|
309
|
+
if spot
|
310
|
+
line = spot[:first_lineno]
|
311
|
+
code = extract_source_fragment_lines(spot[:script_lines], line)
|
312
|
+
|
313
|
+
if line == spot[:last_lineno]
|
314
|
+
code[line] = [
|
315
|
+
code[line][0, spot[:first_column]],
|
316
|
+
code[line][spot[:first_column]...spot[:last_column]],
|
317
|
+
code[line][spot[:last_column]..-1],
|
318
|
+
]
|
319
|
+
end
|
320
|
+
|
321
|
+
return {
|
322
|
+
code: code,
|
323
|
+
line_number: line
|
324
|
+
}
|
325
|
+
end
|
326
|
+
|
327
|
+
file, line_number = extract_file_and_line_number(trace)
|
328
|
+
|
329
|
+
{
|
330
|
+
code: source_fragment(file, line_number),
|
331
|
+
line_number: line_number
|
332
|
+
}
|
333
|
+
end
|
334
|
+
|
335
|
+
def extract_source_fragment_lines(source_lines, line)
|
336
|
+
start = [line - 3, 0].max
|
337
|
+
lines = source_lines.drop(start).take(6)
|
338
|
+
Hash[*(start + 1..(lines.count + start)).zip(lines).flatten]
|
339
|
+
end
|
340
|
+
|
171
341
|
def source_fragment(path, line)
|
172
342
|
return unless Rails.respond_to?(:root) && Rails.root
|
173
343
|
full_path = Rails.root.join(path)
|
174
344
|
if File.exist?(full_path)
|
175
345
|
File.open(full_path, "r") do |file|
|
176
|
-
|
177
|
-
lines = file.each_line.drop(start).take(6)
|
178
|
-
Hash[*(start + 1..(lines.count + start)).zip(lines).flatten]
|
346
|
+
extract_source_fragment_lines(file.each_line, line)
|
179
347
|
end
|
180
348
|
end
|
181
349
|
end
|
182
350
|
|
183
351
|
def extract_file_and_line_number(trace)
|
184
|
-
|
185
|
-
# Windows and Unix path styles.
|
186
|
-
file, line = trace.match(/^(.+?):(\d+).*$/, &:captures) || trace
|
187
|
-
[file, line.to_i]
|
188
|
-
end
|
189
|
-
|
190
|
-
def expand_backtrace
|
191
|
-
@exception.backtrace.unshift(
|
192
|
-
@exception.to_s.split("\n")
|
193
|
-
).flatten!
|
352
|
+
[trace.path, trace.lineno]
|
194
353
|
end
|
195
354
|
end
|
196
355
|
end
|
@@ -12,9 +12,15 @@ module ActionDispatch
|
|
12
12
|
state = @executor.run!(reset: true)
|
13
13
|
begin
|
14
14
|
response = @app.call(env)
|
15
|
+
|
16
|
+
if env["action_dispatch.report_exception"]
|
17
|
+
error = env["action_dispatch.exception"]
|
18
|
+
@executor.error_reporter.report(error, handled: false, source: "application.action_dispatch")
|
19
|
+
end
|
20
|
+
|
15
21
|
returned = response << ::Rack::BodyProxy.new(response.pop) { state.complete! }
|
16
22
|
rescue => error
|
17
|
-
@executor.error_reporter.report(error, handled: false)
|
23
|
+
@executor.error_reporter.report(error, handled: false, source: "application.action_dispatch")
|
18
24
|
raise
|
19
25
|
ensure
|
20
26
|
state.complete! unless returned
|
@@ -3,6 +3,8 @@
|
|
3
3
|
require "active_support/core_ext/hash/keys"
|
4
4
|
|
5
5
|
module ActionDispatch
|
6
|
+
# = Action Dispatch \Flash
|
7
|
+
#
|
6
8
|
# The flash provides a way to pass temporary primitive-types (String, Array, Hash) between actions. Anything you place in the flash will be exposed
|
7
9
|
# to the very next action and then cleared out. This is a great way of doing notices and alerts, such as a create
|
8
10
|
# action that sets <tt>flash[:notice] = "Post successfully created"</tt> before redirecting to a display action that can
|
@@ -175,6 +177,8 @@ module ActionDispatch
|
|
175
177
|
@flashes.key? name.to_s
|
176
178
|
end
|
177
179
|
|
180
|
+
# Immediately deletes the single flash entry. Use this method when you
|
181
|
+
# want remove the message within the current action. See also #discard.
|
178
182
|
def delete(key)
|
179
183
|
key = key.to_s
|
180
184
|
@discard.delete key
|
@@ -243,6 +247,9 @@ module ActionDispatch
|
|
243
247
|
#
|
244
248
|
# flash.discard # discard the entire flash at the end of the current action
|
245
249
|
# flash.discard(:warning) # discard only the "warning" entry at the end of the current action
|
250
|
+
#
|
251
|
+
# Use this method when you want to display the message in the current
|
252
|
+
# action but not in the next one. See also #delete.
|
246
253
|
def discard(k = nil)
|
247
254
|
k = k.to_s if k
|
248
255
|
@discard.merge Array(k || keys)
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActionDispatch
|
4
|
+
# = Action Dispatch \HostAuthorization
|
5
|
+
#
|
4
6
|
# This middleware guards from DNS rebinding attacks by explicitly permitting
|
5
7
|
# the hosts a request can be sent to, and is passed the options set in
|
6
8
|
# +config.host_authorization+.
|
@@ -18,6 +20,7 @@ module ActionDispatch
|
|
18
20
|
class HostAuthorization
|
19
21
|
ALLOWED_HOSTS_IN_DEVELOPMENT = [".localhost", IPAddr.new("0.0.0.0/0"), IPAddr.new("::/0")]
|
20
22
|
PORT_REGEX = /(?::\d+)/ # :nodoc:
|
23
|
+
SUBDOMAIN_REGEX = /(?:[a-z0-9-]+\.)/i # :nodoc:
|
21
24
|
IPV4_HOSTNAME = /(?<host>\d+\.\d+\.\d+\.\d+)#{PORT_REGEX}?/ # :nodoc:
|
22
25
|
IPV6_HOSTNAME = /(?<host>[a-f0-9]*:[a-f0-9.:]+)/i # :nodoc:
|
23
26
|
IPV6_HOSTNAME_WITH_PORT = /\[#{IPV6_HOSTNAME}\]#{PORT_REGEX}/i # :nodoc:
|
@@ -69,7 +72,7 @@ module ActionDispatch
|
|
69
72
|
|
70
73
|
def sanitize_string(host)
|
71
74
|
if host.start_with?(".")
|
72
|
-
/\A
|
75
|
+
/\A#{SUBDOMAIN_REGEX}?#{Regexp.escape(host[1..-1])}#{PORT_REGEX}?\z/i
|
73
76
|
else
|
74
77
|
/\A#{Regexp.escape host}#{PORT_REGEX}?\z/i
|
75
78
|
end
|
@@ -95,14 +98,14 @@ module ActionDispatch
|
|
95
98
|
def response_body(request)
|
96
99
|
return "" unless request.get_header("action_dispatch.show_detailed_exceptions")
|
97
100
|
|
98
|
-
template = DebugView.new(
|
101
|
+
template = DebugView.new(hosts: request.env["action_dispatch.blocked_hosts"])
|
99
102
|
template.render(template: "rescues/blocked_host", layout: "rescues/layout")
|
100
103
|
end
|
101
104
|
|
102
105
|
def response(format, body)
|
103
106
|
[RESPONSE_STATUS,
|
104
|
-
{
|
105
|
-
|
107
|
+
{ Rack::CONTENT_TYPE => "#{format}; charset=#{Response.default_charset}",
|
108
|
+
Rack::CONTENT_LENGTH => body.bytesize.to_s },
|
106
109
|
[body]]
|
107
110
|
end
|
108
111
|
|
@@ -111,7 +114,7 @@ module ActionDispatch
|
|
111
114
|
|
112
115
|
return unless logger
|
113
116
|
|
114
|
-
logger.error("[#{self.class.name}] Blocked
|
117
|
+
logger.error("[#{self.class.name}] Blocked hosts: #{request.env["action_dispatch.blocked_hosts"].join(", ")}")
|
115
118
|
end
|
116
119
|
|
117
120
|
def available_logger(request)
|
@@ -131,21 +134,28 @@ module ActionDispatch
|
|
131
134
|
return @app.call(env) if @permissions.empty?
|
132
135
|
|
133
136
|
request = Request.new(env)
|
137
|
+
hosts = blocked_hosts(request)
|
134
138
|
|
135
|
-
if
|
139
|
+
if hosts.empty? || excluded?(request)
|
136
140
|
mark_as_authorized(request)
|
137
141
|
@app.call(env)
|
138
142
|
else
|
143
|
+
env["action_dispatch.blocked_hosts"] = hosts
|
139
144
|
@response_app.call(env)
|
140
145
|
end
|
141
146
|
end
|
142
147
|
|
143
148
|
private
|
144
|
-
def
|
149
|
+
def blocked_hosts(request)
|
150
|
+
hosts = []
|
151
|
+
|
145
152
|
origin_host = request.get_header("HTTP_HOST")
|
153
|
+
hosts << origin_host unless @permissions.allows?(origin_host)
|
154
|
+
|
146
155
|
forwarded_host = request.x_forwarded_host&.split(/,\s?/)&.last
|
156
|
+
hosts << forwarded_host unless forwarded_host.blank? || @permissions.allows?(forwarded_host)
|
147
157
|
|
148
|
-
|
158
|
+
hosts
|
149
159
|
end
|
150
160
|
|
151
161
|
def excluded?(request)
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActionDispatch
|
4
|
+
# = Action Dispatch \PublicExceptions
|
5
|
+
#
|
4
6
|
# When called, this middleware renders an error page. By default if an HTML
|
5
7
|
# response is expected it will render static error pages from the <tt>/public</tt>
|
6
8
|
# directory. For example when this middleware receives a 500 response it will
|
@@ -42,8 +44,8 @@ module ActionDispatch
|
|
42
44
|
end
|
43
45
|
|
44
46
|
def render_format(status, content_type, body)
|
45
|
-
[status, {
|
46
|
-
|
47
|
+
[status, { Rack::CONTENT_TYPE => "#{content_type}; charset=#{ActionDispatch::Response.default_charset}",
|
48
|
+
Rack::CONTENT_LENGTH => body.bytesize.to_s }, [body]]
|
47
49
|
end
|
48
50
|
|
49
51
|
def render_html(status)
|
@@ -53,7 +55,7 @@ module ActionDispatch
|
|
53
55
|
if found || File.exist?(path)
|
54
56
|
render_format(status, "text/html", File.read(path))
|
55
57
|
else
|
56
|
-
[404, {
|
58
|
+
[404, { Constants::X_CASCADE => "pass" }, []]
|
57
59
|
end
|
58
60
|
end
|
59
61
|
end
|
@@ -1,12 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActionDispatch
|
4
|
-
#
|
5
|
-
# callbacks, intended to assist with code reloading during development.
|
4
|
+
# = Action Dispatch \Reloader
|
6
5
|
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
6
|
+
# ActionDispatch::Reloader wraps the request with callbacks provided by
|
7
|
+
# ActiveSupport::Reloader, intended to assist with code reloading during
|
8
|
+
# development.
|
9
|
+
#
|
10
|
+
# ActionDispatch::Reloader is included in the middleware stack only if
|
11
|
+
# reloading is enabled, which it is by the default in +development+ mode.
|
10
12
|
class Reloader < Executor
|
11
13
|
end
|
12
14
|
end
|