actionpack 7.0.3.1 → 7.0.4.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 57e1640af30905c2b4053161ccf2ac7f772a028c8a731ea8e073c1a1f7b6befb
4
- data.tar.gz: 4b0a7301c52d04a348f59f5db06b3aa17407748c52a0a904581c3f5a37c42d2b
3
+ metadata.gz: 5f31d845cb672a69a48bf5a99d24da4cc0a1911dd90592b7f569954a08040d32
4
+ data.tar.gz: 4f82a27ee5dba8c642621ab247ef345b7daff0e9e4fe25c3ba81163a2a31b8d5
5
5
  SHA512:
6
- metadata.gz: 91557785e0f785dd9c9c2e1157997c433e0ab6296d13de9dfc1c6e278baeb82e5d980534851158d8a8b99b6d4068d923e856a7877293e839670fb55e99253afd
7
- data.tar.gz: b8681d6f414cddd1be9ef4d57ff4a2d9a8a1fdaabff89e2c47e4420a0e2f65d8157c364df278b080a26f9646d16a78d0ea9bd9fd95ae9c78dd5cb7aa19816005
6
+ metadata.gz: 7094329330497de30fa9dbae232a2563a3699129681dbba34092db10f3e07d97b9905abe2d4339f50c39b8f45c6e9765a77523c379dbe53f3ff96cb544586483
7
+ data.tar.gz: db8c045d237562750468b511cd54990c2fe0069fe7942e663f013f6cdfa30e7211b8f9874cdc7187dc0b16f1238cb331dde86bbd38f796688935e541b0fcac25
data/CHANGELOG.md CHANGED
@@ -1,3 +1,31 @@
1
+ ## Rails 7.0.4.1 (January 17, 2023) ##
2
+
3
+ * Fix sec issue with _url_host_allowed?
4
+
5
+ Disallow certain strings from `_url_host_allowed?` to avoid a redirect
6
+ to malicious sites.
7
+
8
+ [CVE-2023-22797]
9
+
10
+ * Avoid regex backtracking on If-None-Match header
11
+
12
+ [CVE-2023-22795]
13
+
14
+ * Use string#split instead of regex for domain parts
15
+
16
+ [CVE-2023-22792]
17
+
18
+
19
+ ## Rails 7.0.4 (September 09, 2022) ##
20
+
21
+ * Prevent `ActionDispatch::ServerTiming` from overwriting existing values in `Server-Timing`.
22
+
23
+ Previously, if another middleware down the chain set `Server-Timing` header,
24
+ it would overwritten by `ActionDispatch::ServerTiming`.
25
+
26
+ *Jakub Malinowski*
27
+
28
+
1
29
  ## Rails 7.0.3.1 (July 12, 2022) ##
2
30
 
3
31
  * No changes.
@@ -110,7 +110,7 @@ module AbstractController
110
110
  # The last two assume that <tt>"foo".camelize</tt> returns "Foo".
111
111
  #
112
112
  # When strings or symbols are passed, the method finds the actual module
113
- # object using +String#constantize+. Therefore, if the module has not been
113
+ # object using String#constantize. Therefore, if the module has not been
114
114
  # yet loaded, it has to be autoloadable, which is normally the case.
115
115
  #
116
116
  # Namespaces are supported. The following calls include +Foo::BarHelper+:
@@ -117,7 +117,7 @@ module ActionController
117
117
  # * <tt>:allow_other_host</tt> - Allow or disallow redirection to the host that is different to the current host, defaults to true.
118
118
  #
119
119
  # All other options that can be passed to #redirect_to are accepted as
120
- # options and the behavior is identical.
120
+ # options, and the behavior is identical.
121
121
  def redirect_back_or_to(fallback_location, allow_other_host: _allow_other_host, **options)
122
122
  if request.referer && (allow_other_host || _url_host_allowed?(request.referer))
123
123
  redirect_to request.referer, allow_other_host: allow_other_host, **options
@@ -195,7 +195,12 @@ module ActionController
195
195
  end
196
196
 
197
197
  def _url_host_allowed?(url)
198
- [request.host, nil].include?(URI(url.to_s).host)
198
+ host = URI(url.to_s).host
199
+
200
+ return true if host == request.host
201
+ return false unless host.nil?
202
+ return false unless url.to_s.start_with?("/")
203
+ return !url.to_s.start_with?("//")
199
204
  rescue ArgumentError, URI::Error
