From f70500e6dcad12827069907d14913319ee24f867 Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Aug 06 2019 08:28:51 +0000 Subject: import ruby-2.0.0.648-36.el7 --- diff --git a/SOURCES/macros.rubygems b/SOURCES/macros.rubygems index ba1c0a2..13c378e 100644 --- a/SOURCES/macros.rubygems +++ b/SOURCES/macros.rubygems @@ -43,8 +43,8 @@ gem install \\\ } -# The 'read' command in gemspec_add _depand gemspec_remove_dep macros is not -# essential, but it is usefull to make the sript appear in build log. +# The 'read' command in %%gemspec_* macros is not essential, but it is usefull +# to make the sript appear in build log. # %gemspec_add_dep - Add dependency into .gemspec. @@ -129,3 +129,68 @@ EOR\ echo "$gemspec_remove_dep_script" | ruby \ unset -v gemspec_remove_dep_script \ %{nil} + + +# %%gemspec_add_file - Add files to various files lists in .gemspec. +# +# Usage: %%gemspec_add_file [options] +# +# Add files to .gemspec file. is expected to be valid Ruby code. +# Path to file is expected. Does not check real files in any way. +# By default, `files` list is edited. +# +# -s Overrides the default .gemspec location. +# -t Edit test_files only. +# -r Edit extra_rdoc_files only. +# +%gemspec_add_file(s:tr) \ +read -d '' gemspec_add_file_script << 'EOR' || : \ + gemspec_file = '%{-s*}%{!?-s:%{_builddir}/%{gem_name}-%{version}.gemspec}' \ + \ + abort("gemspec_add_file: Use only one '-t' or '-r' at a time.") if "%{?-t}%{?-r}" == "-t-r" \ + \ + filenames = %{*}%{!?1:nil} \ + filenames = Array(filenames) \ + \ + spec = Gem::Specification.load(gemspec_file) \ + abort("#{gemspec_file} is not accessible.") unless spec \ + \ + spec.%{?-t:test_}%{?-r:extra_rdoc_}files += filenames \ + File.write gemspec_file, spec.to_ruby \ +EOR\ +echo "$gemspec_add_file_script" | ruby \ +unset -v gemspec_add_file_script \ +%{nil} + + +# %%gemspec_remove_file - Remove files from various files lists in .gemspec. +# +# Usage: %%gemspec_remove_file [options] +# +# Remove files from .gemspec file. is expected to be valid Ruby code. +# Path to file is expected. Does not check/remove real files in any way. +# By default, `files` list is edited. File has to be removed from `test_files` +# first in order to be removable from `files`. +# +# -s Overrides the default .gemspec location. +# -t Edit test_files only. +# -r Edit extra_rdoc_files only. +# +%gemspec_remove_file(s:tr) \ +read -d '' gemspec_remove_file_script << 'EOR' || : \ + gemspec_file = '%{-s*}%{!?-s:%{_builddir}/%{gem_name}-%{version}.gemspec}' \ + \ + abort("gemspec_remove_file: Use only one '-t' or '-r' at a time.") if "%{?-t}%{?-r}" == "-t-r" \ + \ + filenames = %{*}%{!?1:nil} \ + filenames = Array(filenames) \ + \ + spec = Gem::Specification.load(gemspec_file) \ + abort("#{gemspec_file} is not accessible.") unless spec \ + \ + spec.%{?-t:test_}%{?-r:extra_rdoc_}files -= filenames \ + File.write gemspec_file, spec.to_ruby \ +EOR\ +echo "$gemspec_remove_file_script" | ruby \ +unset -v gemspec_remove_file_script \ +%{nil} diff --git a/SOURCES/operating_system.rb b/SOURCES/operating_system.rb index c3b19d6..ff3477e 100644 --- a/SOURCES/operating_system.rb +++ b/SOURCES/operating_system.rb @@ -67,6 +67,7 @@ module Gem def default_path path = default_dirs.collect {|location, paths| paths[:gem_dir]} path.unshift Gem.user_dir if File.exist? Gem.user_home + path end def default_bindir diff --git a/SOURCES/ruby-2-2.10-webrick-avoid-large-request-fix-tests.patch b/SOURCES/ruby-2-2.10-webrick-avoid-large-request-fix-tests.patch new file mode 100644 index 0000000..aad50c7 --- /dev/null +++ b/SOURCES/ruby-2-2.10-webrick-avoid-large-request-fix-tests.patch @@ -0,0 +1,43 @@ +diff --git a/test/webrick/test_httpauth.rb b/test/webrick/test_httpauth.rb +index 842668f54e..8bd976c933 100644 +--- a/test/webrick/test_httpauth.rb ++++ b/test/webrick/test_httpauth.rb +@@ -197,11 +197,11 @@ class TestWEBrickHTTPAuth < Test::Unit::TestCase + } + assert_equal([], log) + } +- TestWEBrick.start_httpserver({}, log_tester) {|server, addr, port, log| ++ TestWEBrick.start_httpserver_with_log({}, log_tester) {|server, addr, port, log| + realm = "wb auth-int realm" + path = "/digest_auth_int" + +- Tempfile.create("test_webrick_auth_int") {|tmpfile| ++ Tempfile.open("test_webrick_auth_int") {|tmpfile| + tmpfile.close + tmp_pass = WEBrick::HTTPAuth::Htdigest.new(tmpfile.path) + tmp_pass.set_passwd(realm, "foo", "Hunter2") +diff --git a/test/webrick/test_httpserver.rb b/test/webrick/test_httpserver.rb +index f1d58b40f5..594f578d2d 100644 +--- a/test/webrick/test_httpserver.rb ++++ b/test/webrick/test_httpserver.rb +@@ -372,7 +372,7 @@ class TestWEBrickHTTPServer < Test::Unit::TestCase + assert_equal 1, log.size + assert log[0].include?('ERROR headers too large') + } +- TestWEBrick.start_httpserver({}, log_tester){|server, addr, port, log| ++ TestWEBrick.start_httpserver_with_log({}, log_tester){|server, addr, port, log| + server.mount('/', WEBrick::HTTPServlet::FileHandler, __FILE__) + TCPSocket.open(addr, port) do |c| + c.write("GET / HTTP/1.0\r\n") +@@ -389,7 +389,7 @@ class TestWEBrickHTTPServer < Test::Unit::TestCase + assert_equal 1, log.size + assert log[0].include?('ERROR bad chunk data size') + end +- TestWEBrick.start_httpserver({}, log_tester){|server, addr, port, log| ++ TestWEBrick.start_httpserver_with_log({}, log_tester){|server, addr, port, log| + server.mount_proc('/', ->(req, res) { res.body = req.body }) + TCPSocket.open(addr, port) do |c| + c.write("POST / HTTP/1.1\r\nHost: example.com\r\n" \ +-- +2.17.1 + diff --git a/SOURCES/ruby-2-2.10-webrick-avoid-large-request.patch b/SOURCES/ruby-2-2.10-webrick-avoid-large-request.patch new file mode 100644 index 0000000..c9555c9 --- /dev/null +++ b/SOURCES/ruby-2-2.10-webrick-avoid-large-request.patch @@ -0,0 +1,386 @@ +From ae0065be15b3253042b65baa54f2953f3a6e6926 Mon Sep 17 00:00:00 2001 +From: usa +Date: Wed, 28 Mar 2018 14:47:30 +0000 +Subject: [PATCH] merge revision(s) 62960-62965: +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + + webrick: use IO.copy_stream for multipart response + + Use the new Proc response body feature to generate a multipart + range response dynamically. We use a flat array to minimize + object overhead as much as possible; as many ranges may fit + into an HTTP request header. + + * lib/webrick/httpservlet/filehandler.rb (multipart_body): new method + (make_partial_content): use multipart_body + ------------------------------------------------------------------------ + r62960 | normal | 2018-03-28 17:06:23 +0900 (水, 28 3 2018) | 13 lines + + webrick/httprequest: limit request headers size + + We use the same 112 KB limit started (AFAIK) by Mongrel, Thin, + and Puma to prevent malicious users from using up all the memory + with a single request. This also limits the damage done by + excessive ranges in multipart Range: requests. + + Due to the way we rely on IO#gets and the desire to keep + the code simple, the actual maximum header may be 4093 bytes + larger than 112 KB, but we're splitting hairs at that point. + + * lib/webrick/httprequest.rb: define MAX_HEADER_LENGTH + (read_header): raise when headers exceed max length + ------------------------------------------------------------------------ + r62961 | normal | 2018-03-28 17:06:28 +0900 (水, 28 3 2018) | 9 lines + + webrick/httpservlet/cgihandler: reduce memory use + + WEBrick::HTTPRequest#body can be passed a block to process the + body in chunks. Use this feature to avoid building a giant + string in memory. + + * lib/webrick/httpservlet/cgihandler.rb (do_GET): + avoid reading entire request body into memory + (do_POST is aliased to do_GET, so it handles bodies) + ------------------------------------------------------------------------ + r62962 | normal | 2018-03-28 17:06:34 +0900 (水, 28 3 2018) | 7 lines + + webrick/httprequest: raise correct exception + + "BadRequest" alone does not resolve correctly, it is in the + HTTPStatus namespace. + + * lib/webrick/httprequest.rb (read_chunked): use correct exception + * test/webrick/test_httpserver.rb (test_eof_in_chunk): new test + ------------------------------------------------------------------------ + r62963 | normal | 2018-03-28 17:06:39 +0900 (水, 28 3 2018) | 9 lines + + webrick/httprequest: use InputBufferSize for chunked requests + + While WEBrick::HTTPRequest#body provides a Proc interface + for streaming large request bodies, clients must not force + the server to use an excessively large chunk size. + + * lib/webrick/httprequest.rb (read_chunk_size): limit each + read and block.call to :InputBufferSize in config. + * test/webrick/test_httpserver.rb (test_big_chunks): new test + ------------------------------------------------------------------------ + r62964 | normal | 2018-03-28 17:06:44 +0900 (水, 28 3 2018) | 9 lines + + webrick: add test for Digest auth-int + + No changes to the actual code, this is a new test for + a feature for which no tests existed. I don't understand + the Digest authentication code well at all, but this is + necessary for the subsequent change. + + * test/webrick/test_httpauth.rb (test_digest_auth_int): new test + (credentials_for_request): support bodies with POST + ------------------------------------------------------------------------ + r62965 | normal | 2018-03-28 17:06:49 +0900 (水, 28 3 2018) | 18 lines + + webrick/httpauth/digestauth: stream req.body + + WARNING! WARNING! WARNING! LIKELY BROKEN CHANGE + + Pass a proc to WEBrick::HTTPRequest#body to avoid reading a + potentially large request body into memory during + authentication. + + WARNING! this will break apps completely which want to do + something with the body besides calculating the MD5 digest + of it. + + Also, keep in mind that probably nobody uses "auth-int". + Servers such as Apache, lighttpd, nginx don't seem to + support it; nor does curl when using POST/PUT bodies; + and we didn't have tests for it until now... + + * lib/webrick/httpauth/digestauth.rb (_authenticate): stream req.body + +git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_2_2@63021 b2dd03c8-39d4-4d8f-98ff-823fe69b080e +--- + lib/webrick/httpauth/digestauth.rb | 8 ++- + lib/webrick/httprequest.rb | 23 +++++-- + lib/webrick/httpservlet/cgihandler.rb | 4 +- + test/webrick/test_httpauth.rb | 90 ++++++++++++++++++++++++++- + test/webrick/test_httpserver.rb | 67 ++++++++++++++++++++ + 5 files changed, 178 insertions(+), 14 deletions(-) + +diff --git a/lib/webrick/httpauth/digestauth.rb b/lib/webrick/httpauth/digestauth.rb +index 78ad45b233..2a2319e9b1 100644 +--- a/lib/webrick/httpauth/digestauth.rb ++++ b/lib/webrick/httpauth/digestauth.rb +@@ -235,9 +235,11 @@ module WEBrick + ha2 = hexdigest(req.request_method, auth_req['uri']) + ha2_res = hexdigest("", auth_req['uri']) + elsif auth_req['qop'] == "auth-int" +- ha2 = hexdigest(req.request_method, auth_req['uri'], +- hexdigest(req.body)) +- ha2_res = hexdigest("", auth_req['uri'], hexdigest(res.body)) ++ body_digest = @h.new ++ req.body { |chunk| body_digest.update(chunk) } ++ body_digest = body_digest.hexdigest ++ ha2 = hexdigest(req.request_method, auth_req['uri'], body_digest) ++ ha2_res = hexdigest("", auth_req['uri'], body_digest) + end + + if auth_req['qop'] == "auth" || auth_req['qop'] == "auth-int" +diff --git a/lib/webrick/httprequest.rb b/lib/webrick/httprequest.rb +index 76420730b1..b3bcea7b3d 100644 +--- a/lib/webrick/httprequest.rb ++++ b/lib/webrick/httprequest.rb +@@ -412,9 +412,13 @@ module WEBrick + + MAX_URI_LENGTH = 2083 # :nodoc: + ++ # same as Mongrel, Thin and Puma ++ MAX_HEADER_LENGTH = (112 * 1024) # :nodoc: ++ + def read_request_line(socket) + @request_line = read_line(socket, MAX_URI_LENGTH) if socket +- if @request_line.bytesize >= MAX_URI_LENGTH and @request_line[-1, 1] != LF ++ @request_bytes = @request_line.bytesize ++ if @request_bytes >= MAX_URI_LENGTH and @request_line[-1, 1] != LF + raise HTTPStatus::RequestURITooLarge + end + @request_time = Time.now +@@ -433,6 +437,9 @@ module WEBrick + if socket + while line = read_line(socket) + break if /\A(#{CRLF}|#{LF})\z/om =~ line ++ if (@request_bytes += line.bytesize) > MAX_HEADER_LENGTH ++ raise HTTPStatus::RequestEntityTooLarge, 'headers too large' ++ end + @raw_header << line + end + end +@@ -500,12 +507,16 @@ module WEBrick + def read_chunked(socket, block) + chunk_size, = read_chunk_size(socket) + while chunk_size > 0 +- data = read_data(socket, chunk_size) # read chunk-data +- if data.nil? || data.bytesize != chunk_size +- raise BadRequest, "bad chunk data size." +- end ++ begin ++ sz = [ chunk_size, @buffer_size ].min ++ data = read_data(socket, sz) # read chunk-data ++ if data.nil? || data.bytesize != sz ++ raise HTTPStatus::BadRequest, "bad chunk data size." ++ end ++ block.call(data) ++ end while (chunk_size -= sz) > 0 ++ + read_line(socket) # skip CRLF +- block.call(data) + chunk_size, = read_chunk_size(socket) + end + read_header(socket) # trailer + CRLF +diff --git a/lib/webrick/httpservlet/cgihandler.rb b/lib/webrick/httpservlet/cgihandler.rb +index 7c012ca64b..d5ba756437 100644 +--- a/lib/webrick/httpservlet/cgihandler.rb ++++ b/lib/webrick/httpservlet/cgihandler.rb +@@ -66,9 +66,7 @@ module WEBrick + cgi_in.write("%8d" % dump.bytesize) + cgi_in.write(dump) + +- if req.body and req.body.bytesize > 0 +- cgi_in.write(req.body) +- end ++ req.body { |chunk| cgi_in.write(chunk) } + ensure + cgi_in.close + status = $?.exitstatus +diff --git a/test/webrick/test_httpauth.rb b/test/webrick/test_httpauth.rb +index 2414be9096..842668f54e 100644 +--- a/test/webrick/test_httpauth.rb ++++ b/test/webrick/test_httpauth.rb +@@ -3,6 +3,7 @@ require "net/http" + require "tempfile" + require "webrick" + require "webrick/httpauth/basicauth" ++require "stringio" + require_relative "utils" + + class TestWEBrickHTTPAuth < Test::Unit::TestCase +@@ -182,12 +183,97 @@ class TestWEBrickHTTPAuth < Test::Unit::TestCase + } + end + ++ def test_digest_auth_int ++ log_tester = lambda {|log, access_log| ++ log.reject! {|line| /\A\s*\z/ =~ line } ++ pats = [ ++ /ERROR Digest wb auth-int realm: no credentials in the request\./, ++ /ERROR WEBrick::HTTPStatus::Unauthorized/, ++ /ERROR Digest wb auth-int realm: foo: digest unmatch\./ ++ ] ++ pats.each {|pat| ++ assert(!log.grep(pat).empty?, "webrick log doesn't have expected error: #{pat.inspect}") ++ log.reject! {|line| pat =~ line } ++ } ++ assert_equal([], log) ++ } ++ TestWEBrick.start_httpserver({}, log_tester) {|server, addr, port, log| ++ realm = "wb auth-int realm" ++ path = "/digest_auth_int" ++ ++ Tempfile.create("test_webrick_auth_int") {|tmpfile| ++ tmpfile.close ++ tmp_pass = WEBrick::HTTPAuth::Htdigest.new(tmpfile.path) ++ tmp_pass.set_passwd(realm, "foo", "Hunter2") ++ tmp_pass.flush ++ ++ htdigest = WEBrick::HTTPAuth::Htdigest.new(tmpfile.path) ++ users = [] ++ htdigest.each{|user, pass| users << user } ++ assert_equal %w(foo), users ++ ++ auth = WEBrick::HTTPAuth::DigestAuth.new( ++ :Realm => realm, :UserDB => htdigest, ++ :Algorithm => 'MD5', ++ :Logger => server.logger, ++ :Qop => %w(auth-int), ++ ) ++ server.mount_proc(path){|req, res| ++ auth.authenticate(req, res) ++ res.body = "bbb" ++ } ++ Net::HTTP.start(addr, port) do |http| ++ post = Net::HTTP::Post.new(path) ++ params = {} ++ data = 'hello=world' ++ body = StringIO.new(data) ++ post.content_length = data.bytesize ++ post['Content-Type'] = 'application/x-www-form-urlencoded' ++ post.body_stream = body ++ ++ http.request(post) do |res| ++ assert_equal('401', res.code, log.call) ++ res["www-authenticate"].scan(DIGESTRES_) do |key, quoted, token| ++ params[key.downcase] = token || quoted.delete('\\') ++ end ++ params['uri'] = "http://#{addr}:#{port}#{path}" ++ end ++ ++ body.rewind ++ cred = credentials_for_request('foo', 'Hunter3', params, body) ++ post['Authorization'] = cred ++ post.body_stream = body ++ http.request(post){|res| ++ assert_equal('401', res.code, log.call) ++ assert_not_equal("bbb", res.body, log.call) ++ } ++ ++ body.rewind ++ cred = credentials_for_request('foo', 'Hunter2', params, body) ++ post['Authorization'] = cred ++ post.body_stream = body ++ http.request(post){|res| assert_equal("bbb", res.body, log.call)} ++ end ++ } ++ } ++ end ++ + private +- def credentials_for_request(user, password, params) ++ def credentials_for_request(user, password, params, body = nil) + cnonce = "hoge" + nonce_count = 1 + ha1 = "#{user}:#{params['realm']}:#{password}" +- ha2 = "GET:#{params['uri']}" ++ if body ++ dig = Digest::MD5.new ++ while buf = body.read(16384) ++ dig.update(buf) ++ end ++ body.rewind ++ ha2 = "POST:#{params['uri']}:#{dig.hexdigest}" ++ else ++ ha2 = "GET:#{params['uri']}" ++ end ++ + request_digest = + "#{Digest::MD5.hexdigest(ha1)}:" \ + "#{params['nonce']}:#{'%08x' % nonce_count}:#{cnonce}:#{params['qop']}:" \ +diff --git a/test/webrick/test_httpserver.rb b/test/webrick/test_httpserver.rb +index ffebf7e843..f1d58b40f5 100644 +--- a/test/webrick/test_httpserver.rb ++++ b/test/webrick/test_httpserver.rb +@@ -366,4 +366,71 @@ class TestWEBrickHTTPServer < Test::Unit::TestCase + } + assert_equal(requested, 1) + end ++ ++ def test_gigantic_request_header ++ log_tester = lambda {|log, access_log| ++ assert_equal 1, log.size ++ assert log[0].include?('ERROR headers too large') ++ } ++ TestWEBrick.start_httpserver({}, log_tester){|server, addr, port, log| ++ server.mount('/', WEBrick::HTTPServlet::FileHandler, __FILE__) ++ TCPSocket.open(addr, port) do |c| ++ c.write("GET / HTTP/1.0\r\n") ++ junk = "X-Junk: #{' ' * 1024}\r\n" ++ assert_raise(Errno::ECONNRESET, Errno::EPIPE) do ++ loop { c.write(junk) } ++ end ++ end ++ } ++ end ++ ++ def test_eof_in_chunk ++ log_tester = lambda do |log, access_log| ++ assert_equal 1, log.size ++ assert log[0].include?('ERROR bad chunk data size') ++ end ++ TestWEBrick.start_httpserver({}, log_tester){|server, addr, port, log| ++ server.mount_proc('/', ->(req, res) { res.body = req.body }) ++ TCPSocket.open(addr, port) do |c| ++ c.write("POST / HTTP/1.1\r\nHost: example.com\r\n" \ ++ "Transfer-Encoding: chunked\r\n\r\n5\r\na") ++ c.shutdown(Socket::SHUT_WR) # trigger EOF in server ++ res = c.read ++ assert_match %r{\AHTTP/1\.1 400 }, res ++ end ++ } ++ end ++ ++ def test_big_chunks ++ nr_out = 3 ++ buf = 'big' # 3 bytes is bigger than 2! ++ config = { :InputBufferSize => 2 }.freeze ++ total = 0 ++ all = '' ++ TestWEBrick.start_httpserver(config){|server, addr, port, log| ++ server.mount_proc('/', ->(req, res) { ++ err = [] ++ ret = req.body do |chunk| ++ n = chunk.bytesize ++ n > config[:InputBufferSize] and err << "#{n} > :InputBufferSize" ++ total += n ++ all << chunk ++ end ++ ret.nil? or err << 'req.body should return nil' ++ (buf * nr_out) == all or err << 'input body does not match expected' ++ res.header['connection'] = 'close' ++ res.body = err.join("\n") ++ }) ++ TCPSocket.open(addr, port) do |c| ++ c.write("POST / HTTP/1.1\r\nHost: example.com\r\n" \ ++ "Transfer-Encoding: chunked\r\n\r\n") ++ chunk = "#{buf.bytesize.to_s(16)}\r\n#{buf}\r\n" ++ nr_out.times { c.write(chunk) } ++ c.write("0\r\n\r\n") ++ head, body = c.read.split("\r\n\r\n") ++ assert_match %r{\AHTTP/1\.1 200 OK}, head ++ assert_nil body ++ end ++ } ++ end + end +-- +2.17.1 + diff --git a/SOURCES/ruby-2.2.10-fix-dir.c-check-nul-bytes.patch b/SOURCES/ruby-2.2.10-fix-dir.c-check-nul-bytes.patch new file mode 100644 index 0000000..b1922c4 --- /dev/null +++ b/SOURCES/ruby-2.2.10-fix-dir.c-check-nul-bytes.patch @@ -0,0 +1,98 @@ +From 85b582da9260df77669409f0859ef253712ae115 Mon Sep 17 00:00:00 2001 +From: usa +Date: Wed, 28 Mar 2018 14:27:51 +0000 +Subject: [PATCH] merge revision(s) 62989: + + dir.c: check NUL bytes + + * dir.c (GlobPathValue): should be used in rb_push_glob only. + other methods should use FilePathValue. + https://hackerone.com/reports/302338 + + * dir.c (rb_push_glob): expand GlobPathValue + +git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_2_2@63015 b2dd03c8-39d4-4d8f-98ff-823fe69b080e +--- + dir.c | 22 ++++++++++------------ + test/ruby/test_dir.rb | 4 ++++ + 2 files changed, 14 insertions(+), 12 deletions(-) + +diff --git a/dir.c b/dir.c +index 7d61b61fdb..a65c1deeea 100644 +--- a/dir.c ++++ b/dir.c +@@ -356,15 +356,6 @@ static const rb_data_type_t dir_data_type = { + + static VALUE dir_close(VALUE); + +-#define GlobPathValue(str, safe) \ +- /* can contain null bytes as separators */ \ +- (!RB_TYPE_P((str), T_STRING) ? \ +- (void)FilePathValue(str) : \ +- (void)(check_safe_glob((str), (safe)), \ +- check_glob_encoding(str), (str))) +-#define check_safe_glob(str, safe) ((safe) ? rb_check_safe_obj(str) : (void)0) +-#define check_glob_encoding(str) rb_enc_check((str), rb_enc_from_encoding(rb_usascii_encoding())) +- + static VALUE + dir_s_alloc(VALUE klass) + { +@@ -406,7 +397,7 @@ dir_initialize(int argc, VALUE *argv, VALUE dir) + } + } + +- GlobPathValue(dirname, FALSE); ++ FilePathValue(dirname); + orig = rb_str_dup_frozen(dirname); + dirname = rb_str_encode_ospath(dirname); + dirname = rb_str_dup_frozen(dirname); +@@ -1711,7 +1702,14 @@ rb_push_glob(VALUE str, int flags) /* '\0' is delimiter */ + long offset = 0; + VALUE ary; + +- GlobPathValue(str, TRUE); ++ /* can contain null bytes as separators */ ++ if (!RB_TYPE_P((str), T_STRING)) { ++ FilePathValue(str); ++ } ++ else { ++ rb_check_safe_obj(str); ++ rb_enc_check(str, rb_enc_from_encoding(rb_usascii_encoding())); ++ } + ary = rb_ary_new(); + + while (offset < RSTRING_LEN(str)) { +@@ -1741,7 +1739,7 @@ dir_globs(long argc, VALUE *argv, int flags) + for (i = 0; i < argc; ++i) { + int status; + VALUE str = argv[i]; +- GlobPathValue(str, TRUE); ++ FilePathValue(str); + status = push_glob(ary, str, flags); + if (status) GLOB_JUMP_TAG(status); + } +diff --git a/test/ruby/test_dir.rb b/test/ruby/test_dir.rb +index 2ad6bdefcb..7624836ff4 100644 +--- a/test/ruby/test_dir.rb ++++ b/test/ruby/test_dir.rb +@@ -164,6 +164,9 @@ class TestDir < Test::Unit::TestCase + + assert_equal([File.join(@root, "a")], Dir.glob(File.join(@root, 'a\\'))) + assert_equal((?a..?f).map {|f| File.join(@root, f) }.sort, Dir.glob(File.join(@root, '[abc/def]')).sort) ++ assert_raise(ArgumentError) { ++ Dir.glob([[@root, File.join(@root, "*")].join("\0")]) ++ } + end + + def test_glob_recursive +@@ -194,6 +197,7 @@ class TestDir < Test::Unit::TestCase + + def test_foreach + assert_equal(Dir.foreach(@root).to_a.sort, %w(. ..) + (?a..?z).to_a) ++ assert_raise(ArgumentError) {Dir.foreach(@root+"\0").to_a} + end + + def test_dir_enc +-- +2.17.1 + diff --git a/SOURCES/ruby-2.2.10-fix-socket-creation-by_null-byte.patch b/SOURCES/ruby-2.2.10-fix-socket-creation-by_null-byte.patch new file mode 100644 index 0000000..7a3331d --- /dev/null +++ b/SOURCES/ruby-2.2.10-fix-socket-creation-by_null-byte.patch @@ -0,0 +1,87 @@ +From 7fb67f8be47706af6380cd251a2666d6969aa6f1 Mon Sep 17 00:00:00 2001 +From: usa +Date: Wed, 28 Mar 2018 14:36:23 +0000 +Subject: [PATCH] merge revision(s) 62991,63000: + + unixsocket.c: check NUL bytes + + * ext/socket/unixsocket.c (rsock_init_unixsock): check NUL bytes. + https://hackerone.com/reports/302997 + + unixsocket.c: abstract namespace + + * ext/socket/unixsocket.c (unixsock_path_value): fix r62991 for + Linux abstract namespace. + +git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_2_2@63018 b2dd03c8-39d4-4d8f-98ff-823fe69b080e +--- + ext/socket/unixsocket.c | 24 +++++++++++++++++++++++- + test/socket/test_unix.rb | 10 ++++++++++ + 2 files changed, 33 insertions(+), 1 deletion(-) + +diff --git a/ext/socket/unixsocket.c b/ext/socket/unixsocket.c +index 75da9c1a78..6d68138af4 100644 +--- a/ext/socket/unixsocket.c ++++ b/ext/socket/unixsocket.c +@@ -25,6 +25,28 @@ unixsock_connect_internal(VALUE a) + arg->sockaddrlen, 0); + } + ++static VALUE ++unixsock_path_value(VALUE path) ++{ ++#ifdef __linux__ ++#define TO_STR_FOR_LINUX_ABSTRACT_NAMESPACE 0 ++ ++ VALUE name = path; ++#if TO_STR_FOR_LINUX_ABSTRACT_NAMESPACE ++ const int isstr = !NIL_P(name = rb_check_string_type(name)); ++#else ++ const int isstr = RB_TYPE_P(name, T_STRING); ++#endif ++ if (isstr) { ++ if (RSTRING_LEN(name) == 0 || RSTRING_PTR(name)[0] == '\0') { ++ rb_check_safe_obj(name); ++ return name; /* ignore encoding */ ++ } ++ } ++#endif ++ return rb_get_path(path); ++} ++ + VALUE + rsock_init_unixsock(VALUE sock, VALUE path, int server) + { +@@ -33,7 +55,7 @@ rsock_init_unixsock(VALUE sock, VALUE path, int server) + int fd, status; + rb_io_t *fptr; + +- SafeStringValue(path); ++ path = unixsock_path_value(path); + fd = rsock_socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) { + rb_sys_fail("socket(2)"); +diff --git a/test/socket/test_unix.rb b/test/socket/test_unix.rb +index eae236a60b..aef23141b8 100644 +--- a/test/socket/test_unix.rb ++++ b/test/socket/test_unix.rb +@@ -253,6 +253,16 @@ class TestSocket_UNIXSocket < Test::Unit::TestCase + File.unlink path if path && File.socket?(path) + end + ++ def test_open_nul_byte ++ tmpfile = Tempfile.new("s") ++ path = tmpfile.path ++ tmpfile.close(true) ++ assert_raise(ArgumentError) {UNIXServer.open(path+"\0")} ++ assert_raise(ArgumentError) {UNIXSocket.open(path+"\0")} ++ ensure ++ File.unlink path if path && File.socket?(path) ++ end ++ + def test_addr + bound_unix_socket(UNIXServer) {|serv, path| + c = UNIXSocket.new(path) +-- +2.17.1 + diff --git a/SOURCES/ruby-2.2.10-ignore-file-separator-from-tmp-file-dir-name-tests.patch b/SOURCES/ruby-2.2.10-ignore-file-separator-from-tmp-file-dir-name-tests.patch new file mode 100644 index 0000000..7bf57fc --- /dev/null +++ b/SOURCES/ruby-2.2.10-ignore-file-separator-from-tmp-file-dir-name-tests.patch @@ -0,0 +1,44 @@ +diff --git a/test/test_tempfile.rb b/test/test_tempfile.rb +index b6790a06af..68de345116 100644 +--- a/test/test_tempfile.rb ++++ b/test/test_tempfile.rb +@@ -320,18 +320,19 @@ puts Tempfile.new('foo').path + end + end + +- def test_create_with_block ++ def test_open_with_block + path = nil +- Tempfile.create("tempfile-create") {|f| ++ Tempfile.open("tempfile-create") {|f| + path = f.path + assert(File.exist?(path)) + } +- assert(!File.exist?(path)) ++ assert(File.exist?(path)) ++ File.unlink path + end + +- def test_create_without_block ++ def test_open_without_block + path = nil +- f = Tempfile.create("tempfile-create") ++ f = Tempfile.open("tempfile-create") + path = f.path + assert(File.exist?(path)) + f.close +@@ -360,11 +361,4 @@ puts Tempfile.new('foo').path + ensure + t.close! + end +- +- def test_create_traversal_dir +- expect = Dir.glob(TRAVERSAL_PATH + '*').count +- Tempfile.create(TRAVERSAL_PATH + 'foo') +- actual = Dir.glob(TRAVERSAL_PATH + '*').count +- assert_equal expect, actual +- end + end +-- +2.17.1 + diff --git a/SOURCES/ruby-2.2.10-ignore-file-separator-from-tmp-file-dir-name.patch b/SOURCES/ruby-2.2.10-ignore-file-separator-from-tmp-file-dir-name.patch new file mode 100644 index 0000000..afce93f --- /dev/null +++ b/SOURCES/ruby-2.2.10-ignore-file-separator-from-tmp-file-dir-name.patch @@ -0,0 +1,139 @@ +From 42f5f956c3dbae0151176762a42ce564d603975c Mon Sep 17 00:00:00 2001 +From: usa +Date: Wed, 28 Mar 2018 14:34:14 +0000 +Subject: [PATCH] merge revision(s) 62990: + + Ignore file separator from tmpfile/tmpdir name. + + From: SHIBATA Hiroshi + +git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_2_2@63017 b2dd03c8-39d4-4d8f-98ff-823fe69b080e +--- + lib/tmpdir.rb | 2 ++ + test/test_tempfile.rb | 49 ++++++++++++++++++++++++++++++++++++++++++- + test/test_tmpdir.rb | 40 +++++++++++++++++++++++++++++++++++ + 3 files changed, 90 insertions(+), 1 deletion(-) + +diff --git a/lib/tmpdir.rb b/lib/tmpdir.rb +index 18d4fb683d..e483f16602 100644 +--- a/lib/tmpdir.rb ++++ b/lib/tmpdir.rb +@@ -116,6 +116,8 @@ class Dir + else + raise ArgumentError, "unexpected prefix_suffix: #{prefix_suffix.inspect}" + end ++ prefix = prefix.delete("#{File::SEPARATOR}#{File::ALT_SEPARATOR}") ++ suffix &&= suffix.delete("#{File::SEPARATOR}#{File::ALT_SEPARATOR}") + t = Time.now.strftime("%Y%m%d") + path = "#{prefix}#{t}-#{$$}-#{rand(0x100000000).to_s(36)}" + path << "-#{n}" if n +diff --git a/test/test_tempfile.rb b/test/test_tempfile.rb +index 087d9ad31f..b6790a06af 100644 +--- a/test/test_tempfile.rb ++++ b/test/test_tempfile.rb +@@ -319,5 +319,52 @@ puts Tempfile.new('foo').path + assert_equal(0600, t.stat.mode & 0777) + end + end +-end + ++ def test_create_with_block ++ path = nil ++ Tempfile.create("tempfile-create") {|f| ++ path = f.path ++ assert(File.exist?(path)) ++ } ++ assert(!File.exist?(path)) ++ end ++ ++ def test_create_without_block ++ path = nil ++ f = Tempfile.create("tempfile-create") ++ path = f.path ++ assert(File.exist?(path)) ++ f.close ++ assert(File.exist?(path)) ++ ensure ++ f.close if f && !f.closed? ++ File.unlink path if path ++ end ++ ++ TRAVERSAL_PATH = Array.new(Dir.pwd.split('/').count, '..').join('/') + Dir.pwd + '/' ++ ++ def test_open_traversal_dir ++ expect = Dir.glob(TRAVERSAL_PATH + '*').count ++ t = Tempfile.open([TRAVERSAL_PATH, 'foo']) ++ actual = Dir.glob(TRAVERSAL_PATH + '*').count ++ assert_equal expect, actual ++ ensure ++ t.close! ++ end ++ ++ def test_new_traversal_dir ++ expect = Dir.glob(TRAVERSAL_PATH + '*').count ++ t = Tempfile.new(TRAVERSAL_PATH + 'foo') ++ actual = Dir.glob(TRAVERSAL_PATH + '*').count ++ assert_equal expect, actual ++ ensure ++ t.close! ++ end ++ ++ def test_create_traversal_dir ++ expect = Dir.glob(TRAVERSAL_PATH + '*').count ++ Tempfile.create(TRAVERSAL_PATH + 'foo') ++ actual = Dir.glob(TRAVERSAL_PATH + '*').count ++ assert_equal expect, actual ++ end ++end +diff --git a/test/test_tmpdir.rb b/test/test_tmpdir.rb +index 3bdce3542c..2585453183 100644 +--- a/test/test_tmpdir.rb ++++ b/test/test_tmpdir.rb +@@ -30,4 +30,44 @@ class TestTmpdir < Test::Unit::TestCase + ENV["HOME"] = home + Dir.rmdir(dir) if dir + end ++ ++ def test_mktmpdir_nil ++ Dir.mktmpdir(nil) {|d| ++ assert_kind_of(String, d) ++ } ++ end ++ ++ TRAVERSAL_PATH = Array.new(Dir.pwd.split('/').count, '..').join('/') + Dir.pwd + '/' ++ TRAVERSAL_PATH.delete!(':') if /mswin|mingw/ =~ RUBY_PLATFORM ++ ++ def test_mktmpdir_traversal ++ expect = Dir.glob(TRAVERSAL_PATH + '*').count ++ Dir.mktmpdir(TRAVERSAL_PATH + 'foo') ++ actual = Dir.glob(TRAVERSAL_PATH + '*').count ++ assert_equal expect, actual ++ end ++ ++ def test_mktmpdir_traversal_array ++ expect = Dir.glob(TRAVERSAL_PATH + '*').count ++ Dir.mktmpdir([TRAVERSAL_PATH, 'foo']) ++ actual = Dir.glob(TRAVERSAL_PATH + '*').count ++ assert_equal expect, actual ++ end ++ ++ TRAVERSAL_PATH = Array.new(Dir.pwd.split('/').count, '..').join('/') + Dir.pwd + '/' ++ TRAVERSAL_PATH.delete!(':') if /mswin|mingw/ =~ RUBY_PLATFORM ++ ++ def test_mktmpdir_traversal ++ expect = Dir.glob(TRAVERSAL_PATH + '*').count ++ Dir.mktmpdir(TRAVERSAL_PATH + 'foo') ++ actual = Dir.glob(TRAVERSAL_PATH + '*').count ++ assert_equal expect, actual ++end ++ ++ def test_mktmpdir_traversal_array ++ expect = Dir.glob(TRAVERSAL_PATH + '*').count ++ Dir.mktmpdir([TRAVERSAL_PATH, 'foo']) ++ actual = Dir.glob(TRAVERSAL_PATH + '*').count ++ assert_equal expect, actual ++ end + end +-- +2.17.1 + diff --git a/SOURCES/ruby-2.2.10-rubygems-2.7.6-multiple-vulnerabilities-tests.patch b/SOURCES/ruby-2.2.10-rubygems-2.7.6-multiple-vulnerabilities-tests.patch new file mode 100644 index 0000000..d9f11a7 --- /dev/null +++ b/SOURCES/ruby-2.2.10-rubygems-2.7.6-multiple-vulnerabilities-tests.patch @@ -0,0 +1,16 @@ +diff --git a/test/rubygems/test_gem_package.rb b/test/rubygems/test_gem_package.rb +index 29e2ce86b1..54ba245b32 100644 +--- a/test/rubygems/test_gem_package.rb ++++ b/test/rubygems/test_gem_package.rb +@@ -608,7 +608,7 @@ class TestGemPackage < Gem::Package::TarTestCase + bogus_data = Gem.gzip 'hello' + fake_signer = Class.new do + def digest_name; 'SHA512'; end +- def digest_algorithm; Digest(:SHA512); end ++ def digest_algorithm; OpenSSL::Digest::SHA512; end + def key; 'key'; end + def sign(*); 'fake_sig'; end + end +-- +2.17.1 + diff --git a/SOURCES/ruby-2.2.10-rubygems-2.7.6-multiple-vulnerabilities.patch b/SOURCES/ruby-2.2.10-rubygems-2.7.6-multiple-vulnerabilities.patch new file mode 100644 index 0000000..65186ba --- /dev/null +++ b/SOURCES/ruby-2.2.10-rubygems-2.7.6-multiple-vulnerabilities.patch @@ -0,0 +1,493 @@ +From d6f00c288f84523eec263455ff5bfc631bc27188 Mon Sep 17 00:00:00 2001 +From: usa +Date: Fri, 16 Feb 2018 16:27:56 +0000 +Subject: [PATCH] merge revision(s) 58471,58493,62436: [Backport #13505] + + load.c: backtrace of circular require + + * load.c (load_lock): print backtrace of circular require via + `Warning.warn` [ruby-core:80850] [Bug #13505] + + Send the backtrace of the circular require warning as a single String to Warning.warn + + * load.c: send as a single string. + * error.c: expose the string formatted by rb_warning as rb_warning_string(). + * test/ruby/test_exception.rb: update tests. + [ruby-core:80850] [Bug #13505] + + fix regexp literal warning. + + * test/rubygems/test_gem_server.rb: eliminate duplicated character class warning. + [Bug #14481] + +git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_2_2@62441 b2dd03c8-39d4-4d8f-98ff-823fe69b080e +--- + lib/rubygems/package.rb | 6 +- + lib/rubygems/package/tar_header.rb | 23 ++- + lib/rubygems/package/tar_writer.rb | 2 + + lib/rubygems/server.rb | 14 +- + lib/rubygems/specification.rb | 15 +- + test/rubygems/test_gem_package.rb | 49 +++++- + test/rubygems/test_gem_package_tar_header.rb | 21 +++ + test/rubygems/test_gem_server.rb | 165 +++++++++++++++++++ + test/rubygems/test_gem_specification.rb | 17 +- + 9 files changed, 295 insertions(+), 17 deletions(-) + +diff --git a/lib/rubygems/package.rb b/lib/rubygems/package.rb +index c2bbda9b05..9ed5b2c9ed 100644 +--- a/lib/rubygems/package.rb ++++ b/lib/rubygems/package.rb +@@ -374,7 +374,7 @@ EOM + destination = File.expand_path destination + + raise Gem::Package::PathError.new(destination, destination_dir) unless +- destination.start_with? destination_dir ++ destination.start_with? destination_dir + '/' + + destination.untaint + destination +@@ -541,6 +541,10 @@ EOM + raise Gem::Package::FormatError.new \ + 'package content (data.tar.gz) is missing', @gem + end ++ ++ if duplicates = @files.group_by {|f| f }.select {|k,v| v.size > 1 }.map(&:first) and duplicates.any? ++ raise Gem::Security::Exception, "duplicate files in the package: (#{duplicates.map(&:inspect).join(', ')})" ++ end + end + + ## +diff --git a/lib/rubygems/package/tar_header.rb b/lib/rubygems/package/tar_header.rb +index 28da1db0b5..7d75ff5d46 100644 +--- a/lib/rubygems/package/tar_header.rb ++++ b/lib/rubygems/package/tar_header.rb +@@ -103,25 +103,30 @@ class Gem::Package::TarHeader + fields = header.unpack UNPACK_FORMAT + + new :name => fields.shift, +- :mode => fields.shift.oct, +- :uid => fields.shift.oct, +- :gid => fields.shift.oct, +- :size => fields.shift.oct, +- :mtime => fields.shift.oct, +- :checksum => fields.shift.oct, ++ :mode => strict_oct(fields.shift), ++ :uid => strict_oct(fields.shift), ++ :gid => strict_oct(fields.shift), ++ :size => strict_oct(fields.shift), ++ :mtime => strict_oct(fields.shift), ++ :checksum => strict_oct(fields.shift), + :typeflag => fields.shift, + :linkname => fields.shift, + :magic => fields.shift, +- :version => fields.shift.oct, ++ :version => strict_oct(fields.shift), + :uname => fields.shift, + :gname => fields.shift, +- :devmajor => fields.shift.oct, +- :devminor => fields.shift.oct, ++ :devmajor => strict_oct(fields.shift), ++ :devminor => strict_oct(fields.shift), + :prefix => fields.shift, + + :empty => empty + end + ++ def self.strict_oct(str) ++ return str.oct if str =~ /\A[0-7]*\z/ ++ raise ArgumentError, "#{str.inspect} is not an octal string" ++ end ++ + ## + # Creates a new TarHeader using +vals+ + +diff --git a/lib/rubygems/package/tar_writer.rb b/lib/rubygems/package/tar_writer.rb +index f2c11e3544..0fbc1770fb 100644 +--- a/lib/rubygems/package/tar_writer.rb ++++ b/lib/rubygems/package/tar_writer.rb +@@ -176,6 +176,8 @@ class Gem::Package::TarWriter + digest.name == signer.digest_name + end + ++ raise "no #{signer.digest_name} in #{digests.values.compact}" unless signature_digest ++ + signature = signer.sign signature_digest.digest + + add_file_simple "#{name}.sig", 0444, signature.length do |io| +diff --git a/lib/rubygems/server.rb b/lib/rubygems/server.rb +index f2d1428489..af1448e79c 100644 +--- a/lib/rubygems/server.rb ++++ b/lib/rubygems/server.rb +@@ -595,6 +595,18 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; } + executables = nil if executables.empty? + executables.last["is_last"] = true if executables + ++ # Pre-process spec homepage for safety reasons ++ begin ++ homepage_uri = URI.parse(spec.homepage) ++ if [URI::HTTP, URI::HTTPS].member? homepage_uri.class ++ homepage_uri = spec.homepage ++ else ++ homepage_uri = "." ++ end ++ rescue URI::InvalidURIError ++ homepage_uri = "." ++ end ++ + specs << { + "authors" => spec.authors.sort.join(", "), + "date" => spec.date.to_s, +@@ -604,7 +616,7 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; } + "only_one_executable" => (executables && executables.size == 1), + "full_name" => spec.full_name, + "has_deps" => !deps.empty?, +- "homepage" => spec.homepage, ++ "homepage" => homepage_uri, + "name" => spec.name, + "rdoc_installed" => Gem::RDoc.new(spec).rdoc_installed?, + "ri_installed" => Gem::RDoc.new(spec).ri_installed?, +diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb +index c3e39b4759..d04c08b828 100644 +--- a/lib/rubygems/specification.rb ++++ b/lib/rubygems/specification.rb +@@ -9,6 +9,7 @@ require 'rubygems/version' + require 'rubygems/requirement' + require 'rubygems/platform' + require 'rubygems/deprecate' ++require 'uri' + + # :stopdoc: + # date.rb can't be loaded for `make install` due to miniruby +@@ -2503,10 +2504,16 @@ class Gem::Specification + raise Gem::InvalidSpecificationException, "#{lazy} is not a summary" + end + +- if homepage and not homepage.empty? and +- homepage !~ /\A[a-z][a-z\d+.-]*:/i then +- raise Gem::InvalidSpecificationException, +- "\"#{homepage}\" is not a URI" ++ # Make sure a homepage is valid HTTP/HTTPS URI ++ if homepage and not homepage.empty? ++ begin ++ homepage_uri = URI.parse(homepage) ++ unless [URI::HTTP, URI::HTTPS].member? homepage_uri.class ++ raise Gem::InvalidSpecificationException, "\"#{homepage}\" is not a valid HTTP URI" ++ end ++ rescue URI::InvalidURIError ++ raise Gem::InvalidSpecificationException, "\"#{homepage}\" is not a valid HTTP URI" ++ end + end + + # Warnings +diff --git a/test/rubygems/test_gem_package.rb b/test/rubygems/test_gem_package.rb +index c77fc47ada..29e2ce86b1 100644 +--- a/test/rubygems/test_gem_package.rb ++++ b/test/rubygems/test_gem_package.rb +@@ -360,6 +360,21 @@ class TestGemPackage < Gem::Package::TarTestCase + "#{@destination} is not allowed", e.message) + end + ++ def test_install_location_suffix ++ package = Gem::Package.new @gem ++ ++ filename = "../#{File.basename(@destination)}suffix.rb" ++ ++ e = assert_raises Gem::Package::PathError do ++ package.install_location filename, @destination ++ end ++ ++ parent = File.expand_path File.join @destination, filename ++ ++ assert_equal("installing into parent path #{parent} of " + ++ "#{@destination} is not allowed", e.message) ++ end ++ + def test_load_spec + entry = StringIO.new Gem.gzip @spec.to_yaml + def entry.full_name() 'metadata.gz' end +@@ -514,6 +529,32 @@ class TestGemPackage < Gem::Package::TarTestCase + assert_match %r%nonexistent.gem$%, e.message + end + ++ def test_verify_duplicate_file ++ FileUtils.mkdir_p 'lib' ++ FileUtils.touch 'lib/code.rb' ++ ++ build = Gem::Package.new @gem ++ build.spec = @spec ++ build.setup_signer ++ open @gem, 'wb' do |gem_io| ++ Gem::Package::TarWriter.new gem_io do |gem| ++ build.add_metadata gem ++ build.add_contents gem ++ ++ gem.add_file_simple 'a.sig', 0444, 0 ++ gem.add_file_simple 'a.sig', 0444, 0 ++ end ++ end ++ ++ package = Gem::Package.new @gem ++ ++ e = assert_raises Gem::Security::Exception do ++ package.verify ++ end ++ ++ assert_equal 'duplicate files in the package: ("a.sig")', e.message ++ end ++ + def test_verify_security_policy + package = Gem::Package.new @gem + package.security_policy = Gem::Security::HighSecurity +@@ -565,7 +606,13 @@ class TestGemPackage < Gem::Package::TarTestCase + + # write bogus data.tar.gz to foil signature + bogus_data = Gem.gzip 'hello' +- gem.add_file_simple 'data.tar.gz', 0444, bogus_data.length do |io| ++ fake_signer = Class.new do ++ def digest_name; 'SHA512'; end ++ def digest_algorithm; Digest(:SHA512); end ++ def key; 'key'; end ++ def sign(*); 'fake_sig'; end ++ end ++ gem.add_file_signed 'data2.tar.gz', 0444, fake_signer.new do |io| + io.write bogus_data + end + +diff --git a/test/rubygems/test_gem_package_tar_header.rb b/test/rubygems/test_gem_package_tar_header.rb +index 5d855435ec..ee30d4afd6 100644 +--- a/test/rubygems/test_gem_package_tar_header.rb ++++ b/test/rubygems/test_gem_package_tar_header.rb +@@ -126,5 +126,26 @@ group\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000 + assert_equal '012467', @tar_header.checksum + end + ++ def test_from_bad_octal ++ test_cases = [ ++ "00000006,44\000", # bogus character ++ "00000006789\000", # non-octal digit ++ "+0000001234\000", # positive sign ++ "-0000001000\000", # negative sign ++ "0x000123abc\000", # radix prefix ++ ] ++ ++ test_cases.each do |val| ++ header_s = @tar_header.to_s ++ # overwrite the size field ++ header_s[124, 12] = val ++ io = TempIO.new header_s ++ assert_raises ArgumentError do ++ new_header = Gem::Package::TarHeader.from io ++ end ++ io.close! if io.respond_to? :close! ++ end ++ end ++ + end + +diff --git a/test/rubygems/test_gem_server.rb b/test/rubygems/test_gem_server.rb +index bf30399db2..5344ebbe69 100644 +--- a/test/rubygems/test_gem_server.rb ++++ b/test/rubygems/test_gem_server.rb +@@ -268,6 +268,171 @@ class TestGemServer < Gem::TestCase + assert_match 'z 9', @res.body + end + ++ ++ def test_xss_homepage_fix_289313 ++ data = StringIO.new "GET / HTTP/1.0\r\n\r\n" ++ dir = "#{@gemhome}2" ++ ++ spec = util_spec 'xsshomepagegem', 1 ++ spec.homepage = "javascript:confirm(document.domain)" ++ ++ specs_dir = File.join dir, 'specifications' ++ FileUtils.mkdir_p specs_dir ++ ++ open File.join(specs_dir, spec.spec_name), 'w' do |io| ++ io.write spec.to_ruby ++ end ++ ++ server = Gem::Server.new dir, process_based_port, false ++ ++ @req.parse data ++ ++ server.root @req, @res ++ ++ assert_equal 200, @res.status ++ assert_match 'xsshomepagegem 1', @res.body ++ ++ # This verifies that the homepage for this spec is not displayed and is set to ".", because it's not a ++ # valid HTTP/HTTPS URL and could be unsafe in an HTML context. We would prefer to throw an exception here, ++ # but spec.homepage is currently free form and not currently required to be a URL, this behavior may be ++ # validated in future versions of Gem::Specification. ++ # ++ # There are two variant we're checking here, one where rdoc is not present, and one where rdoc is present in the same regex: ++ # ++ # Variant #1 - rdoc not installed ++ # ++ # xsshomepagegem 1 ++ # ++ # ++ # [rdoc] ++ # ++ # ++ # ++ # [www] ++ # ++ # Variant #2 - rdoc installed ++ # ++ # xsshomepagegem 1 ++ # ++ # ++ # \[rdoc\]<\/a> ++ # ++ # ++ # ++ # [www] ++ regex_match = /xsshomepagegem 1<\/b>[\s]+(\[rdoc\]<\/span>|\[rdoc\]<\/a>)[\s]+\[www\]<\/a>/ ++ assert_match regex_match, @res.body ++ end ++ ++ def test_invalid_homepage ++ data = StringIO.new "GET / HTTP/1.0\r\n\r\n" ++ dir = "#{@gemhome}2" ++ ++ spec = util_spec 'invalidhomepagegem', 1 ++ spec.homepage = "notavalidhomepageurl" ++ ++ specs_dir = File.join dir, 'specifications' ++ FileUtils.mkdir_p specs_dir ++ ++ open File.join(specs_dir, spec.spec_name), 'w' do |io| ++ io.write spec.to_ruby ++ end ++ ++ server = Gem::Server.new dir, process_based_port, false ++ ++ @req.parse data ++ ++ server.root @req, @res ++ ++ assert_equal 200, @res.status ++ assert_match 'invalidhomepagegem 1', @res.body ++ ++ # This verifies that the homepage for this spec is not displayed and is set to ".", because it's not a ++ # valid HTTP/HTTPS URL and could be unsafe in an HTML context. We would prefer to throw an exception here, ++ # but spec.homepage is currently free form and not currently required to be a URL, this behavior may be ++ # validated in future versions of Gem::Specification. ++ # ++ # There are two variant we're checking here, one where rdoc is not present, and one where rdoc is present in the same regex: ++ # ++ # Variant #1 - rdoc not installed ++ # ++ # invalidhomepagegem 1 ++ # ++ # ++ # [rdoc] ++ # ++ # ++ # ++ # [www] ++ # ++ # Variant #2 - rdoc installed ++ # ++ # invalidhomepagegem 1 ++ # ++ # ++ # \[rdoc\]<\/a> ++ # ++ # ++ # ++ # [www] ++ regex_match = /invalidhomepagegem 1<\/b>[\s]+(\[rdoc\]<\/span>|\[rdoc\]<\/a>)[\s]+\[www\]<\/a>/ ++ assert_match regex_match, @res.body ++ end ++ ++ def test_valid_homepage_http ++ data = StringIO.new "GET / HTTP/1.0\r\n\r\n" ++ dir = "#{@gemhome}2" ++ ++ spec = util_spec 'validhomepagegemhttp', 1 ++ spec.homepage = "http://rubygems.org" ++ ++ specs_dir = File.join dir, 'specifications' ++ FileUtils.mkdir_p specs_dir ++ ++ open File.join(specs_dir, spec.spec_name), 'w' do |io| ++ io.write spec.to_ruby ++ end ++ ++ server = Gem::Server.new dir, process_based_port, false ++ ++ @req.parse data ++ ++ server.root @req, @res ++ ++ assert_equal 200, @res.status ++ assert_match 'validhomepagegemhttp 1', @res.body ++ ++ regex_match = /validhomepagegemhttp 1<\/b>[\s]+(\[rdoc\]<\/span>|\[rdoc\]<\/a>)[\s]+\[www\]<\/a>/ ++ assert_match regex_match, @res.body ++ end ++ ++ def test_valid_homepage_https ++ data = StringIO.new "GET / HTTP/1.0\r\n\r\n" ++ dir = "#{@gemhome}2" ++ ++ spec = util_spec 'validhomepagegemhttps', 1 ++ spec.homepage = "https://rubygems.org" ++ ++ specs_dir = File.join dir, 'specifications' ++ FileUtils.mkdir_p specs_dir ++ ++ open File.join(specs_dir, spec.spec_name), 'w' do |io| ++ io.write spec.to_ruby ++ end ++ ++ server = Gem::Server.new dir, process_based_port, false ++ ++ @req.parse data ++ ++ server.root @req, @res ++ ++ assert_equal 200, @res.status ++ assert_match 'validhomepagegemhttps 1', @res.body ++ ++ regex_match = /validhomepagegemhttps 1<\/b>[\s]+(\[rdoc\]<\/span>|\[rdoc\]<\/a>)[\s]+\[www\]<\/a>/ ++ assert_match regex_match, @res.body ++ end ++ + def test_specs + data = StringIO.new "GET /specs.#{Gem.marshal_version} HTTP/1.0\r\n\r\n" + @req.parse data +diff --git a/test/rubygems/test_gem_specification.rb b/test/rubygems/test_gem_specification.rb +index 85db5356f2..65984675e1 100644 +--- a/test/rubygems/test_gem_specification.rb ++++ b/test/rubygems/test_gem_specification.rb +@@ -1586,7 +1586,22 @@ end + @a1.validate + end + +- assert_equal '"over at my cool site" is not a URI', e.message ++ assert_equal '"over at my cool site" is not a valid HTTP URI', e.message ++ ++ @a1.homepage = 'ftp://rubygems.org' ++ ++ e = assert_raises Gem::InvalidSpecificationException do ++ @a1.validate ++ end ++ ++ assert_equal '"ftp://rubygems.org" is not a valid HTTP URI', e.message ++ ++ @a1.homepage = 'http://rubygems.org' ++ assert_equal true, @a1.validate ++ ++ @a1.homepage = 'https://rubygems.org' ++ assert_equal true, @a1.validate ++ + end + end + +-- +2.17.1 + diff --git a/SOURCES/ruby-2.2.10-webrick-prevent-response.patch b/SOURCES/ruby-2.2.10-webrick-prevent-response.patch new file mode 100644 index 0000000..76a0f26 --- /dev/null +++ b/SOURCES/ruby-2.2.10-webrick-prevent-response.patch @@ -0,0 +1,120 @@ +From 3f591af1e74ec511e38bd40afc9ebbceacdc9fef Mon Sep 17 00:00:00 2001 +From: usa +Date: Wed, 28 Mar 2018 14:50:27 +0000 +Subject: [PATCH] webrick: prevent response splitting and header injection + +Original patch by tenderlove (with minor style adjustments). + +* lib/webrick/httpresponse.rb (send_header): call check_header + (check_header): raise on embedded CRLF in header value +* test/webrick/test_httpresponse.rb + (test_prevent_response_splitting_headers): new test +* (test_prevent_response_splitting_cookie_headers): ditto + +git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_2_2@63022 b2dd03c8-39d4-4d8f-98ff-823fe69b080e +--- + lib/webrick/httpresponse.rb | 27 +++++++++++++++++++++++++-- + test/webrick/test_httpresponse.rb | 23 +++++++++++++++++++++++ + 2 files changed, 48 insertions(+), 2 deletions(-) + +diff --git a/lib/webrick/httpresponse.rb b/lib/webrick/httpresponse.rb +index 8e3eb39a31..11cc78d845 100644 +--- a/lib/webrick/httpresponse.rb ++++ b/lib/webrick/httpresponse.rb +@@ -20,6 +20,8 @@ module WEBrick + # WEBrick HTTP Servlet. + + class HTTPResponse ++ class InvalidHeader < StandardError ++ end + + ## + # HTTP Response version +@@ -285,14 +287,19 @@ module WEBrick + data = status_line() + @header.each{|key, value| + tmp = key.gsub(/\bwww|^te$|\b\w/){ $&.upcase } +- data << "#{tmp}: #{value}" << CRLF ++ data << "#{tmp}: #{check_header(value)}" << CRLF + } + @cookies.each{|cookie| +- data << "Set-Cookie: " << cookie.to_s << CRLF ++ data << "Set-Cookie: " << check_header(cookie.to_s) << CRLF + } + data << CRLF + _write_data(socket, data) + end ++ rescue InvalidHeader => e ++ @header.clear ++ @cookies.clear ++ set_error e ++ retry + end + + ## +@@ -349,6 +356,22 @@ module WEBrick + host, port = @config[:ServerName], @config[:Port] + end + ++ error_body(backtrace, ex, host, port) ++ end ++ ++ private ++ ++ def check_header(header_value) ++ if header_value =~ /\r\n/ ++ raise InvalidHeader ++ else ++ header_value ++ end ++ end ++ ++ # :stopdoc: ++ ++ def error_body(backtrace, ex, host, port) + @body = '' + @body << <<-_end_of_html_ + +diff --git a/test/webrick/test_httpresponse.rb b/test/webrick/test_httpresponse.rb +index d5d5552796..bdf38e6b5c 100644 +--- a/test/webrick/test_httpresponse.rb ++++ b/test/webrick/test_httpresponse.rb +@@ -1,5 +1,7 @@ + require "webrick" + require "minitest/autorun" ++require "stringio" ++require "net/http" + + module WEBrick + class TestHTTPResponse < MiniTest::Unit::TestCase +@@ -26,6 +28,27 @@ module WEBrick + @res.keep_alive = true + end + ++ def test_prevent_response_splitting_headers ++ res['X-header'] = "malicious\r\nCookie: hack" ++ io = StringIO.new ++ res.send_response io ++ io.rewind ++ res = Net::HTTPResponse.read_new(Net::BufferedIO.new(io)) ++ assert_equal '500', res.code ++ refute_match 'hack', io.string ++ end ++ ++ def test_prevent_response_splitting_cookie_headers ++ user_input = "malicious\r\nCookie: hack" ++ res.cookies << WEBrick::Cookie.new('author', user_input) ++ io = StringIO.new ++ res.send_response io ++ io.rewind ++ res = Net::HTTPResponse.read_new(Net::BufferedIO.new(io)) ++ assert_equal '500', res.code ++ refute_match 'hack', io.string ++ end ++ + def test_304_does_not_log_warning + res.status = 304 + res.setup_header +-- +2.17.1 + diff --git a/SOURCES/ruby-2.3.8-fix-flags-not-propagated-in-array-pack-and-string-unpack.patch b/SOURCES/ruby-2.3.8-fix-flags-not-propagated-in-array-pack-and-string-unpack.patch new file mode 100644 index 0000000..76da5d6 --- /dev/null +++ b/SOURCES/ruby-2.3.8-fix-flags-not-propagated-in-array-pack-and-string-unpack.patch @@ -0,0 +1,106 @@ +From 216e73a58ed85014d547cbc89006ff2a987bab1f Mon Sep 17 00:00:00 2001 +From: usa +Date: Wed, 17 Oct 2018 15:09:24 +0000 +Subject: [PATCH] merge revision(s) 65125: + + infect taint flag on Array#pack and String#unpack + with the directives "B", "b", "H" and "h". + + * pack.c (pack_pack, pack_unpack_internal): infect taint flag. + + * test/ruby/test_pack.rb: add test for above. + +git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_2_3@65130 b2dd03c8-39d4-4d8f-98ff-823fe69b080e +--- + pack.c | 8 ++++++++ + test/ruby/test_pack.rb | 15 +++++++++++++++ + 2 files changed, 23 insertions(+) + +diff --git a/pack.c b/pack.c +index d304dd2eea..b64c0f64cb 100644 +--- a/pack.c ++++ b/pack.c +@@ -944,6 +944,7 @@ pack_pack(VALUE ary, VALUE fmt) + StringValue(from); + ptr = RSTRING_PTR(from); + plen = RSTRING_LEN(from); ++ OBJ_INFECT(res, from); + + if (len == 0 && type == 'm') { + encodes(res, ptr, plen, type, 0); +@@ -971,6 +972,7 @@ pack_pack(VALUE ary, VALUE fmt) + + case 'M': /* quoted-printable encoded string */ + from = rb_obj_as_string(NEXTFROM); ++ OBJ_INFECT(res, from); + if (len <= 1) + len = 72; + qpencode(res, from, len); +@@ -996,6 +998,8 @@ pack_pack(VALUE ary, VALUE fmt) + } + else { + t = StringValuePtr(from); ++ OBJ_INFECT(res, from); ++ rb_obj_taint(from); + } + if (!associates) { + associates = rb_ary_new(); +@@ -1487,6 +1491,7 @@ pack_unpack(VALUE str, VALUE fmt) + len = (send - s) * 8; + bits = 0; + UNPACK_PUSH(bitstr = rb_usascii_str_new(0, len)); ++ OBJ_INFECT(bitstr, str); + t = RSTRING_PTR(bitstr); + for (i=0; i>= 1; +@@ -1507,6 +1512,7 @@ pack_unpack(VALUE str, VALUE fmt) + len = (send - s) * 8; + bits = 0; + UNPACK_PUSH(bitstr = rb_usascii_str_new(0, len)); ++ OBJ_INFECT(bitstr, str); + t = RSTRING_PTR(bitstr); + for (i=0; i +Date: Wed, 28 Mar 2018 10:37:07 +0000 +Subject: [PATCH] pack.c: fix underflow + +* pack.c (pack_unpack_internal): get rid of underflow. + https://hackerone.com/reports/298246 + +git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_2_3@62997 b2dd03c8-39d4-4d8f-98ff-823fe69b080e +--- + pack.c | 2 +- + test/ruby/test_pack.rb | 3 +++ + 2 files changed, 4 insertions(+), 1 deletion(-) + +diff --git a/pack.c b/pack.c +index 487fdf82fa..d304dd2eea 100644 +--- a/pack.c ++++ b/pack.c +@@ -1430,7 +1430,7 @@ pack_unpack(VALUE str, VALUE fmt) + else if (ISDIGIT(*p)) { + errno = 0; + len = STRTOUL(p, (char**)&p, 10); +- if (errno) { ++ if (len < 0 || errno) { + rb_raise(rb_eRangeError, "pack length too big"); + } + } +diff --git a/test/ruby/test_pack.rb b/test/ruby/test_pack.rb +index f51d0b44e4..8ca34051a9 100644 +--- a/test/ruby/test_pack.rb ++++ b/test/ruby/test_pack.rb +@@ -471,6 +471,9 @@ class TestPack < Test::Unit::TestCase + assert_equal([1, 2], "\x01\x00\x00\x02".unpack("C@3C")) + assert_equal([nil], "\x00".unpack("@1C")) # is it OK? + assert_raise(ArgumentError) { "\x00".unpack("@2C") } ++ ++ pos = (1 << [nil].pack("p").bytesize * 8) - 100 # -100 ++ assert_raise(RangeError) {"0123456789".unpack("@#{pos}C10")} + end + + def test_pack_unpack_percent +-- +2.17.1 + diff --git a/SOURCES/ruby-2.3.8-rubygems-2.7.6-a-safe-mkdir-to-verify-inside-destination-dir.patch b/SOURCES/ruby-2.3.8-rubygems-2.7.6-a-safe-mkdir-to-verify-inside-destination-dir.patch new file mode 100644 index 0000000..3780572 --- /dev/null +++ b/SOURCES/ruby-2.3.8-rubygems-2.7.6-a-safe-mkdir-to-verify-inside-destination-dir.patch @@ -0,0 +1,79 @@ +diff --git a/lib/rubygems/package.rb b/lib/rubygems/package.rb +index 13eb25bd26..9b1cb3a142 100644 +--- a/lib/rubygems/package.rb ++++ b/lib/rubygems/package.rb +@@ -332,7 +332,16 @@ EOM + + FileUtils.rm_rf destination + +- FileUtils.mkdir_p File.dirname destination ++ mkdir_options = {} ++ mkdir_options[:mode] = entry.header.mode if entry.directory? ++ mkdir = ++ if entry.directory? then ++ destination ++ else ++ File.dirname destination ++ end ++ ++ mkdir_p_safe mkdir, mkdir_options, destination_dir, entry.full_name + + open destination, 'wb', entry.header.mode do |out| + out.write entry.read +@@ -367,12 +376,9 @@ EOM + raise Gem::Package::PathError.new(filename, destination_dir) if + filename.start_with? '/' + +- destination_dir = File.realpath destination_dir if +- File.respond_to? :realpath ++ destination_dir = realpath destination_dir + + destination = File.join destination_dir, filename +- destination = File.realpath destination if +- File.respond_to? :realpath + destination = File.expand_path destination + + raise Gem::Package::PathError.new(destination, destination_dir) unless +@@ -382,6 +388,22 @@ EOM + destination + end + ++ def mkdir_p_safe mkdir, mkdir_options, destination_dir, file_name ++ destination_dir = realpath File.expand_path(destination_dir) ++ parts = mkdir.split(File::SEPARATOR) ++ parts.reduce do |path, basename| ++ path = realpath path unless path == "" ++ path = File.expand_path(path + File::SEPARATOR + basename) ++ lstat = File.lstat path rescue nil ++ if !lstat || !lstat.directory? ++ unless path.start_with? destination_dir and (FileUtils.mkdir path, mkdir_options rescue false) ++ raise Gem::Package::PathError.new(file_name, destination_dir) ++ end ++ end ++ path ++ end ++ end ++ + ## + # Loads a Gem::Specification from the TarEntry +entry+ + +@@ -560,6 +582,16 @@ EOM + raise Gem::Package::FormatError.new(e.message, entry.full_name) + end + ++ if File.respond_to? :realpath ++ def realpath file ++ File.realpath file ++ end ++ else ++ def realpath file ++ file ++ end ++ end ++ + end + + require 'rubygems/package/digest_io' +-- +2.20.1 + diff --git a/SOURCES/ruby-2.3.8-rubygems-2.7.6-fix-unsafe-object-deserialization.patch b/SOURCES/ruby-2.3.8-rubygems-2.7.6-fix-unsafe-object-deserialization.patch new file mode 100644 index 0000000..1e845e5 --- /dev/null +++ b/SOURCES/ruby-2.3.8-rubygems-2.7.6-fix-unsafe-object-deserialization.patch @@ -0,0 +1,81 @@ +From dcd09da317d9710c61000dbda5df2c9a6d59b1fb Mon Sep 17 00:00:00 2001 +From: usa +Date: Fri, 16 Feb 2018 16:21:44 +0000 +Subject: [PATCH] Fix Unsafe Object Deserialization Vulnerability in gem owner. + +merge revision(s) 58471,58493,62436: [Backport #13505] + + load.c: backtrace of circular require + + * load.c (load_lock): print backtrace of circular require via + `Warning.warn` [ruby-core:80850] [Bug #13505] + + Send the backtrace of the circular require warning as a single String to Warning.warn + + * load.c: send as a single string. + * error.c: expose the string formatted by rb_warning as rb_warning_string(). + * test/ruby/test_exception.rb: update tests. + [ruby-core:80850] [Bug #13505] + + fix regexp literal warning. + + * test/rubygems/test_gem_server.rb: eliminate duplicated character class warning. + [Bug #14481] + +git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_2_3@62439 b2dd03c8-39d4-4d8f-98ff-823fe69b080e +--- + lib/rubygems/commands/owner_command.rb | 2 +- + .../test_gem_commands_owner_command.rb | 24 +++++++++++++++++++ + 2 files changed, 25 insertions(+), 1 deletion(-) + +diff --git a/lib/rubygems/commands/owner_command.rb b/lib/rubygems/commands/owner_command.rb +index 11e6e026fd..df64f41e59 100644 +--- a/lib/rubygems/commands/owner_command.rb ++++ b/lib/rubygems/commands/owner_command.rb +@@ -48,7 +48,7 @@ class Gem::Commands::OwnerCommand < Gem::Command + end + + with_response response do |resp| +- owners = YAML.load resp.body ++ owners = Gem::SafeYAML.load resp.body + + say "Owners for gem: #{name}" + owners.each do |owner| +diff --git a/test/rubygems/test_gem_commands_owner_command.rb b/test/rubygems/test_gem_commands_owner_command.rb +index dfbc2572dc..22272d4a22 100644 +--- a/test/rubygems/test_gem_commands_owner_command.rb ++++ b/test/rubygems/test_gem_commands_owner_command.rb +@@ -34,6 +34,30 @@ EOF + assert_match %r{- user2@example.com}, @ui.output + end + ++ def test_show_owners_dont_load_objects ++ skip "testing a psych-only API" unless defined?(::Psych::DisallowedClass) ++ ++ response = </dev/null)" + # Move macros file insto proper place and replace the %%{name} macro, since it # would be wrongly evaluated during build of other packages. mkdir -p %{buildroot}%{_sysconfdir}/rpm @@ -1052,20 +1143,54 @@ OPENSSL_ENABLE_MD5_VERIFY=1 make check TESTS="-v $DISABLE_TESTS" %{ruby_libdir}/tkextlib %changelog -* Thu Apr 04 2019 Vít Ondruch - 2.0.0.648-35 +* Thu Apr 04 2019 Vít Ondruch - 2.0.0.648-36 - Introduce `Gem::UserInteraction#verbose` method as precondition to fix CVE-2019-8321. * rubygems-2.3.0-refactor-checking-really_verbose.patch - Fix escape sequence injection vulnerability in verbose. - Fix escape sequence injection vulnerability in gem owner. + Resolves: CVE-2019-8322 - Fix escape sequence injection vulnerability in API response handling. + Resolves: CVE-2019-8323 - Prohibit arbitrary code execution when installing a malicious gem. + Resolves: CVE-2019-8324 - Fix escape sequence injection vulnerability in errors. + Resolves: CVE-2019-8325 * ruby-2.4.6-Applied-security-patches-for-RubyGems.patch - Resolves: rhbz#1699283 * Mon Feb 04 2019 Jun Aruga - 2.0.0.648-35 +- Kill bundled certificates. +- Add macros to edit files lists in .gemspec +- Fix buffer under-read in String#unpack + Resolves: CVE-2018-8778 +- Fix HTTP response splitting in WEBrick + Resolves: CVE-2017-17742 +- Fix DoS by large request in WEBrick + Resolves: CVE-2018-8777 +- Fix directory traversal by poisoned NULL byte in Dir. + Resolves: CVE-2018-8780 +- Fix file and directory creation with directory traversal. + Resolves: CVE-2018-6914 +- Fix socket creation by poisoned NULL byte. + Resolves: CVE-2018-8779 +- Fix: return default path with nonexistent home dir +- Fix flags not propagated in Array#pack and String#unpack. + Resolves: CVE-2018-16396 +- Fix strictly interpret octal fields in tar headers. + Resolves: CVE-2018-1000075 +- Fix a security error for duplicate files in a package. + Resolves: CVE-2018-1000076 +- Enforce URL validation on spec homepage attribute. + Resolves: CVE-2018-1000077 +- Mitigate XSS vulnerability in homepage attribute. + Resolves: CVE-2018-1000078 +- Prevent Path Traversal issue during gem installation. + Resolves: CVE-2018-1000079 +- Fix unsafe Object Deserialization Vulnerability in gem owner. + Resolves: CVE-2018-1000074 - Refresh expired certificates. +- Fix path traversal when writing to a symlinked basedir outside of the root + Resolves: CVE-2018-1000073 * Thu Nov 01 2018 Jun Aruga - 2.0.0.648-34 - CVE-2018-16395: Fix OpenSSL::X509::Name equality check does not work.