rubygems-update 4.0.21 → 4.0.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -0
- data/Manifest.txt +1 -1
- data/bundler/CHANGELOG.md +28 -0
- data/bundler/lib/bundler/build_metadata.rb +2 -2
- data/bundler/lib/bundler/cli/lock.rb +14 -3
- data/bundler/lib/bundler/cli/platform.rb +15 -1
- data/bundler/lib/bundler/cli.rb +17 -5
- data/bundler/lib/bundler/fetcher/downloader.rb +10 -4
- data/bundler/lib/bundler/installer.rb +4 -0
- data/bundler/lib/bundler/man/bundle-lock.1 +1 -1
- data/bundler/lib/bundler/man/bundle-lock.1.ronn +1 -1
- data/bundler/lib/bundler/man/bundle-outdated.1 +1 -1
- data/bundler/lib/bundler/man/bundle-outdated.1.ronn +1 -1
- data/bundler/lib/bundler/man/bundle-update.1 +2 -2
- data/bundler/lib/bundler/man/bundle-update.1.ronn +2 -2
- data/bundler/lib/bundler/templates/newgem/ext/newgem/Cargo.toml.tt +1 -1
- data/bundler/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb +14 -4
- data/bundler/lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb +1 -1
- data/bundler/lib/bundler/vendor/connection_pool/lib/connection_pool.rb +3 -0
- data/bundler/lib/bundler/vendor/fileutils/lib/fileutils.rb +54 -29
- data/bundler/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/pool.rb +1 -1
- data/bundler/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/timed_stack_multi.rb +10 -1
- data/bundler/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb +12 -6
- data/bundler/lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb +35 -2
- data/bundler/lib/bundler/vendor/thor/lib/thor/base.rb +2 -1
- data/bundler/lib/bundler/vendor/thor/lib/thor/shell/basic.rb +14 -6
- data/bundler/lib/bundler/vendor/thor/lib/thor/shell/color.rb +2 -0
- data/bundler/lib/bundler/vendor/thor/lib/thor/version.rb +1 -1
- data/bundler/lib/bundler/vendor/thor/lib/thor.rb +34 -1
- data/bundler/lib/bundler/vendor/uri/lib/uri/common.rb +1 -1
- data/bundler/lib/bundler/vendor/uri/lib/uri/generic.rb +15 -3
- data/bundler/lib/bundler/vendor/uri/lib/uri/version.rb +1 -1
- data/bundler/lib/bundler/version.rb +1 -1
- data/bundler/lib/bundler.rb +1 -3
- data/lib/rubygems/commands/exec_command.rb +3 -0
- data/lib/rubygems/ext/builder.rb +7 -2
- data/lib/rubygems/gemcutter_utilities/webauthn_listener.rb +9 -0
- data/lib/rubygems/installer.rb +4 -0
- data/lib/rubygems/package.rb +36 -5
- data/lib/rubygems/uninstaller.rb +1 -0
- data/lib/rubygems/vendor/net-http/lib/net/http/exceptions.rb +2 -1
- data/lib/rubygems/vendor/net-http/lib/net/http/generic_request.rb +15 -7
- data/lib/rubygems/vendor/net-http/lib/net/http/header.rb +64 -19
- data/lib/rubygems/vendor/net-http/lib/net/http/request.rb +1 -0
- data/lib/rubygems/vendor/net-http/lib/net/http/requests.rb +74 -25
- data/lib/rubygems/vendor/net-http/lib/net/http/response.rb +42 -16
- data/lib/rubygems/vendor/net-http/lib/net/http/responses.rb +68 -0
- data/lib/rubygems/vendor/net-http/lib/net/http.rb +129 -37
- data/lib/rubygems/vendor/net-protocol/COPYING +56 -0
- data/lib/rubygems/vendor/net-protocol/lib/net/protocol.rb +95 -26
- data/lib/rubygems/vendor/optparse/lib/optparse.rb +23 -30
- data/lib/rubygems/vendor/timeout/lib/timeout.rb +190 -77
- data/lib/rubygems/vendor/uri/lib/uri/common.rb +1 -1
- data/lib/rubygems/vendor/uri/lib/uri/generic.rb +15 -3
- data/lib/rubygems/vendor/uri/lib/uri/version.rb +1 -1
- data/lib/rubygems.rb +1 -1
- metadata +2 -2
- data/lib/rubygems/vendor/net-protocol/LICENSE.txt +0 -22
|
@@ -2,6 +2,38 @@ require_relative "empty_directory"
|
|
|
2
2
|
|
|
3
3
|
class Bundler::Thor
|
|
4
4
|
module Actions
|
|
5
|
+
WARNINGS = {unchanged_no_flag: "File unchanged! Either the supplied flag value not found or the content has already been inserted!"}
|
|
6
|
+
|
|
7
|
+
# Injects the given content into a file, raising an error if the contents of
|
|
8
|
+
# the file are not changed. Different from gsub_file, this method is reversible.
|
|
9
|
+
#
|
|
10
|
+
# ==== Parameters
|
|
11
|
+
# destination<String>:: Relative path to the destination root
|
|
12
|
+
# data<String>:: Data to add to the file. Can be given as a block.
|
|
13
|
+
# config<Hash>:: give :verbose => false to not log the status and the flag
|
|
14
|
+
# for injection (:after or :before) or :force => true for
|
|
15
|
+
# insert two or more times the same content.
|
|
16
|
+
#
|
|
17
|
+
# ==== Examples
|
|
18
|
+
#
|
|
19
|
+
# insert_into_file "config/environment.rb", "config.gem :thor", :after => "Rails::Initializer.run do |config|\n"
|
|
20
|
+
#
|
|
21
|
+
# insert_into_file "config/environment.rb", :after => "Rails::Initializer.run do |config|\n" do
|
|
22
|
+
# gems = ask "Which gems would you like to add?"
|
|
23
|
+
# gems.split(" ").map{ |gem| " config.gem :#{gem}" }.join("\n")
|
|
24
|
+
# end
|
|
25
|
+
#
|
|
26
|
+
def insert_into_file!(destination, *args, &block)
|
|
27
|
+
data = block_given? ? block : args.shift
|
|
28
|
+
|
|
29
|
+
config = args.shift || {}
|
|
30
|
+
config[:after] = /\z/ unless config.key?(:before) || config.key?(:after)
|
|
31
|
+
config = config.merge({error_on_no_change: true})
|
|
32
|
+
|
|
33
|
+
action InjectIntoFile.new(self, destination, data, config)
|
|
34
|
+
end
|
|
35
|
+
alias_method :inject_into_file!, :insert_into_file!
|
|
36
|
+
|
|
5
37
|
# Injects the given content into a file. Different from gsub_file, this
|
|
6
38
|
# method is reversible.
|
|
7
39
|
#
|
|
@@ -21,8 +53,6 @@ class Bundler::Thor
|
|
|
21
53
|
# gems.split(" ").map{ |gem| " config.gem :#{gem}" }.join("\n")
|
|
22
54
|
# end
|
|
23
55
|
#
|
|
24
|
-
WARNINGS = {unchanged_no_flag: "File unchanged! Either the supplied flag value not found or the content has already been inserted!"}
|
|
25
|
-
|
|
26
56
|
def insert_into_file(destination, *args, &block)
|
|
27
57
|
data = block_given? ? block : args.shift
|
|
28
58
|
|
|
@@ -47,6 +77,7 @@ class Bundler::Thor
|
|
|
47
77
|
|
|
48
78
|
@replacement = data.is_a?(Proc) ? data.call : data
|
|
49
79
|
@flag = Regexp.escape(@flag) unless @flag.is_a?(Regexp)
|
|
80
|
+
@error_on_no_change = @config.fetch(:error_on_no_change, false)
|
|
50
81
|
end
|
|
51
82
|
|
|
52
83
|
def invoke!
|
|
@@ -59,6 +90,8 @@ class Bundler::Thor
|
|
|
59
90
|
if exists?
|
|
60
91
|
if replace!(/#{flag}/, content, config[:force])
|
|
61
92
|
say_status(:invoke)
|
|
93
|
+
elsif @error_on_no_change
|
|
94
|
+
raise Bundler::Thor::Error, "The content of #{destination} did not change"
|
|
62
95
|
elsif replacement_present?
|
|
63
96
|
say_status(:unchanged, color: :blue)
|
|
64
97
|
else
|
|
@@ -13,8 +13,9 @@ class Bundler::Thor
|
|
|
13
13
|
autoload :RakeCompat, File.expand_path("rake_compat", __dir__)
|
|
14
14
|
autoload :Group, File.expand_path("group", __dir__)
|
|
15
15
|
|
|
16
|
-
# Shortcuts for help.
|
|
16
|
+
# Shortcuts for help and tree commands.
|
|
17
17
|
HELP_MAPPINGS = %w(-h -? --help -D)
|
|
18
|
+
TREE_MAPPINGS = %w(-t --tree)
|
|
18
19
|
|
|
19
20
|
# Bundler::Thor methods that should not be overwritten by the user.
|
|
20
21
|
THOR_RESERVED_WORDS = %w(invoke shell options behavior root destination_root relative_root
|
|
@@ -311,13 +311,11 @@ class Bundler::Thor
|
|
|
311
311
|
end
|
|
312
312
|
|
|
313
313
|
def show_diff(destination, content) #:nodoc:
|
|
314
|
-
diff_cmd = ENV["THOR_DIFF"] || ENV["RAILS_DIFF"] || "diff -u"
|
|
315
|
-
|
|
316
314
|
require "tempfile"
|
|
317
315
|
Tempfile.open(File.basename(destination), File.dirname(destination), binmode: true) do |temp|
|
|
318
316
|
temp.write content
|
|
319
317
|
temp.rewind
|
|
320
|
-
system
|
|
318
|
+
system(*diff_tool, destination, temp.path)
|
|
321
319
|
end
|
|
322
320
|
end
|
|
323
321
|
|
|
@@ -369,15 +367,25 @@ class Bundler::Thor
|
|
|
369
367
|
|
|
370
368
|
def merge(destination, content) #:nodoc:
|
|
371
369
|
require "tempfile"
|
|
372
|
-
Tempfile.open([File.basename(destination), File.extname(destination)], File.dirname(destination)) do |temp|
|
|
370
|
+
Tempfile.open([File.basename(destination), File.extname(destination)], File.dirname(destination), binmode: true) do |temp|
|
|
373
371
|
temp.write content
|
|
374
372
|
temp.rewind
|
|
375
|
-
system(merge_tool, temp.path, destination)
|
|
373
|
+
system(*merge_tool, temp.path, destination)
|
|
376
374
|
end
|
|
377
375
|
end
|
|
378
376
|
|
|
379
377
|
def merge_tool #:nodoc:
|
|
380
|
-
@merge_tool ||=
|
|
378
|
+
@merge_tool ||= begin
|
|
379
|
+
require "shellwords"
|
|
380
|
+
Shellwords.split(ENV["THOR_MERGE"] || "git difftool --no-index")
|
|
381
|
+
end
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
def diff_tool #:nodoc:
|
|
385
|
+
@diff_cmd ||= begin
|
|
386
|
+
require "shellwords"
|
|
387
|
+
Shellwords.split(ENV["THOR_DIFF"] || ENV["RAILS_DIFF"] || "diff -u")
|
|
388
|
+
end
|
|
381
389
|
end
|
|
382
390
|
end
|
|
383
391
|
end
|
|
@@ -625,7 +625,7 @@ class Bundler::Thor
|
|
|
625
625
|
# alias name.
|
|
626
626
|
def find_command_possibilities(meth)
|
|
627
627
|
len = meth.to_s.length
|
|
628
|
-
possibilities = all_commands.reject {
|
|
628
|
+
possibilities = all_commands.reject {|k, v| v.is_a?(HiddenCommand) }.merge(map).keys.select { |n| meth == n[0, len] }.sort
|
|
629
629
|
unique_possibilities = possibilities.map { |k| map[k] || k }.uniq
|
|
630
630
|
|
|
631
631
|
if possibilities.include?(meth)
|
|
@@ -671,4 +671,37 @@ class Bundler::Thor
|
|
|
671
671
|
self.class.help(shell, subcommand)
|
|
672
672
|
end
|
|
673
673
|
end
|
|
674
|
+
|
|
675
|
+
map TREE_MAPPINGS => :tree
|
|
676
|
+
|
|
677
|
+
desc "tree", "Print a tree of all available commands"
|
|
678
|
+
def tree
|
|
679
|
+
build_command_tree(self.class, "")
|
|
680
|
+
end
|
|
681
|
+
|
|
682
|
+
private
|
|
683
|
+
|
|
684
|
+
def build_command_tree(klass, indent)
|
|
685
|
+
# Print current class name if it's not the root Bundler::Thor class
|
|
686
|
+
unless klass == Bundler::Thor
|
|
687
|
+
say "#{indent}#{klass.namespace || 'default'}", :blue
|
|
688
|
+
indent = "#{indent} "
|
|
689
|
+
end
|
|
690
|
+
|
|
691
|
+
# Print all commands for this class
|
|
692
|
+
visible_commands = klass.commands.reject { |_, cmd| cmd.hidden? || cmd.name == "help" }
|
|
693
|
+
commands_count = visible_commands.count
|
|
694
|
+
visible_commands.sort.each_with_index do |(command_name, command), i|
|
|
695
|
+
description = command.description.split("\n").first || ""
|
|
696
|
+
icon = i == (commands_count - 1) ? "└─" : "├─"
|
|
697
|
+
say "#{indent}#{icon} ", nil, false
|
|
698
|
+
say command_name, :green, false
|
|
699
|
+
say " (#{description})" unless description.empty?
|
|
700
|
+
end
|
|
701
|
+
|
|
702
|
+
# Print all subcommands (from registered Bundler::Thor subclasses)
|
|
703
|
+
klass.subcommand_classes.each do |_, subclass|
|
|
704
|
+
build_command_tree(subclass, indent)
|
|
705
|
+
end
|
|
706
|
+
end
|
|
674
707
|
end
|
|
@@ -466,7 +466,7 @@ module Bundler::URI
|
|
|
466
466
|
#
|
|
467
467
|
# uri = Bundler::URI.parse("http://john:S3nsit1ve@my.example.com")
|
|
468
468
|
# uri.user = "sam"
|
|
469
|
-
# uri.to_s #=> "http://sam
|
|
469
|
+
# uri.to_s #=> "http://sam@my.example.com"
|
|
470
470
|
#
|
|
471
471
|
def user=(user)
|
|
472
472
|
check_user(user)
|
|
@@ -1021,9 +1021,21 @@ module Bundler::URI
|
|
|
1021
1021
|
|
|
1022
1022
|
# RFC2396, Section 5.2, 6), a)
|
|
1023
1023
|
base_path << '' if base_path.last == '..'
|
|
1024
|
-
|
|
1025
|
-
|
|
1024
|
+
# Remove "<segment>/.." pairs in a single left-to-right pass (O(n)).
|
|
1025
|
+
# This deliberately differs from the relative-path stack handling
|
|
1026
|
+
# below: a leading ".." (one with no preceding segment left to
|
|
1027
|
+
# cancel) discards the whole base path, reproducing the previous
|
|
1028
|
+
# index/slice! implementation exactly.
|
|
1029
|
+
reduced = []
|
|
1030
|
+
base_path.each do |seg|
|
|
1031
|
+
if seg == '..'
|
|
1032
|
+
break if reduced.empty?
|
|
1033
|
+
reduced.pop
|
|
1034
|
+
else
|
|
1035
|
+
reduced << seg
|
|
1036
|
+
end
|
|
1026
1037
|
end
|
|
1038
|
+
base_path = reduced
|
|
1027
1039
|
|
|
1028
1040
|
if (first = rel_path.first) and first.empty?
|
|
1029
1041
|
base_path.clear
|
data/bundler/lib/bundler.rb
CHANGED
|
@@ -119,9 +119,7 @@ module Bundler
|
|
|
119
119
|
def bin_path
|
|
120
120
|
@bin_path ||= begin
|
|
121
121
|
path = Bundler.settings[:bin] || "bin"
|
|
122
|
-
|
|
123
|
-
mkdir_p(path)
|
|
124
|
-
path
|
|
122
|
+
Pathname.new(path).expand_path(root).expand_path
|
|
125
123
|
end
|
|
126
124
|
end
|
|
127
125
|
|
|
@@ -3,17 +3,20 @@
|
|
|
3
3
|
require_relative "../command"
|
|
4
4
|
require_relative "../dependency_installer"
|
|
5
5
|
require_relative "../gem_runner"
|
|
6
|
+
require_relative "../local_remote_options"
|
|
6
7
|
require_relative "../package"
|
|
7
8
|
require_relative "../version_option"
|
|
8
9
|
|
|
9
10
|
class Gem::Commands::ExecCommand < Gem::Command
|
|
10
11
|
include Gem::VersionOption
|
|
12
|
+
include Gem::LocalRemoteOptions
|
|
11
13
|
|
|
12
14
|
def initialize
|
|
13
15
|
super "exec", "Run a command from a gem", {
|
|
14
16
|
version: Gem::Requirement.default,
|
|
15
17
|
}
|
|
16
18
|
|
|
19
|
+
add_source_option
|
|
17
20
|
add_version_option
|
|
18
21
|
add_prerelease_option "to be installed"
|
|
19
22
|
|
data/lib/rubygems/ext/builder.rb
CHANGED
|
@@ -204,7 +204,8 @@ class Gem::Ext::Builder
|
|
|
204
204
|
when /Cargo.toml/ then
|
|
205
205
|
Gem::Ext::CargoBuilder.new
|
|
206
206
|
else
|
|
207
|
-
|
|
207
|
+
# Also called to validate a specification, which must not leave a build log.
|
|
208
|
+
raise Gem::Ext::BuildError, "No builder for extension '#{extension}'"
|
|
208
209
|
end
|
|
209
210
|
end
|
|
210
211
|
|
|
@@ -229,7 +230,11 @@ EOF
|
|
|
229
230
|
def build_extension(extension, dest_path) # :nodoc:
|
|
230
231
|
results = []
|
|
231
232
|
|
|
232
|
-
|
|
233
|
+
begin
|
|
234
|
+
builder = builder_for(extension)
|
|
235
|
+
rescue Gem::Ext::BuildError => e
|
|
236
|
+
build_error(e.message)
|
|
237
|
+
end
|
|
233
238
|
|
|
234
239
|
extension_dir =
|
|
235
240
|
File.expand_path File.join(@gem_dir, File.dirname(extension))
|
|
@@ -50,6 +50,13 @@ module Gem::GemcutterUtilities
|
|
|
50
50
|
socket = server.accept
|
|
51
51
|
request_line = socket.gets
|
|
52
52
|
|
|
53
|
+
# Browsers open connections speculatively and close them again without
|
|
54
|
+
# ever sending a request, so wait for the one that carries the code.
|
|
55
|
+
unless request_line
|
|
56
|
+
socket.close
|
|
57
|
+
next
|
|
58
|
+
end
|
|
59
|
+
|
|
53
60
|
method, req_uri, _protocol = request_line.split(" ")
|
|
54
61
|
req_uri = Gem::URI.parse(req_uri)
|
|
55
62
|
|
|
@@ -75,6 +82,8 @@ module Gem::GemcutterUtilities
|
|
|
75
82
|
responder.send(MethodNotAllowedResponse.for(host))
|
|
76
83
|
raise Gem::WebauthnVerificationError, "Invalid HTTP method #{method.upcase} received."
|
|
77
84
|
end
|
|
85
|
+
rescue Errno::ECONNABORTED, Errno::ECONNRESET
|
|
86
|
+
socket&.close
|
|
78
87
|
end
|
|
79
88
|
end
|
|
80
89
|
|
data/lib/rubygems/installer.rb
CHANGED
|
@@ -684,6 +684,10 @@ class Gem::Installer
|
|
|
684
684
|
raise Gem::InstallError, "#{spec} has an invalid name"
|
|
685
685
|
end
|
|
686
686
|
|
|
687
|
+
unless /\A#{Gem::Version::VERSION_PATTERN}\z/.match?(spec.version.to_s)
|
|
688
|
+
raise Gem::InstallError, "#{spec} has an invalid version"
|
|
689
|
+
end
|
|
690
|
+
|
|
687
691
|
if spec.raw_require_paths.any? {|path| path =~ /\R/ }
|
|
688
692
|
raise Gem::InstallError, "#{spec} has an invalid require_paths"
|
|
689
693
|
end
|
data/lib/rubygems/package.rb
CHANGED
|
@@ -89,6 +89,18 @@ class Gem::Package
|
|
|
89
89
|
|
|
90
90
|
class TarInvalidError < Error; end
|
|
91
91
|
|
|
92
|
+
##
|
|
93
|
+
# Raised when a filename contains characters that are invalid on Windows
|
|
94
|
+
|
|
95
|
+
class InvalidWindowsFileNameError < Gem::InstallError
|
|
96
|
+
def initialize(filename, gem_name = nil)
|
|
97
|
+
message = "The gem contains a file '#{filename}' with characters in its name that are not allowed on Windows (e.g., colons)."
|
|
98
|
+
message += " This is a problem with the '#{gem_name}' gem, not Rubygems." if gem_name
|
|
99
|
+
message += " Please report this issue to the gem author."
|
|
100
|
+
super message
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
92
104
|
attr_accessor :build_time # :nodoc:
|
|
93
105
|
|
|
94
106
|
##
|
|
@@ -258,6 +270,10 @@ class Gem::Package
|
|
|
258
270
|
|
|
259
271
|
def add_files(tar) # :nodoc:
|
|
260
272
|
@spec.files.each do |file|
|
|
273
|
+
if invalid_windows_filename?(file)
|
|
274
|
+
alert_warning "filename '#{file}' contains characters that are invalid on Windows (e.g., colons). This gem may fail to install on Windows."
|
|
275
|
+
end
|
|
276
|
+
|
|
261
277
|
stat = File.lstat file
|
|
262
278
|
|
|
263
279
|
if stat.symlink?
|
|
@@ -425,6 +441,11 @@ EOM
|
|
|
425
441
|
full_name = entry.full_name
|
|
426
442
|
next unless File.fnmatch pattern, full_name, File::FNM_DOTMATCH
|
|
427
443
|
|
|
444
|
+
if Gem.win_platform? && invalid_windows_filename?(full_name)
|
|
445
|
+
gem_name = @spec ? @spec.full_name : "unknown"
|
|
446
|
+
raise Gem::Package::InvalidWindowsFileNameError.new(full_name, gem_name)
|
|
447
|
+
end
|
|
448
|
+
|
|
428
449
|
destination = install_location full_name, destination_dir
|
|
429
450
|
|
|
430
451
|
if entry.symlink?
|
|
@@ -445,7 +466,7 @@ EOM
|
|
|
445
466
|
end
|
|
446
467
|
|
|
447
468
|
unless directories.include?(mkdir)
|
|
448
|
-
FileUtils.mkdir_p mkdir, mode: dir_mode ? 0o755 : (entry.header.mode if entry.directory?)
|
|
469
|
+
FileUtils.mkdir_p mkdir, mode: dir_mode ? 0o755 : (entry.header.mode & 0o777 if entry.directory?)
|
|
449
470
|
directories << mkdir
|
|
450
471
|
end
|
|
451
472
|
|
|
@@ -482,10 +503,10 @@ EOM
|
|
|
482
503
|
def file_mode(mode) # :nodoc:
|
|
483
504
|
((mode & 0o111).zero? ? data_mode : prog_mode) ||
|
|
484
505
|
# If we're not using one of the default modes, then we're going to fall
|
|
485
|
-
# back to the
|
|
486
|
-
#
|
|
487
|
-
#
|
|
488
|
-
(mode &
|
|
506
|
+
# back to the permission bits from the tarball. Masking drops setuid,
|
|
507
|
+
# setgid and sticky bits along with anything that does not fit into a
|
|
508
|
+
# mode in CRuby (an unsigned short).
|
|
509
|
+
(mode & 0o777)
|
|
489
510
|
end
|
|
490
511
|
|
|
491
512
|
##
|
|
@@ -531,6 +552,16 @@ EOM
|
|
|
531
552
|
end
|
|
532
553
|
end
|
|
533
554
|
|
|
555
|
+
##
|
|
556
|
+
# Checks if a filename contains characters that are invalid on Windows.
|
|
557
|
+
# Windows doesn't allow: < > : " | ? * \ and control characters (0x00-0x1F).
|
|
558
|
+
# Colons are the most common issue since they're allowed on Unix.
|
|
559
|
+
# Note: Colons are only valid as drive letter separators (e.g., C:), not in filenames.
|
|
560
|
+
|
|
561
|
+
def invalid_windows_filename?(filename) # :nodoc:
|
|
562
|
+
filename.to_s.split("/").any? {|part| part.match?(/[:<>"|?*\\\x00-\x1f]/) }
|
|
563
|
+
end
|
|
564
|
+
|
|
534
565
|
##
|
|
535
566
|
# Loads a Gem::Specification from the TarEntry +entry+
|
|
536
567
|
|
data/lib/rubygems/uninstaller.rb
CHANGED
|
@@ -3,7 +3,7 @@ module Gem::Net
|
|
|
3
3
|
# Gem::Net::HTTP exception class.
|
|
4
4
|
# You cannot use Gem::Net::HTTPExceptions directly; instead, you must use
|
|
5
5
|
# its subclasses.
|
|
6
|
-
module HTTPExceptions
|
|
6
|
+
module HTTPExceptions # :nodoc:
|
|
7
7
|
def initialize(msg, res) #:nodoc:
|
|
8
8
|
super msg
|
|
9
9
|
@response = res
|
|
@@ -12,6 +12,7 @@ module Gem::Net
|
|
|
12
12
|
alias data response #:nodoc: obsolete
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
+
# :stopdoc:
|
|
15
16
|
class HTTPError < ProtocolError
|
|
16
17
|
include HTTPExceptions
|
|
17
18
|
end
|
|
@@ -19,16 +19,13 @@ class Gem::Net::HTTPGenericRequest
|
|
|
19
19
|
|
|
20
20
|
if Gem::URI === uri_or_path then
|
|
21
21
|
raise ArgumentError, "not an HTTP Gem::URI" unless Gem::URI::HTTP === uri_or_path
|
|
22
|
-
hostname = uri_or_path.
|
|
22
|
+
hostname = uri_or_path.host
|
|
23
23
|
raise ArgumentError, "no host component for Gem::URI" unless (hostname && hostname.length > 0)
|
|
24
24
|
@uri = uri_or_path.dup
|
|
25
|
-
host = @uri.hostname.dup
|
|
26
|
-
host << ":" << @uri.port.to_s if @uri.port != @uri.default_port
|
|
27
25
|
@path = uri_or_path.request_uri
|
|
28
26
|
raise ArgumentError, "no HTTP request path given" unless @path
|
|
29
27
|
else
|
|
30
28
|
@uri = nil
|
|
31
|
-
host = nil
|
|
32
29
|
raise ArgumentError, "no HTTP request path given" unless uri_or_path
|
|
33
30
|
raise ArgumentError, "HTTP request path is empty" if uri_or_path.empty?
|
|
34
31
|
@path = uri_or_path.dup
|
|
@@ -51,7 +48,7 @@ class Gem::Net::HTTPGenericRequest
|
|
|
51
48
|
initialize_http_header initheader
|
|
52
49
|
self['Accept'] ||= '*/*'
|
|
53
50
|
self['User-Agent'] ||= 'Ruby'
|
|
54
|
-
self['Host'] ||=
|
|
51
|
+
self['Host'] ||= @uri.authority if @uri
|
|
55
52
|
@body = nil
|
|
56
53
|
@body_stream = nil
|
|
57
54
|
@body_data = nil
|
|
@@ -245,7 +242,7 @@ class Gem::Net::HTTPGenericRequest
|
|
|
245
242
|
end
|
|
246
243
|
|
|
247
244
|
if host = self['host']
|
|
248
|
-
host.
|
|
245
|
+
host = Gem::URI.parse("//#{host}").host # Remove a port component from the existing Host header
|
|
249
246
|
elsif host = @uri.host
|
|
250
247
|
else
|
|
251
248
|
host = addr
|
|
@@ -264,6 +261,8 @@ class Gem::Net::HTTPGenericRequest
|
|
|
264
261
|
|
|
265
262
|
private
|
|
266
263
|
|
|
264
|
+
# :stopdoc:
|
|
265
|
+
|
|
267
266
|
class Chunker #:nodoc:
|
|
268
267
|
def initialize(sock)
|
|
269
268
|
@sock = sock
|
|
@@ -337,6 +336,9 @@ class Gem::Net::HTTPGenericRequest
|
|
|
337
336
|
boundary = opt[:boundary]
|
|
338
337
|
require 'securerandom' unless defined?(SecureRandom)
|
|
339
338
|
boundary ||= SecureRandom.urlsafe_base64(40)
|
|
339
|
+
if /[\r\n]/.match?(boundary.to_s)
|
|
340
|
+
raise ArgumentError, "multipart boundary cannot include CR/LF"
|
|
341
|
+
end
|
|
340
342
|
chunked_p = chunked?
|
|
341
343
|
|
|
342
344
|
buf = +''
|
|
@@ -350,7 +352,10 @@ class Gem::Net::HTTPGenericRequest
|
|
|
350
352
|
buf << "--#{boundary}\r\n"
|
|
351
353
|
if filename
|
|
352
354
|
filename = quote_string(filename, charset)
|
|
353
|
-
type = h[:content_type] || 'application/octet-stream'
|
|
355
|
+
type = (h[:content_type] || 'application/octet-stream').to_s
|
|
356
|
+
if /[\r\n]/.match?(type)
|
|
357
|
+
raise ArgumentError, "field content type cannot include CR/LF"
|
|
358
|
+
end
|
|
354
359
|
buf << "Content-Disposition: form-data; " \
|
|
355
360
|
"name=\"#{key}\"; filename=\"#{filename}\"\r\n" \
|
|
356
361
|
"Content-Type: #{type}\r\n\r\n"
|
|
@@ -385,6 +390,9 @@ class Gem::Net::HTTPGenericRequest
|
|
|
385
390
|
|
|
386
391
|
def quote_string(str, charset)
|
|
387
392
|
str = str.encode(charset, fallback:->(c){'&#%d;'%c.encode("UTF-8").ord}) if charset
|
|
393
|
+
if /[\r\n]/.match?(str)
|
|
394
|
+
raise ArgumentError, "multipart field name or filename cannot include CR/LF"
|
|
395
|
+
end
|
|
388
396
|
str.gsub(/[\\"]/, '\\\\\&')
|
|
389
397
|
end
|
|
390
398
|
|