Hello community, here is the log from the commit of package rubygem-rack for openSUSE:Leap:15.2 checked in at 2020-06-06 15:47:48 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Leap:15.2/rubygem-rack (Old) and /work/SRC/openSUSE:Leap:15.2/.rubygem-rack.new.3606 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "rubygem-rack" Sat Jun 6 15:47:48 2020 rev:12 rq:771825 version:2.0.8 Changes: -------- --- /work/SRC/openSUSE:Leap:15.2/rubygem-rack/rubygem-rack.changes 2020-01-15 15:59:37.411804720 +0100 +++ /work/SRC/openSUSE:Leap:15.2/.rubygem-rack.new.3606/rubygem-rack.changes 2020-06-06 15:47:49.163918197 +0200 @@ -1,0 +2,50 @@ +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 + +------------------------------------------------------------------- +Sat Apr 6 17:52:23 UTC 2019 - manuel <[email protected]> + +- updated to version 2.0.7 + + no changelog found + +------------------------------------------------------------------- +Tue Nov 6 23:24:32 UTC 2018 - Marcus Rueckert <[email protected]> + +- update to 2.0.6: + * CVE-2018-16471: cross-site scripting (XSS) flaw via the scheme + method on Rack::Request (bsc#1114828) + +------------------------------------------------------------------- +Mon Apr 23 18:18:04 UTC 2018 - [email protected] + +- updated to version 2.0.5 + see installed HISTORY.md + +------------------------------------------------------------------- +Mon Apr 16 15:47:33 UTC 2018 - [email protected] + +- Only build against ruby versions 2.3.x, 2.4.x, and 2.5.x +- Fix package build by removing the executable bit for 'test.gz' file in gem + +------------------------------------------------------------------- +Thu Feb 8 06:21:32 UTC 2018 - [email protected] + +- updated to version 2.0.4 + see installed HISTORY.md + +------------------------------------------------------------------- +Tue Oct 31 14:09:19 UTC 2017 - [email protected] + +- only build for 2.3+ from now + +------------------------------------------------------------------- +Wed Jun 7 16:24:31 UTC 2017 - [email protected] + +- re-add the rb_build_versions and rb_default_ruby_abi as otherwise + building on older distros fails. +- add ruby 2.4 + +------------------------------------------------------------------- Old: ---- rack-2.0.3.gem New: ---- rack-2.0.8.gem ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ rubygem-rack.spec ++++++ --- /var/tmp/diff_new_pack.FECRmK/_old 2020-06-06 15:47:49.755920294 +0200 +++ /var/tmp/diff_new_pack.FECRmK/_new 2020-06-06 15:47:49.759920307 +0200 @@ -1,7 +1,7 @@ # # spec file for package rubygem-rack # -# Copyright (c) 2017 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,19 +24,24 @@ # Name: rubygem-rack -Version: 2.0.3 +Version: 2.0.8 Release: 0 %define mod_name rack %define mod_full_name %{mod_name}-%{version} # MANUAL BuildRequires: fdupes + +%if 0%{?suse_version} && 0%{?suse_version} < 1330 +%define rb_build_versions ruby23 ruby24 ruby25 +%define rb_build_ruby_abi ruby:2.3.0 ruby:2.4.0 ruby:2.5.0 +%endif # /MANUAL BuildRoot: %{_tmppath}/%{name}-%{version}-build BuildRequires: %{ruby >= 2.2.2} BuildRequires: %{rubygem gem2rpm} BuildRequires: ruby-macros >= 5 BuildRequires: update-alternatives -Url: http://rack.github.io/ +URL: https://rack.github.io/ Source: https://rubygems.org/gems/%{mod_full_name}.gem Source1: rubygem-rack-rpmlintrc Source2: gem2rpm.yml @@ -51,7 +56,7 @@ the simplest way possible, it unifies and distills the API for web servers, web frameworks, and software in between (the so-called middleware) into a single method call. -Also see http://rack.github.io/. +Also see https://rack.github.io/. %prep @@ -64,6 +69,7 @@ -f # MANUAL %fdupes %{buildroot}%{_libdir}/ruby/gems/*/gems/%{mod_name}-%{version}/ +chmod 644 %{buildroot}%{_libdir}/ruby/gems/*/gems/%{mod_name}-%{version}/test/cgi/test.gz # /MANUAL %gem_packages ++++++ gem2rpm.yml ++++++ --- /var/tmp/diff_new_pack.FECRmK/_old 2020-06-06 15:47:49.823920534 +0200 +++ /var/tmp/diff_new_pack.FECRmK/_new 2020-06-06 15:47:49.823920534 +0200 @@ -76,8 +76,12 @@ :preamble: |- BuildRequires: fdupes -# %define rb_build_versions ruby22 ruby23 -# %define rb_default_ruby_abi ruby:2.2.0 ruby:2.3.0 + + %if 0%{?suse_version} && 0%{?suse_version} < 1330 + %define rb_build_versions ruby23 ruby24 ruby25 + %define rb_build_ruby_abi ruby:2.3.0 ruby:2.4.0 ruby:2.5.0 + %endif :post_install: |- %fdupes %{buildroot}%{_libdir}/ruby/gems/*/gems/%{mod_name}-%{version}/ + chmod 644 %{buildroot}%{_libdir}/ruby/gems/*/gems/%{mod_name}-%{version}/test/cgi/test.gz ++++++ rack-2.0.3.gem -> rack-2.0.8.gem ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/README.rdoc new/README.rdoc --- old/README.rdoc 2017-05-15 18:49:44.000000000 +0200 +++ new/README.rdoc 2019-12-18 19:05:05.000000000 +0100 @@ -228,7 +228,7 @@ The Rack Core Team, consisting of -* Christian Neukirchen (chneukirchen[https://github.com/chneukirchen]) +* Leah Neukirchen (chneukirchen[https://github.com/chneukirchen]) * James Tucker (raggi[https://github.com/raggi]) * Josh Peek (josh[https://github.com/josh]) * José Valim (josevalim[https://github.com/josevalim]) @@ -295,10 +295,8 @@ == Links -Rack:: <http://rack.github.io/> +Rack:: <https://rack.github.io/> Official Rack repositories:: <https://github.com/rack> Rack Bug Tracking:: <https://github.com/rack/rack/issues> rack-devel mailing list:: <https://groups.google.com/group/rack-devel> Rack's Rubyforge project:: <http://rubyforge.org/projects/rack> - -Christian Neukirchen:: <http://chneukirchen.org/> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/SPEC new/SPEC --- old/SPEC 2017-05-15 18:49:44.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/chunked.rb new/lib/rack/chunked.rb --- old/lib/rack/chunked.rb 2017-05-15 18:49:44.000000000 +0200 +++ new/lib/rack/chunked.rb 2019-12-18 19:05:05.000000000 +0100 @@ -24,7 +24,7 @@ size = chunk.bytesize next if size == 0 - chunk = chunk.dup.force_encoding(Encoding::BINARY) + chunk = chunk.b yield [size.to_s(16), term, chunk, term].join end yield TAIL diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/rack/etag.rb new/lib/rack/etag.rb --- old/lib/rack/etag.rb 2017-05-15 18:49:44.000000000 +0200 +++ new/lib/rack/etag.rb 2019-12-18 19:05:05.000000000 +0100 @@ -1,5 +1,5 @@ require 'rack' -require 'digest/md5' +require 'digest/sha2' module Rack # Automatically sets the ETag header on all String bodies. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/rack/lock.rb new/lib/rack/lock.rb --- old/lib/rack/lock.rb 2017-05-15 18:49:44.000000000 +0200 +++ new/lib/rack/lock.rb 2019-12-18 19:05:05.000000000 +0100 @@ -11,12 +11,21 @@ def call(env) @mutex.lock + @env = env + @old_rack_multithread = env[RACK_MULTITHREAD] begin - response = @app.call(env.merge(RACK_MULTITHREAD => false)) - returned = response << BodyProxy.new(response.pop) { @mutex.unlock } + response = @app.call(env.merge!(RACK_MULTITHREAD => false)) + returned = response << BodyProxy.new(response.pop) { unlock } ensure - @mutex.unlock unless returned + unlock unless returned end end + + private + + def unlock + @mutex.unlock + @env[RACK_MULTITHREAD] = @old_rack_multithread + end end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/rack/method_override.rb new/lib/rack/method_override.rb --- old/lib/rack/method_override.rb 2017-05-15 18:49:44.000000000 +0200 +++ new/lib/rack/method_override.rb 2019-12-18 19:05:05.000000000 +0100 @@ -26,7 +26,11 @@ req = Request.new(env) method = method_override_param(req) || env[HTTP_METHOD_OVERRIDE_HEADER] - method.to_s.upcase + begin + method.to_s.upcase + rescue ArgumentError + env[RACK_ERRORS].puts "Invalid string for method" + end end private diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/rack/mock.rb new/lib/rack/mock.rb --- old/lib/rack/mock.rb 2017-05-15 18:49:44.000000000 +0200 +++ new/lib/rack/mock.rb 2019-12-18 19:05:05.000000000 +0100 @@ -128,7 +128,7 @@ end end - empty_str = String.new.force_encoding(Encoding::ASCII_8BIT) + empty_str = String.new opts[:input] ||= empty_str if String === opts[:input] rack_input = StringIO.new(opts[:input]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/rack/multipart/parser.rb new/lib/rack/multipart/parser.rb --- old/lib/rack/multipart/parser.rb 2017-05-15 18:49:44.000000000 +0200 +++ new/lib/rack/multipart/parser.rb 2019-12-18 19:05:05.000000000 +0100 @@ -39,8 +39,6 @@ str end - def eof?; @content_length == @cursor; end - def rewind @io.rewind end @@ -65,11 +63,11 @@ 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 @@ -135,7 +133,7 @@ klass = TempfilePart @open_files += 1 else - body = ''.force_encoding(Encoding::ASCII_8BIT) + body = String.new klass = BufferPart end @@ -165,15 +163,15 @@ attr_reader :state def initialize(boundary, tempfile, bufsize, query_parser) - @buf = "".force_encoding(Encoding::ASCII_8BIT) + @buf = String.new @query_parser = query_parser @params = query_parser.make_params @boundary = "--#{boundary}" - @boundary_size = @boundary.bytesize + EOL.size @bufsize = bufsize @rx = /(?:#{EOL})?#{Regexp.quote(@boundary)}(#{EOL}|--)/n + @rx_max_size = EOL.size + @boundary.bytesize + [EOL.size, '--'.size].max @full_boundary = @boundary @end_boundary = @boundary + '--' @state = :FAST_FORWARD @@ -181,8 +179,8 @@ @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 @@ -263,15 +261,17 @@ end def handle_mime_body - if @buf =~ rx + if i = @buf.index(rx) # Save the rest. - if i = @buf.index(rx) - @collector.on_mime_body @mime_index, @buf.slice!(0, i) - @buf.slice!(0, 2) # Remove \r\n after the content - end + @collector.on_mime_body @mime_index, @buf.slice!(0, i) + @buf.slice!(0, 2) # Remove \r\n after the content @state = :CONSUME_TOKEN @mime_index += 1 else + # Save the read body part. + if @rx_max_size < @buf.size + @collector.on_mime_body @mime_index, @buf.slice!(0, @buf.size - @rx_max_size) + end :want_read end end @@ -356,10 +356,9 @@ 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 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/rack/request.rb new/lib/rack/request.rb --- old/lib/rack/request.rb 2017-05-15 18:49:44.000000000 +0200 +++ new/lib/rack/request.rb 2019-12-18 19:05:05.000000000 +0100 @@ -11,6 +11,8 @@ # req.params["data"] class Request + SCHEME_WHITELIST = %w(https http).freeze + def initialize(env) @params = nil super(env) @@ -188,10 +190,8 @@ '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 @@ -261,7 +261,7 @@ 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 @@ -479,6 +479,19 @@ 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 -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 2017-05-15 18:49:44.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 2017-05-15 18:49:44.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 2017-05-15 18:49:44.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 2017-05-15 18:49:44.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/show_exceptions.rb new/lib/rack/show_exceptions.rb --- old/lib/rack/show_exceptions.rb 2017-05-15 18:49:44.000000000 +0200 +++ new/lib/rack/show_exceptions.rb 2019-12-18 19:05:05.000000000 +0100 @@ -46,7 +46,7 @@ end def prefers_plaintext?(env) - !accepts_html(env) + !accepts_html?(env) end def accepts_html?(env) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/rack.rb new/lib/rack.rb --- old/lib/rack.rb 2017-05-15 18:49:44.000000000 +0200 +++ new/lib/rack.rb 2019-12-18 19:05:05.000000000 +0100 @@ -18,7 +18,7 @@ VERSION.join(".") end - RELEASE = "2.0.3" + 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 2017-05-15 18:49:44.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.3 + version: 2.0.8 platform: ruby authors: -- Christian Neukirchen +- Leah Neukirchen autorequire: bindir: bin cert_chain: [] -date: 2017-05-15 00:00:00.000000000 Z +date: 2019-12-18 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: minitest @@ -39,20 +39,6 @@ - !ruby/object:Gem::Version version: '0' - !ruby/object:Gem::Dependency - name: concurrent-ruby - requirement: !ruby/object:Gem::Requirement - requirements: - - - ">=" - - !ruby/object:Gem::Version - version: '0' - type: :development - prerelease: false - version_requirements: !ruby/object:Gem::Requirement - requirements: - - - ">=" - - !ruby/object:Gem::Version - version: '0' -- !ruby/object:Gem::Dependency name: rake requirement: !ruby/object:Gem::Requirement requirements: @@ -73,8 +59,8 @@ servers, web frameworks, and software in between (the so-called middleware) into a single method call. - Also see http://rack.github.io/. -email: [email protected] + Also see https://rack.github.io/. +email: [email protected] executables: - rackup extensions: [] @@ -269,7 +255,7 @@ - test/testrequest.rb - test/unregistered_handler/rack/handler/unregistered.rb - test/unregistered_handler/rack/handler/unregistered_long_one.rb -homepage: http://rack.github.io/ +homepage: https://rack.github.io/ licenses: - MIT metadata: {} @@ -288,60 +274,59 @@ - !ruby/object:Gem::Version version: '0' requirements: [] -rubyforge_project: -rubygems_version: 2.6.8 +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/rack.gemspec new/rack.gemspec --- old/rack.gemspec 2017-05-15 18:49:44.000000000 +0200 +++ new/rack.gemspec 2019-12-18 19:05:05.000000000 +0100 @@ -12,7 +12,7 @@ servers, web frameworks, and software in between (the so-called middleware) into a single method call. -Also see http://rack.github.io/. +Also see https://rack.github.io/. EOF s.files = Dir['{bin/*,contrib/*,example/*,lib/**/*,test/**/*}'] + @@ -23,13 +23,12 @@ s.extra_rdoc_files = ['README.rdoc', 'HISTORY.md'] s.test_files = Dir['test/spec_*.rb'] - s.author = 'Christian Neukirchen' - s.email = '[email protected]' - s.homepage = 'http://rack.github.io/' + s.author = 'Leah Neukirchen' + s.email = '[email protected]' + s.homepage = 'https://rack.github.io/' s.required_ruby_version = '>= 2.2.2' s.add_development_dependency 'minitest', "~> 5.0" s.add_development_dependency 'minitest-sprint' - s.add_development_dependency 'concurrent-ruby' s.add_development_dependency 'rake' end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/test/spec_lock.rb new/test/spec_lock.rb --- old/test/spec_lock.rb 2017-05-15 18:49:44.000000000 +0200 +++ new/test/spec_lock.rb 2019-12-18 19:05:05.000000000 +0100 @@ -147,7 +147,8 @@ }, false) env = Rack::MockRequest.env_for("/") env['rack.multithread'].must_equal true - app.call(env) + _, _, body = app.call(env) + body.close env['rack.multithread'].must_equal true end @@ -191,4 +192,13 @@ lambda { app.call(env) }.must_raise Exception lock.synchronized.must_equal false end + + it "not replace the environment" do + env = Rack::MockRequest.env_for("/") + app = lock_app(lambda { |inner_env| [200, {"Content-Type" => "text/plain"}, [inner_env.object_id.to_s]] }) + + _, _, body = app.call(env) + + body.to_enum.to_a.must_equal [env.object_id.to_s] + end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/test/spec_method_override.rb new/test/spec_method_override.rb --- old/test/spec_method_override.rb 2017-05-15 18:49:44.000000000 +0200 +++ new/test/spec_method_override.rb 2019-12-18 19:05:05.000000000 +0100 @@ -17,6 +17,20 @@ env["REQUEST_METHOD"].must_equal "GET" end + it "sets rack.errors for invalid UTF8 _method values" do + errors = StringIO.new + env = Rack::MockRequest.env_for("/", + :method => "POST", + :input => "_method=\xBF".b, + Rack::RACK_ERRORS => errors) + + app.call env + + errors.rewind + errors.read.must_equal "Invalid string for method\n" + env["REQUEST_METHOD"].must_equal "POST" + end + it "modify REQUEST_METHOD for POST requests when _method parameter is set" do env = Rack::MockRequest.env_for("/", :method => "POST", :input => "_method=put") app.call env diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/test/spec_request.rb new/test/spec_request.rb --- old/test/spec_request.rb 2017-05-15 18:49:44.000000000 +0200 +++ new/test/spec_request.rb 2019-12-18 19:05:05.000000000 +0100 @@ -572,6 +572,11 @@ request.must_be :ssl? end + it "prevents scheme abuse" do + request = make_request(Rack::MockRequest.env_for("/", 'HTTP_X_FORWARDED_SCHEME' => 'a."><script>alert(1)</script>')) + request.scheme.must_equal 'http' + end + it "parse cookies" do req = make_request \ Rack::MockRequest.env_for("", "HTTP_COOKIE" => "foo=bar;quux=h&m") @@ -1281,7 +1286,16 @@ 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 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 2017-05-15 18:49:44.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 2017-05-15 18:49:45.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 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/test/spec_show_exceptions.rb new/test/spec_show_exceptions.rb --- old/test/spec_show_exceptions.rb 2017-05-15 18:49:45.000000000 +0200 +++ new/test/spec_show_exceptions.rb 2019-12-18 19:05:05.000000000 +0100 @@ -77,4 +77,17 @@ 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 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/test/spec_webrick.rb new/test/spec_webrick.rb --- old/test/spec_webrick.rb 2017-05-15 18:49:45.000000000 +0200 +++ new/test/spec_webrick.rb 2019-12-18 19:05:05.000000000 +0100 @@ -1,7 +1,6 @@ require 'minitest/autorun' require 'rack/mock' -require 'concurrent/utility/native_integer' -require 'concurrent/atomic/count_down_latch' +require 'thread' require File.expand_path('../testrequest', __FILE__) Thread.abort_on_exception = true @@ -120,8 +119,7 @@ end it "provide a .run" do - block_ran = false - latch = Concurrent::CountDownLatch.new 1 + queue = Queue.new t = Thread.new do Rack::Handler::WEBrick.run(lambda {}, @@ -132,13 +130,12 @@ :AccessLog => []}) { |server| block_ran = true assert_kind_of WEBrick::HTTPServer, server - @s = server - latch.count_down + queue.push(server) } end - latch.wait - @s.shutdown + server = queue.pop + server.shutdown t.join end
