rack 1.4.2 → 1.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +33 -0
- data/lib/rack/auth/abstract/request.rb +5 -1
- data/lib/rack/file.rb +6 -11
- data/lib/rack/multipart/parser.rb +10 -3
- data/lib/rack/session/cookie.rb +1 -1
- data/lib/rack/utils.rb +12 -1
- data/lib/rack.rb +12 -0
- data/rack.gemspec +1 -1
- data/test/spec_auth.rb +57 -0
- data/test/spec_lock.rb +1 -1
- data/test/spec_multipart.rb +53 -0
- data/test/spec_sendfile.rb +6 -5
- data/test/spec_utils.rb +5 -0
- metadata +7 -5
data/README.rdoc
CHANGED
|
@@ -473,11 +473,44 @@ run on port 11211) and memcache-client installed.
|
|
|
473
473
|
* Rack::BodyProxy now explicitly defines #each, useful for C extensions
|
|
474
474
|
* Cookies that are not URI escaped no longer cause exceptions
|
|
475
475
|
|
|
476
|
+
* January 7th, 2013: Thirtieth public release 1.3.8
|
|
477
|
+
* Security: Prevent unbounded reads in large multipart boundaries
|
|
478
|
+
|
|
479
|
+
* January 7th, 2013: Thirty first public release 1.4.3
|
|
480
|
+
* Security: Prevent unbounded reads in large multipart boundaries
|
|
481
|
+
|
|
482
|
+
* January 13th, 2013: Thirty second public release 1.4.4, 1.3.9, 1.2.7, 1.1.5
|
|
483
|
+
* [SEC] Rack::Auth::AbstractRequest no longer symbolizes arbitrary strings
|
|
484
|
+
* Fixed erroneous test case in the 1.3.x series
|
|
485
|
+
|
|
486
|
+
* February 7th, Thirty fifth public release 1.1.6, 1.2.8, 1.3.10
|
|
487
|
+
* Fix CVE-2013-0263, timing attack against Rack::Session::Cookie
|
|
488
|
+
|
|
489
|
+
* February 7th, Thirty fifth public release 1.4.5
|
|
490
|
+
* Fix CVE-2013-0263, timing attack against Rack::Session::Cookie
|
|
491
|
+
* Fix CVE-2013-0262, symlink path traversal in Rack::File
|
|
492
|
+
|
|
493
|
+
* February 7th, Thirty fifth public release 1.5.2
|
|
494
|
+
* Fix CVE-2013-0263, timing attack against Rack::Session::Cookie
|
|
495
|
+
* Fix CVE-2013-0262, symlink path traversal in Rack::File
|
|
496
|
+
* Add various methods to Session for enhanced Rails compatibility
|
|
497
|
+
* Request#trusted_proxy? now only matches whole stirngs
|
|
498
|
+
* Add JSON cookie coder, to be default in Rack 1.6+ due to security concerns
|
|
499
|
+
* URLMap host matching in environments that don't set the Host header fixed
|
|
500
|
+
* Fix a race condition that could result in overwritten pidfiles
|
|
501
|
+
* Various documentation additions
|
|
502
|
+
|
|
476
503
|
== Contact
|
|
477
504
|
|
|
478
505
|
Please post bugs, suggestions and patches to
|
|
479
506
|
the bug tracker at <http://github.com/rack/rack/issues>.
|
|
480
507
|
|
|
508
|
+
Please post security related bugs and suggestions to the core team at
|
|
509
|
+
<https://groups.google.com/group/rack-core> or rack-core@googlegroups.com. Due
|
|
510
|
+
to wide usage of the library, it is strongly preferred that we manage timing in
|
|
511
|
+
order to provide viable patches at the time of disclosure. Your assistance in
|
|
512
|
+
this matter is greatly appreciated.
|
|
513
|
+
|
|
481
514
|
Mailing list archives are available at
|
|
482
515
|
<http://groups.google.com/group/rack-devel>.
|
|
483
516
|
|
data/lib/rack/file.rb
CHANGED
|
@@ -47,19 +47,14 @@ module Rack
|
|
|
47
47
|
@path_info = Utils.unescape(env["PATH_INFO"])
|
|
48
48
|
parts = @path_info.split SEPS
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
return fail(404, "Not Found") if depth - 1 < 0
|
|
56
|
-
depth - 1
|
|
57
|
-
else
|
|
58
|
-
depth + 1
|
|
59
|
-
end
|
|
50
|
+
clean = []
|
|
51
|
+
|
|
52
|
+
parts.each do |part|
|
|
53
|
+
next if part.empty? || part == '.'
|
|
54
|
+
part == '..' ? clean.pop : clean << part
|
|
60
55
|
end
|
|
61
56
|
|
|
62
|
-
@path = F.join(@root, *
|
|
57
|
+
@path = F.join(@root, *clean)
|
|
63
58
|
|
|
64
59
|
available = begin
|
|
65
60
|
F.file?(@path) && F.readable?(@path)
|
|
@@ -70,9 +70,16 @@ module Rack
|
|
|
70
70
|
|
|
71
71
|
def fast_forward_to_first_boundary
|
|
72
72
|
loop do
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
content = @io.read(BUFSIZE)
|
|
74
|
+
raise EOFError, "bad content body" unless content
|
|
75
|
+
@buf << content
|
|
76
|
+
|
|
77
|
+
while @buf.gsub!(/\A([^\n]*\n)/, '')
|
|
78
|
+
read_buffer = $1
|
|
79
|
+
return if read_buffer == full_boundary
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
raise EOFError, "bad content body" if Utils.bytesize(@buf) >= BUFSIZE
|
|
76
83
|
end
|
|
77
84
|
end
|
|
78
85
|
|
data/lib/rack/session/cookie.rb
CHANGED
data/lib/rack/utils.rb
CHANGED
|
@@ -5,7 +5,6 @@ require 'tempfile'
|
|
|
5
5
|
require 'rack/multipart'
|
|
6
6
|
|
|
7
7
|
major, minor, patch = RUBY_VERSION.split('.').map { |v| v.to_i }
|
|
8
|
-
ruby_engine = defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby'
|
|
9
8
|
|
|
10
9
|
if major == 1 && minor < 9
|
|
11
10
|
require 'rack/backports/uri/common_18'
|
|
@@ -343,6 +342,18 @@ module Rack
|
|
|
343
342
|
end
|
|
344
343
|
module_function :byte_ranges
|
|
345
344
|
|
|
345
|
+
# Constant time string comparison.
|
|
346
|
+
def secure_compare(a, b)
|
|
347
|
+
return false unless bytesize(a) == bytesize(b)
|
|
348
|
+
|
|
349
|
+
l = a.unpack("C*")
|
|
350
|
+
|
|
351
|
+
r, i = 0, -1
|
|
352
|
+
b.each_byte { |v| r |= v ^ l[i+=1] }
|
|
353
|
+
r == 0
|
|
354
|
+
end
|
|
355
|
+
module_function :secure_compare
|
|
356
|
+
|
|
346
357
|
# Context allows the use of a compatible middleware at different points
|
|
347
358
|
# in a request handling stack. A compatible middleware must define
|
|
348
359
|
# #context which should take the arguments env and app. The first of which
|
data/lib/rack.rb
CHANGED
|
@@ -73,6 +73,18 @@ module Rack
|
|
|
73
73
|
autoload :Params, "rack/auth/digest/params"
|
|
74
74
|
autoload :Request, "rack/auth/digest/request"
|
|
75
75
|
end
|
|
76
|
+
|
|
77
|
+
# Not all of the following schemes are "standards", but they are used often.
|
|
78
|
+
@schemes = %w[basic digest bearer mac token oauth oauth2]
|
|
79
|
+
|
|
80
|
+
def self.add_scheme scheme
|
|
81
|
+
@schemes << scheme
|
|
82
|
+
@schemes.uniq!
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def self.schemes
|
|
86
|
+
@schemes.dup
|
|
87
|
+
end
|
|
76
88
|
end
|
|
77
89
|
|
|
78
90
|
module Session
|
data/rack.gemspec
CHANGED
data/test/spec_auth.rb
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
require 'rack'
|
|
2
|
+
|
|
3
|
+
describe Rack::Auth do
|
|
4
|
+
it "should have all common authentication schemes" do
|
|
5
|
+
Rack::Auth.schemes.should.include? 'basic'
|
|
6
|
+
Rack::Auth.schemes.should.include? 'digest'
|
|
7
|
+
Rack::Auth.schemes.should.include? 'bearer'
|
|
8
|
+
Rack::Auth.schemes.should.include? 'token'
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it "should allow registration of new auth schemes" do
|
|
12
|
+
Rack::Auth.schemes.should.not.include "test"
|
|
13
|
+
Rack::Auth.add_scheme "test"
|
|
14
|
+
Rack::Auth.schemes.should.include "test"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
describe Rack::Auth::AbstractRequest do
|
|
19
|
+
it "should symbolize known auth schemes" do
|
|
20
|
+
env = Rack::MockRequest.env_for('/')
|
|
21
|
+
env['HTTP_AUTHORIZATION'] = 'Basic aXJyZXNwb25zaWJsZQ=='
|
|
22
|
+
req = Rack::Auth::AbstractRequest.new(env)
|
|
23
|
+
req.scheme.should.equal :basic
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
env['HTTP_AUTHORIZATION'] = 'Digest aXJyZXNwb25zaWJsZQ=='
|
|
27
|
+
req = Rack::Auth::AbstractRequest.new(env)
|
|
28
|
+
req.scheme.should.equal :digest
|
|
29
|
+
|
|
30
|
+
env['HTTP_AUTHORIZATION'] = 'Bearer aXJyZXNwb25zaWJsZQ=='
|
|
31
|
+
req = Rack::Auth::AbstractRequest.new(env)
|
|
32
|
+
req.scheme.should.equal :bearer
|
|
33
|
+
|
|
34
|
+
env['HTTP_AUTHORIZATION'] = 'MAC aXJyZXNwb25zaWJsZQ=='
|
|
35
|
+
req = Rack::Auth::AbstractRequest.new(env)
|
|
36
|
+
req.scheme.should.equal :mac
|
|
37
|
+
|
|
38
|
+
env['HTTP_AUTHORIZATION'] = 'Token aXJyZXNwb25zaWJsZQ=='
|
|
39
|
+
req = Rack::Auth::AbstractRequest.new(env)
|
|
40
|
+
req.scheme.should.equal :token
|
|
41
|
+
|
|
42
|
+
env['HTTP_AUTHORIZATION'] = 'OAuth aXJyZXNwb25zaWJsZQ=='
|
|
43
|
+
req = Rack::Auth::AbstractRequest.new(env)
|
|
44
|
+
req.scheme.should.equal :oauth
|
|
45
|
+
|
|
46
|
+
env['HTTP_AUTHORIZATION'] = 'OAuth2 aXJyZXNwb25zaWJsZQ=='
|
|
47
|
+
req = Rack::Auth::AbstractRequest.new(env)
|
|
48
|
+
req.scheme.should.equal :oauth2
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it "should not symbolize unknown auth schemes" do
|
|
52
|
+
env = Rack::MockRequest.env_for('/')
|
|
53
|
+
env['HTTP_AUTHORIZATION'] = 'magic aXJyZXNwb25zaWJsZQ=='
|
|
54
|
+
req = Rack::Auth::AbstractRequest.new(env)
|
|
55
|
+
req.scheme.should == "magic"
|
|
56
|
+
end
|
|
57
|
+
end
|
data/test/spec_lock.rb
CHANGED
|
@@ -142,7 +142,7 @@ describe Rack::Lock do
|
|
|
142
142
|
should "unlock if the app throws" do
|
|
143
143
|
lock = Lock.new
|
|
144
144
|
env = Rack::MockRequest.env_for("/")
|
|
145
|
-
app = lock_app(lambda {|
|
|
145
|
+
app = lock_app(lambda {|_| throw :bacon }, lock)
|
|
146
146
|
lambda { app.call(env) }.should.throw(:bacon)
|
|
147
147
|
lock.synchronized.should.equal false
|
|
148
148
|
end
|
data/test/spec_multipart.rb
CHANGED
|
@@ -48,6 +48,59 @@ describe Rack::Multipart do
|
|
|
48
48
|
params['profile']['bio'].should.include 'hello'
|
|
49
49
|
end
|
|
50
50
|
|
|
51
|
+
should "reject insanely long boundaries" do
|
|
52
|
+
# using a pipe since a tempfile can use up too much space
|
|
53
|
+
rd, wr = IO.pipe
|
|
54
|
+
|
|
55
|
+
# we only call rewind once at start, so make sure it succeeds
|
|
56
|
+
# and doesn't hit ESPIPE
|
|
57
|
+
def rd.rewind; end
|
|
58
|
+
wr.sync = true
|
|
59
|
+
|
|
60
|
+
# mock out length to make this pipe look like a Tempfile
|
|
61
|
+
def rd.length
|
|
62
|
+
1024 * 1024 * 8
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# write to a pipe in a background thread, this will write a lot
|
|
66
|
+
# unless Rack (properly) shuts down the read end
|
|
67
|
+
thr = Thread.new do
|
|
68
|
+
begin
|
|
69
|
+
wr.write("--AaB03x")
|
|
70
|
+
|
|
71
|
+
# make the initial boundary a few gigs long
|
|
72
|
+
longer = "0123456789" * 1024 * 1024
|
|
73
|
+
(1024 * 1024).times { wr.write(longer) }
|
|
74
|
+
|
|
75
|
+
wr.write("\r\n")
|
|
76
|
+
wr.write('Content-Disposition: form-data; name="a"; filename="a.txt"')
|
|
77
|
+
wr.write("\r\n")
|
|
78
|
+
wr.write("Content-Type: text/plain\r\n")
|
|
79
|
+
wr.write("\r\na")
|
|
80
|
+
wr.write("--AaB03x--\r\n")
|
|
81
|
+
wr.close
|
|
82
|
+
rescue => err # this is EPIPE if Rack shuts us down
|
|
83
|
+
err
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
fixture = {
|
|
88
|
+
"CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x",
|
|
89
|
+
"CONTENT_LENGTH" => rd.length.to_s,
|
|
90
|
+
:input => rd,
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
env = Rack::MockRequest.env_for '/', fixture
|
|
94
|
+
lambda {
|
|
95
|
+
Rack::Multipart.parse_multipart(env)
|
|
96
|
+
}.should.raise(EOFError)
|
|
97
|
+
rd.close
|
|
98
|
+
|
|
99
|
+
err = thr.value
|
|
100
|
+
err.should.be.instance_of Errno::EPIPE
|
|
101
|
+
wr.close
|
|
102
|
+
end
|
|
103
|
+
|
|
51
104
|
should "parse multipart upload with text file" do
|
|
52
105
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:text))
|
|
53
106
|
params = Rack::Multipart.parse_multipart(env)
|
data/test/spec_sendfile.rb
CHANGED
|
@@ -2,6 +2,7 @@ require 'fileutils'
|
|
|
2
2
|
require 'rack/lint'
|
|
3
3
|
require 'rack/sendfile'
|
|
4
4
|
require 'rack/mock'
|
|
5
|
+
require 'tmpdir'
|
|
5
6
|
|
|
6
7
|
describe Rack::File do
|
|
7
8
|
should "respond to #to_path" do
|
|
@@ -11,9 +12,9 @@ end
|
|
|
11
12
|
|
|
12
13
|
describe Rack::Sendfile do
|
|
13
14
|
def sendfile_body
|
|
14
|
-
FileUtils.touch "
|
|
15
|
+
FileUtils.touch File.join(Dir.tmpdir, "rack_sendfile")
|
|
15
16
|
res = ['Hello World']
|
|
16
|
-
def res.to_path ; "
|
|
17
|
+
def res.to_path ; File.join(Dir.tmpdir, "rack_sendfile") ; end
|
|
17
18
|
res
|
|
18
19
|
end
|
|
19
20
|
|
|
@@ -44,7 +45,7 @@ describe Rack::Sendfile do
|
|
|
44
45
|
response.should.be.ok
|
|
45
46
|
response.body.should.be.empty
|
|
46
47
|
response.headers['Content-Length'].should.equal '0'
|
|
47
|
-
response.headers['X-Sendfile'].should.equal
|
|
48
|
+
response.headers['X-Sendfile'].should.equal File.join(Dir.tmpdir, "rack_sendfile")
|
|
48
49
|
end
|
|
49
50
|
end
|
|
50
51
|
|
|
@@ -53,14 +54,14 @@ describe Rack::Sendfile do
|
|
|
53
54
|
response.should.be.ok
|
|
54
55
|
response.body.should.be.empty
|
|
55
56
|
response.headers['Content-Length'].should.equal '0'
|
|
56
|
-
response.headers['X-Lighttpd-Send-File'].should.equal
|
|
57
|
+
response.headers['X-Lighttpd-Send-File'].should.equal File.join(Dir.tmpdir, "rack_sendfile")
|
|
57
58
|
end
|
|
58
59
|
end
|
|
59
60
|
|
|
60
61
|
it "sets X-Accel-Redirect response header and discards body" do
|
|
61
62
|
headers = {
|
|
62
63
|
'HTTP_X_SENDFILE_TYPE' => 'X-Accel-Redirect',
|
|
63
|
-
'HTTP_X_ACCEL_MAPPING' =>
|
|
64
|
+
'HTTP_X_ACCEL_MAPPING' => "#{Dir.tmpdir}/=/foo/bar/"
|
|
64
65
|
}
|
|
65
66
|
request headers do |response|
|
|
66
67
|
response.should.be.ok
|
data/test/spec_utils.rb
CHANGED
|
@@ -331,6 +331,11 @@ describe Rack::Utils do
|
|
|
331
331
|
Rack::Utils.bytesize("FOO\xE2\x82\xAC").should.equal 6
|
|
332
332
|
end
|
|
333
333
|
|
|
334
|
+
should "should perform constant time string comparison" do
|
|
335
|
+
Rack::Utils.secure_compare('a', 'a').should.equal true
|
|
336
|
+
Rack::Utils.secure_compare('a', 'b').should.equal false
|
|
337
|
+
end
|
|
338
|
+
|
|
334
339
|
should "return status code for integer" do
|
|
335
340
|
Rack::Utils.status_code(200).should.equal 200
|
|
336
341
|
end
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rack
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
hash:
|
|
4
|
+
hash: 13
|
|
5
5
|
prerelease:
|
|
6
6
|
segments:
|
|
7
7
|
- 1
|
|
8
8
|
- 4
|
|
9
|
-
-
|
|
10
|
-
version: 1.4.
|
|
9
|
+
- 5
|
|
10
|
+
version: 1.4.5
|
|
11
11
|
platform: ruby
|
|
12
12
|
authors:
|
|
13
13
|
- Christian Neukirchen
|
|
@@ -15,7 +15,7 @@ autorequire:
|
|
|
15
15
|
bindir: bin
|
|
16
16
|
cert_chain: []
|
|
17
17
|
|
|
18
|
-
date: 2013-
|
|
18
|
+
date: 2013-02-08 00:00:00 Z
|
|
19
19
|
dependencies:
|
|
20
20
|
- !ruby/object:Gem::Dependency
|
|
21
21
|
name: bacon
|
|
@@ -81,7 +81,7 @@ dependencies:
|
|
|
81
81
|
requirements:
|
|
82
82
|
- - ">="
|
|
83
83
|
- !ruby/object:Gem::Version
|
|
84
|
-
hash:
|
|
84
|
+
hash: 3904189667
|
|
85
85
|
segments:
|
|
86
86
|
- 1
|
|
87
87
|
- 2
|
|
@@ -237,6 +237,7 @@ files:
|
|
|
237
237
|
- test/multipart/webkit
|
|
238
238
|
- test/rackup/config.ru
|
|
239
239
|
- test/registering_handler/rack/handler/registering_myself.rb
|
|
240
|
+
- test/spec_auth.rb
|
|
240
241
|
- test/spec_auth_basic.rb
|
|
241
242
|
- test/spec_auth_digest.rb
|
|
242
243
|
- test/spec_body_proxy.rb
|
|
@@ -328,6 +329,7 @@ signing_key:
|
|
|
328
329
|
specification_version: 3
|
|
329
330
|
summary: a modular Ruby webserver interface
|
|
330
331
|
test_files:
|
|
332
|
+
- test/spec_auth.rb
|
|
331
333
|
- test/spec_auth_basic.rb
|
|
332
334
|
- test/spec_auth_digest.rb
|
|
333
335
|
- test/spec_body_proxy.rb
|