actionpack 5.2.8.1 → 6.0.0.beta1

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.

Files changed (108) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +109 -482
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/lib/abstract_controller/base.rb +4 -2
  6. data/lib/abstract_controller/caching/fragments.rb +6 -21
  7. data/lib/abstract_controller/callbacks.rb +12 -0
  8. data/lib/abstract_controller/collector.rb +1 -1
  9. data/lib/abstract_controller/helpers.rb +2 -2
  10. data/lib/abstract_controller/railties/routes_helpers.rb +1 -1
  11. data/lib/action_controller/api.rb +2 -1
  12. data/lib/action_controller/base.rb +2 -7
  13. data/lib/action_controller/caching.rb +1 -1
  14. data/lib/action_controller/log_subscriber.rb +8 -5
  15. data/lib/action_controller/metal/conditional_get.rb +9 -3
  16. data/lib/action_controller/metal/data_streaming.rb +5 -6
  17. data/lib/action_controller/metal/default_headers.rb +17 -0
  18. data/lib/action_controller/metal/exceptions.rb +22 -1
  19. data/lib/action_controller/metal/flash.rb +5 -5
  20. data/lib/action_controller/metal/force_ssl.rb +17 -57
  21. data/lib/action_controller/metal/head.rb +1 -1
  22. data/lib/action_controller/metal/helpers.rb +1 -2
  23. data/lib/action_controller/metal/http_authentication.rb +21 -22
  24. data/lib/action_controller/metal/implicit_render.rb +2 -12
  25. data/lib/action_controller/metal/instrumentation.rb +3 -5
  26. data/lib/action_controller/metal/live.rb +28 -26
  27. data/lib/action_controller/metal/mime_responds.rb +13 -2
  28. data/lib/action_controller/metal/params_wrapper.rb +18 -14
  29. data/lib/action_controller/metal/redirecting.rb +32 -11
  30. data/lib/action_controller/metal/rendering.rb +1 -1
  31. data/lib/action_controller/metal/request_forgery_protection.rb +32 -97
  32. data/lib/action_controller/metal/strong_parameters.rb +57 -34
  33. data/lib/action_controller/metal/url_for.rb +1 -1
  34. data/lib/action_controller/metal.rb +2 -2
  35. data/lib/action_controller/railties/helpers.rb +1 -1
  36. data/lib/action_controller/renderer.rb +15 -2
  37. data/lib/action_controller/test_case.rb +5 -9
  38. data/lib/action_controller.rb +1 -0
  39. data/lib/action_dispatch/http/cache.rb +14 -10
  40. data/lib/action_dispatch/http/content_disposition.rb +45 -0
  41. data/lib/action_dispatch/http/content_security_policy.rb +17 -8
  42. data/lib/action_dispatch/http/filter_parameters.rb +8 -6
  43. data/lib/action_dispatch/http/filter_redirect.rb +1 -1
  44. data/lib/action_dispatch/http/headers.rb +1 -1
  45. data/lib/action_dispatch/http/mime_negotiation.rb +7 -10
  46. data/lib/action_dispatch/http/mime_type.rb +1 -5
  47. data/lib/action_dispatch/http/parameter_filter.rb +5 -79
  48. data/lib/action_dispatch/http/parameters.rb +13 -3
  49. data/lib/action_dispatch/http/request.rb +10 -13
  50. data/lib/action_dispatch/http/response.rb +14 -14
  51. data/lib/action_dispatch/http/upload.rb +5 -0
  52. data/lib/action_dispatch/http/url.rb +81 -81
  53. data/lib/action_dispatch/journey/formatter.rb +1 -1
  54. data/lib/action_dispatch/journey/nfa/simulator.rb +0 -2
  55. data/lib/action_dispatch/journey/nodes/node.rb +9 -8
  56. data/lib/action_dispatch/journey/path/pattern.rb +3 -4
  57. data/lib/action_dispatch/journey/router/utils.rb +10 -10
  58. data/lib/action_dispatch/journey/router.rb +0 -3
  59. data/lib/action_dispatch/journey/scanner.rb +11 -4
  60. data/lib/action_dispatch/journey/visitors.rb +1 -1
  61. data/lib/action_dispatch/middleware/callbacks.rb +2 -4
  62. data/lib/action_dispatch/middleware/cookies.rb +49 -70
  63. data/lib/action_dispatch/middleware/debug_exceptions.rb +32 -58
  64. data/lib/action_dispatch/middleware/debug_locks.rb +5 -5
  65. data/lib/action_dispatch/middleware/debug_view.rb +50 -0
  66. data/lib/action_dispatch/middleware/exception_wrapper.rb +36 -7
  67. data/lib/action_dispatch/middleware/executor.rb +1 -1
  68. data/lib/action_dispatch/middleware/flash.rb +1 -1
  69. data/lib/action_dispatch/middleware/host_authorization.rb +103 -0
  70. data/lib/action_dispatch/middleware/remote_ip.rb +6 -8
  71. data/lib/action_dispatch/middleware/request_id.rb +2 -2
  72. data/lib/action_dispatch/middleware/session/abstract_store.rb +0 -14
  73. data/lib/action_dispatch/middleware/session/cache_store.rb +6 -11
  74. data/lib/action_dispatch/middleware/session/cookie_store.rb +11 -27
  75. data/lib/action_dispatch/middleware/ssl.rb +8 -8
  76. data/lib/action_dispatch/middleware/stack.rb +2 -2
  77. data/lib/action_dispatch/middleware/static.rb +5 -6
  78. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +4 -2
  79. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +45 -35
  80. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -0
  81. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -0
  82. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +20 -2
  83. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +4 -4
  84. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +2 -2
  85. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -0
  86. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
  87. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +2 -2
  88. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -1
  89. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +2 -2
  90. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +3 -0
  91. data/lib/action_dispatch/railtie.rb +1 -0
  92. data/lib/action_dispatch/request/session.rb +8 -6
  93. data/lib/action_dispatch/routing/inspector.rb +99 -50
  94. data/lib/action_dispatch/routing/mapper.rb +36 -29
  95. data/lib/action_dispatch/routing/polymorphic_routes.rb +7 -12
  96. data/lib/action_dispatch/routing/route_set.rb +11 -12
  97. data/lib/action_dispatch/routing/url_for.rb +1 -0
  98. data/lib/action_dispatch/routing.rb +3 -2
  99. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +3 -3
  100. data/lib/action_dispatch/testing/assertions/response.rb +2 -3
  101. data/lib/action_dispatch/testing/assertions/routing.rb +7 -2
  102. data/lib/action_dispatch/testing/integration.rb +11 -5
  103. data/lib/action_dispatch/testing/test_process.rb +2 -2
  104. data/lib/action_dispatch/testing/test_response.rb +4 -32
  105. data/lib/action_dispatch.rb +7 -6
  106. data/lib/action_pack/gem_version.rb +4 -4
  107. data/lib/action_pack.rb +1 -1
  108. metadata +25 -23
