summaryrefslogtreecommitdiff
path: root/system/ruby/ruby-2.5.3-rubygems-v2.patch
blob: cf2b2c7f1024d84f2120e054db1dbeddac9be030 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
diff --git lib/rubygems.rb lib/rubygems.rb
index 2762bfcb88..cd7434ca87 100644
--- a/lib/rubygems.rb
+++ b/lib/rubygems.rb
@@ -10,7 +10,7 @@
 require 'thread'
 
 module Gem
-  VERSION = "2.7.6"
+  VERSION = "2.7.6.1"
 end
 
 # Must be first since it unloads the prelude from 1.9.2
diff --git lib/rubygems/command_manager.rb lib/rubygems/command_manager.rb
index 887272378e..3bee1c30a4 100644
--- a/lib/rubygems/command_manager.rb
+++ b/lib/rubygems/command_manager.rb
@@ -7,6 +7,7 @@
 
 require 'rubygems/command'
 require 'rubygems/user_interaction'
+require 'rubygems/text'
 
 ##
 # The command manager registers and installs all the individual sub-commands
@@ -32,6 +33,7 @@
 
 class Gem::CommandManager
 
+  include Gem::Text
   include Gem::UserInteraction
 
   BUILTIN_COMMANDS = [ # :nodoc:
@@ -140,12 +142,12 @@ def command_names
   def run(args, build_args=nil)
     process_args(args, build_args)
   rescue StandardError, Timeout::Error => ex
-    alert_error "While executing gem ... (#{ex.class})\n    #{ex}"
+    alert_error clean_text("While executing gem ... (#{ex.class})\n    #{ex}")
     ui.backtrace ex
 
     terminate_interaction(1)
   rescue Interrupt
-    alert_error "Interrupted"
+    alert_error clean_text("Interrupted")
     terminate_interaction(1)
   end
 
@@ -163,7 +165,7 @@ def process_args(args, build_args=nil)
       say Gem::VERSION
       terminate_interaction 0
     when /^-/ then
-      alert_error "Invalid option: #{args.first}. See 'gem --help'."
+      alert_error clean_text("Invalid option: #{args.first}. See 'gem --help'.")
       terminate_interaction 1
     else
       cmd_name = args.shift.downcase
@@ -212,7 +214,7 @@ def load_and_instantiate(command_name)
     rescue Exception => e
       e = load_error if load_error
 
-      alert_error "Loading command: #{command_name} (#{e.class})\n\t#{e}"
+      alert_error clean_text("Loading command: #{command_name} (#{e.class})\n\t#{e}")
       ui.backtrace e
     end
   end
diff --git lib/rubygems/commands/owner_command.rb lib/rubygems/commands/owner_command.rb
index 637b5bdc4d..cac6c5a17d 100644
--- a/lib/rubygems/commands/owner_command.rb
+++ b/lib/rubygems/commands/owner_command.rb
@@ -2,8 +2,11 @@
 require 'rubygems/command'
 require 'rubygems/local_remote_options'
 require 'rubygems/gemcutter_utilities'
+require 'rubygems/text'
 
 class Gem::Commands::OwnerCommand < Gem::Command
+
+  include Gem::Text
   include Gem::LocalRemoteOptions
   include Gem::GemcutterUtilities
 
@@ -64,7 +67,7 @@ def show_owners name
     end
 
     with_response response do |resp|
-      owners = Gem::SafeYAML.load resp.body
+      owners = Gem::SafeYAML.load clean_text(resp.body)
 
       say "Owners for gem: #{name}"
       owners.each do |owner|
diff --git lib/rubygems/gemcutter_utilities.rb lib/rubygems/gemcutter_utilities.rb
index 7c6d6bb364..623d9301b5 100644
--- a/lib/rubygems/gemcutter_utilities.rb
+++ b/lib/rubygems/gemcutter_utilities.rb
@@ -1,11 +1,14 @@
 # frozen_string_literal: true
 require 'rubygems/remote_fetcher'
+require 'rubygems/text'
 
 ##
 # Utility methods for using the RubyGems API.
 
 module Gem::GemcutterUtilities
 
+  include Gem::Text
+
   # TODO: move to Gem::Command
   OptionParser.accept Symbol do |value|
     value.to_sym
@@ -145,13 +148,13 @@ def with_response response, error_prefix = nil
       if block_given? then
         yield response
       else
-        say response.body
+        say clean_text(response.body)
       end
     else
       message = response.body
       message = "#{error_prefix}: #{message}" if error_prefix
 
-      say message
+      say clean_text(message)
       terminate_interaction 1 # TODO: question this
     end
   end
diff --git lib/rubygems/installer.rb lib/rubygems/installer.rb
index ee5fedeb64..904d5a0c7c 100644
--- a/lib/rubygems/installer.rb
+++ b/lib/rubygems/installer.rb
@@ -707,9 +707,26 @@ def verify_gem_home(unpack = false) # :nodoc:
       unpack or File.writable?(gem_home)
   end
 
-  def verify_spec_name
-    return if spec.name =~ Gem::Specification::VALID_NAME_PATTERN
-    raise Gem::InstallError, "#{spec} has an invalid name"
+  def verify_spec
+    unless spec.name =~ Gem::Specification::VALID_NAME_PATTERN
+      raise Gem::InstallError, "#{spec} has an invalid name"
+    end
+
+    if spec.raw_require_paths.any?{|path| path =~ /\r\n|\r|\n/ }
+      raise Gem::InstallError, "#{spec} has an invalid require_paths"
+    end
+
+    if spec.extensions.any?{|ext| ext =~ /\r\n|\r|\n/ }
+      raise Gem::InstallError, "#{spec} has an invalid extensions"
+    end
+
+    unless spec.specification_version.to_s =~ /\A\d+\z/
+      raise Gem::InstallError, "#{spec} has an invalid specification_version"
+    end
+
+    if spec.dependencies.any? {|dep| dep.type =~ /\r\n|\r|\n/ || dep.name =~ /\r\n|\r|\n/ }
+      raise Gem::InstallError, "#{spec} has an invalid dependencies"
+    end
   end
 
   ##
@@ -836,10 +853,12 @@ def dir
   def pre_install_checks
     verify_gem_home options[:unpack]
 
+    # The name and require_paths must be verified first, since it could contain
+    # ruby code that would be eval'ed in #ensure_loadable_spec
+    verify_spec
+
     ensure_loadable_spec
 
-    verify_spec_name
-
     if options[:install_as_default]
       Gem.ensure_default_gem_subdirectories gem_home
     else
diff --git lib/rubygems/package.rb lib/rubygems/package.rb
index b924122827..b472b97a07 100644
--- a/lib/rubygems/package.rb
+++ b/lib/rubygems/package.rb
@@ -425,6 +425,16 @@ def install_location filename, destination_dir # :nodoc:
     raise Gem::Package::PathError.new(destination, destination_dir) unless
       destination.start_with? destination_dir + '/'
 
+    begin
+      real_destination = File.expand_path(File.realpath(destination))
+    rescue
+      # it's fine if the destination doesn't exist, because rm -rf'ing it can't cause any damage
+      nil
+    else
+      raise Gem::Package::PathError.new(real_destination, destination_dir) unless
+        real_destination.start_with? destination_dir + '/'
+    end
+
     destination.untaint
     destination
   end
diff --git lib/rubygems/user_interaction.rb lib/rubygems/user_interaction.rb
index cacd782e08..eff8f9533c 100644
--- a/lib/rubygems/user_interaction.rb
+++ b/lib/rubygems/user_interaction.rb
@@ -6,6 +6,7 @@
 #++
 
 require 'rubygems/util'
+require 'rubygems/text'
 
 ##
 # Module that defines the default UserInteraction.  Any class including this
@@ -13,6 +14,8 @@
 
 module Gem::DefaultUserInteraction
 
+  include Gem::Text
+
   ##
   # The default UI is a class variable of the singleton class for this
   # module.
@@ -160,8 +163,8 @@ def terminate_interaction exit_code = 0
   # Calls +say+ with +msg+ or the results of the block if really_verbose
   # is true.
 
-  def verbose msg = nil
-    say(msg || yield) if Gem.configuration.really_verbose
+  def verbose(msg = nil)
+    say(clean_text(msg || yield)) if Gem.configuration.really_verbose
   end
 end
 
diff --git test/rubygems/test_gem_installer.rb test/rubygems/test_gem_installer.rb
index 93b0482407..a47a307049 100644
--- a/test/rubygems/test_gem_installer.rb
+++ b/test/rubygems/test_gem_installer.rb
@@ -1474,6 +1474,114 @@ def spec.validate; end
     end
   end
 
+  def test_pre_install_checks_malicious_name_before_eval
+    spec = util_spec "malicious\n::Object.const_set(:FROM_EVAL, true)#", '1'
+    def spec.full_name # so the spec is buildable
+      "malicious-1"
+    end
+    def spec.validate(*args); end
+
+    util_build_gem spec
+
+    gem = File.join(@gemhome, 'cache', spec.file_name)
+
+    use_ui @ui do
+      @installer = Gem::Installer.at gem
+      e = assert_raises Gem::InstallError do
+        @installer.pre_install_checks
+      end
+      assert_equal "#<Gem::Specification name=malicious\n::Object.const_set(:FROM_EVAL, true)# version=1> has an invalid name", e.message
+    end
+    refute defined?(::Object::FROM_EVAL)
+  end
+
+  def test_pre_install_checks_malicious_require_paths_before_eval
+    spec = util_spec "malicious", '1'
+    def spec.full_name # so the spec is buildable
+      "malicious-1"
+    end
+    def spec.validate(*args); end
+    spec.require_paths = ["malicious\n``"]
+
+    util_build_gem spec
+
+    gem = File.join(@gemhome, 'cache', spec.file_name)
+
+    use_ui @ui do
+      @installer = Gem::Installer.at gem
+      e = assert_raises Gem::InstallError do
+        @installer.pre_install_checks
+      end
+      assert_equal "#<Gem::Specification name=malicious version=1> has an invalid require_paths", e.message
+    end
+  end
+
+  def test_pre_install_checks_malicious_extensions_before_eval
+    skip "mswin environment disallow to create file contained the carriage return code." if Gem.win_platform?
+
+    spec = util_spec "malicious", '1'
+    def spec.full_name # so the spec is buildable
+      "malicious-1"
+    end
+    def spec.validate(*args); end
+    spec.extensions = ["malicious\n``"]
+
+    util_build_gem spec
+
+    gem = File.join(@gemhome, 'cache', spec.file_name)
+
+    use_ui @ui do
+      @installer = Gem::Installer.at gem
+      e = assert_raises Gem::InstallError do
+        @installer.pre_install_checks
+      end
+      assert_equal "#<Gem::Specification name=malicious version=1> has an invalid extensions", e.message
+    end
+  end
+
+  def test_pre_install_checks_malicious_specification_version_before_eval
+    spec = util_spec "malicious", '1'
+    def spec.full_name # so the spec is buildable
+      "malicious-1"
+    end
+    def spec.validate(*args); end
+    spec.specification_version = "malicious\n``"
+
+    util_build_gem spec
+
+    gem = File.join(@gemhome, 'cache', spec.file_name)
+
+    use_ui @ui do
+      @installer = Gem::Installer.at gem
+      e = assert_raises Gem::InstallError do
+        @installer.pre_install_checks
+      end
+      assert_equal "#<Gem::Specification name=malicious version=1> has an invalid specification_version", e.message
+    end
+  end
+
+  def test_pre_install_checks_malicious_dependencies_before_eval
+    spec = util_spec "malicious", '1'
+    def spec.full_name # so the spec is buildable
+      "malicious-1"
+    end
+    def spec.validate(*args); end
+    spec.add_dependency "b\nfoo", '> 5'
+
+    util_build_gem spec
+
+    gem = File.join(@gemhome, 'cache', spec.file_name)
+
+    use_ui @ui do
+      @installer = Gem::Installer.at gem
+      @installer.ignore_dependencies = true
+      e = assert_raises Gem::InstallError do
+        @installer.pre_install_checks
+      end
+      assert_equal "#<Gem::Specification name=malicious version=1> has an invalid dependencies", e.message
+    end
+  end
+
   def test_shebang
     util_make_exec @spec, "#!/usr/bin/ruby"
 
diff --git test/rubygems/test_gem_package.rb test/rubygems/test_gem_package.rb
index d1664cf285..0b03ee2e0c 100644
--- a/test/rubygems/test_gem_package.rb
+++ b/test/rubygems/test_gem_package.rb
@@ -480,6 +480,42 @@ def test_extract_symlink_parent
                  "#{destination_subdir} is not allowed", e.message)
   end
 
