Skip to content

Commit 6f3e462

Browse files
hsbtmameclaude
committed
[ruby/net-http] Validate header key and field value length in set_field
The length limits only ran in initialize_http_header, which responses bypass: each_response_header builds the response through add_field and set_field, so an oversized response header field was never bounded. Check the field value length in set_field and append_field_value, and fold the key length check into validate_field_name so set_field and initialize_http_header share one place for validating field names. ruby/net-http@ab084fdc0c Co-authored-by: Yusuke Endoh <mame@ruby-lang.org> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 8756cee commit 6f3e462

2 files changed

Lines changed: 32 additions & 8 deletions

File tree

lib/net/http/header.rb

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -193,12 +193,9 @@ def initialize_http_header(initheader) #:nodoc:
193193
warn "net/http: nil HTTP header: #{key}", uplevel: 3 if $VERBOSE
194194
else
195195
value = value.strip # raise error for invalid byte sequences
196-
if key.to_s.bytesize > MAX_KEY_LENGTH
197-
raise ArgumentError, "too long (#{key.bytesize} bytes) header: #{key[0, 30].inspect}..."
198-
end
199196
validate_field_name(key)
200197
if value.to_s.bytesize > MAX_FIELD_LENGTH
201-
raise ArgumentError, "header #{key} has too long field value: #{value.bytesize}"
198+
raise ArgumentError, "header #{key} has too long field value: #{value.bytesize} bytes (limit #{MAX_FIELD_LENGTH})"
202199
end
203200
if value.count("\r\n") > 0
204201
raise ArgumentError, "header #{key} has field value #{value.inspect}, this cannot include CR/LF"
@@ -264,14 +261,17 @@ def []=(key, val)
264261
def add_field(key, val)
265262
stringified_downcased_key = key.downcase.to_s
266263
if @header.key?(stringified_downcased_key)
267-
append_field_value(@header[stringified_downcased_key], val)
264+
append_field_value(@header[stringified_downcased_key], val, key)
268265
else
269266
set_field(key, val)
270267
end
271268
end
272269

273270
# :stopdoc:
274271
private def validate_field_name(key)
272+
if key.to_s.bytesize > MAX_KEY_LENGTH
273+
raise ArgumentError, "too long (#{key.to_s.bytesize} bytes) header: #{key.to_s[0, 30].inspect}..."
274+
end
275275
if /[\x00-\x1f\x7f:]/n.match?(key.to_s.b)
276276
raise ArgumentError, "header field name cannot include control characters or colon: #{key.to_s[0, 30].inspect}"
277277
end
@@ -282,23 +282,29 @@ def add_field(key, val)
282282
case val
283283
when Enumerable
284284
ary = []
285-
append_field_value(ary, val)
285+
append_field_value(ary, val, key)
286286
@header[key.downcase.to_s] = ary
287287
else
288288
val = val.to_s # for compatibility use to_s instead of to_str
289+
if val.bytesize > MAX_FIELD_LENGTH
290+
raise ArgumentError, "header #{key} has too long field value: #{val.bytesize} bytes (limit #{MAX_FIELD_LENGTH})"
291+
end
289292
if val.b.count("\r\n") > 0
290293
raise ArgumentError, 'header field value cannot include CR/LF'
291294
end
292295
@header[key.downcase.to_s] = [val]
293296
end
294297
end
295298

296-
private def append_field_value(ary, val)
299+
private def append_field_value(ary, val, key)
297300
case val
298301
when Enumerable
299-
val.each{|x| append_field_value(ary, x)}
302+
val.each{|x| append_field_value(ary, x, key)}
300303
else
301304
val = val.to_s
305+
if val.bytesize > MAX_FIELD_LENGTH
306+
raise ArgumentError, "header #{key} has too long field value: #{val.bytesize} bytes (limit #{MAX_FIELD_LENGTH})"
307+
end
302308
if /[\r\n]/n.match?(val.b)
303309
raise ArgumentError, 'header field value cannot include CR/LF'
304310
end

test/net/http/test_httpheader.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,24 @@ def test_ASET
8989
assert_raise(ArgumentError){ @c['foo'] = ["a\nb"] }
9090
end
9191

92+
def test_set_field_too_long_key
93+
assert_raise(ArgumentError){ @c['x' * (Net::HTTPHeader::MAX_KEY_LENGTH + 1)] = 'a' }
94+
assert_nothing_raised{ @c['x' * Net::HTTPHeader::MAX_KEY_LENGTH] = 'a' }
95+
end
96+
97+
def test_set_field_too_long_value
98+
long = 'a' * (Net::HTTPHeader::MAX_FIELD_LENGTH + 1)
99+
assert_raise(ArgumentError){ @c['foo'] = long }
100+
assert_raise(ArgumentError){ @c['foo'] = [long] }
101+
assert_raise(ArgumentError){ @c.add_field 'foo', long }
102+
103+
# the error message names the key and the limit on every path
104+
@c['foo'] = 'ok'
105+
e = assert_raise(ArgumentError){ @c.add_field 'foo', long }
106+
assert_match(/foo/, e.message)
107+
assert_match(/#{Net::HTTPHeader::MAX_FIELD_LENGTH}/, e.message)
108+
end
109+
92110
def test_AREF
93111
@c['My-Header'] = 'test string'
94112
assert_equal 'test string', @c['my-header']

0 commit comments

Comments
 (0)