From 216b7cad1baa65ba1213ae51c85776928d6e2d86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Fran=C3=A7a?= Date: Wed, 12 Sep 2018 21:59:04 -0400 Subject: [PATCH 01/21] Merge pull request #1296 from tomelm/fix-prefers-plaintext Call the correct accepts_html? method for prefer_plaintext --- lib/rack/show_exceptions.rb | 2 +- test/spec_show_exceptions.rb | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/rack/show_exceptions.rb b/lib/rack/show_exceptions.rb index ca86b2b2a..ef30fce43 100644 --- a/lib/rack/show_exceptions.rb +++ b/lib/rack/show_exceptions.rb @@ -46,7 +46,7 @@ def call(env) end def prefers_plaintext?(env) - !accepts_html(env) + !accepts_html?(env) end def accepts_html?(env) diff --git a/test/spec_show_exceptions.rb b/test/spec_show_exceptions.rb index cd44c8168..61e5d92d6 100644 --- a/test/spec_show_exceptions.rb +++ b/test/spec_show_exceptions.rb @@ -77,4 +77,17 @@ def show_exceptions(app) assert_match(res, /ShowExceptions/) assert_match(res, /unknown location/) end + + it "knows to prefer plaintext for non-html" do + # We don't need an app for this + exc = Rack::ShowExceptions.new(nil) + + [ + [{ "HTTP_ACCEPT" => "text/plain" }, true], + [{ "HTTP_ACCEPT" => "text/foo" }, true], + [{ "HTTP_ACCEPT" => "text/html" }, false] + ].each do |env, expected| + assert_equal(expected, exc.prefers_plaintext?(env)) + end + end end From 37c1160b2360074d20858792f23a7eb3afeabebd Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 1 Nov 2018 14:58:04 -0700 Subject: [PATCH 02/21] Reduce buffer size to avoid pathological parsing [CVE-2018-16470] Revert "Merge pull request #1192 from jkowens/master" This reverts commit c43217a81917de03aa6ceb1aa485ae69b8bb4598. --- lib/rack/multipart/parser.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rack/multipart/parser.rb b/lib/rack/multipart/parser.rb index c02e26f6b..a19d6ea32 100644 --- a/lib/rack/multipart/parser.rb +++ b/lib/rack/multipart/parser.rb @@ -5,7 +5,7 @@ module Multipart class MultipartPartLimitError < Errno::EMFILE; end class Parser - BUFSIZE = 1_048_576 + BUFSIZE = 16384 TEXT_PLAIN = "text/plain" TEMPFILE_FACTORY = lambda { |filename, content_type| Tempfile.new(["RackMultipart", ::File.extname(filename.gsub("\0".freeze, '%00'.freeze))]) From 313dd6a05a5924ed6c82072299c53fed09e39ae7 Mon Sep 17 00:00:00 2001 From: Patrick Tulskie Date: Wed, 22 Aug 2018 12:56:43 -0400 Subject: [PATCH 03/21] Whitelist http/https schemes [CVE-2018-16471] --- lib/rack/request.rb | 21 +++++++++++++++++---- test/spec_request.rb | 5 +++++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/lib/rack/request.rb b/lib/rack/request.rb index 2a00de704..6307b6142 100644 --- a/lib/rack/request.rb +++ b/lib/rack/request.rb @@ -11,6 +11,8 @@ module Rack # req.params["data"] class Request + SCHEME_WHITELIST = %w(https http).freeze + def initialize(env) @params = nil super(env) @@ -188,10 +190,8 @@ def scheme 'https' elsif get_header(HTTP_X_FORWARDED_SSL) == 'on' 'https' - elsif get_header(HTTP_X_FORWARDED_SCHEME) - get_header(HTTP_X_FORWARDED_SCHEME) - elsif get_header(HTTP_X_FORWARDED_PROTO) - get_header(HTTP_X_FORWARDED_PROTO).split(',')[0] + elsif forwarded_scheme + forwarded_scheme else get_header(RACK_URL_SCHEME) end @@ -479,6 +479,19 @@ def split_ip_addresses(ip_addresses) def reject_trusted_ip_addresses(ip_addresses) ip_addresses.reject { |ip| trusted_proxy?(ip) } end + + def forwarded_scheme + scheme_headers = [ + get_header(HTTP_X_FORWARDED_SCHEME), + get_header(HTTP_X_FORWARDED_PROTO).to_s.split(',')[0] + ] + + scheme_headers.each do |header| + return header if SCHEME_WHITELIST.include?(header) + end + + nil + end end include Env diff --git a/test/spec_request.rb b/test/spec_request.rb index bdad68fa7..cfaedbcfe 100644 --- a/test/spec_request.rb +++ b/test/spec_request.rb @@ -572,6 +572,11 @@ def initialize(*) request.must_be :ssl? end + it "prevents scheme abuse" do + request = make_request(Rack::MockRequest.env_for("/", 'HTTP_X_FORWARDED_SCHEME' => 'a.">')) + request.scheme.must_equal 'http' + end + it "parse cookies" do req = make_request \ Rack::MockRequest.env_for("", "HTTP_COOKIE" => "foo=bar;quux=h&m") From 8376dd11e6526a53432ee59b7a5d092bda9fc901 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 5 Nov 2018 11:27:14 -0800 Subject: [PATCH 04/21] Bumping version for release --- lib/rack.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rack.rb b/lib/rack.rb index 6441e9927..edf80dd9f 100644 --- a/lib/rack.rb +++ b/lib/rack.rb @@ -18,7 +18,7 @@ def self.version VERSION.join(".") end - RELEASE = "2.0.5" + RELEASE = "2.0.6" # Return the Rack release as a dotted string. def self.release From cb1fdb600bc525258b3c34ea95f1598ee6def9c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Fran=C3=A7a?= Date: Mon, 11 Jun 2018 15:23:42 -0400 Subject: [PATCH 05/21] Merge pull request #1201 from janko-m/make-multipart-parsing-work-for-chunked-requests Don't use #eof? when parsing multipart --- lib/rack/multipart/parser.rb | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/lib/rack/multipart/parser.rb b/lib/rack/multipart/parser.rb index a19d6ea32..94f0fae39 100644 --- a/lib/rack/multipart/parser.rb +++ b/lib/rack/multipart/parser.rb @@ -39,8 +39,6 @@ def read(size) str end - def eof?; @content_length == @cursor; end - def rewind @io.rewind end @@ -65,11 +63,11 @@ def self.parse(io, content_length, content_type, tmpfile, bufsize, qp) io = BoundedIO.new(io, content_length) if content_length parser = new(boundary, tmpfile, bufsize, qp) - parser.on_read io.read(bufsize), io.eof? + parser.on_read io.read(bufsize) loop do break if parser.state == :DONE - parser.on_read io.read(bufsize), io.eof? + parser.on_read io.read(bufsize) end io.rewind @@ -181,8 +179,8 @@ def initialize(boundary, tempfile, bufsize, query_parser) @collector = Collector.new tempfile end - def on_read content, eof - handle_empty_content!(content, eof) + def on_read content + handle_empty_content!(content) @buf << content run_parser end @@ -358,10 +356,9 @@ def tag_multipart_encoding(filename, content_type, name, body) end - def handle_empty_content!(content, eof) + def handle_empty_content!(content) if content.nil? || content.empty? - raise EOFError if eof - return true + raise EOFError end end end From 1bf218818502e820192a41c4da61aa0b0b6109af Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 5 Jan 2018 16:53:50 +1100 Subject: [PATCH 06/21] Preserve forwarded IP address for trusted proxy chains Sometimes proxies make requests to Rack applications, for example HAProxy health checks and so on. Previously the forwarded IP implementation ate up these IP addresses, making it hard to tell in Rack applications who made the request --- lib/rack/request.rb | 2 +- test/spec_request.rb | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/rack/request.rb b/lib/rack/request.rb index 6307b6142..6fcf6ee9e 100644 --- a/lib/rack/request.rb +++ b/lib/rack/request.rb @@ -261,7 +261,7 @@ def ip forwarded_ips = split_ip_addresses(get_header('HTTP_X_FORWARDED_FOR')) - return reject_trusted_ip_addresses(forwarded_ips).last || get_header("REMOTE_ADDR") + return reject_trusted_ip_addresses(forwarded_ips).last || forwarded_ips.first || get_header("REMOTE_ADDR") end # The media type (type/subtype) portion of the CONTENT_TYPE header diff --git a/test/spec_request.rb b/test/spec_request.rb index cfaedbcfe..6ed27ced0 100644 --- a/test/spec_request.rb +++ b/test/spec_request.rb @@ -1286,7 +1286,16 @@ def ip_app res.body.must_equal '2.2.2.3' end - it "regard local addresses as proxies" do + it "preserves ip for trusted proxy chain" do + mock = Rack::MockRequest.new(Rack::Lint.new(ip_app)) + res = mock.get '/', + 'HTTP_X_FORWARDED_FOR' => '192.168.0.11, 192.168.0.7', + 'HTTP_CLIENT_IP' => '127.0.0.1' + res.body.must_equal '192.168.0.11' + + end + + it "regards local addresses as proxies" do req = make_request(Rack::MockRequest.env_for("/")) req.trusted_proxy?('127.0.0.1').must_equal 0 req.trusted_proxy?('10.0.0.1').must_equal 0 From 7fb95dbec28dc70f3cfbba0a684db0735d8ab2ca Mon Sep 17 00:00:00 2001 From: eileencodes Date: Tue, 2 Apr 2019 12:51:51 -0400 Subject: [PATCH 07/21] Bumping to 2.0.7 for release --- lib/rack.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rack.rb b/lib/rack.rb index edf80dd9f..c8b6cc86a 100644 --- a/lib/rack.rb +++ b/lib/rack.rb @@ -18,7 +18,7 @@ def self.version VERSION.join(".") end - RELEASE = "2.0.6" + RELEASE = "2.0.7" # Return the Rack release as a dotted string. def self.release From 83d4bd12c7e88455d21230bc24ec3a543654e2aa Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 13 Aug 2019 15:32:20 -0400 Subject: [PATCH 08/21] try to ensure we always have some kind of object --- lib/rack/session/abstract/id.rb | 11 +++++++++-- lib/rack/session/cookie.rb | 4 ++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/rack/session/abstract/id.rb b/lib/rack/session/abstract/id.rb index 1bb8d5d06..acb4e5362 100644 --- a/lib/rack/session/abstract/id.rb +++ b/lib/rack/session/abstract/id.rb @@ -11,6 +11,10 @@ module Rack module Session + class NullSessionId + def empty?; true; end + end + module Abstract # SessionHash is responsible to lazily load the session from store. @@ -39,8 +43,11 @@ def initialize(store, req) end def id - return @id if @loaded or instance_variable_defined?(:@id) - @id = @store.send(:extract_session_id, @req) + if @loaded or instance_variable_defined?(:@id) + else + @id = @store.send(:extract_session_id, @req) + end + @id || NullSessionId.new end def options diff --git a/lib/rack/session/cookie.rb b/lib/rack/session/cookie.rb index 71bb96f4f..00d28fecf 100644 --- a/lib/rack/session/cookie.rb +++ b/lib/rack/session/cookie.rb @@ -125,11 +125,11 @@ def initialize(app, options={}) def find_session(req, sid) data = unpacked_cookie_data(req) data = persistent_session_id!(data) - [data["session_id"], data] + [data["session_id"] || raise, data] end def extract_session_id(request) - unpacked_cookie_data(request)["session_id"] + unpacked_cookie_data(request)["session_id"] || NullSessionId.new end def unpacked_cookie_data(request) From 77f3aab73089abe518f62c46268b104bacd7114b Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 13 Aug 2019 15:43:58 -0400 Subject: [PATCH 09/21] remove more nils --- lib/rack/session/abstract/id.rb | 11 ++++++----- lib/rack/session/cookie.rb | 6 +++++- lib/rack/session/pool.rb | 6 +++++- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/lib/rack/session/abstract/id.rb b/lib/rack/session/abstract/id.rb index acb4e5362..643cfb201 100644 --- a/lib/rack/session/abstract/id.rb +++ b/lib/rack/session/abstract/id.rb @@ -13,6 +13,7 @@ module Session class NullSessionId def empty?; true; end + def nil?; true; end end module Abstract @@ -47,7 +48,7 @@ def id else @id = @store.send(:extract_session_id, @req) end - @id || NullSessionId.new + @id || raise end def options @@ -93,7 +94,7 @@ def clear def destroy clear - @id = @store.send(:delete_session, @req, id, options) + @id = @store.send(:delete_session, @req, id, options) || raise end def to_hash @@ -285,7 +286,7 @@ def prepare_session(req) def load_session(req) sid = current_session_id(req) sid, session = find_session(req, sid) - [sid, session || {}] + [sid || NullSessionId.new, session || {}] end # Extract session id from request object. @@ -293,7 +294,7 @@ def load_session(req) def extract_session_id(request) sid = request.cookies[@key] sid ||= request.params[@key] unless @cookie_only - sid + sid || NullSessionId.new end # Returns the current session id from the SessionHash. @@ -349,7 +350,7 @@ def commit_session(req, res) if options[:drop] || options[:renew] session_id = delete_session(req, session.id || generate_sid, options) - return unless session_id + return if session_id.nil? end return unless commit_session?(req, session, options) diff --git a/lib/rack/session/cookie.rb b/lib/rack/session/cookie.rb index 00d28fecf..ff5e0f8ed 100644 --- a/lib/rack/session/cookie.rb +++ b/lib/rack/session/cookie.rb @@ -171,7 +171,11 @@ def write_session(req, session_id, session, options) def delete_session(req, session_id, options) # Nothing to do here, data is in the client - generate_sid unless options[:drop] + if options[:drop] + NullSessionId.new + else + generate_sid + end end def digest_match?(data, digest) diff --git a/lib/rack/session/pool.rb b/lib/rack/session/pool.rb index 4c9c25c7a..b8a375231 100644 --- a/lib/rack/session/pool.rb +++ b/lib/rack/session/pool.rb @@ -61,7 +61,11 @@ def write_session(req, session_id, new_session, options) def delete_session(req, session_id, options) with_lock(req) do @pool.delete(session_id) - generate_sid unless options[:drop] + if options[:drop] + NullSessionId.new + else + generate_sid + end end end From bb3d486644755b2e0c7824b3910db1a83c98fcd2 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 13 Aug 2019 16:20:32 -0400 Subject: [PATCH 10/21] use session id objects --- lib/rack/session/abstract/id.rb | 28 ++++++++++++++++++++++++---- lib/rack/session/cookie.rb | 11 ++++++++++- lib/rack/session/memcache.rb | 10 +++++----- lib/rack/session/pool.rb | 8 ++++---- test/spec_session_abstract_id.rb | 2 +- 5 files changed, 44 insertions(+), 15 deletions(-) diff --git a/lib/rack/session/abstract/id.rb b/lib/rack/session/abstract/id.rb index 643cfb201..5c8f9e5ed 100644 --- a/lib/rack/session/abstract/id.rb +++ b/lib/rack/session/abstract/id.rb @@ -16,6 +16,20 @@ def empty?; true; end def nil?; true; end end + class SessionId + attr_reader :public_id + + def initialize(public_id) + @public_id = public_id + end + + alias :cookie_value :public_id + + def empty?; false; end + def to_s; raise; end + def inspect; public_id.inspect; end + end + module Abstract # SessionHash is responsible to lazily load the session from store. @@ -62,7 +76,11 @@ def each(&block) def [](key) load_for_read! - @data[key.to_s] + if key == "session_id" + id.public_id + else + @data[key.to_s] + end end def fetch(key, default=Unspecified, &block) @@ -260,11 +278,13 @@ def initialize_sid # Monkey patch this to use custom methods for session id generation. def generate_sid(secure = @sid_secure) - if secure + public_id = if secure secure.hex(@sid_length) else "%0#{@sid_length}x" % Kernel.rand(2**@sidbits - 1) end + + SessionId.new(public_id) rescue NotImplementedError generate_sid(false) end @@ -294,7 +314,7 @@ def load_session(req) def extract_session_id(request) sid = request.cookies[@key] sid ||= request.params[@key] unless @cookie_only - sid || NullSessionId.new + (sid && SessionId.new(sid)) || NullSessionId.new end # Returns the current session id from the SessionHash. @@ -365,7 +385,7 @@ def commit_session(req, res) req.get_header(RACK_ERRORS).puts("Deferring cookie for #{session_id}") if $VERBOSE else cookie = Hash.new - cookie[:value] = data + cookie[:value] = data.cookie_value 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)) diff --git a/lib/rack/session/cookie.rb b/lib/rack/session/cookie.rb index ff5e0f8ed..e69d81f63 100644 --- a/lib/rack/session/cookie.rb +++ b/lib/rack/session/cookie.rb @@ -153,6 +153,15 @@ def persistent_session_id!(data, sid=nil) 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 @@ def write_session(req, session_id, session, options) 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 --git a/lib/rack/session/memcache.rb b/lib/rack/session/memcache.rb index 4cf5ea09e..b2325352a 100644 --- a/lib/rack/session/memcache.rb +++ b/lib/rack/session/memcache.rb @@ -42,15 +42,15 @@ def initialize(app, options={}) def generate_sid loop do sid = super - break sid unless @pool.get(sid, true) + break sid unless @pool.get(sid.public_id, true) end end def get_session(env, sid) with_lock(env) do - unless sid and session = @pool.get(sid) + unless !sid.nil? and session = @pool.get(sid.public_id) sid, session = generate_sid, {} - unless /^STORED/ =~ @pool.add(sid, session) + unless /^STORED/ =~ @pool.add(sid.public_id, session) raise "Session collision on '#{sid.inspect}'" end end @@ -63,14 +63,14 @@ def set_session(env, session_id, new_session, options) expiry = expiry.nil? ? 0 : expiry + 1 with_lock(env) do - @pool.set session_id, new_session, expiry + @pool.set session_id.public_id, new_session, expiry session_id end end def destroy_session(env, session_id, options) with_lock(env) do - @pool.delete(session_id) + @pool.delete(session_id.public_id) generate_sid unless options[:drop] end end diff --git a/lib/rack/session/pool.rb b/lib/rack/session/pool.rb index b8a375231..368172b8a 100644 --- a/lib/rack/session/pool.rb +++ b/lib/rack/session/pool.rb @@ -43,9 +43,9 @@ def generate_sid def find_session(req, sid) with_lock(req) do - unless sid and session = @pool[sid] + unless !sid.nil? and session = @pool[sid.public_id] sid, session = generate_sid, {} - @pool.store sid, session + @pool.store sid.public_id, session end [sid, session] end @@ -53,14 +53,14 @@ def find_session(req, sid) def write_session(req, session_id, new_session, options) with_lock(req) do - @pool.store session_id, new_session + @pool.store session_id.public_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) if options[:drop] NullSessionId.new else diff --git a/test/spec_session_abstract_id.rb b/test/spec_session_abstract_id.rb index a6568f198..05608aa59 100644 --- a/test/spec_session_abstract_id.rb +++ b/test/spec_session_abstract_id.rb @@ -25,7 +25,7 @@ def hex(*args) end end id = Rack::Session::Abstract::ID.new nil, :secure_random => secure_random.new - id.send(:generate_sid).must_equal 'fake_hex' + id.send(:generate_sid).public_id.must_equal 'fake_hex' end end From 2b205ed5a047d9e50a13bb7a411bc48745b515ec Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 13 Aug 2019 16:31:06 -0400 Subject: [PATCH 11/21] store hashed id, send public id --- lib/rack/session/abstract/id.rb | 10 ++++++++++ lib/rack/session/memcache.rb | 10 +++++----- lib/rack/session/pool.rb | 8 ++++---- test/spec_session_memcache.rb | 6 +++--- 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/lib/rack/session/abstract/id.rb b/lib/rack/session/abstract/id.rb index 5c8f9e5ed..8ff0d93cd 100644 --- a/lib/rack/session/abstract/id.rb +++ b/lib/rack/session/abstract/id.rb @@ -23,11 +23,21 @@ def initialize(public_id) @public_id = public_id end + def private_id + 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 diff --git a/lib/rack/session/memcache.rb b/lib/rack/session/memcache.rb index b2325352a..c97eedfad 100644 --- a/lib/rack/session/memcache.rb +++ b/lib/rack/session/memcache.rb @@ -42,15 +42,15 @@ def initialize(app, options={}) def generate_sid loop do sid = super - break sid unless @pool.get(sid.public_id, true) + break sid unless @pool.get(sid.private_id, true) end end def get_session(env, sid) with_lock(env) do - unless !sid.nil? and session = @pool.get(sid.public_id) + unless !sid.nil? and session = @pool.get(sid.private_id) sid, session = generate_sid, {} - unless /^STORED/ =~ @pool.add(sid.public_id, session) + unless /^STORED/ =~ @pool.add(sid.private_id, session) raise "Session collision on '#{sid.inspect}'" end end @@ -63,14 +63,14 @@ def set_session(env, session_id, new_session, options) expiry = expiry.nil? ? 0 : expiry + 1 with_lock(env) do - @pool.set session_id.public_id, new_session, expiry + @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.public_id) + @pool.delete(session_id.private_id) generate_sid unless options[:drop] end end diff --git a/lib/rack/session/pool.rb b/lib/rack/session/pool.rb index 368172b8a..7e07797bb 100644 --- a/lib/rack/session/pool.rb +++ b/lib/rack/session/pool.rb @@ -43,9 +43,9 @@ def generate_sid def find_session(req, sid) with_lock(req) do - unless !sid.nil? and session = @pool[sid.public_id] + unless !sid.nil? and session = @pool[sid.private_id] sid, session = generate_sid, {} - @pool.store sid.public_id, session + @pool.store sid.private_id, session end [sid, session] end @@ -53,14 +53,14 @@ def find_session(req, sid) def write_session(req, session_id, new_session, options) with_lock(req) do - @pool.store session_id.public_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.public_id) + @pool.delete(session_id.private_id) if options[:drop] NullSessionId.new else diff --git a/test/spec_session_memcache.rb b/test/spec_session_memcache.rb index 93a03d120..1e9f05c57 100644 --- a/test/spec_session_memcache.rb +++ b/test/spec_session_memcache.rb @@ -226,11 +226,11 @@ 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 From 1c7e3b259f0741c869dcfbabeb3e0670c4d3f848 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 13 Aug 2019 16:38:01 -0400 Subject: [PATCH 12/21] remove || raise and get closer to master --- lib/rack/session/abstract/id.rb | 9 +++------ lib/rack/session/cookie.rb | 2 +- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/lib/rack/session/abstract/id.rb b/lib/rack/session/abstract/id.rb index 8ff0d93cd..ac96787e2 100644 --- a/lib/rack/session/abstract/id.rb +++ b/lib/rack/session/abstract/id.rb @@ -68,11 +68,8 @@ def initialize(store, req) end def id - if @loaded or instance_variable_defined?(:@id) - else - @id = @store.send(:extract_session_id, @req) - end - @id || raise + return @id if @loaded or instance_variable_defined?(:@id) + @id = @store.send(:extract_session_id, @req) end def options @@ -122,7 +119,7 @@ def clear def destroy clear - @id = @store.send(:delete_session, @req, id, options) || raise + @id = @store.send(:delete_session, @req, id, options) end def to_hash diff --git a/lib/rack/session/cookie.rb b/lib/rack/session/cookie.rb index e69d81f63..a93f559e6 100644 --- a/lib/rack/session/cookie.rb +++ b/lib/rack/session/cookie.rb @@ -125,7 +125,7 @@ def initialize(app, options={}) def find_session(req, sid) data = unpacked_cookie_data(req) data = persistent_session_id!(data) - [data["session_id"] || raise, data] + [data["session_id"], data] end def extract_session_id(request) From 4e322629e0c6698c75a3fb541a42571f8543c34c Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 13 Aug 2019 16:45:04 -0400 Subject: [PATCH 13/21] remove NullSession --- lib/rack/session/abstract/id.rb | 9 ++------- lib/rack/session/cookie.rb | 8 ++------ lib/rack/session/pool.rb | 6 +----- 3 files changed, 5 insertions(+), 18 deletions(-) diff --git a/lib/rack/session/abstract/id.rb b/lib/rack/session/abstract/id.rb index ac96787e2..aca52d4b8 100644 --- a/lib/rack/session/abstract/id.rb +++ b/lib/rack/session/abstract/id.rb @@ -11,11 +11,6 @@ module Rack module Session - class NullSessionId - def empty?; true; end - def nil?; true; end - end - class SessionId attr_reader :public_id @@ -313,7 +308,7 @@ def prepare_session(req) def load_session(req) sid = current_session_id(req) sid, session = find_session(req, sid) - [sid || NullSessionId.new, session || {}] + [sid, session || {}] end # Extract session id from request object. @@ -321,7 +316,7 @@ def load_session(req) def extract_session_id(request) sid = request.cookies[@key] sid ||= request.params[@key] unless @cookie_only - (sid && SessionId.new(sid)) || NullSessionId.new + sid && SessionId.new(sid) end # Returns the current session id from the SessionHash. diff --git a/lib/rack/session/cookie.rb b/lib/rack/session/cookie.rb index a93f559e6..847686026 100644 --- a/lib/rack/session/cookie.rb +++ b/lib/rack/session/cookie.rb @@ -129,7 +129,7 @@ def find_session(req, sid) end def extract_session_id(request) - unpacked_cookie_data(request)["session_id"] || NullSessionId.new + unpacked_cookie_data(request)["session_id"] end def unpacked_cookie_data(request) @@ -180,11 +180,7 @@ def write_session(req, session_id, session, options) def delete_session(req, session_id, options) # Nothing to do here, data is in the client - if options[:drop] - NullSessionId.new - else - generate_sid - end + generate_sid unless options[:drop] end def digest_match?(data, digest) diff --git a/lib/rack/session/pool.rb b/lib/rack/session/pool.rb index 7e07797bb..0bd0c8c09 100644 --- a/lib/rack/session/pool.rb +++ b/lib/rack/session/pool.rb @@ -61,11 +61,7 @@ def write_session(req, session_id, new_session, options) def delete_session(req, session_id, options) with_lock(req) do @pool.delete(session_id.private_id) - if options[:drop] - NullSessionId.new - else - generate_sid - end + generate_sid unless options[:drop] end end From 73a5f79f6854eed81ecc3e5fb9f8154e967ccc49 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 13 Aug 2019 16:48:41 -0400 Subject: [PATCH 14/21] revert conditionals to master --- lib/rack/session/abstract/id.rb | 2 +- lib/rack/session/memcache.rb | 2 +- lib/rack/session/pool.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/rack/session/abstract/id.rb b/lib/rack/session/abstract/id.rb index aca52d4b8..e4f7fd037 100644 --- a/lib/rack/session/abstract/id.rb +++ b/lib/rack/session/abstract/id.rb @@ -372,7 +372,7 @@ def commit_session(req, res) if options[:drop] || options[:renew] session_id = delete_session(req, session.id || generate_sid, options) - return if session_id.nil? + return unless session_id end return unless commit_session?(req, session, options) diff --git a/lib/rack/session/memcache.rb b/lib/rack/session/memcache.rb index c97eedfad..19d5c5c69 100644 --- a/lib/rack/session/memcache.rb +++ b/lib/rack/session/memcache.rb @@ -48,7 +48,7 @@ def generate_sid def get_session(env, sid) with_lock(env) do - unless !sid.nil? and session = @pool.get(sid.private_id) + unless sid and session = @pool.get(sid.private_id) sid, session = generate_sid, {} unless /^STORED/ =~ @pool.add(sid.private_id, session) raise "Session collision on '#{sid.inspect}'" diff --git a/lib/rack/session/pool.rb b/lib/rack/session/pool.rb index 0bd0c8c09..5fd4f458b 100644 --- a/lib/rack/session/pool.rb +++ b/lib/rack/session/pool.rb @@ -43,7 +43,7 @@ def generate_sid def find_session(req, sid) with_lock(req) do - unless !sid.nil? and session = @pool[sid.private_id] + unless sid and session = @pool[sid.private_id] sid, session = generate_sid, {} @pool.store sid.private_id, session end From dc45a06b339c707c1f658c123ec7216151878f7a Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 13 Aug 2019 17:16:23 -0400 Subject: [PATCH 15/21] Add the private id --- lib/rack/session/pool.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rack/session/pool.rb b/lib/rack/session/pool.rb index 5fd4f458b..017fec452 100644 --- a/lib/rack/session/pool.rb +++ b/lib/rack/session/pool.rb @@ -37,7 +37,7 @@ def initialize(app, options={}) def generate_sid loop do sid = super - break sid unless @pool.key? sid + break sid unless @pool.key? sid.private_id end end From 6a04bbf6b742c305d3a56f9bd6242e6c943cc2ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Wed, 9 Oct 2019 17:50:45 -0400 Subject: [PATCH 16/21] Fallback to the legacy id when the new id is not found This will avoid all session to be invalidated. --- lib/rack/session/memcache.rb | 7 ++++++- test/spec_session_memcache.rb | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/lib/rack/session/memcache.rb b/lib/rack/session/memcache.rb index 19d5c5c69..bd73722b3 100644 --- a/lib/rack/session/memcache.rb +++ b/lib/rack/session/memcache.rb @@ -48,7 +48,7 @@ def generate_sid def get_session(env, sid) with_lock(env) do - unless sid and session = @pool.get(sid.private_id) + unless sid and session = get_session_with_fallback(sid) sid, session = generate_sid, {} unless /^STORED/ =~ @pool.add(sid.private_id, session) raise "Session collision on '#{sid.inspect}'" @@ -88,6 +88,11 @@ def with_lock(env) @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 --git a/test/spec_session_memcache.rb b/test/spec_session_memcache.rb index 1e9f05c57..c9a386a5d 100644 --- a/test/spec_session_memcache.rb +++ b/test/spec_session_memcache.rb @@ -235,6 +235,24 @@ 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 + # anyone know how to do this better? it "cleanly merges sessions when multithreaded" do skip unless $DEBUG From 3ba123d278f1085ba78fc000df954e507af2d622 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Wed, 9 Oct 2019 18:06:23 -0400 Subject: [PATCH 17/21] Also drop the session with the public id when destroying sessions --- lib/rack/session/memcache.rb | 1 + test/spec_session_memcache.rb | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/lib/rack/session/memcache.rb b/lib/rack/session/memcache.rb index bd73722b3..7e44eeabe 100644 --- a/lib/rack/session/memcache.rb +++ b/lib/rack/session/memcache.rb @@ -70,6 +70,7 @@ def set_session(env, session_id, new_session, options) def destroy_session(env, session_id, options) with_lock(env) do + @pool.delete(session_id.public_id) @pool.delete(session_id.private_id) generate_sid unless options[:drop] end diff --git a/test/spec_session_memcache.rb b/test/spec_session_memcache.rb index c9a386a5d..623b7487e 100644 --- a/test/spec_session_memcache.rb +++ b/test/spec_session_memcache.rb @@ -253,6 +253,27 @@ 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 From 1e96e0f197777458216bb3dfdbcce57a0bbba0c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Wed, 9 Oct 2019 19:14:08 -0400 Subject: [PATCH 18/21] Fallback to the public id when reading the session in the pool adapter --- lib/rack/session/abstract/id.rb | 1 + lib/rack/session/pool.rb | 9 ++++++- test/spec_session_pool.rb | 43 ++++++++++++++++++++++++++++++--- 3 files changed, 49 insertions(+), 4 deletions(-) diff --git a/lib/rack/session/abstract/id.rb b/lib/rack/session/abstract/id.rb index e4f7fd037..7a88429eb 100644 --- a/lib/rack/session/abstract/id.rb +++ b/lib/rack/session/abstract/id.rb @@ -6,6 +6,7 @@ require 'rack/request' require 'rack/response' require 'securerandom' +require 'digest/sha2' module Rack diff --git a/lib/rack/session/pool.rb b/lib/rack/session/pool.rb index 017fec452..36e7cde27 100644 --- a/lib/rack/session/pool.rb +++ b/lib/rack/session/pool.rb @@ -43,7 +43,7 @@ def generate_sid def find_session(req, sid) with_lock(req) do - unless sid and session = @pool[sid.private_id] + unless sid and session = get_session_with_fallback(sid) sid, session = generate_sid, {} @pool.store sid.private_id, session end @@ -60,6 +60,7 @@ def write_session(req, session_id, new_session, options) 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 @@ -71,6 +72,12 @@ def with_lock(req) 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 --git a/test/spec_session_pool.rb b/test/spec_session_pool.rb index 2d0616915..62a0505e7 100644 --- a/test/spec_session_pool.rb +++ b/test/spec_session_pool.rb @@ -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 From 5b1cab667270d7ad1a4d2088adf5ff4eb9845496 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Wed, 16 Oct 2019 14:07:36 -0400 Subject: [PATCH 19/21] Add a version prefix to the private id to make easier to migrate old values --- lib/rack/session/abstract/id.rb | 4 +++- test/spec_session_memcache.rb | 2 -- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/rack/session/abstract/id.rb b/lib/rack/session/abstract/id.rb index 7a88429eb..90e7848be 100644 --- a/lib/rack/session/abstract/id.rb +++ b/lib/rack/session/abstract/id.rb @@ -13,6 +13,8 @@ module Rack module Session class SessionId + ID_VERSION = 2 + attr_reader :public_id def initialize(public_id) @@ -20,7 +22,7 @@ def initialize(public_id) end def private_id - hash_sid public_id + "#{ID_VERSION}::#{hash_sid(public_id)}" end alias :cookie_value :public_id diff --git a/test/spec_session_memcache.rb b/test/spec_session_memcache.rb index 623b7487e..824db6836 100644 --- a/test/spec_session_memcache.rb +++ b/test/spec_session_memcache.rb @@ -246,7 +246,6 @@ 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}' @@ -266,7 +265,6 @@ 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}' From f1a79b208c4ea877420beee62646e0b146402bd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Mon, 21 Oct 2019 16:39:00 -0400 Subject: [PATCH 20/21] Introduce a new base class to avoid breaking when upgrading Third-party session store would still need to be chaged to be more secure but only upgrading rack will not break any application. --- lib/rack/session/abstract/id.rb | 52 ++++++++++++++++++++++++++------ lib/rack/session/cookie.rb | 2 +- lib/rack/session/memcache.rb | 18 +++++------ lib/rack/session/pool.rb | 2 +- test/spec_session_abstract_id.rb | 2 +- 5 files changed, 54 insertions(+), 22 deletions(-) diff --git a/lib/rack/session/abstract/id.rb b/lib/rack/session/abstract/id.rb index 90e7848be..9d63ca2dd 100644 --- a/lib/rack/session/abstract/id.rb +++ b/lib/rack/session/abstract/id.rb @@ -81,11 +81,7 @@ def each(&block) def [](key) load_for_read! - if key == "session_id" - id.public_id - else - @data[key.to_s] - end + @data[key.to_s] end def fetch(key, default=Unspecified, &block) @@ -283,13 +279,11 @@ def initialize_sid # Monkey patch this to use custom methods for session id generation. def generate_sid(secure = @sid_secure) - public_id = if secure + if secure secure.hex(@sid_length) else "%0#{@sid_length}x" % Kernel.rand(2**@sidbits - 1) end - - SessionId.new(public_id) rescue NotImplementedError generate_sid(false) end @@ -319,7 +313,7 @@ def load_session(req) def extract_session_id(request) sid = request.cookies[@key] sid ||= request.params[@key] unless @cookie_only - sid && SessionId.new(sid) + sid end # Returns the current session id from the SessionHash. @@ -390,7 +384,7 @@ def commit_session(req, res) req.get_header(RACK_ERRORS).puts("Deferring cookie for #{session_id}") if $VERBOSE else cookie = Hash.new - cookie[:value] = data.cookie_value + 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)) @@ -398,6 +392,10 @@ def commit_session(req, res) 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. @@ -439,6 +437,40 @@ def delete_session(req, sid, options) 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 --git a/lib/rack/session/cookie.rb b/lib/rack/session/cookie.rb index 847686026..90ed5cf17 100644 --- a/lib/rack/session/cookie.rb +++ b/lib/rack/session/cookie.rb @@ -45,7 +45,7 @@ module Session # }) # - class Cookie < Abstract::Persisted + class Cookie < Abstract::PersistedSecure # Encode session cookies as Base64 class Base64 def encode(str) diff --git a/lib/rack/session/memcache.rb b/lib/rack/session/memcache.rb index 7e44eeabe..1f9d3ec56 100644 --- a/lib/rack/session/memcache.rb +++ b/lib/rack/session/memcache.rb @@ -19,7 +19,7 @@ module Session # 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 \ @@ -46,8 +46,8 @@ def generate_sid end end - def get_session(env, sid) - with_lock(env) do + 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.private_id, session) @@ -58,26 +58,26 @@ def get_session(env, sid) 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 + 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 + 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 diff --git a/lib/rack/session/pool.rb b/lib/rack/session/pool.rb index 36e7cde27..6c0f66811 100644 --- a/lib/rack/session/pool.rb +++ b/lib/rack/session/pool.rb @@ -24,7 +24,7 @@ module Session # ) # 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 diff --git a/test/spec_session_abstract_id.rb b/test/spec_session_abstract_id.rb index 05608aa59..a6568f198 100644 --- a/test/spec_session_abstract_id.rb +++ b/test/spec_session_abstract_id.rb @@ -25,7 +25,7 @@ def hex(*args) end end id = Rack::Session::Abstract::ID.new nil, :secure_random => secure_random.new - id.send(:generate_sid).public_id.must_equal 'fake_hex' + id.send(:generate_sid).must_equal 'fake_hex' end end From e7ee459546d217f32afc83e0b168c5eb9f95d784 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 18 Dec 2019 10:04:52 -0800 Subject: [PATCH 21/21] Bumping version --- lib/rack.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rack.rb b/lib/rack.rb index c8b6cc86a..48ac3e211 100644 --- a/lib/rack.rb +++ b/lib/rack.rb @@ -18,7 +18,7 @@ def self.version VERSION.join(".") end - RELEASE = "2.0.7" + RELEASE = "2.0.8" # Return the Rack release as a dotted string. def self.release