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.

Files changed (107) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +101 -450
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +2 -3
  5. data/lib/abstract_controller/asset_paths.rb +1 -1
  6. data/lib/abstract_controller/base.rb +7 -21
  7. data/lib/abstract_controller/caching/fragments.rb +2 -2
  8. data/lib/abstract_controller/caching.rb +1 -1
  9. data/lib/abstract_controller/callbacks.rb +9 -8
  10. data/lib/abstract_controller/collector.rb +4 -2
  11. data/lib/abstract_controller/error.rb +1 -1
  12. data/lib/abstract_controller/helpers.rb +3 -2
  13. data/lib/abstract_controller/logger.rb +1 -1
  14. data/lib/abstract_controller/railties/routes_helpers.rb +2 -0
  15. data/lib/abstract_controller/translation.rb +0 -2
  16. data/lib/abstract_controller/url_for.rb +4 -6
  17. data/lib/action_controller/api.rb +1 -1
  18. data/lib/action_controller/log_subscriber.rb +3 -1
  19. data/lib/action_controller/metal/conditional_get.rb +38 -1
  20. data/lib/action_controller/metal/content_security_policy.rb +1 -1
  21. data/lib/action_controller/metal/cookies.rb +1 -1
  22. data/lib/action_controller/metal/data_streaming.rb +5 -13
  23. data/lib/action_controller/metal/exceptions.rb +19 -30
  24. data/lib/action_controller/metal/flash.rb +6 -2
  25. data/lib/action_controller/metal/http_authentication.rb +15 -15
  26. data/lib/action_controller/metal/instrumentation.rb +55 -52
  27. data/lib/action_controller/metal/live.rb +42 -2
  28. data/lib/action_controller/metal/mime_responds.rb +3 -3
  29. data/lib/action_controller/metal/params_wrapper.rb +7 -7
  30. data/lib/action_controller/metal/permissions_policy.rb +1 -1
  31. data/lib/action_controller/metal/query_tags.rb +16 -0
  32. data/lib/action_controller/metal/redirecting.rb +50 -16
  33. data/lib/action_controller/metal/rendering.rb +7 -7
  34. data/lib/action_controller/metal/request_forgery_protection.rb +64 -20
  35. data/lib/action_controller/metal/rescue.rb +1 -1
  36. data/lib/action_controller/metal/streaming.rb +1 -3
  37. data/lib/action_controller/metal/strong_parameters.rb +24 -28
  38. data/lib/action_controller/metal/testing.rb +0 -2
  39. data/lib/action_controller/metal.rb +7 -10
  40. data/lib/action_controller/railtie.rb +42 -5
  41. data/lib/action_controller/test_case.rb +6 -2
  42. data/lib/action_controller.rb +2 -5
  43. data/lib/action_dispatch/http/cache.rb +13 -6
  44. data/lib/action_dispatch/http/content_security_policy.rb +39 -35
  45. data/lib/action_dispatch/http/filter_parameters.rb +5 -0
  46. data/lib/action_dispatch/http/mime_negotiation.rb +13 -3
  47. data/lib/action_dispatch/http/mime_type.rb +9 -11
  48. data/lib/action_dispatch/http/parameters.rb +4 -4
  49. data/lib/action_dispatch/http/permissions_policy.rb +1 -1
  50. data/lib/action_dispatch/http/request.rb +10 -19
  51. data/lib/action_dispatch/http/response.rb +3 -3
  52. data/lib/action_dispatch/http/url.rb +9 -10
  53. data/lib/action_dispatch/journey/gtg/builder.rb +11 -12
  54. data/lib/action_dispatch/journey/gtg/simulator.rb +10 -4
  55. data/lib/action_dispatch/journey/gtg/transition_table.rb +77 -21
  56. data/lib/action_dispatch/journey/nodes/node.rb +70 -5
  57. data/lib/action_dispatch/journey/path/pattern.rb +22 -13
  58. data/lib/action_dispatch/journey/route.rb +5 -12
  59. data/lib/action_dispatch/journey/router/utils.rb +2 -2
  60. data/lib/action_dispatch/journey/router.rb +1 -1
  61. data/lib/action_dispatch/journey/routes.rb +3 -3
  62. data/lib/action_dispatch/journey/visualizer/fsm.js +49 -24
  63. data/lib/action_dispatch/journey/visualizer/index.html.erb +1 -1
  64. data/lib/action_dispatch/middleware/actionable_exceptions.rb +0 -1
  65. data/lib/action_dispatch/middleware/cookies.rb +7 -3
  66. data/lib/action_dispatch/middleware/debug_exceptions.rb +6 -4
  67. data/lib/action_dispatch/middleware/debug_locks.rb +3 -3
  68. data/lib/action_dispatch/middleware/exception_wrapper.rb +4 -0
  69. data/lib/action_dispatch/middleware/flash.rb +9 -11
  70. data/lib/action_dispatch/middleware/host_authorization.rb +15 -34
  71. data/lib/action_dispatch/middleware/remote_ip.rb +16 -4
  72. data/lib/action_dispatch/middleware/session/abstract_store.rb +1 -1
  73. data/lib/action_dispatch/middleware/show_exceptions.rb +7 -9
  74. data/lib/action_dispatch/middleware/stack.rb +50 -9
  75. data/lib/action_dispatch/middleware/static.rb +2 -5
  76. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +1 -1
  77. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +4 -11
  78. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +2 -2
  79. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +3 -3
  80. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +1 -1
  81. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +4 -4
  82. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +3 -3
  83. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +28 -18
  84. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +3 -3
  85. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +3 -3
  86. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +3 -3
  87. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +3 -3
  88. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +3 -3
  89. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +5 -14
  90. data/lib/action_dispatch/railtie.rb +8 -2
  91. data/lib/action_dispatch/request/session.rb +43 -13
  92. data/lib/action_dispatch/routing/mapper.rb +44 -72
  93. data/lib/action_dispatch/routing/redirection.rb +0 -2
  94. data/lib/action_dispatch/routing/route_set.rb +9 -6
  95. data/lib/action_dispatch/routing/routes_proxy.rb +1 -1
  96. data/lib/action_dispatch/routing/url_for.rb +1 -2
  97. data/lib/action_dispatch/routing.rb +2 -2
  98. data/lib/action_dispatch/system_test_case.rb +5 -5
  99. data/lib/action_dispatch/system_testing/driver.rb +24 -4
  100. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +10 -6
  101. data/lib/action_dispatch/testing/assertions.rb +2 -5
  102. data/lib/action_dispatch/testing/integration.rb +6 -8
  103. data/lib/action_dispatch/testing/test_process.rb +12 -9
  104. data/lib/action_dispatch.rb +1 -1
  105. data/lib/action_pack/gem_version.rb +4 -4
  106. data/lib/action_pack.rb +1 -1
  107. 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('polygon')
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 = tt();
80
- var states = [0];
81
- var regexp_states = table['regexp_states'];
82
- var string_states = table['string_states'];
83
- var accepting = table['accepting'];
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 state = states[key];
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
- if(string_states[state] && string_states[state][token]) {
93
- var new_state = string_states[state][token];
94
- highlight_edge(state, new_state);
95
- highlight_state(new_state);
96
- new_states.push(new_state);
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
- if(re.test(token)) {
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 state = states[key];
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
- for(var mkey in svg_edges[state]) {
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" type="text/javascript"></script>
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">
@@ -2,7 +2,6 @@
2
2
 
3
3
  require "erb"
4
4
  require "uri"
5
- require "action_dispatch/http/request"
6
5
  require "active_support/actionable_error"
7
6
 
8
7
  module ActionDispatch
@@ -7,7 +7,7 @@ require "active_support/json"
7
7
  require "rack/utils"
8
8
 
9
9
  module ActionDispatch
10
- class Request
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 based. Other data types need to be serialized.
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 #:nodoc:
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, array)
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 known to
13
- # the interlock, which lock level they are holding or awaiting, and their
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 = self.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 (!session.respond_to?(:loaded?) || session.loaded?) && # reset_session uses {}, which doesn't implement #loaded?
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 #:nodoc:
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) #:nodoc:
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 #:nodoc:
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 = []) #:nodoc:
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) #:nodoc:
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) #:nodoc:
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 #:nodoc:
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
- if allowed.is_a?(IPAddr)
41
- begin
42
- allowed === extract_hostname(host)
43
- rescue
44
- # IPAddr#=== raises an error if you give it a hostname instead of
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}#{PORT_REGEX}?\z/
47
+ /\A#{host}\z/
67
48
  end
