Skip to content

Commit db04887

Browse files

File tree

10 files changed

+192
-7
lines changed

10 files changed

+192
-7
lines changed

lib/rubygems/commands/query_command.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ def output_versions output, versions
226226
end
227227
end
228228

229-
output << make_entry(matching_tuples, platforms)
229+
output << clean_text(make_entry(matching_tuples, platforms))
230230
end
231231
end
232232

@@ -353,7 +353,8 @@ def spec_platforms entry, platforms
353353
end
354354

355355
def spec_summary entry, spec
356-
entry << "\n\n" << format_text(spec.summary, 68, 4)
356+
summary = truncate_text(spec.summary, "the summary for #{spec.full_name}")
357+
entry << "\n\n" << format_text(summary, 68, 4)
357358
end
358359

359360
end

lib/rubygems/installer.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,11 @@ def verify_gem_home(unpack = false) # :nodoc:
697697
unpack or File.writable?(gem_home)
698698
end
699699

700+
def verify_spec_name
701+
return if spec.name =~ Gem::Specification::VALID_NAME_PATTERN
702+
raise Gem::InstallError, "#{spec} has an invalid name"
703+
end
704+
700705
##
701706
# Return the text for an application file.
702707

@@ -823,6 +828,8 @@ def pre_install_checks
823828

824829
ensure_loadable_spec
825830

831+
verify_spec_name
832+
826833
if options[:install_as_default]
827834
Gem.ensure_default_gem_subdirectories gem_home
828835
else

lib/rubygems/remote_fetcher.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ def api_endpoint(uri)
110110
else
111111
target = res.target.to_s.strip
112112

113-
if /\.#{Regexp.quote(host)}\z/ =~ target
113+
if URI("http://" + target).host.end_with?(".#{host}")
114114
return URI.parse "#{uri.scheme}://#{target}#{uri.path}"
115115
end
116116

lib/rubygems/specification.rb

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ class Gem::Specification < Gem::BasicSpecification
108108

109109
private_constant :LOAD_CACHE if defined? private_constant
110110

111+
VALID_NAME_PATTERN = /\A[a-zA-Z0-9\.\-\_]+\z/ # :nodoc:
112+
111113
# :startdoc:
112114

113115
##
@@ -2668,9 +2670,15 @@ def validate packaging = true
26682670
end
26692671
end
26702672

2671-
unless String === name then
2673+
if !name.is_a?(String) then
2674+
raise Gem::InvalidSpecificationException,
2675+
"invalid value for attribute name: \"#{name.inspect}\" must be a string"
2676+
elsif name !~ /[a-zA-Z]/ then
2677+
raise Gem::InvalidSpecificationException,
2678+
"invalid value for attribute name: #{name.dump} must include at least one letter"
2679+
elsif name !~ VALID_NAME_PATTERN then
26722680
raise Gem::InvalidSpecificationException,
2673-
"invalid value for attribute name: \"#{name.inspect}\""
2681+
"invalid value for attribute name: #{name.dump} can only include letters, numbers, dashes, and underscores"
26742682
end
26752683

26762684
if raw_require_paths.empty? then

lib/rubygems/text.rb

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,26 @@
66

77
module Gem::Text
88

9+
##
10+
# Remove any non-printable characters and make the text suitable for
11+
# printing.
12+
def clean_text(text)
13+
text.gsub(/[\000-\b\v-\f\016-\037\177]/, ".".freeze)
14+
end
15+
16+
def truncate_text(text, description, max_length = 100_000)
17+
raise ArgumentError, "max_length must be positive" unless max_length > 0
18+
return text if text.size <= max_length
19+
"Truncating #{description} to #{max_length.to_s.reverse.gsub(/...(?=.)/,'\&,').reverse} characters:\n" + text[0, max_length]
20+
end
21+
922
##
1023
# Wraps +text+ to +wrap+ characters and optionally indents by +indent+
1124
# characters
1225

