1require "test/unit" 2require_relative "utils.rb" 3require "webrick" 4require "stringio" 5 6class WEBrick::TestFileHandler < Test::Unit::TestCase 7 def default_file_handler(filename) 8 klass = WEBrick::HTTPServlet::DefaultFileHandler 9 klass.new(WEBrick::Config::HTTP, filename) 10 end 11 12 def windows? 13 File.directory?("\\") 14 end 15 16 def get_res_body(res) 17 if defined? res.body.read 18 res.body.read 19 else 20 res.body 21 end 22 end 23 24 def make_range_request(range_spec) 25 msg = <<-END_OF_REQUEST 26 GET / HTTP/1.0 27 Range: #{range_spec} 28 29 END_OF_REQUEST 30 return StringIO.new(msg.gsub(/^ {6}/, "")) 31 end 32 33 def make_range_response(file, range_spec) 34 req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP) 35 req.parse(make_range_request(range_spec)) 36 res = WEBrick::HTTPResponse.new(WEBrick::Config::HTTP) 37 size = File.size(file) 38 handler = default_file_handler(file) 39 handler.make_partial_content(req, res, file, size) 40 return res 41 end 42 43 def test_make_partial_content 44 filename = __FILE__ 45 filesize = File.size(filename) 46 47 res = make_range_response(filename, "bytes=#{filesize-100}-") 48 assert_match(%r{^text/plain}, res["content-type"]) 49 assert_equal(get_res_body(res).size, 100) 50 51 res = make_range_response(filename, "bytes=-100") 52 assert_match(%r{^text/plain}, res["content-type"]) 53 assert_equal(get_res_body(res).size, 100) 54 55 res = make_range_response(filename, "bytes=0-99") 56 assert_match(%r{^text/plain}, res["content-type"]) 57 assert_equal(get_res_body(res).size, 100) 58 59 res = make_range_response(filename, "bytes=100-199") 60 assert_match(%r{^text/plain}, res["content-type"]) 61 assert_equal(get_res_body(res).size, 100) 62 63 res = make_range_response(filename, "bytes=0-0") 64 assert_match(%r{^text/plain}, res["content-type"]) 65 assert_equal(get_res_body(res).size, 1) 66 67 res = make_range_response(filename, "bytes=-1") 68 assert_match(%r{^text/plain}, res["content-type"]) 69 assert_equal(get_res_body(res).size, 1) 70 71 res = make_range_response(filename, "bytes=0-0, -2") 72 assert_match(%r{^multipart/byteranges}, res["content-type"]) 73 end 74 75 def test_filehandler 76 config = { :DocumentRoot => File.dirname(__FILE__), } 77 this_file = File.basename(__FILE__) 78 filesize = File.size(__FILE__) 79 this_data = File.open(__FILE__, "rb") {|f| f.read} 80 range = nil 81 bug2593 = '[ruby-dev:40030]' 82 83 TestWEBrick.start_httpserver(config) do |server, addr, port, log| 84 http = Net::HTTP.new(addr, port) 85 req = Net::HTTP::Get.new("/") 86 http.request(req){|res| 87 assert_equal("200", res.code, log.call) 88 assert_equal("text/html", res.content_type, log.call) 89 assert_match(/HREF="#{this_file}"/, res.body, log.call) 90 } 91 req = Net::HTTP::Get.new("/#{this_file}") 92 http.request(req){|res| 93 assert_equal("200", res.code, log.call) 94 assert_equal("text/plain", res.content_type, log.call) 95 assert_equal(File.read(__FILE__), res.body, log.call) 96 } 97 98 req = Net::HTTP::Get.new("/#{this_file}", "range"=>"bytes=#{filesize-100}-") 99 http.request(req){|res| 100 assert_equal("206", res.code, log.call) 101 assert_equal("text/plain", res.content_type, log.call) 102 assert_nothing_raised(bug2593) {range = res.content_range} 103 assert_equal((filesize-100)..(filesize-1), range, log.call) 104 assert_equal(this_data[-100..-1], res.body, log.call) 105 } 106 107 req = Net::HTTP::Get.new("/#{this_file}", "range"=>"bytes=-100") 108 http.request(req){|res| 109 assert_equal("206", res.code, log.call) 110 assert_equal("text/plain", res.content_type, log.call) 111 assert_nothing_raised(bug2593) {range = res.content_range} 112 assert_equal((filesize-100)..(filesize-1), range, log.call) 113 assert_equal(this_data[-100..-1], res.body, log.call) 114 } 115 116 req = Net::HTTP::Get.new("/#{this_file}", "range"=>"bytes=0-99") 117 http.request(req){|res| 118 assert_equal("206", res.code, log.call) 119 assert_equal("text/plain", res.content_type, log.call) 120 assert_nothing_raised(bug2593) {range = res.content_range} 121 assert_equal(0..99, range, log.call) 122 assert_equal(this_data[0..99], res.body, log.call) 123 } 124 125 req = Net::HTTP::Get.new("/#{this_file}", "range"=>"bytes=100-199") 126 http.request(req){|res| 127 assert_equal("206", res.code, log.call) 128 assert_equal("text/plain", res.content_type, log.call) 129 assert_nothing_raised(bug2593) {range = res.content_range} 130 assert_equal(100..199, range, log.call) 131 assert_equal(this_data[100..199], res.body, log.call) 132 } 133 134 req = Net::HTTP::Get.new("/#{this_file}", "range"=>"bytes=0-0") 135 http.request(req){|res| 136 assert_equal("206", res.code, log.call) 137 assert_equal("text/plain", res.content_type, log.call) 138 assert_nothing_raised(bug2593) {range = res.content_range} 139 assert_equal(0..0, range, log.call) 140 assert_equal(this_data[0..0], res.body, log.call) 141 } 142 143 req = Net::HTTP::Get.new("/#{this_file}", "range"=>"bytes=-1") 144 http.request(req){|res| 145 assert_equal("206", res.code, log.call) 146 assert_equal("text/plain", res.content_type, log.call) 147 assert_nothing_raised(bug2593) {range = res.content_range} 148 assert_equal((filesize-1)..(filesize-1), range, log.call) 149 assert_equal(this_data[-1, 1], res.body, log.call) 150 } 151 152 req = Net::HTTP::Get.new("/#{this_file}", "range"=>"bytes=0-0, -2") 153 http.request(req){|res| 154 assert_equal("206", res.code, log.call) 155 assert_equal("multipart/byteranges", res.content_type, log.call) 156 } 157 158 end 159 end 160 161 def test_non_disclosure_name 162 config = { :DocumentRoot => File.dirname(__FILE__), } 163 this_file = File.basename(__FILE__) 164 TestWEBrick.start_httpserver(config) do |server, addr, port, log| 165 http = Net::HTTP.new(addr, port) 166 doc_root_opts = server[:DocumentRootOptions] 167 doc_root_opts[:NondisclosureName] = %w(.ht* *~ test_*) 168 req = Net::HTTP::Get.new("/") 169 http.request(req){|res| 170 assert_equal("200", res.code, log.call) 171 assert_equal("text/html", res.content_type, log.call) 172 assert_no_match(/HREF="#{File.basename(__FILE__)}"/, res.body) 173 } 174 req = Net::HTTP::Get.new("/#{this_file}") 175 http.request(req){|res| 176 assert_equal("404", res.code, log.call) 177 } 178 doc_root_opts[:NondisclosureName] = %w(.ht* *~ TEST_*) 179 http.request(req){|res| 180 assert_equal("404", res.code, log.call) 181 } 182 end 183 end 184 185 def test_directory_traversal 186 config = { :DocumentRoot => File.dirname(__FILE__), } 187 this_file = File.basename(__FILE__) 188 TestWEBrick.start_httpserver(config) do |server, addr, port, log| 189 http = Net::HTTP.new(addr, port) 190 req = Net::HTTP::Get.new("/../../") 191 http.request(req){|res| assert_equal("400", res.code, log.call) } 192 req = Net::HTTP::Get.new("/..%5c../#{File.basename(__FILE__)}") 193 http.request(req){|res| assert_equal(windows? ? "200" : "404", res.code, log.call) } 194 req = Net::HTTP::Get.new("/..%5c..%5cruby.c") 195 http.request(req){|res| assert_equal("404", res.code, log.call) } 196 end 197 end 198 199 def test_unwise_in_path 200 if windows? 201 config = { :DocumentRoot => File.dirname(__FILE__), } 202 this_file = File.basename(__FILE__) 203 TestWEBrick.start_httpserver(config) do |server, addr, port, log| 204 http = Net::HTTP.new(addr, port) 205 req = Net::HTTP::Get.new("/..%5c..") 206 http.request(req){|res| assert_equal("301", res.code, log.call) } 207 end 208 end 209 end 210 211 def test_short_filename 212 config = { 213 :CGIInterpreter => TestWEBrick::RubyBin, 214 :DocumentRoot => File.dirname(__FILE__), 215 :CGIPathEnv => ENV['PATH'], 216 } 217 TestWEBrick.start_httpserver(config) do |server, addr, port, log| 218 http = Net::HTTP.new(addr, port) 219 if windows? 220 fname = nil 221 Dir.chdir(config[:DocumentRoot]) do 222 fname = IO.popen("dir /x webrick_long_filename.cgi", "r").read.match(/\s(w.+?cgi)\s/i)[1].downcase 223 end 224 else 225 fname = "webric~1.cgi" 226 end 227 req = Net::HTTP::Get.new("/#{fname}/test") 228 http.request(req) do |res| 229 if windows? 230 assert_equal("200", res.code, log.call) 231 assert_equal("/test", res.body, log.call) 232 else 233 assert_equal("404", res.code, log.call) 234 end 235 end 236 237 req = Net::HTTP::Get.new("/.htaccess") 238 http.request(req) {|res| assert_equal("404", res.code, log.call) } 239 req = Net::HTTP::Get.new("/htacce~1") 240 http.request(req) {|res| assert_equal("404", res.code, log.call) } 241 req = Net::HTTP::Get.new("/HTACCE~1") 242 http.request(req) {|res| assert_equal("404", res.code, log.call) } 243 end 244 end 245 246 def test_script_disclosure 247 config = { 248 :CGIInterpreter => TestWEBrick::RubyBin, 249 :DocumentRoot => File.dirname(__FILE__), 250 :CGIPathEnv => ENV['PATH'], 251 :RequestCallback => Proc.new{|req, res| 252 def req.meta_vars 253 meta = super 254 meta["RUBYLIB"] = $:.join(File::PATH_SEPARATOR) 255 meta[RbConfig::CONFIG['LIBPATHENV']] = ENV[RbConfig::CONFIG['LIBPATHENV']] if RbConfig::CONFIG['LIBPATHENV'] 256 return meta 257 end 258 }, 259 } 260 TestWEBrick.start_httpserver(config) do |server, addr, port, log| 261 http = Net::HTTP.new(addr, port) 262 263 req = Net::HTTP::Get.new("/webrick.cgi/test") 264 http.request(req) do |res| 265 assert_equal("200", res.code, log.call) 266 assert_equal("/test", res.body, log.call) 267 end 268 269 response_assertion = Proc.new do |res| 270 if windows? 271 assert_equal("200", res.code, log.call) 272 assert_equal("/test", res.body, log.call) 273 else 274 assert_equal("404", res.code, log.call) 275 end 276 end 277 req = Net::HTTP::Get.new("/webrick.cgi%20/test") 278 http.request(req, &response_assertion) 279 req = Net::HTTP::Get.new("/webrick.cgi./test") 280 http.request(req, &response_assertion) 281 req = Net::HTTP::Get.new("/webrick.cgi::$DATA/test") 282 http.request(req, &response_assertion) 283 end 284 end 285end 286