@@ -162,14 +162,12 @@ module ActionDispatch
162
162
  # Split the comma-separated list into an array of strings.
163
163
  ips = header.strip.split(/[,\s]+/)
164
164
  ips.select do |ip|
165
- begin
166
- # Only return IPs that are valid according to the IPAddr#new method.
167
- range = IPAddr.new(ip).to_range
168
- # We want to make sure nobody is sneaking a netmask in.
169
- range.begin == range.end
170
- rescue ArgumentError
171
- nil
172
- end
165
+ # Only return IPs that are valid according to the IPAddr#new method.
166
+ range = IPAddr.new(ip).to_range
167
+ # We want to make sure nobody is sneaking a netmask in.
168
+ range.begin == range.end
169
+ rescue ArgumentError
170
+ nil
173
171
  end
174
172
  end
175
173
 
@@ -15,7 +15,7 @@ module ActionDispatch
15
15
  # The unique request id can be used to trace a request end-to-end and would typically end up being part of log files
16
16
  # from multiple pieces of the stack.
17
17
  class RequestId
18
- X_REQUEST_ID = "X-Request-Id".freeze #:nodoc:
18
+ X_REQUEST_ID = "X-Request-Id" #:nodoc:
19
19
 
20
20
  def initialize(app)
21
21
  @app = app
@@ -30,7 +30,7 @@ module ActionDispatch
30
30
  private
31
31
  def make_request_id(request_id)