200
205
  false
201
206
  end
@@ -78,8 +78,8 @@ module ActionController
78
78
  end
79
79
 
80
80
  def _set_vary_header
81
- if self.headers["Vary"].blank? && request.should_apply_vary_header?
82
- self.headers["Vary"] = "Accept"
81
+ if response.headers["Vary"].blank? && request.should_apply_vary_header?
82
+ response.headers["Vary"] = "Accept"
83
83
  end
84
84
  end
85
85
 
@@ -279,10 +279,15 @@ module ActionController
279
279
  @parameters == other
280
280
  end
281
281
  end
282
- alias eql? ==
282
+
283
+ def eql?(other)
284
+ self.class == other.class &&
285
+ permitted? == other.permitted? &&
286
+ parameters.eql?(other.parameters)
287
+ end
283
288
 
284
289
  def hash
285
- [@parameters.hash, @permitted].hash
290
+ [self.class, @parameters, @permitted].hash
286
291
  end
287
292
 
288
293
  # Returns a safe <tt>ActiveSupport::HashWithIndifferentAccess</tt>
@@ -18,7 +18,7 @@ module ActionDispatch
18
18
  end
19
19
 
20
20
  def if_none_match_etags
21
- if_none_match ? if_none_match.split(/\s*,\s*/) : []
21
+ if_none_match ? if_none_match.split(",").each(&:strip!) : []
22
22
  end
23
23
 
24
24
  def not_modified?(modified_at)
@@ -21,9 +21,8 @@ module ActionDispatch # :nodoc:
21
21
  # Nevertheless, integration tests may want to inspect controller responses in
22
22
  # more detail, and that's when \Response can be useful for application
23
23
  # developers. Integration test methods such as
24
- # ActionDispatch::Integration::Session#get and
25
- # ActionDispatch::Integration::Session#post return objects of type
26
- # TestResponse (which are of course also of type \Response).
24
+ # Integration::RequestHelpers#get and Integration::RequestHelpers#post return
25
+ # objects of type TestResponse (which are of course also of type \Response).
27
26
  #
28
27
  # For example, the following demo integration test prints the body of the
29
28
  # controller response to the console:
@@ -92,7 +92,7 @@ module ActionDispatch
92
92
  include RequestCookieMethods
93
93
  end
94
94
 
95
- # Read and write data to cookies through ActionController#cookies.
95
+ # Read and write data to cookies through ActionController::Base#cookies.
96
96
  #
97
97
  # When reading cookie data, the data is read from the HTTP request header, Cookie.
98
98
  # When writing cookie data, the data is sent out in the HTTP response header, Set-Cookie.
@@ -178,8 +178,7 @@ module ActionDispatch
178
178
  # only HTTP. Defaults to +false+.
179
179
  # * <tt>:same_site</tt> - The value of the +SameSite+ cookie attribute, which
180
180
  # determines how this cookie should be restricted in cross-site contexts.
181
- # Possible values are +nil+, +:none+, +:lax+, and +:strict+. Defaults to
182
- # +:lax+.
181
+ # Possible values are +:none+, +:lax+, and +:strict+. Defaults to +:lax+.
183
182
  class Cookies
184
183
  HTTP_HEADER = "Set-Cookie"
185
184
  GENERATOR_KEY = "action_dispatch.key_generator"
@@ -291,20 +290,6 @@ module ActionDispatch
291
290
  class CookieJar # :nodoc:
292
291
  include Enumerable, ChainedCookieJars
293
292
 
294
- # This regular expression is used to split the levels of a domain.
295
- # The top level domain can be any string without a period or
296
- # **.**, ***.** style TLDs like co.uk or com.au
297
- #
298
- # www.example.co.uk gives:
299
- # $& => example.co.uk
300
- #
301
- # example.com gives:
302
- # $& => example.com
303
- #
304
- # lots.of.subdomains.example.local gives:
305
- # $& => example.local
306
- DOMAIN_REGEXP = /[^.]*\.([^.]*|..\...|...\...)$/
307
-
308
293
  def self.build(req, cookies)
309
294
  jar = new(req)