68
49
 
69
50
  def sanitize_string(host)
70
51
  if host.start_with?(".")
71
- /\A([a-z0-9-]+\.)?#{Regexp.escape(host[1..-1])}#{PORT_REGEX}?\z/i
52
+ /\A(.+\.)?#{Regexp.escape(host[1..-1])}\z/i
72
53
  else
73
- /\A#{Regexp.escape host}#{PORT_REGEX}?\z/i
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 6.2.
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&.split(/,\s?/)&.last
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 Array of string, IPAddr, or
55
- # Regexp objects which will be used instead of +TRUSTED_PROXIES+. If a
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 #:nodoc:
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 FAILSAFE_RESPONSE.
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
- FAILSAFE_RESPONSE
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 { |x| yield x }
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) if respond_to?(:ruby2_keywords, true)
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) if respond_to?(:ruby2_keywords, true)
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) if respond_to?(:ruby2_keywords, true)
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) if respond_to?(:ruby2_keywords, true)
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.delete_if { |m| m.klass == target }
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) if respond_to?(:ruby2_keywords, true)
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 : middlewares.index { |m| m.klass == 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
- file_stat = File.stat(File.join(@root, path.b))
141
- rescue SystemCallError
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)
@@ -11,7 +11,7 @@
11
11
  <b>Did you mean?</b>
12
12
  <ul>
13
13
  <% corrections.each do |correction| %>
14
- <li style="list-style-type: none"><%= h correction %></li>
14
+ <li class="correction"><%= h correction %></li>
15
15
  <% end %>
16
16
  </ul>
17
17
  <% end %>
@@ -1,24 +1,17 @@
1
- <% unless @exception.blamed_files.blank? %>
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" style="display:none"><pre><%= debug_hash @request.session %></pre></div>
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" style="display:none"><pre><%= debug_hash @request.env.slice(*@request.class::ENV_METHODS) %></pre></div>
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 style="margin-top: 30px">Response</h2>
16
+ <h2 class="response-heading">Response</h2>
24
17
  <p><b>Headers</b>:</p> <pre><%= debug_headers(defined?(@response) ? @response.headers : {}) %></pre>