32
32
  if request_id.presence
33
- request_id.gsub(/[^\w\-@]/, "".freeze).first(255)
33
+ request_id.gsub(/[^\w\-@]/, "").first(255)
34
34
  else
35
35
  internal_request_id
36
36
  end
@@ -83,21 +83,7 @@ module ActionDispatch
83
83
  include SessionObject
84
84
 
85
85
  private
86
- def set_cookie(request, session_id, cookie)
87
- request.cookie_jar[key] = cookie
88
- end
89
- end
90
-
91
- class AbstractSecureStore < Rack::Session::Abstract::PersistedSecure
92
- include Compatibility
93
- include StaleSessionCheck
94
- include SessionObject
95
-
96
- def generate_sid
97
- Rack::Session::SessionId.new(super)
98
- end
99
86
 
100
- private
101
87
  def set_cookie(request, session_id, cookie)
102
88
  request.cookie_jar[key] = cookie
103
89
  end
@@ -12,7 +12,7 @@ module ActionDispatch
12
12
  # * <tt>cache</tt> - The cache to use. If it is not specified, <tt>Rails.cache</tt> will be used.
13
13
  # * <tt>expire_after</tt> - The length of time a session will be stored before automatically expiring.
14
14
  # By default, the <tt>:expires_in</tt> option of the cache is used.
15
- class CacheStore < AbstractSecureStore
15
+ class CacheStore < AbstractStore
16
16
  def initialize(app, options = {})
17
17
  @cache = options[:cache] || Rails.cache
18
18
  options[:expire_after] ||= @cache.options[:expires_in]
@@ -21,7 +21,7 @@ module ActionDispatch
21
21
 
22
22
  # Get a session from the cache.
23
23
  def find_session(env, sid)
24
- unless sid && (session = get_session_with_fallback(sid))
24
+ unless sid && (session = @cache.read(cache_key(sid)))
25
25
  sid, session = generate_sid, {}
26
26
  end
27
27
  [sid, session]
@@ -29,7 +29,7 @@ module ActionDispatch
29
29
 
30
30
  # Set a session in the cache.
31
31
  def write_session(env, sid, session, options)
32
- key = cache_key(sid.private_id)
32
+ key = cache_key(sid)
33
33
  if session
34
34
  @cache.write(key, session, expires_in: options[:expire_after])
35
35
  else
@@ -40,19 +40,14 @@ module ActionDispatch
40
40
 
41
41
  # Remove a session from the cache.
42
42
  def delete_session(env, sid, options)
43
- @cache.delete(cache_key(sid.private_id))
44
- @cache.delete(cache_key(sid.public_id))
43
+ @cache.delete(cache_key(sid))
45
44
  generate_sid
46
45
  end
47
46
 
48
47
  private
49
48
  # Turn the session id into a cache key.
50
- def cache_key(id)
51
- "_session_id:#{id}"
52
- end
53
-
54
- def get_session_with_fallback(sid)
55
- @cache.read(cache_key(sid.private_id)) || @cache.read(cache_key(sid.public_id))
49
+ def cache_key(sid)
50
+ "_session_id:#{sid}"
56
51
  end
57
52
  end
58
53
  end
@@ -16,23 +16,17 @@ module ActionDispatch
16
16
  # The cookie jar used for storage is automatically configured to be the
17
17
  # best possible option given your application's configuration.
18
18
  #
19
- # If you only have secret_token set, your cookies will be signed, but
20
- # not encrypted. This means a user cannot alter their +user_id+ without
21
- # knowing your app's secret key, but can easily read their +user_id+. This
22
- # was the default for Rails 3 apps.
23
- #
24
19
  # Your cookies will be encrypted using your apps secret_key_base. This
25
20
  # goes a step further than signed cookies in that encrypted cookies cannot
26
21
  # be altered or read by users. This is the default starting in Rails 4.
27
22
  #
28
- # Configure your session store in <tt>config/initializers/session_store.rb</tt>:
23
+ # Configure your session store in an initializer:
29
24
  #
30
25
  # Rails.application.config.session_store :cookie_store, key: '_your_app_session'
31
26
  #