310
295
  jar.update(cookies)
@@ -457,13 +442,35 @@ module ActionDispatch
457
442
  options[:same_site] ||= cookies_same_site_protection.call(request)
458
443
 
459
444
  if options[:domain] == :all || options[:domain] == "all"
460
- # If there is a provided tld length then we use it otherwise default domain regexp.
461
- domain_regexp = options[:tld_length] ? /([^.]+\.?){#{options[:tld_length]}}$/ : DOMAIN_REGEXP
445
+ cookie_domain = ""
446
+ dot_splitted_host = request.host.split('.', -1)
447
+
448
+ # Case where request.host is not an IP address or it's an invalid domain
449
+ # (ip confirms to the domain structure we expect so we explicitly check for ip)
450
+ if request.host.match?(/^[\d.]+$/) || dot_splitted_host.include?("") || dot_splitted_host.length == 1
451
+ options[:domain] = nil
452
+ return
453
+ end
454
+
455
+ # If there is a provided tld length then we use it otherwise default domain.
456
+ if options[:tld_length].present?
457
+ # Case where the tld_length provided is valid
458
+ if dot_splitted_host.length >= options[:tld_length]
459
+ cookie_domain = dot_splitted_host.last(options[:tld_length]).join('.')
460
+ end
461
+ # Case where tld_length is not provided
462
+ else
463
+ # Regular TLDs
464
+ if !(/([^.]{2,3}\.[^.]{2})$/.match?(request.host))
465
+ cookie_domain = dot_splitted_host.last(2).join('.')
466
+ # **.**, ***.** style TLDs like co.uk and com.au
467
+ else
468
+ cookie_domain = dot_splitted_host.last(3).join('.')
469
+ end
470
+ end
462
471
 
463
- # If host is not ip and matches domain regexp.
464
- # (ip confirms to domain regexp so we explicitly check for ip)
465
- options[:domain] = if !request.host.match?(/^[\d.]+$/) && (request.host =~ domain_regexp)
466
- ".#{$&}"
472
+ options[:domain] = if cookie_domain.present?
473
+ ".#{cookie_domain}"
467
474
  end
468
475
  elsif options[:domain].is_a? Array
469
476
  # If host matches one of the supplied domains.
@@ -6,28 +6,71 @@ module ActionDispatch
6
6
  class ServerTiming
7
7
  SERVER_TIMING_HEADER = "Server-Timing"
8
8
 
9
+ class Subscriber # :nodoc:
10
+ include Singleton
11
+ KEY = :action_dispatch_server_timing_events
12
+
13
+ def initialize
14
+ @mutex = Mutex.new
15
+ end
16
+
17
+ def call(event)
18
+ if events = ActiveSupport::IsolatedExecutionState[KEY]
19
+ events << event
20
+ end
21
+ end
22
+
23
+ def collect_events
24
+ events = []
25
+ ActiveSupport::IsolatedExecutionState[KEY] = events
26
+ yield
27
+ events
28
+ ensure
29
+ ActiveSupport::IsolatedExecutionState.delete(KEY)
30
+ end
31
+
32
+ def ensure_subscribed
33
+ @mutex.synchronize do
34
+ # Subscribe to all events, except those beginning with "!"
35
+ # Ideally we would be more selective of what is being measured
36
+ @subscriber ||= ActiveSupport::Notifications.subscribe(/\A[^!]/, self)
37
+ end
38
+ end
39
+
40
+ def unsubscribe
41
+ @mutex.synchronize do
42
+ ActiveSupport::Notifications.unsubscribe @subscriber
43
+ @subscriber = nil
44
+ end
45
+ end
46
+ end
47
+
48
+ def self.unsubscribe # :nodoc:
49
+ Subscriber.instance.unsubscribe
50
+ end
51
+
9
52
  def initialize(app)
10
53
  @app = app
54
+ @subscriber = Subscriber.instance
55
+ @subscriber.ensure_subscribed
11
56
  end
12
57
 
13
58
  def call(env)
14
- events = []
15
- subscriber = ActiveSupport::Notifications.subscribe(/.*/) do |*args|
16
- events << ActiveSupport::Notifications::Event.new(*args)
59
+ response = nil
60
+ events = @subscriber.collect_events do
61
+ response = @app.call(env)
17
62
  end
18
63
 
19
- status, headers, body = begin
20
- @app.call(env)
21
- ensure
22
- ActiveSupport::Notifications.unsubscribe(subscriber)
23
- end
64
+ headers = response[1]
24
65
 
25
66
  header_info = events.group_by(&:name).map do |event_name, events_collection|
26
- "#{event_name};dur=#{events_collection.sum(&:duration)}"
67
+ "%s;dur=%.2f" % [event_name, events_collection.sum(&:duration)]
27
68
  end
69
+
70
+ header_info.prepend(headers[SERVER_TIMING_HEADER]) if headers[SERVER_TIMING_HEADER].present?
28
71
  headers[SERVER_TIMING_HEADER] = header_info.join(", ")
29
72
 
30
- [ status, headers, body ]
73
+ response
31
74
  end
32
75
  end
33
76
  end
@@ -9,14 +9,14 @@ module ActionDispatch
9
9
  # This cookie-based session store is the Rails default. It is
10
10
  # dramatically faster than the alternatives.
11
11
  #
12
- # Sessions typically contain at most a user_id and flash message; both fit
13
- # within the 4096 bytes cookie size limit. A CookieOverflow exception is raised if
12
+ # Sessions typically contain at most a user ID and flash message; both fit
13
+ # within the 4096 bytes cookie size limit. A +CookieOverflow+ exception is raised if
14
14
  # you attempt to store more than 4096 bytes of data.
15
15
  #
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
- # Your cookies will be encrypted using your apps secret_key_base. This
19
+ # Your cookies will be encrypted using your application's +secret_key_base+. This
20
20
  # goes a step further than signed cookies in that encrypted cookies cannot
21
21
  # be altered or read by users. This is the default starting in Rails 4.
22
22
  #
@@ -24,20 +24,20 @@ module ActionDispatch
24
24
  #
25
25
  # Rails.application.config.session_store :cookie_store, key: '_your_app_session'
26
26
  #
27
- # In the development and test environments your application's secret key base is
27
+ # In the development and test environments your application's +secret_key_base+ is
28
28
  # generated by Rails and stored in a temporary file in <tt>tmp/development_secret.txt</tt>.
29
29
  # In all other environments, it is stored encrypted in the
30
30
  # <tt>config/credentials.yml.enc</tt> file.
31
31
  #
32
- # If your application was not updated to Rails 5.2 defaults, the secret_key_base
32
+ # If your application was not updated to Rails 5.2 defaults, the +secret_key_base+
33
33
  # will be found in the old <tt>config/secrets.yml</tt> file.
34
34
  #
35
- # Note that changing your secret_key_base will invalidate all existing session.
35
+ # Note that changing your +secret_key_base+ will invalidate all existing session.
36
36
  # Additionally, you should take care to make sure you are not relying on the
37
37
  # ability to decode signed cookies generated by your app in external
38
38
  # applications or JavaScript before changing it.
39
39
  #
40
- # Because CookieStore extends Rack::Session::Abstract::Persisted, many of the
40
+ # Because CookieStore extends +Rack::Session::Abstract::Persisted+, many of the
41
41
  # options described there can be used to customize the session cookie that
42
42
  # is generated. For example:
43
43
  #
@@ -609,7 +609,7 @@ module ActionDispatch
609
609
  target_as = name_for_action(options[:as], path)
610
610
  options[:via] ||= :all
611
611
 
612
- match(path, options.merge(to: app, anchor: false, format: false))
612
+ match(path, { to: app, anchor: false, format: false }.merge(options))
613
613
 
614
614
  define_generate_prefix(app, target_as) if rails_app
615
615
  self
@@ -3,8 +3,8 @@
3
3
  require "action_dispatch/testing/request_encoder"
4
4
 
5
5
  module ActionDispatch
6
- # Integration test methods such as ActionDispatch::Integration::Session#get
7
- # and ActionDispatch::Integration::Session#post return objects of class
6
+ # Integration test methods such as Integration::RequestHelpers#get
7
+ # and Integration::RequestHelpers#post return objects of class
8
8
  # TestResponse, which represent the HTTP response results of the requested
9
9
  # controller actions.
10
10
  #
@@ -14,6 +14,24 @@ module ActionDispatch
14
14
  new response.status, response.headers, response.body
15
15
  end
16
16
 
17
+ # Returns a parsed body depending on the response MIME type. When a parser
18
+ # corresponding to the MIME type is not found, it returns the raw body.
19
+ #
20
+ # ==== Examples
21
+ # get "/posts"
22
+ # response.content_type # => "text/html; charset=utf-8"
23
+ # response.parsed_body.class # => String
24
+ # response.parsed_body # => "<!DOCTYPE html>\n<html>\n..."
25
+ #
26
+ # get "/posts.json"
27
+ # response.content_type # => "application/json; charset=utf-8"
28
+ # response.parsed_body.class # => Array
29
+ # response.parsed_body # => [{"id"=>42, "title"=>"Title"},...
30
+ #
31
+ # get "/posts/42.json"
32
+ # response.content_type # => "application/json; charset=utf-8"
33
+ # response.parsed_body.class # => Hash
34
+ # response.parsed_body # => {"id"=>42, "title"=>"Title"}
17
35
  def parsed_body
18
36
  @parsed_body ||= response_parser.call(body)
19
37
  end
@@ -9,7 +9,7 @@ module ActionPack
9
9
  module VERSION
10
10
  MAJOR = 7
11
11
  MINOR = 0
12
- TINY = 3
12
+ TINY = 4
13
13
  PRE = "1"
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: actionpack
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.0.3.1
4
+ version: 7.0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-07-12 00:00:00.000000000 Z
11
+ date: 2023-01-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 7.0.3.1
19
+ version: 7.0.4.1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 7.0.3.1
26
+ version: 7.0.4.1
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rack
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -98,28 +98,28 @@ dependencies:
98
98
  requirements:
99
99
  - - '='
100
100
  - !ruby/object:Gem::Version
101
- version: 7.0.3.1
101
+ version: 7.0.4.1
102
102
  type: :runtime
103
103
  prerelease: false
104
104
  version_requirements: !ruby/object:Gem::Requirement
105
105
  requirements:
106
106
  - - '='
107
107
  - !ruby/object:Gem::Version
108
- version: 7.0.3.1
108
+ version: 7.0.4.1
109
109
  - !ruby/object:Gem::Dependency
110
110
  name: activemodel
111
111
  requirement: !ruby/object:Gem::Requirement
112
112
  requirements:
113
113
  - - '='
114
114
  - !ruby/object:Gem::Version
115
- version: 7.0.3.1
115
+ version: 7.0.4.1
116
116
  type: :development
117
117
  prerelease: false
118
118
  version_requirements: !ruby/object:Gem::Requirement
119
119
  requirements:
120
120
  - - '='
121
121
  - !ruby/object:Gem::Version
122
- version: 7.0.3.1
122
+ version: 7.0.4.1
123
123
  description: Web apps on Rails. Simple, battle-tested conventions for building and
124
124
  testing MVC web applications. Works with any Rack-compatible server.
125
125
  email: david@loudthinking.com
@@ -310,10 +310,10 @@ licenses:
310
310
  - MIT
311
311
  metadata:
312
312
  bug_tracker_uri: https://github.com/rails/rails/issues
313
- changelog_uri: https://github.com/rails/rails/blob/v7.0.3.1/actionpack/CHANGELOG.md
314
- documentation_uri: https://api.rubyonrails.org/v7.0.3.1/
313
+ changelog_uri: https://github.com/rails/rails/blob/v7.0.4.1/actionpack/CHANGELOG.md
314
+ documentation_uri: https://api.rubyonrails.org/v7.0.4.1/
315
315
  mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
316
- source_code_uri: https://github.com/rails/rails/tree/v7.0.3.1/actionpack
316
+ source_code_uri: https://github.com/rails/rails/tree/v7.0.4.1/actionpack
317
317
  rubygems_mfa_required: 'true'
318
318
  post_install_message:
319
319
  rdoc_options: []
@@ -331,7 +331,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
331
331
  version: '0'
332
332
  requirements:
333
333
  - none
334
- rubygems_version: 3.3.3
334
+ rubygems_version: 3.4.3
335
335
  signing_key:
336
336
  specification_version: 4
337
337
  summary: Web-flow and rendering framework putting the VC in MVC (part of Rails).