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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -0
  3. data/Manifest.txt +1 -1
  4. data/bundler/CHANGELOG.md +28 -0
  5. data/bundler/lib/bundler/build_metadata.rb +2 -2
  6. data/bundler/lib/bundler/cli/lock.rb +14 -3
  7. data/bundler/lib/bundler/cli/platform.rb +15 -1
  8. data/bundler/lib/bundler/cli.rb +17 -5
  9. data/bundler/lib/bundler/fetcher/downloader.rb +10 -4
  10. data/bundler/lib/bundler/installer.rb +4 -0
  11. data/bundler/lib/bundler/man/bundle-lock.1 +1 -1
  12. data/bundler/lib/bundler/man/bundle-lock.1.ronn +1 -1
  13. data/bundler/lib/bundler/man/bundle-outdated.1 +1 -1
  14. data/bundler/lib/bundler/man/bundle-outdated.1.ronn +1 -1
  15. data/bundler/lib/bundler/man/bundle-update.1 +2 -2
  16. data/bundler/lib/bundler/man/bundle-update.1.ronn +2 -2
  17. data/bundler/lib/bundler/templates/newgem/ext/newgem/Cargo.toml.tt +1 -1
  18. data/bundler/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb +14 -4
  19. data/bundler/lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb +1 -1
  20. data/bundler/lib/bundler/vendor/connection_pool/lib/connection_pool.rb +3 -0
  21. data/bundler/lib/bundler/vendor/fileutils/lib/fileutils.rb +54 -29
  22. data/bundler/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/pool.rb +1 -1
  23. data/bundler/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/timed_stack_multi.rb +10 -1
  24. data/bundler/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb +12 -6
  25. data/bundler/lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb +35 -2
  26. data/bundler/lib/bundler/vendor/thor/lib/thor/base.rb +2 -1
  27. data/bundler/lib/bundler/vendor/thor/lib/thor/shell/basic.rb +14 -6
  28. data/bundler/lib/bundler/vendor/thor/lib/thor/shell/color.rb +2 -0
  29. data/bundler/lib/bundler/vendor/thor/lib/thor/version.rb +1 -1
  30. data/bundler/lib/bundler/vendor/thor/lib/thor.rb +34 -1
  31. data/bundler/lib/bundler/vendor/uri/lib/uri/common.rb +1 -1
  32. data/bundler/lib/bundler/vendor/uri/lib/uri/generic.rb +15 -3
  33. data/bundler/lib/bundler/vendor/uri/lib/uri/version.rb +1 -1
  34. data/bundler/lib/bundler/version.rb +1 -1
  35. data/bundler/lib/bundler.rb +1 -3
  36. data/lib/rubygems/commands/exec_command.rb +3 -0
  37. data/lib/rubygems/ext/builder.rb +7 -2
  38. data/lib/rubygems/gemcutter_utilities/webauthn_listener.rb +9 -0
  39. data/lib/rubygems/installer.rb +4 -0
  40. data/lib/rubygems/package.rb +36 -5
  41. data/lib/rubygems/uninstaller.rb +1 -0
  42. data/lib/rubygems/vendor/net-http/lib/net/http/exceptions.rb +2 -1
  43. data/lib/rubygems/vendor/net-http/lib/net/http/generic_request.rb +15 -7
  44. data/lib/rubygems/vendor/net-http/lib/net/http/header.rb +64 -19
  45. data/lib/rubygems/vendor/net-http/lib/net/http/request.rb +1 -0
  46. data/lib/rubygems/vendor/net-http/lib/net/http/requests.rb +74 -25
  47. data/lib/rubygems/vendor/net-http/lib/net/http/response.rb +42 -16
  48. data/lib/rubygems/vendor/net-http/lib/net/http/responses.rb +68 -0
  49. data/lib/rubygems/vendor/net-http/lib/net/http.rb +129 -37
  50. data/lib/rubygems/vendor/net-protocol/COPYING +56 -0
  51. data/lib/rubygems/vendor/net-protocol/lib/net/protocol.rb +95 -26
  52. data/lib/rubygems/vendor/optparse/lib/optparse.rb +23 -30
  53. data/lib/rubygems/vendor/timeout/lib/timeout.rb +190 -77
  54. data/lib/rubygems/vendor/uri/lib/uri/common.rb +1 -1
  55. data/lib/rubygems/vendor/uri/lib/uri/generic.rb +15 -3
  56. data/lib/rubygems/vendor/uri/lib/uri/version.rb +1 -1
  57. data/lib/rubygems.rb +1 -1
  58. metadata +2 -2
  59. 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 %(#{diff_cmd} "#{destination}" "#{temp.path}")
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 ||= ENV["THOR_MERGE"] || "git difftool --no-index"
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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative "basic"
2
4
 
3
5
  class Bundler::Thor
@@ -1,3 +1,3 @@
1
1
  class Bundler::Thor
2
- VERSION = "1.4.0"
2
+ VERSION = "1.5.0"
3
3
  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 { |_k, c| c.hidden? }.merge(map).keys.select { |n| meth == n[0, len] }.sort
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
@@ -271,7 +271,7 @@ module Bundler::URI
271
271
  # # => #<Bundler::URI::HTTP http://example.com/foo/bar>
272
272
  #
273
273
  def self.join(*str)
274
- DEFAULT_PARSER.join(*str)
274
+ PARSER.join(*str)
275
275
  end
276
276
 
277
277
  #
@@ -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:V3ry_S3nsit1ve@my.example.com"
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
- while i = base_path.index('..')
1025
- base_path.slice!(i - 1, 2)
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
@@ -1,6 +1,6 @@
1
1
  module Bundler::URI
2
2
  # :stopdoc:
3
- VERSION = '1.1.1'.freeze
3
+ VERSION = '1.1.2'.freeze
4
4
  VERSION_CODE = VERSION.split('.').map{|s| s.rjust(2, '0')}.join.freeze
5
5
  # :startdoc:
6
6
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: false
2
2
 
3
3
  module Bundler
4
- VERSION = "4.0.21".freeze
4
+ VERSION = "4.0.23".freeze
5
5
 
6
6
  def self.bundler_major_version
7
7
  @bundler_major_version ||= gem_version.segments.first
@@ -119,9 +119,7 @@ module Bundler
119
119
  def bin_path
120
120
  @bin_path ||= begin
121
121
  path = Bundler.settings[:bin] || "bin"
122
- path = Pathname.new(path).expand_path(root).expand_path
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
 
@@ -204,7 +204,8 @@ class Gem::Ext::Builder
204
204
  when /Cargo.toml/ then
205
205
  Gem::Ext::CargoBuilder.new
206
206
  else
207
- build_error("No builder for extension '#{extension}'")
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
- builder = builder_for(extension)
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
 
@@ -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
@@ -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 mode from the tarball. In this case we need to mask it down
486
- # to fit into 2^16 bits (the maximum value for a mode in CRuby since it
487
- # gets put into an unsigned short).
488
- (mode & ((1 << 16) - 1))
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
 
@@ -272,6 +272,7 @@ class Gem::Uninstaller
272
272
 
273
273
  safe_delete { rm_r full_gem_path, exclusions: exclusions }
274
274
  safe_delete { FileUtils.rm_r spec.extension_dir }
275
+ safe_delete { FileUtils.rm_f spec.build_info_file }
275
276
 
276
277
  old_platform_name = spec.original_name
277
278
 
@@ -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.hostname
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'] ||= host if 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.sub!(/:.*/m, '')
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