32
- # In the development and test environments your application's secret key base is
33
- # generated by Rails and stored in a temporary file in <tt>tmp/development_secret.txt</tt>.
34
- # In all other environments, it is stored encrypted in the
35
- # <tt>config/credentials.yml.enc</tt> file.
27
+ # By default, your secret key base is derived from your application name in
28
+ # the test and development environments. In all other environments, it is stored
29
+ # encrypted in the <tt>config/credentials.yml.enc</tt> file.
36
30
  #
37
31
  # If your application was not updated to Rails 5.2 defaults, the secret_key_base
38
32
  # will be found in the old <tt>config/secrets.yml</tt> file.
@@ -51,16 +45,7 @@ module ActionDispatch
51
45
  # would set the session cookie to expire automatically 14 days after creation.
52
46
  # Other useful options include <tt>:key</tt>, <tt>:secure</tt> and
53
47
  # <tt>:httponly</tt>.
54
- class CookieStore < AbstractSecureStore
55
- class SessionId < DelegateClass(Rack::Session::SessionId)
56
- attr_reader :cookie_value
57
-
58
- def initialize(session_id, cookie_value = {})
59
- super(session_id)
60
- @cookie_value = cookie_value
61
- end
62
- end
63
-
48
+ class CookieStore < AbstractStore
64
49
  def initialize(app, options = {})
65
50
  super(app, options.merge!(cookie_only: true))
66
51
  end
@@ -68,7 +53,7 @@ module ActionDispatch
68
53
  def delete_session(req, session_id, options)
69
54
  new_sid = generate_sid unless options[:drop]
70
55
  # Reset hash and Assign the new session id
71
- req.set_header("action_dispatch.request.unsigned_session_cookie", new_sid ? { "session_id" => new_sid.public_id } : {})
56
+ req.set_header("action_dispatch.request.unsigned_session_cookie", new_sid ? { "session_id" => new_sid } : {})
72
57
  new_sid
73
58
  end
74
59
 
@@ -76,7 +61,7 @@ module ActionDispatch
76
61
  stale_session_check! do
77
62
  data = unpacked_cookie_data(req)
78
63
  data = persistent_session_id!(data)
79
- [Rack::Session::SessionId.new(data["session_id"]), data]
64
+ [data["session_id"], data]
80
65
  end
81
66
  end
82
67
 
@@ -84,8 +69,7 @@ module ActionDispatch
84
69
 
85
70
  def extract_session_id(req)
86
71
  stale_session_check! do
87
- sid = unpacked_cookie_data(req)["session_id"]
88
- sid && Rack::Session::SessionId.new(sid)
72
+ unpacked_cookie_data(req)["session_id"]
89
73
  end
90
74
  end
91
75
 
@@ -103,13 +87,13 @@ module ActionDispatch
103
87
 
104
88
  def persistent_session_id!(data, sid = nil)
105
89
  data ||= {}
106
- data["session_id"] ||= sid || generate_sid.public_id
90
+ data["session_id"] ||= sid || generate_sid
107
91
  data
108
92
  end
109
93
 
110
94
  def write_session(req, sid, session_data, options)
111
- session_data["session_id"] = sid.public_id
112
- SessionId.new(sid, session_data)
95
+ session_data["session_id"] = sid
96
+ session_data
113
97
  end
114
98
 
115
99
  def set_cookie(request, session_id, cookie)
@@ -83,7 +83,7 @@ module ActionDispatch
83
83
 
84
84
  private
85
85
  def set_hsts_header!(headers)
86
- headers["Strict-Transport-Security".freeze] ||= @hsts_header
86
+ headers["Strict-Transport-Security"] ||= @hsts_header
87
87
  end
88
88
 
89
89
  def normalize_hsts_options(options)
@@ -102,23 +102,23 @@ module ActionDispatch
102
102
 
103
103
  # https://tools.ietf.org/html/rfc6797#section-6.1
104
104
  def build_hsts_header(hsts)
105
- value = "max-age=#{hsts[:expires].to_i}".dup
105
+ value = +"max-age=#{hsts[:expires].to_i}"
106
106
  value << "; includeSubDomains" if hsts[:subdomains]
107
107
  value << "; preload" if hsts[:preload]
108
108
  value
109
109
  end
110
110
 
111
111
  def flag_cookies_as_secure!(headers)
