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.

Potentially problematic release.


This version of actionpack might be problematic. Click here for more details.

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).