Skip to content

Commit 9714f75

Browse files
committed
* lib/webrick/accesslog.rb : Escape needed.
* lib/webrick/httpstatus.rb : ditto. * lib/webrick/httprequest.rb : ditto. * lib/webrick/httputils.rb : ditto. * test/webrick/test_cgi.rb (TestWEBrickCGI::test_bad_): Test for it. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@26267 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
1 parent 6f6686e commit 9714f75

File tree

6 files changed

+75
-22
lines changed

6 files changed

+75
-22
lines changed

ChangeLog

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
Sun Jan 10 17:25:24 2010 Nobuyoshi Nakada <[email protected]>
2+
3+
* lib/webrick/accesslog.rb : Escape needed.
4+
5+
* lib/webrick/httpstatus.rb : ditto.
6+
7+
* lib/webrick/httprequest.rb : ditto.
8+
9+
* lib/webrick/httputils.rb : ditto.
10+
11+
* test/webrick/test_cgi.rb (TestWEBrickCGI::test_bad_): Test for it.
12+
113
Sun Jan 10 04:54:36 2010 Nobuyoshi Nakada <[email protected]>
214

315
* class.c (rb_define_class): raise TypeError same as class

lib/webrick/accesslog.rb

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,23 @@ def format(format_string, params)
5353
when ?e, ?i, ?n, ?o
5454
raise AccessLogError,
5555
"parameter is required for \"#{spec}\"" unless param
56-
params[spec][param] || "-"
56+
param = params[spec][param] ? escape(param) : "-"
5757
when ?t
5858
params[spec].strftime(param || CLF_TIME_FORMAT)
5959
when ?%
6060
"%"
6161
else
62-
params[spec]
62+
escape(params[spec].to_s)
6363
end
6464
}
6565
end
66+
67+
def escape(data)
68+
if data.tainted?
69+
data.gsub(/[[:cntrl:]\\]+/) {$&.dump[1...-1]}.untaint
70+
else
71+
data
72+
end
73+
end
6674
end
6775
end

lib/webrick/httprequest.rb

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -266,11 +266,7 @@ def read_header(socket)
266266
@raw_header << line
267267
end
268268
end
269-
begin
270-
@header = HTTPUtils::parse_header(@raw_header.join)
271-
rescue => ex
272-
raise HTTPStatus::BadRequest, ex.message
273-
end
269+
@header = HTTPUtils::parse_header(@raw_header.join)
274270
end
275271

276272
def parse_uri(str, scheme="http")

lib/webrick/httpstatus.rb

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,17 @@ module WEBrick
1212

1313
module HTTPStatus
1414

15-
class Status < StandardError; end
15+
class Status < StandardError
16+
def initialize(message, *rest)
17+
super(AccessLog.escape(message), *rest)
18+
end
19+
class << self
20+
attr_reader :code, :reason_phrase
21+
end
22+
def code() self::class::code end
23+
def reason_phrase() self::class::reason_phrase end
24+
alias to_i code
25+
end
1626
class Info < Status; end
1727
class Success < Status; end
1828
class Redirect < Status; end
@@ -68,6 +78,7 @@ class EOFError < StandardError; end
6878
CodeToError = {}
6979

7080
StatusMessage.each{|code, message|
81+
message.freeze
7182
var_name = message.gsub(/[ \-]/,'_').upcase
7283
err_name = message.gsub(/[ \-]/,'')
7384

@@ -79,18 +90,12 @@ class EOFError < StandardError; end
7990
when 500...600; parent = ServerError
8091
end
8192

82-
eval %-
83-
RC_#{var_name} = #{code}
84-
class #{err_name} < #{parent}
85-
def self.code() RC_#{var_name} end
86-
def self.reason_phrase() StatusMessage[code] end
87-
def code() self::class::code end
88-
def reason_phrase() self::class::reason_phrase end
89-
alias to_i code
90-
end
91-
-
92-
93-
CodeToError[code] = const_get(err_name)
93+
const_set("RC_#{var_name}", code)
94+
err_class = Class.new(parent)
95+
err_class.instance_variable_set(:@code, code)
96+
err_class.instance_variable_set(:@reason_phrase, message)
97+
const_set(err_name, err_class)
98+
CodeToError[code] = err_class
9499
}
95100

96101
def reason_phrase(code)

lib/webrick/httputils.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,11 +129,11 @@ def parse_header(raw)
129129
when /^\s+(.*?)\s*\z/om
130130
value = $1
131131
unless field
132-
raise "bad header '#{line.inspect}'."
132+
raise HTTPStatus::BadRequest, "bad header '#{line}'."
133133
end
134134
header[field][-1] << " " << value
135135
else
136-
raise "bad header '#{line.inspect}'."
136+
raise HTTPStatus::BadRequest, "bad header '#{line}'."
137137
end
138138
}
139139
header.each{|key, values|

test/webrick/test_cgi.rb

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,4 +98,36 @@ def test_bad_request
9898
end
9999
}
100100
end
101+
102+
CtrlSeq = [0x7f, *(1..31)].pack("C*").gsub(/\s+/, '')
103+
CtrlPat = /#{Regexp.quote(CtrlSeq)}/o
104+
DumpPat = /#{Regexp.quote(CtrlSeq.dump[1...-1])}/o
105+
106+
def test_bad_uri
107+
start_cgi_server{|server, addr, port, log|
108+
res = TCPSocket.open(addr, port) {|sock|
109+
sock << "GET /#{CtrlSeq}#{CRLF}#{CRLF}"
110+
sock.close_write
111+
sock.read
112+
}
113+
assert_match(%r{\AHTTP/\d.\d 400 Bad Request}, res)
114+
s = log.call.each_line.grep(/ERROR bad URI/)[0]
115+
assert_match(DumpPat, s)
116+
assert_not_match(CtrlPat, s)
117+
}
118+
end
119+
120+
def test_bad_header
121+
start_cgi_server{|server, addr, port, log|
122+
res = TCPSocket.open(addr, port) {|sock|
123+
sock << "GET / HTTP/1.0#{CRLF}#{CtrlSeq}#{CRLF}#{CRLF}"
124+
sock.close_write
125+
sock.read
126+
}
127+
assert_match(%r{\AHTTP/\d.\d 400 Bad Request}, res)
128+
s = log.call.each_line.grep(/ERROR bad header/)[0]
129+
assert_match(DumpPat, s)
130+
assert_not_match(CtrlPat, s)
131+
}
132+
end
101133
end

0 commit comments

Comments
 (0)