112
- if cookies = headers["Set-Cookie".freeze]
113
- cookies = cookies.split("\n".freeze)
112
+ if cookies = headers["Set-Cookie"]
113
+ cookies = cookies.split("\n")
114
114
 
115
- headers["Set-Cookie".freeze] = cookies.map { |cookie|
116
- if cookie !~ /;\s*secure\s*(;|$)/i
115
+ headers["Set-Cookie"] = cookies.map { |cookie|
116
+ if !/;\s*secure\s*(;|$)/i.match?(cookie)
117
117
  "#{cookie}; secure"
118
118
  else
119
119
  cookie
120
120
  end
121
- }.join("\n".freeze)
121
+ }.join("\n")
122
122
  end
123
123
  end
124
124
 
@@ -141,7 +141,7 @@ module ActionDispatch
141
141
  host = @redirect[:host] || request.host
142
142
  port = @redirect[:port] || request.port
143
143
 
144
- location = "https://#{host}".dup
144
+ location = +"https://#{host}"
145
145
  location << ":#{port}" if port != 80 && port != 443
146
146
  location << request.fullpath
147
147
  location
@@ -97,8 +97,8 @@ module ActionDispatch
97
97
  middlewares.push(build_middleware(klass, args, block))
98
98
  end
99
99
 
100
- def build(app = nil, &block)
101
- middlewares.freeze.reverse.inject(app || block) { |a, e| e.build(a) }
100
+ def build(app = Proc.new)
101
+ middlewares.freeze.reverse.inject(app) { |a, e| e.build(a) }
102
102
  end
103
103
 
104
104
  private
@@ -41,7 +41,6 @@ module ActionDispatch
41
41
  rescue SystemCallError
42
42
  false
43
43
  end
44
-
45
44
  }
46
45
  return ::Rack::Utils.escape_path(match).b
47
46
  end
@@ -69,7 +68,7 @@ module ActionDispatch
69
68
 
70
69
  headers["Vary"] = "Accept-Encoding" if gzip_path
71
70
 
72
- return [status, headers, body]
71
+ [status, headers, body]
73
72
  ensure
74
73
  request.path_info = path
75
74
  end
@@ -80,7 +79,7 @@ module ActionDispatch
80
79
  end
81
80
 
82
81
  def content_type(path)
83
- ::Rack::Mime.mime_type(::File.extname(path), "text/plain".freeze)
82
+ ::Rack::Mime.mime_type(::File.extname(path), "text/plain")
84
83
  end
85
84
 
86
85
  def gzip_encoding_accepted?(request)
@@ -90,8 +89,8 @@ module ActionDispatch
90
89
  def gzip_file_path(path)
91
90
  can_gzip_mime = content_type(path) =~ /\A(?:text\/|application\/javascript)/
92
91
  gzip_path = "#{path}.gz"
93
- if can_gzip_mime && File.exist?(File.join(@root, ::Rack::Utils.unescape_path(gzip_path).b))
94
- gzip_path.b
92
+ if can_gzip_mime && File.exist?(File.join(@root, ::Rack::Utils.unescape_path(gzip_path)))
93
+ gzip_path
95
94
  else
96
95
  false
97
96
  end
@@ -117,7 +116,7 @@ module ActionDispatch
117
116
  req = Rack::Request.new env
118
117
 
119
118
  if req.get? || req.head?
120
- path = req.path_info.chomp("/".freeze)
119
+ path = req.path_info.chomp("/")
121
120
  if match = @file_handler.match?(path)
122
121
  req.path_info = match
123
122
  return @file_handler.serve(req)
@@ -1,6 +1,8 @@
1
- <% @source_extracts.each_with_index do |source_extract, index| %>
1
+ <% error_index = local_assigns[:error_index] || 0 %>
2
+
3
+ <% source_extracts.each_with_index do |source_extract, index| %>
2
4
  <% if source_extract[:code] %>
3
- <div class="source <%="hidden" if @show_source_idx != index%>" id="frame-source-<%=index%>">
5
+ <div class="source <%= "hidden" if show_source_idx != index %>" id="frame-source-<%= error_index %>-<%= index %>">
4
6
  <div class="info">
