rack 1.3.10 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rack might be problematic. Click here for more details.
- data/COPYING +1 -1
- data/KNOWN-ISSUES +0 -9
- data/README.rdoc +4 -118
- data/Rakefile +15 -0
- data/SPEC +3 -5
- data/lib/rack.rb +0 -12
- data/lib/rack/auth/abstract/request.rb +1 -5
- data/lib/rack/auth/basic.rb +1 -1
- data/lib/rack/auth/digest/nonce.rb +1 -1
- data/lib/rack/backports/uri/common_18.rb +28 -14
- data/lib/rack/backports/uri/common_192.rb +17 -14
- data/lib/rack/body_proxy.rb +0 -10
- data/lib/rack/builder.rb +26 -18
- data/lib/rack/cascade.rb +1 -12
- data/lib/rack/chunked.rb +2 -0
- data/lib/rack/content_type.rb +7 -1
- data/lib/rack/deflater.rb +1 -5
- data/lib/rack/directory.rb +5 -1
- data/lib/rack/file.rb +26 -9
- data/lib/rack/handler.rb +2 -2
- data/lib/rack/head.rb +0 -1
- data/lib/rack/lint.rb +3 -5
- data/lib/rack/methodoverride.rb +10 -4
- data/lib/rack/mime.rb +606 -171
- data/lib/rack/mock.rb +2 -1
- data/lib/rack/multipart.rb +2 -2
- data/lib/rack/multipart/parser.rb +3 -10
- data/lib/rack/reloader.rb +1 -1
- data/lib/rack/request.rb +45 -13
- data/lib/rack/response.rb +15 -14
- data/lib/rack/sendfile.rb +8 -6
- data/lib/rack/server.rb +4 -30
- data/lib/rack/session/abstract/id.rb +25 -6
- data/lib/rack/session/cookie.rb +12 -16
- data/lib/rack/static.rb +21 -8
- data/lib/rack/urlmap.rb +28 -13
- data/lib/rack/utils.rb +22 -28
- data/rack.gemspec +5 -5
- data/test/builder/end.ru +2 -0
- data/test/cgi/lighttpd.conf +1 -0
- data/test/cgi/sample_rackup.ru +1 -1
- data/test/cgi/test+directory/test+file +1 -0
- data/test/cgi/test.ru +1 -1
- data/test/gemloader.rb +6 -2
- data/test/spec_auth_basic.rb +4 -9
- data/test/spec_auth_digest.rb +3 -16
- data/test/spec_body_proxy.rb +0 -4
- data/test/spec_builder.rb +63 -20
- data/test/spec_cascade.rb +10 -13
- data/test/spec_cgi.rb +1 -1
- data/test/spec_chunked.rb +39 -12
- data/test/spec_commonlogger.rb +4 -3
- data/test/spec_conditionalget.rb +16 -12
- data/test/spec_content_length.rb +1 -1
- data/test/spec_content_type.rb +6 -0
- data/test/spec_deflater.rb +2 -2
- data/test/spec_directory.rb +12 -0
- data/test/spec_fastcgi.rb +1 -1
- data/test/spec_file.rb +58 -8
- data/test/spec_head.rb +6 -18
- data/test/spec_lint.rb +2 -2
- data/test/spec_methodoverride.rb +15 -0
- data/test/spec_mock.rb +6 -2
- data/test/spec_mongrel.rb +8 -8
- data/test/spec_multipart.rb +10 -63
- data/test/spec_request.rb +94 -21
- data/test/spec_response.rb +22 -24
- data/test/spec_sendfile.rb +3 -0
- data/test/spec_server.rb +2 -49
- data/test/spec_session_cookie.rb +58 -22
- data/test/spec_session_memcache.rb +31 -1
- data/test/spec_session_pool.rb +10 -4
- data/test/spec_static.rb +8 -0
- data/test/spec_thin.rb +2 -2
- data/test/spec_utils.rb +38 -35
- data/test/spec_webrick.rb +5 -3
- data/test/static/index.html +1 -0
- metadata +13 -18
- data/contrib/rack.png +0 -0
- data/contrib/rack.svg +0 -150
- data/lib/rack/backports/uri/common_193.rb +0 -29
- data/test/builder/line.ru +0 -1
- data/test/spec_auth.rb +0 -57
data/test/spec_cascade.rb
CHANGED
@@ -1,9 +1,14 @@
|
|
1
1
|
require 'rack/cascade'
|
2
2
|
require 'rack/file'
|
3
|
+
require 'rack/lint'
|
3
4
|
require 'rack/urlmap'
|
4
5
|
require 'rack/mock'
|
5
6
|
|
6
7
|
describe Rack::Cascade do
|
8
|
+
def cascade(*args)
|
9
|
+
Rack::Lint.new Rack::Cascade.new(*args)
|
10
|
+
end
|
11
|
+
|
7
12
|
docroot = File.expand_path(File.dirname(__FILE__))
|
8
13
|
app1 = Rack::File.new(docroot)
|
9
14
|
|
@@ -13,20 +18,20 @@ describe Rack::Cascade do
|
|
13
18
|
[200, { "Content-Type" => "text/plain"}, [""]]})
|
14
19
|
|
15
20
|
should "dispatch onward on 404 by default" do
|
16
|
-
cascade =
|
21
|
+
cascade = cascade([app1, app2, app3])
|
17
22
|
Rack::MockRequest.new(cascade).get("/cgi/test").should.be.ok
|
18
23
|
Rack::MockRequest.new(cascade).get("/foo").should.be.ok
|
19
24
|
Rack::MockRequest.new(cascade).get("/toobad").should.be.not_found
|
20
|
-
Rack::MockRequest.new(cascade).get("/cgi
|
25
|
+
Rack::MockRequest.new(cascade).get("/cgi/../..").should.be.forbidden
|
21
26
|
end
|
22
27
|
|
23
28
|
should "dispatch onward on whatever is passed" do
|
24
|
-
cascade =
|
29
|
+
cascade = cascade([app1, app2, app3], [404, 403])
|
25
30
|
Rack::MockRequest.new(cascade).get("/cgi/../bla").should.be.not_found
|
26
31
|
end
|
27
32
|
|
28
33
|
should "return 404 if empty" do
|
29
|
-
Rack::MockRequest.new(
|
34
|
+
Rack::MockRequest.new(cascade([])).get('/').should.be.not_found
|
30
35
|
end
|
31
36
|
|
32
37
|
should "append new app" do
|
@@ -37,17 +42,9 @@ describe Rack::Cascade do
|
|
37
42
|
Rack::MockRequest.new(cascade).get('/cgi/../bla').should.be.not_found
|
38
43
|
cascade << app1
|
39
44
|
Rack::MockRequest.new(cascade).get('/cgi/test').should.be.ok
|
40
|
-
Rack::MockRequest.new(cascade).get('/cgi
|
45
|
+
Rack::MockRequest.new(cascade).get('/cgi/../..').should.be.forbidden
|
41
46
|
Rack::MockRequest.new(cascade).get('/foo').should.be.not_found
|
42
47
|
cascade << app3
|
43
48
|
Rack::MockRequest.new(cascade).get('/foo').should.be.ok
|
44
49
|
end
|
45
|
-
|
46
|
-
should "close the body on cascade" do
|
47
|
-
body = StringIO.new
|
48
|
-
closer = lambda { |env| [404, {}, body] }
|
49
|
-
cascade = Rack::Cascade.new([closer, app3], [404])
|
50
|
-
Rack::MockRequest.new(cascade).get("/foo").should.be.ok
|
51
|
-
body.should.be.closed
|
52
|
-
end
|
53
50
|
end
|
data/test/spec_cgi.rb
CHANGED
data/test/spec_chunked.rb
CHANGED
@@ -1,31 +1,56 @@
|
|
1
1
|
require 'rack/chunked'
|
2
|
+
require 'rack/lint'
|
2
3
|
require 'rack/mock'
|
3
4
|
|
4
5
|
describe Rack::Chunked do
|
6
|
+
Enumerator = ::Enumerable::Enumerator unless defined?(Enumerator)
|
7
|
+
|
8
|
+
def chunked(app)
|
9
|
+
proc do |env|
|
10
|
+
app = Rack::Chunked.new(app)
|
11
|
+
Rack::Lint.new(app).call(env).tap do |response|
|
12
|
+
# we want to use body like an array, but it only has #each
|
13
|
+
response[2] = Enumerator.new(response[2]).to_a
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
5
18
|
before do
|
6
19
|
@env = Rack::MockRequest.
|
7
20
|
env_for('/', 'HTTP_VERSION' => '1.1', 'REQUEST_METHOD' => 'GET')
|
8
21
|
end
|
9
22
|
|
10
23
|
should 'chunk responses with no Content-Length' do
|
11
|
-
app = lambda { |env| [200, {}, ['Hello', ' ', 'World!']] }
|
12
|
-
response = Rack::MockResponse.new(*
|
24
|
+
app = lambda { |env| [200, {"Content-Type" => "text/plain"}, ['Hello', ' ', 'World!']] }
|
25
|
+
response = Rack::MockResponse.new(*chunked(app).call(@env))
|
13
26
|
response.headers.should.not.include 'Content-Length'
|
14
27
|
response.headers['Transfer-Encoding'].should.equal 'chunked'
|
15
28
|
response.body.should.equal "5\r\nHello\r\n1\r\n \r\n6\r\nWorld!\r\n0\r\n\r\n"
|
16
29
|
end
|
17
30
|
|
18
31
|
should 'chunks empty bodies properly' do
|
19
|
-
app = lambda { |env| [200, {}, []] }
|
20
|
-
response = Rack::MockResponse.new(*
|
32
|
+
app = lambda { |env| [200, {"Content-Type" => "text/plain"}, []] }
|
33
|
+
response = Rack::MockResponse.new(*chunked(app).call(@env))
|
21
34
|
response.headers.should.not.include 'Content-Length'
|
22
35
|
response.headers['Transfer-Encoding'].should.equal 'chunked'
|
23
36
|
response.body.should.equal "0\r\n\r\n"
|
24
37
|
end
|
25
38
|
|
39
|
+
should 'chunks encoded bodies properly' do
|
40
|
+
body = ["\uFFFEHello", " ", "World"].map {|t| t.encode("UTF-16LE") }
|
41
|
+
app = lambda { |env| [200, {"Content-Type" => "text/plain"}, body] }
|
42
|
+
response = Rack::MockResponse.new(*chunked(app).call(@env))
|
43
|
+
response.headers.should.not.include 'Content-Length'
|
44
|
+
response.headers['Transfer-Encoding'].should.equal 'chunked'
|
45
|
+
response.body.encoding.to_s.should == "ASCII-8BIT"
|
46
|
+
response.body.should.equal "c\r\n\xFE\xFFH\x00e\x00l\x00l\x00o\x00\r\n2\r\n \x00\r\na\r\nW\x00o\x00r\x00l\x00d\x00\r\n0\r\n\r\n"
|
47
|
+
end if RUBY_VERSION >= "1.9"
|
48
|
+
|
26
49
|
should 'not modify response when Content-Length header present' do
|
27
|
-
app = lambda { |env|
|
28
|
-
|
50
|
+
app = lambda { |env|
|
51
|
+
[200, {"Content-Type" => "text/plain", 'Content-Length'=>'12'}, ['Hello', ' ', 'World!']]
|
52
|
+
}
|
53
|
+
status, headers, body = chunked(app).call(@env)
|
29
54
|
status.should.equal 200
|
30
55
|
headers.should.not.include 'Transfer-Encoding'
|
31
56
|
headers.should.include 'Content-Length'
|
@@ -33,26 +58,28 @@ describe Rack::Chunked do
|
|
33
58
|
end
|
34
59
|
|
35
60
|
should 'not modify response when client is HTTP/1.0' do
|
36
|
-
app = lambda { |env| [200, {}, ['Hello', ' ', 'World!']] }
|
61
|
+
app = lambda { |env| [200, {"Content-Type" => "text/plain"}, ['Hello', ' ', 'World!']] }
|
37
62
|
@env['HTTP_VERSION'] = 'HTTP/1.0'
|
38
|
-
status, headers, body =
|
63
|
+
status, headers, body = chunked(app).call(@env)
|
39
64
|
status.should.equal 200
|
40
65
|
headers.should.not.include 'Transfer-Encoding'
|
41
66
|
body.join.should.equal 'Hello World!'
|
42
67
|
end
|
43
68
|
|
44
69
|
should 'not modify response when Transfer-Encoding header already present' do
|
45
|
-
app = lambda { |env|
|
46
|
-
|
70
|
+
app = lambda { |env|
|
71
|
+
[200, {"Content-Type" => "text/plain", 'Transfer-Encoding' => 'identity'}, ['Hello', ' ', 'World!']]
|
72
|
+
}
|
73
|
+
status, headers, body = chunked(app).call(@env)
|
47
74
|
status.should.equal 200
|
48
75
|
headers['Transfer-Encoding'].should.equal 'identity'
|
49
76
|
body.join.should.equal 'Hello World!'
|
50
77
|
end
|
51
78
|
|
52
|
-
[100, 204, 304].each do |status_code|
|
79
|
+
[100, 204, 205, 304].each do |status_code|
|
53
80
|
should "not modify response when status code is #{status_code}" do
|
54
81
|
app = lambda { |env| [status_code, {}, []] }
|
55
|
-
status, headers, _ =
|
82
|
+
status, headers, _ = chunked(app).call(@env)
|
56
83
|
status.should.equal status_code
|
57
84
|
headers.should.not.include 'Transfer-Encoding'
|
58
85
|
end
|
data/test/spec_commonlogger.rb
CHANGED
@@ -1,19 +1,20 @@
|
|
1
1
|
require 'rack/commonlogger'
|
2
|
+
require 'rack/lint'
|
2
3
|
require 'rack/mock'
|
3
4
|
|
4
5
|
describe Rack::CommonLogger do
|
5
6
|
obj = 'foobar'
|
6
7
|
length = obj.size
|
7
8
|
|
8
|
-
app = lambda { |env|
|
9
|
+
app = Rack::Lint.new lambda { |env|
|
9
10
|
[200,
|
10
11
|
{"Content-Type" => "text/html", "Content-Length" => length.to_s},
|
11
12
|
[obj]]}
|
12
|
-
app_without_length = lambda { |env|
|
13
|
+
app_without_length = Rack::Lint.new lambda { |env|
|
13
14
|
[200,
|
14
15
|
{"Content-Type" => "text/html"},
|
15
16
|
[]]}
|
16
|
-
app_with_zero_length = lambda { |env|
|
17
|
+
app_with_zero_length = Rack::Lint.new lambda { |env|
|
17
18
|
[200,
|
18
19
|
{"Content-Type" => "text/html", "Content-Length" => "0"},
|
19
20
|
[]]}
|
data/test/spec_conditionalget.rb
CHANGED
@@ -3,9 +3,13 @@ require 'rack/conditionalget'
|
|
3
3
|
require 'rack/mock'
|
4
4
|
|
5
5
|
describe Rack::ConditionalGet do
|
6
|
+
def conditional_get(app)
|
7
|
+
Rack::Lint.new Rack::ConditionalGet.new(app)
|
8
|
+
end
|
9
|
+
|
6
10
|
should "set a 304 status and truncate body when If-Modified-Since hits" do
|
7
11
|
timestamp = Time.now.httpdate
|
8
|
-
app =
|
12
|
+
app = conditional_get(lambda { |env|
|
9
13
|
[200, {'Last-Modified'=>timestamp}, ['TEST']] })
|
10
14
|
|
11
15
|
response = Rack::MockRequest.new(app).
|
@@ -16,7 +20,7 @@ describe Rack::ConditionalGet do
|
|
16
20
|
end
|
17
21
|
|
18
22
|
should "set a 304 status and truncate body when If-Modified-Since hits and is higher than current time" do
|
19
|
-
app =
|
23
|
+
app = conditional_get(lambda { |env|
|
20
24
|
[200, {'Last-Modified'=>(Time.now - 3600).httpdate}, ['TEST']] })
|
21
25
|
|
22
26
|
response = Rack::MockRequest.new(app).
|
@@ -27,7 +31,7 @@ describe Rack::ConditionalGet do
|
|
27
31
|
end
|
28
32
|
|
29
33
|
should "set a 304 status and truncate body when If-None-Match hits" do
|
30
|
-
app =
|
34
|
+
app = conditional_get(lambda { |env|
|
31
35
|
[200, {'Etag'=>'1234'}, ['TEST']] })
|
32
36
|
|
33
37
|
response = Rack::MockRequest.new(app).
|
@@ -39,8 +43,8 @@ describe Rack::ConditionalGet do
|
|
39
43
|
|
40
44
|
should "not set a 304 status if If-Modified-Since hits but Etag does not" do
|
41
45
|
timestamp = Time.now.httpdate
|
42
|
-
app =
|
43
|
-
[200, {'Last-Modified'=>timestamp, 'Etag'=>'1234'}, ['TEST']] })
|
46
|
+
app = conditional_get(lambda { |env|
|
47
|
+
[200, {'Last-Modified'=>timestamp, 'Etag'=>'1234', 'Content-Type' => 'text/plain'}, ['TEST']] })
|
44
48
|
|
45
49
|
response = Rack::MockRequest.new(app).
|
46
50
|
get("/", 'HTTP_IF_MODIFIED_SINCE' => timestamp, 'HTTP_IF_NONE_MATCH' => '4321')
|
@@ -51,7 +55,7 @@ describe Rack::ConditionalGet do
|
|
51
55
|
|
52
56
|
should "set a 304 status and truncate body when both If-None-Match and If-Modified-Since hits" do
|
53
57
|
timestamp = Time.now.httpdate
|
54
|
-
app =
|
58
|
+
app = conditional_get(lambda { |env|
|
55
59
|
[200, {'Last-Modified'=>timestamp, 'Etag'=>'1234'}, ['TEST']] })
|
56
60
|
|
57
61
|
response = Rack::MockRequest.new(app).
|
@@ -62,8 +66,8 @@ describe Rack::ConditionalGet do
|
|
62
66
|
end
|
63
67
|
|
64
68
|
should "not affect non-GET/HEAD requests" do
|
65
|
-
app =
|
66
|
-
[200, {'Etag'=>'1234'}, ['TEST']] })
|
69
|
+
app = conditional_get(lambda { |env|
|
70
|
+
[200, {'Etag'=>'1234', 'Content-Type' => 'text/plain'}, ['TEST']] })
|
67
71
|
|
68
72
|
response = Rack::MockRequest.new(app).
|
69
73
|
post("/", 'HTTP_IF_NONE_MATCH' => '1234')
|
@@ -73,8 +77,8 @@ describe Rack::ConditionalGet do
|
|
73
77
|
end
|
74
78
|
|
75
79
|
should "not affect non-200 requests" do
|
76
|
-
app =
|
77
|
-
[302, {'Etag'=>'1234'}, ['TEST']] })
|
80
|
+
app = conditional_get(lambda { |env|
|
81
|
+
[302, {'Etag'=>'1234', 'Content-Type' => 'text/plain'}, ['TEST']] })
|
78
82
|
|
79
83
|
response = Rack::MockRequest.new(app).
|
80
84
|
get("/", 'HTTP_IF_NONE_MATCH' => '1234')
|
@@ -85,8 +89,8 @@ describe Rack::ConditionalGet do
|
|
85
89
|
|
86
90
|
should "not affect requests with malformed HTTP_IF_NONE_MATCH" do
|
87
91
|
bad_timestamp = Time.now.strftime('%Y-%m-%d %H:%M:%S %z')
|
88
|
-
app =
|
89
|
-
[200,{'Last-Modified'=>(Time.now - 3600).httpdate}, ['TEST']] })
|
92
|
+
app = conditional_get(lambda { |env|
|
93
|
+
[200,{'Last-Modified'=>(Time.now - 3600).httpdate, 'Content-Type' => 'text/plain'}, ['TEST']] })
|
90
94
|
|
91
95
|
response = Rack::MockRequest.new(app).
|
92
96
|
get("/", 'HTTP_IF_MODIFIED_SINCE' => bad_timestamp)
|
data/test/spec_content_length.rb
CHANGED
data/test/spec_content_type.rb
CHANGED
@@ -26,4 +26,10 @@ describe Rack::ContentType do
|
|
26
26
|
headers.to_a.select { |k,v| k.downcase == "content-type" }.
|
27
27
|
should.equal [["CONTENT-Type","foo/bar"]]
|
28
28
|
end
|
29
|
+
|
30
|
+
should "not set Content-Type on 304 responses" do
|
31
|
+
app = lambda { |env| [304, {}, []] }
|
32
|
+
response = Rack::ContentType.new(app, "text/html").call({})
|
33
|
+
response[1]['Content-Type'].should.equal nil
|
34
|
+
end
|
29
35
|
end
|
data/test/spec_deflater.rb
CHANGED
@@ -51,7 +51,7 @@ describe Rack::Deflater do
|
|
51
51
|
response[2].each { |part| buf << inflater.inflate(part) }
|
52
52
|
buf << inflater.finish
|
53
53
|
buf.delete_if { |part| part.empty? }
|
54
|
-
buf.should.equal(
|
54
|
+
buf.join.should.equal("foobar")
|
55
55
|
end
|
56
56
|
|
57
57
|
# TODO: This is really just a special case of the above...
|
@@ -104,7 +104,7 @@ describe Rack::Deflater do
|
|
104
104
|
response[2].each { |part| buf << inflater.inflate(part) }
|
105
105
|
buf << inflater.finish
|
106
106
|
buf.delete_if { |part| part.empty? }
|
107
|
-
buf.should.equal(
|
107
|
+
buf.join.should.equal("foobar")
|
108
108
|
end
|
109
109
|
|
110
110
|
should "be able to fallback to no deflation" do
|
data/test/spec_directory.rb
CHANGED
@@ -54,4 +54,16 @@ describe Rack::Directory do
|
|
54
54
|
|
55
55
|
res.should.be.not_found
|
56
56
|
end
|
57
|
+
|
58
|
+
should "uri escape path parts" do # #265, properly escape file names
|
59
|
+
mr = Rack::MockRequest.new(Rack::Lint.new(app))
|
60
|
+
|
61
|
+
res = mr.get("/cgi/test%2bdirectory")
|
62
|
+
|
63
|
+
res.should.be.ok
|
64
|
+
res.body.should =~ %r[/cgi/test%2Bdirectory/test%2Bfile]
|
65
|
+
|
66
|
+
res = mr.get("/cgi/test%2bdirectory/test%2bfile")
|
67
|
+
res.should.be.ok
|
68
|
+
end
|
57
69
|
end
|
data/test/spec_fastcgi.rb
CHANGED
data/test/spec_file.rb
CHANGED
@@ -22,6 +22,23 @@ describe Rack::File do
|
|
22
22
|
res["Last-Modified"].should.equal File.mtime(path).httpdate
|
23
23
|
end
|
24
24
|
|
25
|
+
should "return 304 if file isn't modified since last serve" do
|
26
|
+
path = File.join(DOCROOT, "/cgi/test")
|
27
|
+
res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
|
28
|
+
get("/cgi/test", 'HTTP_IF_MODIFIED_SINCE' => File.mtime(path).httpdate)
|
29
|
+
|
30
|
+
res.status.should.equal 304
|
31
|
+
res.body.should.be.empty
|
32
|
+
end
|
33
|
+
|
34
|
+
should "return the file if it's modified since last serve" do
|
35
|
+
path = File.join(DOCROOT, "/cgi/test")
|
36
|
+
res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
|
37
|
+
get("/cgi/test", 'HTTP_IF_MODIFIED_SINCE' => (File.mtime(path) - 100).httpdate)
|
38
|
+
|
39
|
+
res.should.be.ok
|
40
|
+
end
|
41
|
+
|
25
42
|
should "serve files with URL encoded filenames" do
|
26
43
|
res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
|
27
44
|
get("/cgi/%74%65%73%74") # "/cgi/test"
|
@@ -30,9 +47,23 @@ describe Rack::File do
|
|
30
47
|
res.should =~ /ruby/
|
31
48
|
end
|
32
49
|
|
33
|
-
should "
|
50
|
+
should "allow safe directory traversal" do
|
51
|
+
req = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT)))
|
52
|
+
|
53
|
+
res = req.get('/cgi/../cgi/test')
|
54
|
+
res.should.be.successful
|
55
|
+
|
56
|
+
res = req.get('.')
|
57
|
+
res.should.be.not_found
|
58
|
+
|
59
|
+
res = req.get("test/..")
|
60
|
+
res.should.be.not_found
|
61
|
+
end
|
62
|
+
|
63
|
+
should "not allow unsafe directory traversal" do
|
34
64
|
req = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT)))
|
35
|
-
|
65
|
+
|
66
|
+
res = req.get("/../README")
|
36
67
|
res.should.be.forbidden
|
37
68
|
|
38
69
|
res = req.get("../test")
|
@@ -40,9 +71,6 @@ describe Rack::File do
|
|
40
71
|
|
41
72
|
res = req.get("..")
|
42
73
|
res.should.be.forbidden
|
43
|
-
|
44
|
-
res = req.get("test/..")
|
45
|
-
res.should.be.forbidden
|
46
74
|
end
|
47
75
|
|
48
76
|
should "allow files with .. in their name" do
|
@@ -57,13 +85,20 @@ describe Rack::File do
|
|
57
85
|
res.should.be.not_found
|
58
86
|
end
|
59
87
|
|
60
|
-
should "not allow directory traversal with encoded periods" do
|
88
|
+
should "not allow unsafe directory traversal with encoded periods" do
|
61
89
|
res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
|
62
90
|
get("/%2E%2E/README")
|
63
91
|
|
64
92
|
res.should.be.forbidden
|
65
93
|
end
|
66
94
|
|
95
|
+
should "allow safe directory traversal with encoded periods" do
|
96
|
+
res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
|
97
|
+
get("/cgi/%2E%2E/cgi/test")
|
98
|
+
|
99
|
+
res.should.be.successful
|
100
|
+
end
|
101
|
+
|
67
102
|
should "404 if it can't find the file" do
|
68
103
|
res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
|
69
104
|
get("/cgi/blubb")
|
@@ -113,10 +148,25 @@ describe Rack::File do
|
|
113
148
|
env = Rack::MockRequest.env_for("/cgi/test")
|
114
149
|
status, heads, _ = Rack::File.new(DOCROOT, 'public, max-age=38').call(env)
|
115
150
|
|
116
|
-
path = File.join(DOCROOT, "/cgi/test")
|
117
|
-
|
118
151
|
status.should.equal 200
|
119
152
|
heads['Cache-Control'].should.equal 'public, max-age=38'
|
120
153
|
end
|
121
154
|
|
155
|
+
should "only support GET and HEAD requests" do
|
156
|
+
req = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT)))
|
157
|
+
|
158
|
+
forbidden = %w[post put delete]
|
159
|
+
forbidden.each do |method|
|
160
|
+
|
161
|
+
res = req.send(method, "/cgi/test")
|
162
|
+
res.should.be.forbidden
|
163
|
+
end
|
164
|
+
|
165
|
+
allowed = %w[get head]
|
166
|
+
allowed.each do |method|
|
167
|
+
res = req.send(method, "/cgi/test")
|
168
|
+
res.should.be.successful
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
122
172
|
end
|