Skip to content

Commit 8518e42

Browse files
committed
[GR-14449] Apply security patch for RubyGems
PullRequest: truffleruby/685
2 parents 3a8b2c2 + 78600e8 commit 8518e42

25 files changed

+384
-35
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# 1.0 RC 14
22

3-
Updated to Ruby 2.6.1.
3+
Updated to Ruby 2.6.1, with the RubyGems 3.0.3 security patch.
44

55
Bug fixes:
66

doc/user/security.md

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,29 +35,35 @@ https://www.ruby-lang.org/en/security/.
3535

3636
Number | Description | Their Mitigation | Test | Our Mitigation
3737
--- | --- | --- | --- | ---
38+
CVE-2019-8320 | Delete directory using symlink when decompressing `tar` | Check the expanded path | Tested in MRI `test/rubygems/test_gem_package.rb` | Applied the same patch
39+
CVE-2019-8321 | Escape sequence injection in `verbose` | Sanitise message | Tested in `ruby/spec` `:security` | Applied the same patch
40+
CVE-2019-8322 | Escape sequence injection in `gem owner` | Sanitise message | Tested in `ruby/spec` `:security` | Applied the same patch
41+
CVE-2019-8323 | Escape sequence injection vulnerability in API response handling | Sanitise message | Tested in `ruby/spec` `:security` | Applied the same patch
42+
CVE-2019-8324 | Installing a malicious gem may lead to arbitrary code execution | Verifying gems before pre-install checks | Tested in MRI `test/rubygems/test_gem_installer.rb` | Applied the same patch
43+
CVE-2019-8325 | Escape sequence injection in errors | Sanitise error messages | Tested in `ruby/spec` `:security` | Applied the same patch
3844
CVE-2018-16395 | `OpenSSL::X509::Name` equality check does not work correctly | *todo* | *todo* | *todo*
39-
CVE-2018-16396 | Tainted flags are not propagated in `Array#pack` and `String#unpack` with some directives | Additional taint operations | Tested in ruby/spec `:security` | Additional taint operations
40-
CVE-2018-6914 | Unintentional file and directory creation with directory traversal in `tempfile` and `tmpdir` | Sanitization of paths | Tested in ruby/spec `:security` | Sanitization of paths
41-
CVE-2018-8779 | Unintentional socket creation by poisoned NUL byte in `UNIXServer` and `UNIXSocket` | Check for NUL bytes | Tested in ruby/spec `:security` | Check for NUL bytes
42-
CVE-2018-8780 | Unintentional directory traversal by poisoned NUL byte in `Dir` | Check for NUL bytes | Tested in ruby/spec `:security` | Check for NUL bytes
43-
CVE-2018-8777 | DoS by large request in WEBrick | Logic for header length | Tested in MRI `test/webrick/test_httpserver.rb` | We share the same code, so also have the mitigation
44-
CVE-2017-17742 | HTTP response splitting in WEBrick | Logic for invalid headers | Tested in ruby/spec `:security` | We share the same code, so also have the mitigation
45-
CVE-2018-8778 | Buffer under-read in String#unpack | A range check | Tested in ruby/spec `:security` | A range check
46-
CVE-2017-17405 | Command injection vulnerability in `Net::FTP` | Treat paths in commands explicitly as paths, not general IO commands | Tested in MRI `test/net/ftp/test_ftp.rb` | We share the same code, so also have the mitigation
47-
CVE-2017-10784 | Escape sequence injection vulnerability in the Basic authentication of WEBrick | Proper escaping of logs | Tested in MRI `test/webrick/test_httpauth.rb` | We share the same code, so also have the mitigation
45+
CVE-2018-16396 | Tainted flags are not propagated in `Array#pack` and `String#unpack` with some directives | Additional taint operations | Tested in `ruby/spec` `:security` | Additional taint operations
46+
CVE-2018-6914 | Unintentional file and directory creation with directory traversal in `tempfile` and `tmpdir` | Sanitization of paths | Tested in `ruby/spec` `:security` | Sanitization of paths
47+
CVE-2018-8779 | Unintentional socket creation by poisoned NUL byte in `UNIXServer` and `UNIXSocket` | Check for NUL bytes | Tested in `ruby/spec` `:security` | Check for NUL bytes
48+
CVE-2018-8780 | Unintentional directory traversal by poisoned NUL byte in `Dir` | Check for NUL bytes | Tested in `ruby/spec` `:security` | Check for NUL bytes
49+
CVE-2018-8777 | DoS by large request in WEBrick | Logic for header length | Tested in MRI `test/webrick/test_httpserver.rb` | Applied the same mitigation
50+
CVE-2017-17742 | HTTP response splitting in WEBrick | Logic for invalid headers | Tested in `ruby/spec` `:security` |Applied the same mitigation
51+
CVE-2018-8778 | Buffer under-read in String#unpack | A range check | Tested in `ruby/spec` `:security` | A range check
52+
CVE-2017-17405 | Command injection vulnerability in `Net::FTP` | Treat paths in commands explicitly as paths, not general IO commands | Tested in MRI `test/net/ftp/test_ftp.rb` | Applied the same mitigation
53+
CVE-2017-10784 | Escape sequence injection vulnerability in the Basic authentication of WEBrick | Proper escaping of logs | Tested in MRI `test/webrick/test_httpauth.rb` | Applied the same mitigation
4854
CVE-2017-0898 | Buffer underrun vulnerability in `Kernel.sprintf` | *todo* | *todo* | *todo*
4955
CVE-2017-14033 | Buffer underrun vulnerability in OpenSSL ASN1 decode | *todo* | *todo* | *todo*
5056
CVE-2017-14064 | Heap exposure vulnerability in generating JSON | *todo* | *todo* | *todo*
5157
CVE-2017-0902, CVE-2017-0899, CVE-2017-0900, CVE-2017-0901 | Multiple vulnerabilities in RubyGems | *todo* | *todo* | *todo*
5258
CVE-2015-7551 | Unsafe tainted string usage in Fiddle and DL (regression of the mitigation of CVE-2009-5147) | Additional taint checks | Tested in MRI `test/mri/tests/fiddle/test_handle.rb` | Not applicable as we do not support `$SAFE`, and the `DL` module was removed in Ruby 2.2.0
5359
CVE-2015-1855 | Ruby OpenSSL Hostname Verification | *todo* | *todo* | *todo*
5460
CVE-2014-8090 | Another Denial of Service XML Expansion | *todo* | *todo* | *todo*
55-
CVE-2014-8080 | Denial of Service XML Expansion | *todo* | Tested in ruby/spec `:security` | *todo*
61+
CVE-2014-8080 | Denial of Service XML Expansion | *todo* | Tested in `ruby/spec` `:security` | *todo*
5662
None | Changed default settings of ext/openssl | *todo* | *todo* | *todo*
5763
CVE-2014-2734 | Dispute of Vulnerability | *todo* | *todo* | *todo*
5864
CVE-2014-0160 | OpenSSL Severe Vulnerability in TLS Heartbeat Extension | *todo* | *todo* | *todo*
5965
CVE-2014-2525 | Heap Overflow in YAML URI Escape Parsing | *todo* | *todo* | *todo*
60-
CVE-2013-4164 | Heap Overflow in Floating Point Parsing | *todo* | Tested in ruby/spec `:security` | *todo*
66+
CVE-2013-4164 | Heap Overflow in Floating Point Parsing | *todo* | Tested in `ruby/spec` `:security` | *todo*
6167
CVE-2013-4073 | Hostname check bypassing vulnerability in SSL client | *todo* | *todo* | *todo*
6268
CVE-2013-2065 | Object taint bypassing in DL and Fiddle in Ruby | Additional taint checks | Tested in MRI `test/mri/tests/fiddle/test_func.rb` | Not applicable as we do not support `$SAFE`, and the `DL` module was removed in Ruby 2.2.0
6369
CVE-2013-1821 | Entity expansion DoS vulnerability in REXML | *todo* | *todo* | *todo*
@@ -68,7 +74,7 @@ CVE-2012-4522 | Unintentional file creation caused by inserting a illegal NUL ch
6874
CVE-2012-4464, CVE-2012-4466 | $SAFE escaping vulnerability about `Exception#to_s` / `NameError#to_s` | *todo* | *todo* | Not applicable as we do not support `$SAFE`
6975
None | Security Fix for RubyGems: SSL server verification failure for remote repository | *todo* | *todo* | *todo*
7076
CVE-2011-3389 | Security Fix for Ruby OpenSSL module: Allow 0/n splitting as a prevention for the TLS BEAST attack | *todo* | *todo* | *todo*
71-
CVE-2011-4815 | Denial of service attack was found for Ruby's Hash algorithm (cross-reference CVE-2011-4838, CVE-2012-5370, CVE-2012-5372) | Hashes are made non-deterministic by incorporating process start time | Tested in ruby/spec `:security` | Hashes are made non-deterministic by incorporating a seed from `/dev/urandom`
77+
CVE-2011-4815 | Denial of service attack was found for Ruby's Hash algorithm (cross-reference CVE-2011-4838, CVE-2012-5370, CVE-2012-5372) | Hashes are made non-deterministic by incorporating process start time | Tested in `ruby/spec` `:security` | Hashes are made non-deterministic by incorporating a seed from `/dev/urandom`
7278
None | Exception methods can bypass `$SAFE` | *todo* | *todo* | Not applicable as we do not support `$SAFE`
7379
None | FileUtils is vulnerable to symlink race attacks | *todo* | *todo* | *todo*
7480
CVE-2010-0541 | XSS in WEBrick | *todo* | *todo* | *todo*
@@ -93,8 +99,8 @@ apply to TruffleRuby.
9399

