Hello community,

here is the log from the commit of package rubygem-rack for openSUSE:Factory 
checked in at 2019-12-21 12:31:35
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/rubygem-rack (Old)
 and      /work/SRC/openSUSE:Factory/.rubygem-rack.new.6675 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "rubygem-rack"

Sat Dec 21 12:31:35 2019 rev:16 rq:758120 version:2.0.8

Changes:
--------
--- /work/SRC/openSUSE:Factory/rubygem-rack/rubygem-rack.changes        
2019-04-08 10:40:32.523298437 +0200
+++ /work/SRC/openSUSE:Factory/.rubygem-rack.new.6675/rubygem-rack.changes      
2019-12-21 12:32:03.167382393 +0100
@@ -1,0 +2,6 @@
+Thu Dec 19 08:55:14 UTC 2019 - David Kang <[email protected]>
+
+- updated to version 2.0.8
+  * CVE-2019-16782: Possible information leak / session hijack vulnerability
+
+-------------------------------------------------------------------

Old:
----
  rack-2.0.7.gem

New:
----
  rack-2.0.8.gem

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ rubygem-rack.spec ++++++
--- /var/tmp/diff_new_pack.EWVfKh/_old  2019-12-21 12:32:04.135382854 +0100
+++ /var/tmp/diff_new_pack.EWVfKh/_new  2019-12-21 12:32:04.139382855 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package rubygem-rack
 #
-# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2019 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -12,7 +12,7 @@
 # license that conforms to the Open Source Definition (Version 1.9)
 # published by the Open Source Initiative.
 
-# Please submit bugfixes or comments via http://bugs.opensuse.org/
+# Please submit bugfixes or comments via https://bugs.opensuse.org/
 #
 
 
@@ -24,7 +24,7 @@
 #
 
 Name:           rubygem-rack
-Version:        2.0.7
+Version:        2.0.8
 Release:        0
 %define mod_name rack
 %define mod_full_name %{mod_name}-%{version}
@@ -41,7 +41,7 @@
 BuildRequires:  %{rubygem gem2rpm}
 BuildRequires:  ruby-macros >= 5
 BuildRequires:  update-alternatives
-Url:            https://rack.github.io/
+URL:            https://rack.github.io/
 Source:         https://rubygems.org/gems/%{mod_full_name}.gem
 Source1:        rubygem-rack-rpmlintrc
 Source2:        gem2rpm.yml

++++++ rack-2.0.7.gem -> rack-2.0.8.gem ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/SPEC new/SPEC
--- old/SPEC    2019-04-02 18:54:06.000000000 +0200
+++ new/SPEC    2019-12-18 19:05:05.000000000 +0100
@@ -60,8 +60,8 @@
                            the presence or absence of the
                            appropriate HTTP header in the
                            request. See
-                           {https://tools.ietf.org/html/rfc3875#section-4.1.18
-                           RFC3875 section 4.1.18} for
+                           <a 
href="https://tools.ietf.org/html/rfc3875#section-4.1.18";>
+                           RFC3875 section 4.1.18</a> for
                            specific behavior.
 In addition to this, the Rack environment must include these
 Rack-specific variables:
@@ -98,12 +98,13 @@
 Additional environment specifications have approved to
 standardized middleware APIs.  None of these are required to
 be implemented by the server.
-<tt>rack.session</tt>:: A hash like interface for storing request session data.
+<tt>rack.session</tt>:: A hash like interface for storing
+                        request session data.
                         The store must implement:
-                         store(key, value)         (aliased as []=);
-                         fetch(key, default = nil) (aliased as []);
-                         delete(key);
-                         clear;
+                        store(key, value)         (aliased as []=);
+                        fetch(key, default = nil) (aliased as []);
+                        delete(key);
+                        clear;
 <tt>rack.logger</tt>:: A common object interface for logging messages.
                        The object must implement:
                         info(message, &block)
Binary files old/checksums.yaml.gz and new/checksums.yaml.gz differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/rack/session/abstract/id.rb 
new/lib/rack/session/abstract/id.rb
--- old/lib/rack/session/abstract/id.rb 2019-04-02 18:54:06.000000000 +0200
+++ new/lib/rack/session/abstract/id.rb 2019-12-18 19:05:05.000000000 +0100
@@ -6,11 +6,38 @@
 require 'rack/request'
 require 'rack/response'
 require 'securerandom'
+require 'digest/sha2'
 
 module Rack
 
   module Session
 