+  def test_extract_symlink_parent_doesnt_delete_user_dir
+    skip if RUBY_VERSION <= "1.8.7"
+
+    package = Gem::Package.new @gem
+
+    # Extract into a subdirectory of @destination; if this test fails it writes
+    # a file outside destination_subdir, but we want the file to remain inside
+    # @destination so it will be cleaned up.
+    destination_subdir = File.join @destination, 'subdir'
+    FileUtils.mkdir_p destination_subdir
+
+    destination_user_dir = File.join @destination, 'user'
+    destination_user_subdir = File.join destination_user_dir, 'dir'
+    FileUtils.mkdir_p destination_user_subdir
+
+    tgz_io = util_tar_gz do |tar|
+      tar.add_symlink 'link', destination_user_dir, 16877
+      tar.add_symlink 'link/dir', '.', 16877
+    end
+
+    e = assert_raises(Gem::Package::PathError, Errno::EACCES) do
+      package.extract_tar_gz tgz_io, destination_subdir
+    end
+
+    assert_path_exists destination_user_subdir
+
+    if Gem::Package::PathError === e
+      assert_equal("installing into parent path #{destination_user_subdir} of " +
+                  "#{destination_subdir} is not allowed", e.message)
+    elsif win_platform?
+      skip "symlink - must be admin with no UAC on Windows"
+    else
+      raise e
+    end
+  end
+
   def test_extract_tar_gz_directory
     package = Gem::Package.new @gem
 
diff --git test/rubygems/test_gem_text.rb test/rubygems/test_gem_text.rb
index 04f3f605e8..8ce6df94bb 100644
--- a/test/rubygems/test_gem_text.rb
+++ b/test/rubygems/test_gem_text.rb
@@ -85,4 +85,9 @@ def test_truncate_text
     s = "ab" * 500_001
     assert_equal "Truncating desc to 1,000,000 characters:\n#{s[0, 1_000_000]}", truncate_text(s, "desc", 1_000_000)
   end
+
+  def test_clean_text
+    assert_equal ".]2;nyan.", clean_text("\e]2;nyan\a")
+  end
+
 end