1326
def format_text(text, wrap, indent=0)
1427
result = []
15-
work = text.dup
28+
work = clean_text(text)
1629

1730
while work.length > wrap do
1831
if work =~ /^(.{0,#{wrap}})[ \n]/ then

test/rubygems/test_gem_commands_query_command.rb

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,86 @@ def test_execute_details
116116
This is a lot of text. This is a lot of text. This is a lot of text.
117117
This is a lot of text.
118118
119+
pl (1)
120+
Platform: i386-linux
121+
Author: A User
122+
Homepage: http://example.com
123+
124+
this is a summary
125+
EOF
126+
127+
assert_equal expected, @ui.output
128+
assert_equal '', @ui.error
129+
end
130+
131+
def test_execute_details_cleans_text
132+
spec_fetcher do |fetcher|
133+
fetcher.spec 'a', 2 do |s|
134+
s.summary = 'This is a lot of text. ' * 4
135+
s.authors = ["Abraham Lincoln \x01", "\x02 Hirohito"]
136+
s.homepage = "http://a.example.com/\x03"
137+
end
138+
139+
fetcher.legacy_platform
140+
end
141+
142+
@cmd.handle_options %w[-r -d]
143+
144+
use_ui @ui do
145+
@cmd.execute
146+
end
147+
148+
expected = <<-EOF
149+
150+
*** REMOTE GEMS ***
151+
152+
a (2)
153+
Authors: Abraham Lincoln ., . Hirohito
154+
Homepage: http://a.example.com/.
155+
156+
This is a lot of text. This is a lot of text. This is a lot of text.
157+
This is a lot of text.
158+
159+
pl (1)
160+
Platform: i386-linux
161+
Author: A User
162+
Homepage: http://example.com
163+
164+
this is a summary
165+
EOF
166+
167+
assert_equal expected, @ui.output
168+
assert_equal '', @ui.error
169+
end
170+
171+
def test_execute_details_truncates_summary
172+
spec_fetcher do |fetcher|
173+
fetcher.spec 'a', 2 do |s|
174+
s.summary = 'This is a lot of text. ' * 10_000
175+
s.authors = ["Abraham Lincoln \x01", "\x02 Hirohito"]
176+
s.homepage = "http://a.example.com/\x03"
177+
end
178+
179+
fetcher.legacy_platform
180+
end
181+
182+
@cmd.handle_options %w[-r -d]
183+
184+
use_ui @ui do
185+
@cmd.execute
186+
end
187+
188+
expected = <<-EOF
189+
190+
*** REMOTE GEMS ***
191+
192+
a (2)
193+
Authors: Abraham Lincoln ., . Hirohito
194+
Homepage: http://a.example.com/.
195+
196+
Truncating the summary for a-2 to 100,000 characters:
197+
#{" This is a lot of text. This is a lot of text. This is a lot of text.\n" * 1449} This is a lot of te
198+
119199
pl (1)
120200
Platform: i386-linux
121201
Author: A User

test/rubygems/test_gem_installer.rb

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1448,6 +1448,26 @@ def test_pre_install_checks_wrong_rubygems_version
14481448
end
14491449
end
14501450

1451+
def test_pre_install_checks_malicious_name
1452+
spec = util_spec '../malicious', '1'
1453+
def spec.full_name # so the spec is buildable
1454+
"malicious-1"
1455+
end
1456+
def spec.validate; end
1457+
1458+
util_build_gem spec
1459+
1460+
gem = File.join(@gemhome, 'cache', spec.file_name)
1461+
1462+
use_ui @ui do
1463+
@installer = Gem::Installer.at gem
1464+
e = assert_raises Gem::InstallError do
1465+
@installer.pre_install_checks
1466+
end
1467+
assert_equal '#<Gem::Specification name=../malicious version=1> has an invalid name', e.message
1468+
end
1469+
end
1470+
14511471
def test_shebang
14521472
util_make_exec @spec, "#!/usr/bin/ruby"
14531473

test/rubygems/test_gem_remote_fetcher.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,21 @@ def test_api_endpoint_ignores_trans_domain_values_that_end_with_original
241241
dns.verify
242242
end
243243

244+
def test_api_endpoint_ignores_trans_domain_values_that_end_with_original_in_path
245+
uri = URI.parse "http://example.com/foo"
246+
target = MiniTest::Mock.new
247+
target.expect :target, "evil.com/a.example.com"
248+
249+
dns = MiniTest::Mock.new
250+
dns.expect :getresource, target, [String, Object]
251+
252+
fetch = Gem::RemoteFetcher.new nil, dns
253+
assert_equal URI.parse("http://example.com/foo"), fetch.api_endpoint(uri)
254+
255+
target.verify
256+
dns.verify
257+
end
258+
244259
def test_api_endpoint_timeout_warning
245260
uri = URI.parse "http://gems.example.com/foo"
246261

test/rubygems/test_gem_specification.rb

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2985,7 +2985,37 @@ def test_validate_name
29852985
@a1.validate
29862986
end
29872987

2988-
assert_equal 'invalid value for attribute name: ":json"', e.message
2988+
assert_equal 'invalid value for attribute name: ":json" must be a string', e.message
2989+
2990+
@a1.name = []
2991+
e = assert_raises Gem::InvalidSpecificationException do
2992+
@a1.validate
2993+
end
2994+
assert_equal "invalid value for attribute name: \"[]\" must be a string", e.message
2995+
2996+
@a1.name = ""
2997+
e = assert_raises Gem::InvalidSpecificationException do
2998+
@a1.validate
2999+
end
3000+
assert_equal "invalid value for attribute name: \"\" must include at least one letter", e.message
3001+
3002+
@a1.name = "12345"
3003+
e = assert_raises Gem::InvalidSpecificationException do
3004+
@a1.validate
3005+
end
3006+
assert_equal "invalid value for attribute name: \"12345\" must include at least one letter", e.message
3007+
3008+
@a1.name = "../malicious"
3009+
e = assert_raises Gem::InvalidSpecificationException do
3010+
@a1.validate
3011+
end
3012+
assert_equal "invalid value for attribute name: \"../malicious\" can only include letters, numbers, dashes, and underscores", e.message
3013+
3014+
@a1.name = "\ba\t"
3015+
e = assert_raises Gem::InvalidSpecificationException do
3016+
@a1.validate
3017+
end
3018+
assert_equal "invalid value for attribute name: \"\\ba\\t\" can only include letters, numbers, dashes, and underscores", e.message
29893019
end
29903020

29913021
def test_validate_non_nil

test/rubygems/test_gem_text.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ def test_format_text_trailing # for two spaces after .
3636
assert_equal expected, format_text(text, 78)
3737
end
3838

39+
def test_format_removes_nonprintable_characters
40+
assert_equal "text with weird .. stuff .", format_text("text with weird \x1b\x02 stuff \x7f", 40)
41+
end
42+
3943
def test_min3
4044
assert_equal 1, min3(1, 1, 1)
4145
assert_equal 1, min3(1, 1, 2)
@@ -74,4 +78,11 @@ def test_levenshtein_distance_replace
7478
assert_equal 7, levenshtein_distance("xxxxxxx", "ZenTest")
7579
assert_equal 7, levenshtein_distance("zentest", "xxxxxxx")
7680
end
81+
82+
def test_truncate_text
83+
assert_equal "abc", truncate_text("abc", "desc")
84+
assert_equal "Truncating desc to 2 characters:\nab", truncate_text("abc", "desc", 2)
85+
s = "ab" * 500_001
86+
assert_equal "Truncating desc to 1,000,000 characters:\n#{s[0, 1_000_000]}", truncate_text(s, "desc", 1_000_000)
87+
end
7788
end

0 commit comments

Comments
 (0)