94100
Number | Description | Their Mitigation | Test | Our Mitigation
95101
--- | --- | --- | --- | ---
96-
CVE-2012-5370 | JRuby computes hash values without properly restricting the ability to trigger hash collisions predictably (cross-reference CVE-2011-4815, CVE-2011-4838, CVE-2012-5372) | Hashes are made non-deterministic by incorporating process start time | Tested in ruby/spec `:security` | Hashes are made non-deterministic by incorporating a seed from `/dev/urandom`
97-
CVE-2011-4838 | JRuby before 1.6.5.1 computes hash values without restricting the ability to trigger hash collisions predictably (cross-reference CVE-2011-4815, CVE-2012-5370, CVE-2012-5372) | Hashes are made non-deterministic by incorporating process start time | Tested in ruby/spec `:security` | Hashes are made non-deterministic by incorporating a seed from `/dev/urandom`
102+
CVE-2012-5370 | JRuby computes hash values without properly restricting the ability to trigger hash collisions predictably (cross-reference CVE-2011-4815, CVE-2011-4838, CVE-2012-5372) | Hashes are made non-deterministic by incorporating process start time | Tested in `ruby/spec` `:security` | Hashes are made non-deterministic by incorporating a seed from `/dev/urandom`
103+
CVE-2011-4838 | JRuby before 1.6.5.1 computes hash values without restricting the ability to trigger hash collisions predictably (cross-reference CVE-2011-4815, CVE-2012-5370, CVE-2012-5372) | Hashes are made non-deterministic by incorporating process start time | Tested in `ruby/spec` `:security` | Hashes are made non-deterministic by incorporating a seed from `/dev/urandom`
98104