5
7
  Extracted source (around line <strong>#<%= source_extract[:line_number] %></strong>):
6
8
  </div>
@@ -1,52 +1,62 @@
1
- <% names = @traces.keys %>
1
+ <% names = traces.keys %>
2
+ <% error_index = local_assigns[:error_index] || 0 %>
2
3
 
3
4
  <p><code>Rails.root: <%= defined?(Rails) && Rails.respond_to?(:root) ? Rails.root : "unset" %></code></p>
4
5
 
5
- <div id="traces">
6
+ <div id="traces-<%= error_index %>">
6
7
  <% names.each do |name| %>
7
8
  <%
8
- show = "show('#{name.gsub(/\s/, '-')}');"
9
- hide = (names - [name]).collect {|hide_name| "hide('#{hide_name.gsub(/\s/, '-')}');"}
9
+ show = "show('#{name.gsub(/\s/, '-')}-#{error_index}');"
10
+ hide = (names - [name]).collect {|hide_name| "hide('#{hide_name.gsub(/\s/, '-')}-#{error_index}');"}
10
11
  %>
11
12
  <a href="#" onclick="<%= hide.join %><%= show %>; return false;"><%= name %></a> <%= '|' unless names.last == name %>
12
13
  <% end %>
13
14
 
14
- <% @traces.each do |name, trace| %>
15
- <div id="<%= name.gsub(/\s/, '-') %>" style="display: <%= (name == @trace_to_show) ? 'block' : 'none' %>;">
16
- <pre><code><% trace.each do |frame| %><a class="trace-frames" data-frame-id="<%= frame[:id] %>" href="#"><%= frame[:trace] %></a><br><% end %></code></pre>
15
+ <% traces.each do |name, trace| %>
16
+ <div id="<%= "#{name.gsub(/\s/, '-')}-#{error_index}" %>" style="display: <%= (name == trace_to_show) ? 'block' : 'none' %>;">
17
+ <code style="font-size: 11px;">
18
+ <% trace.each do |frame| %>
19
+ <a class="trace-frames trace-frames-<%= error_index %>" data-exception-object-id="<%= frame[:exception_object_id] %>" data-frame-id="<%= frame[:id] %>" href="#">
20
+ <%= frame[:trace] %>
21
+ </a>
22
+ <br>
23
+ <% end %>
24
+ </code>
17
25
  </div>
18
26
  <% end %>
19
27
 
20
28
  <script type="text/javascript">
21
- var traceFrames = document.getElementsByClassName('trace-frames');
22
- var selectedFrame, currentSource = document.getElementById('frame-source-0');
23
-
24
- // Add click listeners for all stack frames
25
- for (var i = 0; i < traceFrames.length; i++) {
26
- traceFrames[i].addEventListener('click', function(e) {
27
- e.preventDefault();
28
- var target = e.target;
29
- var frame_id = target.dataset.frameId;
30
-
31
- if (selectedFrame) {
32
- selectedFrame.className = selectedFrame.className.replace("selected", "");
33
- }
34
-
35
- target.className += " selected";
36
- selectedFrame = target;
37
-
38
- // Change the extracted source code
39
- changeSourceExtract(frame_id);
40
- });
41
-
42
- function changeSourceExtract(frame_id) {
43
- var el = document.getElementById('frame-source-' + frame_id);
44
- if (currentSource && el) {
45
- currentSource.className += " hidden";
46
- el.className = el.className.replace(" hidden", "");
47
- currentSource = el;
29
+ (function() {
30
+ var traceFrames = document.getElementsByClassName('trace-frames-<%= error_index %>');
31
+ var selectedFrame, currentSource = document.getElementById('frame-source-<%= error_index %>-0');
32
+
33
+ // Add click listeners for all stack frames
34
+ for (var i = 0; i < traceFrames.length; i++) {
35
+ traceFrames[i].addEventListener('click', function(e) {
36
+ e.preventDefault();
37
+ var target = e.target;
38
+ var frame_id = target.dataset.frameId;
39
+
40
+ if (selectedFrame) {
41
+ selectedFrame.className = selectedFrame.className.replace("selected", "");
42
+ }
43
+
44
+ target.className += " selected";
45
+ selectedFrame = target;
46
+
47
+ // Change the extracted source code
48
+ changeSourceExtract(frame_id);
49
+ });
50
+
51
+ function changeSourceExtract(frame_id) {
52
+ var el = document.getElementById('frame-source-<%= error_index %>-' + frame_id);
53
+ if (currentSource && el) {
54
+ currentSource.className += " hidden";
55
+ el.className = el.className.replace(" hidden", "");
56
+ currentSource = el;
57
+ }
48
58
  }
49
59
  }
50
- }
60
+ })();
51
61
  </script>
52
62
  </div>
@@ -0,0 +1,7 @@
1
+ <header>
2
+ <h1>Blocked host: <%= @host %></h1>
3
+ </header>
4
+ <div id="container">
5
+ <h2>To allow requests to <%= @host %>, add the following configuration:</h2>
6
+ <pre>Rails.application.config.hosts &lt;&lt; "<%= @host %>"</pre>
7
+ </div>
@@ -0,0 +1,5 @@
1
+ Blocked host: <%= @host %>
2
+
3
+ To allow requests to <%= @host %>, add the following configuration:
4
+
5
+ Rails.application.config.hosts << "<%= @host %>"
@@ -10,7 +10,25 @@
10
10
  <div id="container">
11
11
  <h2><%= h @exception.message %></h2>
12
12
 
13
- <%= render template: "rescues/_source" %>
14
- <%= render template: "rescues/_trace" %>
13
+ <%= render "rescues/source", source_extracts: @source_extracts, show_source_idx: @show_source_idx, error_index: 0 %>
14
+ <%= render "rescues/trace", traces: @traces, trace_to_show: @trace_to_show, error_index: 0 %>
15
+
16
+ <% if @exception.cause %>
17
+ <h2>Exception Causes</h2>
18
+ <% end %>
19
+
20
+ <% @exception_wrapper.wrapped_causes.each.with_index(1) do |wrapper, index| %>
21
+ <div class="details">
22
+ <a class="summary" href="#" style="color: #F0F0F0; text-decoration: none; background: #C52F24; border-bottom: none;" onclick="return toggle(<%= wrapper.exception.object_id %>)">
23
+ <%= wrapper.exception.class.name %>: <%= h wrapper.exception.message %>
24
+ </a>
25
+ </div>
26
+
27
+ <div id="<%= wrapper.exception.object_id %>" style="display: none;">
28
+ <%= render "rescues/source", source_extracts: wrapper.source_extracts, show_source_idx: wrapper.source_to_show_id, error_index: index %>
29
+ <%= render "rescues/trace", traces: wrapper.traces, trace_to_show: wrapper.trace_to_show, error_index: index %>
30
+ </div>
31
+ <% end %>
32
+
15
33
  <%= render template: "rescues/_request_and_response" %>
16
34
  </div>
@@ -10,12 +10,12 @@
10
10
  <div id="container">
11
11
  <h2>
12
12
  <%= h @exception.message %>
13
- <% if %r{#{ActiveStorage::Blob.table_name}|#{ActiveStorage::Attachment.table_name}}.match?(@exception.message) %>
14
- <br />To resolve this issue run: bin/rails active_storage:install
13
+ <% if @exception.message.match? %r{#{ActiveStorage::Blob.table_name}|#{ActiveStorage::Attachment.table_name}} %>
14
+ <br />To resolve this issue run: rails active_storage:install
15
15
  <% end %>
16
16
  </h2>
17
17
 
18
- <%= render template: "rescues/_source" %>
19
- <%= render template: "rescues/_trace" %>
18
+ <%= render "rescues/source", source_extracts: @source_extracts, show_source_idx: @show_source_idx %>
19
+ <%= render "rescues/trace", traces: @traces, trace_to_show: @trace_to_show %>
20
20
  <%= render template: "rescues/_request_and_response" %>
21
21
  </div>
@@ -4,8 +4,8 @@
4
4
  <% end %>
5
5
 
6
6
  <%= @exception.message %>
7
- <% if %r{#{ActiveStorage::Blob.table_name}|#{ActiveStorage::Attachment.table_name}}.match?(@exception.message) %>
8
- To resolve this issue run: bin/rails active_storage:install
7
+ <% if @exception.message.match? %r{#{ActiveStorage::Blob.table_name}|#{ActiveStorage::Attachment.table_name}} %>
8
+ To resolve this issue run: rails active_storage:install
9
9
  <% end %>
10
10
 
11
11
  <%= render template: "rescues/_source" %>
@@ -0,0 +1,19 @@
1
+ <header>
2
+ <h1>No template for interactive request</h1>
3
+ </header>
4
+
5
+ <div id="container">
6
+ <h2><%= h @exception.message %></h2>
7
+
8
+ <p class="summary">
9
+ <strong>NOTE!</strong><br>
10
+ Unless told otherwise, Rails expects an action to render a template with the same name,<br>
11
+ contained in a folder named after its controller.
12
+
13
+ If this controller is an API responding with 204 (No Content), <br>
14
+ which does not require a template,
15
+ then this error will occur when trying to access it via browser,<br>
16
+ since we expect an HTML template
17
+ to be rendered for such requests. If that's the case, carry on.
18
+ </p>
19
+ </div>
@@ -0,0 +1,3 @@
1
+ Missing exact template
2
+
3
+ <%= @exception.message %>
@@ -5,7 +5,7 @@
5
5
  <div id="container">
6
6
  <h2><%= h @exception.message %></h2>
7
7
 
8
- <%= render template: "rescues/_source" %>
9
- <%= render template: "rescues/_trace" %>
8
+ <%= render "rescues/source", source_extracts: @source_extracts, show_source_idx: @show_source_idx %>
9
+ <%= render "rescues/trace", traces: @traces, trace_to_show: @trace_to_show %>
10
10
  <%= render template: "rescues/_request_and_response" %>
11
11
  </div>
@@ -14,7 +14,7 @@
14
14
  </p>
15
15
  <% end %>
16
16
 
17
- <%= render template: "rescues/_trace" %>
17
+ <%= render "rescues/trace", traces: @traces, trace_to_show: @trace_to_show %>
18
18
 
19
19
  <% if @routes_inspector %>
20
20
  <h2>
@@ -11,10 +11,10 @@
11
11
  </p>
12
12
  <pre><code><%= h @exception.message %></code></pre>
13
13
 
14
- <%= render template: "rescues/_source" %>
14
+ <%= render "rescues/source", source_extracts: @source_extracts, show_source_idx: @show_source_idx %>
15
15
 
16
16
  <p><%= @exception.sub_template_message %></p>
17
17
 
18
- <%= render template: "rescues/_trace" %>
18
+ <%= render "rescues/trace", traces: @traces, trace_to_show: @trace_to_show %>
19
19
  <%= render template: "rescues/_request_and_response" %>
20
20
  </div>
@@ -197,4 +197,7 @@
197
197
 
198
198
  setupMatchPaths();
199
199
  setupRouteToggleHelperLinks();
200
+
201
+ // Focus the search input after page has loaded
202
+ document.getElementById('search').focus();
200
203
  </script>
@@ -21,6 +21,7 @@ module ActionDispatch
21
21
  config.action_dispatch.encrypted_signed_cookie_salt = "signed encrypted cookie"
22
22
  config.action_dispatch.authenticated_encrypted_cookie_salt = "authenticated encrypted cookie"
23
23
  config.action_dispatch.use_authenticated_cookie_encryption = false
24
+ config.action_dispatch.use_cookies_with_metadata = false
24
25
  config.action_dispatch.perform_deep_munge = true
25
26
 
26
27
  config.action_dispatch.default_headers = {
@@ -90,13 +90,15 @@ module ActionDispatch
90
90
  # +nil+ if the given key is not found in the session.
91
91
  def [](key)
92
92
  load_for_read!
93
- key = key.to_s
93
+ @delegate[key.to_s]
94
+ end
94
95
 
95
- if key == "session_id"
96
- id && id.public_id
97
- else
98
- @delegate[key]
99
- end
96
+ # Returns the nested value specified by the sequence of keys, returning
97
+ # +nil+ if any intermediate step is +nil+.
98
+ def dig(*keys)
99
+ load_for_read!
100
+ keys = keys.map.with_index { |key, i| i.zero? ? key.to_s : key }
101
+ @delegate.dig(*keys)
100
102
  end
101
103
 
102
104
  # Returns true if the session has the given key or false.