+    class SessionId
+      ID_VERSION = 2
+
+      attr_reader :public_id
+
+      def initialize(public_id)
+        @public_id = public_id
+      end
+
+      def private_id
+        "#{ID_VERSION}::#{hash_sid(public_id)}"
+      end
+
+      alias :cookie_value :public_id
+
+      def empty?; false; end
+      def to_s; raise; end
+      def inspect; public_id.inspect; end
+
+      private
+
+      def hash_sid(sid)
+        Digest::SHA256.hexdigest(sid)
+      end
+    end
+
     module Abstract
       # SessionHash is responsible to lazily load the session from store.
 
@@ -357,7 +384,7 @@
             req.get_header(RACK_ERRORS).puts("Deferring cookie for 
#{session_id}") if $VERBOSE
           else
             cookie = Hash.new
-            cookie[:value] = data
+            cookie[:value] = cookie_value(data)
             cookie[:expires] = Time.now + options[:expire_after] if 
options[:expire_after]
             cookie[:expires] = Time.now + options[:max_age] if 
options[:max_age]
             set_cookie(req, res, cookie.merge!(options))
@@ -365,6 +392,10 @@
         end
         public :commit_session
 
+        def cookie_value(data)
+          data
+        end
+
         # Sets the cookie back to the client with session id. We skip the 
cookie
         # setting if the value didn't change (sid is the same) or expires was 
given.
 
@@ -406,6 +437,40 @@
         end
       end
 
+      class PersistedSecure < Persisted
+        class SecureSessionHash < SessionHash
+          def [](key)
+            if key == "session_id"
+              load_for_read!
+              id.public_id
+            else
+              super
+            end
+          end
+        end
+
+        def generate_sid(*)
+          public_id = super
+
+          SessionId.new(public_id)
+        end
+
+        def extract_session_id(*)
+          public_id = super
+          public_id && SessionId.new(public_id)
+        end
+
+        private
+
+        def session_class
+          SecureSessionHash
+        end
+
+        def cookie_value(data)
+          data.cookie_value
+        end
+      end
+
       class ID < Persisted
         def self.inherited(klass)
           k = klass.ancestors.find { |kl| kl.respond_to?(:superclass) && 
kl.superclass == ID }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/rack/session/cookie.rb 
new/lib/rack/session/cookie.rb
--- old/lib/rack/session/cookie.rb      2019-04-02 18:54:06.000000000 +0200
+++ new/lib/rack/session/cookie.rb      2019-12-18 19:05:05.000000000 +0100
@@ -45,7 +45,7 @@
     #   })
     #
 
-    class Cookie < Abstract::Persisted
+    class Cookie < Abstract::PersistedSecure
       # Encode session cookies as Base64
       class Base64
         def encode(str)
@@ -153,6 +153,15 @@
         data
       end
 
+      class SessionId < DelegateClass(Session::SessionId)
+        attr_reader :cookie_value
+
+        def initialize(session_id, cookie_value)
+          super(session_id)
+          @cookie_value = cookie_value
+        end
+      end
+
       def write_session(req, session_id, session, options)
         session = session.merge("session_id" => session_id)
         session_data = coder.encode(session)
@@ -165,7 +174,7 @@
           req.get_header(RACK_ERRORS).puts("Warning! Rack::Session::Cookie 
data size exceeds 4K.")
           nil
         else
-          session_data
+          SessionId.new(session_id, session_data)
         end
       end
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/rack/session/memcache.rb 
new/lib/rack/session/memcache.rb
--- old/lib/rack/session/memcache.rb    2019-04-02 18:54:06.000000000 +0200
+++ new/lib/rack/session/memcache.rb    2019-12-18 19:05:05.000000000 +0100
@@ -19,7 +19,7 @@
     # Note that memcache does drop data before it may be listed to expire. For
     # a full description of behaviour, please see memcache's documentation.
 
-    class Memcache < Abstract::ID
+    class Memcache < Abstract::PersistedSecure
       attr_reader :mutex, :pool
 
       DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge \
@@ -42,15 +42,15 @@
       def generate_sid
         loop do
           sid = super
-          break sid unless @pool.get(sid, true)
+          break sid unless @pool.get(sid.private_id, true)
         end
       end
 
-      def get_session(env, sid)
-        with_lock(env) do
-          unless sid and session = @pool.get(sid)
+      def find_session(req, sid)
+        with_lock(req) do
+          unless sid and session = get_session_with_fallback(sid)
             sid, session = generate_sid, {}
-            unless /^STORED/ =~ @pool.add(sid, session)
+            unless /^STORED/ =~ @pool.add(sid.private_id, session)
               raise "Session collision on '#{sid.inspect}'"
             end
           end
@@ -58,25 +58,26 @@
         end
       end
 
-      def set_session(env, session_id, new_session, options)
+      def write_session(req, session_id, new_session, options)
         expiry = options[:expire_after]
         expiry = expiry.nil? ? 0 : expiry + 1
 
-        with_lock(env) do
-          @pool.set session_id, new_session, expiry
+        with_lock(req) do
+          @pool.set session_id.private_id, new_session, expiry
           session_id
         end
       end
 
-      def destroy_session(env, session_id, options)
-        with_lock(env) do
-          @pool.delete(session_id)
+      def delete_session(req, session_id, options)
+        with_lock(req) do
+          @pool.delete(session_id.public_id)
+          @pool.delete(session_id.private_id)
           generate_sid unless options[:drop]
         end
       end
 
-      def with_lock(env)
-        @mutex.lock if env[RACK_MULTITHREAD]
+      def with_lock(req)
+        @mutex.lock if req.multithread?
         yield
       rescue MemCache::MemCacheError, Errno::ECONNREFUSED
         if $VERBOSE
@@ -88,6 +89,11 @@
         @mutex.unlock if @mutex.locked?
       end
 
+      private
+
+      def get_session_with_fallback(sid)
+        @pool.get(sid.private_id) || @pool.get(sid.public_id)
+      end
     end
   end
 end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/rack/session/pool.rb new/lib/rack/session/pool.rb
--- old/lib/rack/session/pool.rb        2019-04-02 18:54:06.000000000 +0200
+++ new/lib/rack/session/pool.rb        2019-12-18 19:05:05.000000000 +0100
@@ -24,7 +24,7 @@
     #   )
     #   Rack::Handler::WEBrick.run sessioned
 
-    class Pool < Abstract::Persisted
+    class Pool < Abstract::PersistedSecure
       attr_reader :mutex, :pool
       DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge :drop => false
 
@@ -37,15 +37,15 @@
       def generate_sid
         loop do
           sid = super
-          break sid unless @pool.key? sid
+          break sid unless @pool.key? sid.private_id
         end
       end
 
       def find_session(req, sid)
         with_lock(req) do
-          unless sid and session = @pool[sid]
+          unless sid and session = get_session_with_fallback(sid)
             sid, session = generate_sid, {}
-            @pool.store sid, session
+            @pool.store sid.private_id, session
           end
           [sid, session]
         end
@@ -53,14 +53,15 @@
 
       def write_session(req, session_id, new_session, options)
         with_lock(req) do
-          @pool.store session_id, new_session
+          @pool.store session_id.private_id, new_session
           session_id
         end
       end
 
       def delete_session(req, session_id, options)
         with_lock(req) do
-          @pool.delete(session_id)
+          @pool.delete(session_id.public_id)
+          @pool.delete(session_id.private_id)
           generate_sid unless options[:drop]
         end
       end
@@ -71,6 +72,12 @@
       ensure
         @mutex.unlock if @mutex.locked?
       end
+
+      private
+
+      def get_session_with_fallback(sid)
+        @pool[sid.private_id] || @pool[sid.public_id]
+      end
     end
   end
 end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/rack.rb new/lib/rack.rb
--- old/lib/rack.rb     2019-04-02 18:54:06.000000000 +0200
+++ new/lib/rack.rb     2019-12-18 19:05:05.000000000 +0100
@@ -18,7 +18,7 @@
     VERSION.join(".")
   end
 
-  RELEASE = "2.0.7"
+  RELEASE = "2.0.8"
 
   # Return the Rack release as a dotted string.
   def self.release
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/metadata new/metadata
--- old/metadata        2019-04-02 18:54:06.000000000 +0200
+++ new/metadata        2019-12-18 19:05:05.000000000 +0100
@@ -1,14 +1,14 @@
 --- !ruby/object:Gem::Specification
 name: rack
 version: !ruby/object:Gem::Version
-  version: 2.0.7
+  version: 2.0.8
 platform: ruby
 authors:
 - Leah Neukirchen
 autorequire: 
 bindir: bin
 cert_chain: []
-date: 2019-04-02 00:00:00.000000000 Z
+date: 2019-12-18 00:00:00.000000000 Z
 dependencies:
 - !ruby/object:Gem::Dependency
   name: minitest
@@ -274,60 +274,59 @@
     - !ruby/object:Gem::Version
       version: '0'
 requirements: []
-rubyforge_project: 
-rubygems_version: 2.6.13
+rubygems_version: 3.0.3
 signing_key: 
 specification_version: 4
 summary: a modular Ruby webserver interface
 test_files:
-- test/spec_auth_basic.rb
-- test/spec_auth_digest.rb
-- test/spec_body_proxy.rb
-- test/spec_builder.rb
-- test/spec_cascade.rb
-- test/spec_cgi.rb
-- test/spec_chunked.rb
-- test/spec_common_logger.rb
-- test/spec_conditional_get.rb
-- test/spec_config.rb
-- test/spec_content_length.rb
-- test/spec_content_type.rb
+- test/spec_multipart.rb
 - test/spec_deflater.rb
-- test/spec_directory.rb
+- test/spec_static.rb
+- test/spec_session_cookie.rb
+- test/spec_session_pool.rb
 - test/spec_etag.rb
-- test/spec_events.rb
-- test/spec_fastcgi.rb
-- test/spec_file.rb
+- test/spec_version.rb
 - test/spec_handler.rb
-- test/spec_head.rb
-- test/spec_lint.rb
-- test/spec_lobster.rb
-- test/spec_lock.rb
-- test/spec_logger.rb
-- test/spec_media_type.rb
-- test/spec_method_override.rb
+- test/spec_thin.rb
+- test/spec_session_abstract_id.rb
 - test/spec_mime.rb
-- test/spec_mock.rb
-- test/spec_multipart.rb
-- test/spec_null_logger.rb
 - test/spec_recursive.rb
+- test/spec_null_logger.rb
+- test/spec_media_type.rb
+- test/spec_cgi.rb
+- test/spec_method_override.rb
+- test/spec_content_type.rb
+- test/spec_session_abstract_session_hash.rb
 - test/spec_request.rb
-- test/spec_response.rb
-- test/spec_rewindable_input.rb
+- test/spec_chunked.rb
+- test/spec_show_exceptions.rb
 - test/spec_runtime.rb
+- test/spec_fastcgi.rb
+- test/spec_common_logger.rb
+- test/spec_builder.rb
+- test/spec_config.rb
+- test/spec_utils.rb
 - test/spec_sendfile.rb
+- test/spec_lobster.rb
+- test/spec_lint.rb
+- test/spec_conditional_get.rb
+- test/spec_tempfile_reaper.rb
+- test/spec_mock.rb
 - test/spec_server.rb
-- test/spec_session_abstract_id.rb
-- test/spec_session_abstract_session_hash.rb
-- test/spec_session_cookie.rb
-- test/spec_session_memcache.rb
-- test/spec_session_pool.rb
-- test/spec_show_exceptions.rb
+- test/spec_directory.rb
+- test/spec_webrick.rb
+- test/spec_response.rb
+- test/spec_file.rb
 - test/spec_show_status.rb
-- test/spec_static.rb
-- test/spec_tempfile_reaper.rb
-- test/spec_thin.rb
+- test/spec_body_proxy.rb
+- test/spec_logger.rb
+- test/spec_auth_digest.rb
 - test/spec_urlmap.rb
-- test/spec_utils.rb
-- test/spec_version.rb
-- test/spec_webrick.rb
+- test/spec_events.rb
+- test/spec_cascade.rb
+- test/spec_auth_basic.rb
+- test/spec_head.rb
+- test/spec_lock.rb
+- test/spec_rewindable_input.rb
+- test/spec_session_memcache.rb
+- test/spec_content_length.rb
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/test/spec_session_memcache.rb 
new/test/spec_session_memcache.rb
--- old/test/spec_session_memcache.rb   2019-04-02 18:54:06.000000000 +0200
+++ new/test/spec_session_memcache.rb   2019-12-18 19:05:05.000000000 +0100
@@ -226,15 +226,52 @@
       req = Rack::MockRequest.new(pool)
 
       res0 = req.get("/")
-      session_id = (cookie = res0["Set-Cookie"])[session_match, 1]
-      ses0 = pool.pool.get(session_id, true)
+      session_id = Rack::Session::SessionId.new (cookie = 
res0["Set-Cookie"])[session_match, 1]
+      ses0 = pool.pool.get(session_id.private_id, true)
 
       req.get("/", "HTTP_COOKIE" => cookie)
-      ses1 = pool.pool.get(session_id, true)
+      ses1 = pool.pool.get(session_id.private_id, true)
 
       ses1.wont_equal ses0
     end
 
+    it "can read the session with the legacy id" do
+      pool = Rack::Session::Memcache.new(incrementor)
+      req = Rack::MockRequest.new(pool)
+
+      res0 = req.get("/")
+      cookie = res0["Set-Cookie"]
+      session_id = Rack::Session::SessionId.new cookie[session_match, 1]
+      ses0 = pool.pool.get(session_id.private_id, true)
+      pool.pool.set(session_id.public_id, ses0, 0, true)
+      pool.pool.delete(session_id.private_id)
+
+      res1 = req.get("/", "HTTP_COOKIE" => cookie)
+      res1["Set-Cookie"].must_be_nil
+      res1.body.must_equal '{"counter"=>2}'
+      pool.pool.get(session_id.private_id, true).wont_be_nil
+    end
+
+    it "drops the session in the legacy id as well" do
+      pool = Rack::Session::Memcache.new(incrementor)
+      req = Rack::MockRequest.new(pool)
+      drop = Rack::Utils::Context.new(pool, drop_session)
+      dreq = Rack::MockRequest.new(drop)
+
+      res0 = req.get("/")
+      cookie = res0["Set-Cookie"]
+      session_id = Rack::Session::SessionId.new cookie[session_match, 1]
+      ses0 = pool.pool.get(session_id.private_id, true)
+      pool.pool.set(session_id.public_id, ses0, 0, true)
+      pool.pool.delete(session_id.private_id)
+
+      res2 = dreq.get("/", "HTTP_COOKIE" => cookie)
+      res2["Set-Cookie"].must_be_nil
+      res2.body.must_equal '{"counter"=>2}'
+      pool.pool.get(session_id.private_id, true).must_be_nil
+      pool.pool.get(session_id.public_id, true).must_be_nil
+    end
+
     # anyone know how to do this better?
     it "cleanly merges sessions when multithreaded" do
       skip unless $DEBUG
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/test/spec_session_pool.rb 
new/test/spec_session_pool.rb
--- old/test/spec_session_pool.rb       2019-04-02 18:54:06.000000000 +0200
+++ new/test/spec_session_pool.rb       2019-12-18 19:05:05.000000000 +0100
@@ -6,7 +6,7 @@
 
 describe Rack::Session::Pool do
   session_key = Rack::Session::Pool::DEFAULT_OPTIONS[:key]
-  session_match = /#{session_key}=[0-9a-fA-F]+;/
+  session_match = /#{session_key}=([0-9a-fA-F]+);/
 
   incrementor = lambda do |env|
     env["rack.session"]["counter"] ||= 0
@@ -14,7 +14,7 @@
     Rack::Response.new(env["rack.session"].inspect).to_a
   end
 
-  session_id = Rack::Lint.new(lambda do |env|
+  get_session_id = Rack::Lint.new(lambda do |env|
     Rack::Response.new(env["rack.session"].inspect).to_a
   end)
 
@@ -143,6 +143,43 @@
     pool.pool.size.must_equal 1
   end
 
+  it "can read the session with the legacy id" do
+    pool = Rack::Session::Pool.new(incrementor)
+    req = Rack::MockRequest.new(pool)
+
+    res0 = req.get("/")
+    cookie = res0["Set-Cookie"]
+    session_id = Rack::Session::SessionId.new cookie[session_match, 1]
+    ses0 = pool.pool[session_id.private_id]
+    pool.pool[session_id.public_id] = ses0
+    pool.pool.delete(session_id.private_id)
+
+    res1 = req.get("/", "HTTP_COOKIE" => cookie)
+    res1["Set-Cookie"].must_be_nil
+    res1.body.must_equal '{"counter"=>2}'
+    pool.pool[session_id.private_id].wont_be_nil
+  end
+
+  it "drops the session in the legacy id as well" do
+    pool = Rack::Session::Pool.new(incrementor)
+    req = Rack::MockRequest.new(pool)
+    drop = Rack::Utils::Context.new(pool, drop_session)
+    dreq = Rack::MockRequest.new(drop)
+
+    res0 = req.get("/")
+    cookie = res0["Set-Cookie"]
+    session_id = Rack::Session::SessionId.new cookie[session_match, 1]
+    ses0 = pool.pool[session_id.private_id]
+    pool.pool[session_id.public_id] = ses0
+    pool.pool.delete(session_id.private_id)
+
+    res2 = dreq.get("/", "HTTP_COOKIE" => cookie)
+    res2["Set-Cookie"].must_be_nil
+    res2.body.must_equal '{"counter"=>2}'
+    pool.pool[session_id.private_id].must_be_nil
+    pool.pool[session_id.public_id].must_be_nil
+  end
+
   # anyone know how to do this better?
   it "should merge sessions when multithreaded" do
     unless $DEBUG
@@ -191,7 +228,7 @@
   end
 
   it "does not return a cookie if cookie was not written (only read)" do
-    app = Rack::Session::Pool.new(session_id)
+    app = Rack::Session::Pool.new(get_session_id)
     res = Rack::MockRequest.new(app).get("/")
     res["Set-Cookie"].must_be_nil
   end


Reply via email to