99105
## Rubinius Vulnerabilities
100106

@@ -103,7 +109,7 @@ Rubinius may apply to TruffleRuby.
103109

104110
Number | Description | Their Mitigation | Test | Our Mitigation
105111
--- | --- | --- | --- | ---
106-
CVE-2012-5372 | Rubinius computes hash values without properly restricting the ability to trigger hash collisions predictably (cross-reference CVE-2011-4815, CVE-2011-4838, CVE-2012-5370) | Hashes are made non-deterministic by incorporating output from `/dev/urandom` | Tested in ruby/spec `:security` | Hashes are made non-deterministic by incorporating a seed from `/dev/urandom`
112+
CVE-2012-5372 | Rubinius computes hash values without properly restricting the ability to trigger hash collisions predictably (cross-reference CVE-2011-4815, CVE-2011-4838, CVE-2012-5370) | Hashes are made non-deterministic by incorporating output from `/dev/urandom` | Tested in `ruby/spec` `:security` | Hashes are made non-deterministic by incorporating a seed from `/dev/urandom`
107113

108114
## Java Dependency Vulnerabilities
109115

@@ -115,7 +121,7 @@ We are not aware of any known vulnerabilities.
115121

116122
Number | Description | Their Mitigation | Test | Our Mitigation
117123
--- | --- | --- | --- | ---
118-
CVE-2010-1330 | The regular expression engine in JRuby before 1.4.1, when `$KCODE` is set to `'u'`, does not properly handle characters immediately after a UTF-8 character | Check byte sequences for the UTF-8 encoding when perform regexp operations | Tested in ruby/spec `:security` | We share the same code, so also have the mitigation
124+
CVE-2010-1330 | The regular expression engine in JRuby before 1.4.1, when `$KCODE` is set to `'u'`, does not properly handle characters immediately after a UTF-8 character | Check byte sequences for the UTF-8 encoding when perform regexp operations | Tested in `ruby/spec` `:security` | Applied the same mitigation
119125

120126
## Other Dependency Vulnerabilities
121127

lib/mri/rubygems.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
require 'rbconfig'
1010

1111
module Gem
12-
VERSION = "3.0.1".freeze
12+
VERSION = "3.0.3".freeze
1313
end
1414

1515
# Must be first since it unloads the prelude from 1.9.2

lib/mri/rubygems/command_manager.rb

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
require 'rubygems/command'
99
require 'rubygems/user_interaction'
10+
require 'rubygems/text'
1011

1112
##
1213
# The command manager registers and installs all the individual sub-commands
@@ -32,6 +33,7 @@
3233

3334
class Gem::CommandManager
3435

36+
include Gem::Text
3537
include Gem::UserInteraction
3638

3739
BUILTIN_COMMANDS = [ # :nodoc:
@@ -145,12 +147,12 @@ def command_names
145147
def run(args, build_args=nil)
146148
process_args(args, build_args)
147149
rescue StandardError, Timeout::Error => ex
148-
alert_error "While executing gem ... (#{ex.class})\n #{ex}"
150+
alert_error clean_text("While executing gem ... (#{ex.class})\n #{ex}")
149151
ui.backtrace ex
150152

151153
terminate_interaction(1)
152154
rescue Interrupt
153-
alert_error "Interrupted"
155+
alert_error clean_text("Interrupted")
154156
terminate_interaction(1)
155157
end
156158

@@ -168,7 +170,7 @@ def process_args(args, build_args=nil)
168170
say Gem::VERSION
169171
terminate_interaction 0
170172
when /^-/ then
171-
alert_error "Invalid option: #{args.first}. See 'gem --help'."
173+
alert_error clean_text("Invalid option: #{args.first}. See 'gem --help'.")
172174
terminate_interaction 1
173175
else
174176
cmd_name = args.shift.downcase
@@ -224,7 +226,7 @@ def load_and_instantiate(command_name)
224226
rescue Exception => e
225227
e = load_error if load_error
226228

227-
alert_error "Loading command: #{command_name} (#{e.class})\n\t#{e}"
229+
alert_error clean_text("Loading command: #{command_name} (#{e.class})\n\t#{e}")
228230
ui.backtrace e
229231
end
230232
end

lib/mri/rubygems/commands/owner_command.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22
require 'rubygems/command'
33
require 'rubygems/local_remote_options'
44
require 'rubygems/gemcutter_utilities'
5+
require 'rubygems/text'
56

67
class Gem::Commands::OwnerCommand < Gem::Command
8+
9+
include Gem::Text
710
include Gem::LocalRemoteOptions
811
include Gem::GemcutterUtilities
912

@@ -65,7 +68,7 @@ def show_owners(name)
6568
end
6669

6770
with_response response do |resp|
68-
owners = Gem::SafeYAML.load resp.body
71+
owners = Gem::SafeYAML.load clean_text(resp.body)
6972

7073
say "Owners for gem: #{name}"
7174
owners.each do |owner|

lib/mri/rubygems/gemcutter_utilities.rb

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
# frozen_string_literal: true
22
require 'rubygems/remote_fetcher'
3+
require 'rubygems/text'
34

45
##
56
# Utility methods for using the RubyGems API.
67

78
module Gem::GemcutterUtilities
89

10+
include Gem::Text
11+
912
# TODO: move to Gem::Command
1013
OptionParser.accept Symbol do |value|
1114
value.to_sym
@@ -162,13 +165,13 @@ def with_response(response, error_prefix = nil)
162165
if block_given?
163166
yield response
164167
else
165-
say response.body
168+
say clean_text(response.body)
166169
end
167170
else
168171
message = response.body
169172
message = "#{error_prefix}: #{message}" if error_prefix
170173

171-
say message
174+
say clean_text(message)
172175
terminate_interaction 1 # TODO: question this
173176
end
174177
end

lib/mri/rubygems/install_update_options.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def add_install_update_options
3030
options[:bin_dir] = File.expand_path(value)
3131
end
3232

33-
add_option(:"Install/Update", '--[no-]document [TYPES]', Array,
33+
add_option(:"Install/Update", '--document [TYPES]', Array,
3434
'Generate documentation for installed gems',
3535
'List the documentation types you wish to',
3636
'generate. For example: rdoc,ri') do |value, options|

lib/mri/rubygems/installer.rb

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -725,9 +725,26 @@ def verify_gem_home(unpack = false) # :nodoc:
725725
unpack or File.writable?(gem_home)
726726
end
727727

728-
def verify_spec_name
729-
return if spec.name =~ Gem::Specification::VALID_NAME_PATTERN
730-
raise Gem::InstallError, "#{spec} has an invalid name"
728+
def verify_spec
729+
unless spec.name =~ Gem::Specification::VALID_NAME_PATTERN
730+
raise Gem::InstallError, "#{spec} has an invalid name"
731+
end
732+
733+
if spec.raw_require_paths.any?{|path| path =~ /\R/ }
734+
raise Gem::InstallError, "#{spec} has an invalid require_paths"
735+
end
736+
737+
if spec.extensions.any?{|ext| ext =~ /\R/ }
738+
raise Gem::InstallError, "#{spec} has an invalid extensions"
739+
end
740+
741+
unless spec.specification_version.to_s =~ /\A\d+\z/
742+
raise Gem::InstallError, "#{spec} has an invalid specification_version"
743+
end
744+
745+
if spec.dependencies.any? {|dep| dep.type =~ /\R/ || dep.name =~ /\R/ }
746+
raise Gem::InstallError, "#{spec} has an invalid dependencies"
747+
end
731748
end
732749

733750
##
@@ -876,9 +893,11 @@ def dir
876893
def pre_install_checks
877894
verify_gem_home options[:unpack]
878895

879-
ensure_loadable_spec
896+
# The name and require_paths must be verified first, since it could contain
897+
# ruby code that would be eval'ed in #ensure_loadable_spec
898+
verify_spec
880899

881-
verify_spec_name
900+
ensure_loadable_spec
882901

883902
if options[:install_as_default]
884903
Gem.ensure_default_gem_subdirectories gem_home

lib/mri/rubygems/package.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,16 @@ def install_location(filename, destination_dir) # :nodoc:
456456
raise Gem::Package::PathError.new(destination, destination_dir) unless
457457
destination.start_with? destination_dir + '/'
458458

459+
begin
460+
real_destination = File.expand_path(File.realpath(destination))
461+
rescue
462+
# it's fine if the destination doesn't exist, because rm -rf'ing it can't cause any damage
463+
nil
464+
else
465+
raise Gem::Package::PathError.new(real_destination, destination_dir) unless
466+
real_destination.start_with? destination_dir + '/'
467+
end
468+
459469
destination.untaint
460470
destination
461471
end

lib/mri/rubygems/requirement.rb

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,22 @@ def to_s # :nodoc:
280280

281281
def ==(other) # :nodoc:
282282
return unless Gem::Requirement === other
283-
requirements == other.requirements
283+
284+
# An == check is always necessary
285+
return false unless requirements == other.requirements
286+
287+
# An == check is sufficient unless any requirements use ~>
288+
return true unless _tilde_requirements.any?
289+
290+
# If any requirements use ~> we use the stricter `#eql?` that also checks
291+
# that version precision is the same
292+
_tilde_requirements.eql?(other._tilde_requirements)
293+
end
294+
295+
protected
296+
297+
def _tilde_requirements
298+
requirements.select { |r| r.first == "~>" }
284299
end
285300

286301
private

0 commit comments

Comments
 (0)