rack 1.6.8 → 1.6.13
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.
- checksums.yaml +5 -5
- data/lib/rack/methodoverride.rb +8 -1
- data/lib/rack/request.rb +17 -4
- data/lib/rack/session/abstract/id.rb +79 -3
- data/lib/rack/session/cookie.rb +11 -2
- data/lib/rack/session/memcache.rb +12 -6
- data/lib/rack/session/pool.rb +13 -6
- data/lib/rack/showexceptions.rb +1 -1
- data/lib/rack.rb +1 -1
- data/rack.gemspec +1 -1
- data/test/spec_methodoverride.rb +34 -5
- data/test/spec_request.rb +5 -0
- data/test/spec_session_abstract_id.rb +1 -1
- data/test/spec_session_memcache.rb +40 -3
- data/test/spec_session_persisted_secure_secure_session_hash.rb +73 -0
- data/test/spec_session_pool.rb +40 -3
- data/test/spec_showexceptions.rb +13 -0
- metadata +51 -51
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 1284c246863ad0e0c1472c12dc028993a5c84223a53deb943183f396c21f05ec
|
4
|
+
data.tar.gz: 6aba4634a7f953dff80b391f999eae11404516f22158d6b2931f8fc7d65aa02b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 81f24112cf528aa9f672dede1598fe9527a7d3aa5578e11fe6d6064a96d3091f0afae7f9d4bf453f1eff8c9a3d2ce4bb57991e5a45d41abfdc1948a62c8f65e2
|
7
|
+
data.tar.gz: bfde816a21a1293b1b8be7e7f3fd583887e854c57c96afc2d9b823cf16eb40938ef563294bb323389ab241322cfd3add5503e69e3e0ce8678739988d4990e43d
|
data/lib/rack/methodoverride.rb
CHANGED
@@ -26,7 +26,11 @@ module Rack
|
|
26
26
|
req = Request.new(env)
|
27
27
|
method = method_override_param(req) ||
|
28
28
|
env[HTTP_METHOD_OVERRIDE_HEADER]
|
29
|
-
|
29
|
+
begin
|
30
|
+
method.to_s.upcase
|
31
|
+
rescue ArgumentError
|
32
|
+
env["rack.errors"].puts "Invalid string for method"
|
33
|
+
end
|
30
34
|
end
|
31
35
|
|
32
36
|
private
|
@@ -38,6 +42,9 @@ module Rack
|
|
38
42
|
def method_override_param(req)
|
39
43
|
req.POST[METHOD_OVERRIDE_PARAM_KEY]
|
40
44
|
rescue Utils::InvalidParameterError, Utils::ParameterTypeError
|
45
|
+
req.env["rack.errors"].puts "Invalid or incomplete POST params"
|
46
|
+
rescue EOFError
|
47
|
+
req.env["rack.errors"].puts "Bad request content body"
|
41
48
|
end
|
42
49
|
end
|
43
50
|
end
|
data/lib/rack/request.rb
CHANGED
@@ -13,6 +13,8 @@ module Rack
|
|
13
13
|
# The environment of the request.
|
14
14
|
attr_reader :env
|
15
15
|
|
16
|
+
SCHEME_WHITELIST = %w(https http).freeze
|
17
|
+
|
16
18
|
def initialize(env)
|
17
19
|
@env = env
|
18
20
|
end
|
@@ -68,10 +70,8 @@ module Rack
|
|
68
70
|
'https'
|
69
71
|
elsif @env['HTTP_X_FORWARDED_SSL'] == 'on'
|
70
72
|
'https'
|
71
|
-
elsif
|
72
|
-
|
73
|
-
elsif @env['HTTP_X_FORWARDED_PROTO']
|
74
|
-
@env['HTTP_X_FORWARDED_PROTO'].split(',')[0]
|
73
|
+
elsif forwarded_scheme
|
74
|
+
forwarded_scheme
|
75
75
|
else
|
76
76
|
@env["rack.url_scheme"]
|
77
77
|
end
|
@@ -394,5 +394,18 @@ module Rack
|
|
394
394
|
s
|
395
395
|
end
|
396
396
|
end
|
397
|
+
|
398
|
+
def forwarded_scheme
|
399
|
+
scheme_headers = [
|
400
|
+
@env['HTTP_X_FORWARDED_SCHEME'],
|
401
|
+
@env['HTTP_X_FORWARDED_PROTO'].to_s.split(',')[0]
|
402
|
+
]
|
403
|
+
|
404
|
+
scheme_headers.each do |header|
|
405
|
+
return header if SCHEME_WHITELIST.include?(header)
|
406
|
+
end
|
407
|
+
|
408
|
+
nil
|
409
|
+
end
|
397
410
|
end
|
398
411
|
end
|
@@ -9,11 +9,38 @@ begin
|
|
9
9
|
rescue LoadError
|
10
10
|
# We just won't get securerandom
|
11
11
|
end
|
12
|
+
require "digest/sha2"
|
12
13
|
|
13
14
|
module Rack
|
14
15
|
|
15
16
|
module Session
|
16
17
|
|
18
|
+
class SessionId
|
19
|
+
ID_VERSION = 2
|
20
|
+
|
21
|
+
attr_reader :public_id
|
22
|
+
|
23
|
+
def initialize(public_id)
|
24
|
+
@public_id = public_id
|
25
|
+
end
|
26
|
+
|
27
|
+
def private_id
|
28
|
+
"#{ID_VERSION}::#{hash_sid(public_id)}"
|
29
|
+
end
|
30
|
+
|
31
|
+
alias :cookie_value :public_id
|
32
|
+
alias :to_s :public_id
|
33
|
+
|
34
|
+
def empty?; false; end
|
35
|
+
def inspect; public_id.inspect; end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def hash_sid(sid)
|
40
|
+
Digest::SHA256.hexdigest(sid)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
17
44
|
module Abstract
|
18
45
|
ENV_SESSION_KEY = 'rack.session'.freeze
|
19
46
|
ENV_SESSION_OPTIONS_KEY = 'rack.session.options'.freeze
|
@@ -191,7 +218,7 @@ module Rack
|
|
191
218
|
# Not included by default; you must require 'rack/session/abstract/id'
|
192
219
|
# to use.
|
193
220
|
|
194
|
-
class
|
221
|
+
class Persisted
|
195
222
|
DEFAULT_OPTIONS = {
|
196
223
|
:key => 'rack.session',
|
197
224
|
:path => '/',
|
@@ -342,10 +369,10 @@ module Rack
|
|
342
369
|
if not data = set_session(env, session_id, session_data, options)
|
343
370
|
env["rack.errors"].puts("Warning! #{self.class.name} failed to save session. Content dropped.")
|
344
371
|
elsif options[:defer] and not options[:renew]
|
345
|
-
env["rack.errors"].puts("Deferring cookie for #{session_id}") if $VERBOSE
|
372
|
+
env["rack.errors"].puts("Deferring cookie for #{session_id.public_id}") if $VERBOSE
|
346
373
|
else
|
347
374
|
cookie = Hash.new
|
348
|
-
cookie[:value] = data
|
375
|
+
cookie[:value] = cookie_value(data)
|
349
376
|
cookie[:expires] = Time.now + options[:expire_after] if options[:expire_after]
|
350
377
|
cookie[:expires] = Time.now + options[:max_age] if options[:max_age]
|
351
378
|
set_cookie(env, headers, cookie.merge!(options))
|
@@ -354,6 +381,10 @@ module Rack
|
|
354
381
|
[status, headers, body]
|
355
382
|
end
|
356
383
|
|
384
|
+
def cookie_value(data)
|
385
|
+
data
|
386
|
+
end
|
387
|
+
|
357
388
|
# Sets the cookie back to the client with session id. We skip the cookie
|
358
389
|
# setting if the value didn't change (sid is the same) or expires was given.
|
359
390
|
|
@@ -394,6 +425,51 @@ module Rack
|
|
394
425
|
raise '#destroy_session not implemented'
|
395
426
|
end
|
396
427
|
end
|
428
|
+
|
429
|
+
class PersistedSecure < Persisted
|
430
|
+
class SecureSessionHash < SessionHash
|
431
|
+
def [](key)
|
432
|
+
if key == "session_id"
|
433
|
+
load_for_read!
|
434
|
+
id.public_id if id
|
435
|
+
else
|
436
|
+
super
|
437
|
+
end
|
438
|
+
end
|
439
|
+
end
|
440
|
+
|
441
|
+
def generate_sid(*)
|
442
|
+
public_id = super
|
443
|
+
|
444
|
+
SessionId.new(public_id)
|
445
|
+
end
|
446
|
+
|
447
|
+
def extract_session_id(*)
|
448
|
+
public_id = super
|
449
|
+
public_id && SessionId.new(public_id)
|
450
|
+
end
|
451
|
+
|
452
|
+
private
|
453
|
+
|
454
|
+
def session_class
|
455
|
+
SecureSessionHash
|
456
|
+
end
|
457
|
+
|
458
|
+
def cookie_value(data)
|
459
|
+
data.cookie_value
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
463
|
+
class ID < Persisted
|
464
|
+
def self.inherited(klass)
|
465
|
+
k = klass.ancestors.find { |kl| kl.respond_to?(:superclass) && kl.superclass == ID }
|
466
|
+
unless k.instance_variable_defined?(:"@_rack_warned")
|
467
|
+
warn "#{klass} is inheriting from #{ID}. Inheriting from #{ID} is deprecated, please inherit from #{Persisted} instead" if $VERBOSE
|
468
|
+
k.instance_variable_set(:"@_rack_warned", true)
|
469
|
+
end
|
470
|
+
super
|
471
|
+
end
|
472
|
+
end
|
397
473
|
end
|
398
474
|
end
|
399
475
|
end
|
data/lib/rack/session/cookie.rb
CHANGED
@@ -44,7 +44,7 @@ module Rack
|
|
44
44
|
# })
|
45
45
|
#
|
46
46
|
|
47
|
-
class Cookie < Abstract::
|
47
|
+
class Cookie < Abstract::PersistedSecure
|
48
48
|
# Encode session cookies as Base64
|
49
49
|
class Base64
|
50
50
|
def encode(str)
|
@@ -151,6 +151,15 @@ module Rack
|
|
151
151
|
data
|
152
152
|
end
|
153
153
|
|
154
|
+
class SessionId < DelegateClass(Session::SessionId)
|
155
|
+
attr_reader :cookie_value
|
156
|
+
|
157
|
+
def initialize(session_id, cookie_value)
|
158
|
+
super(session_id)
|
159
|
+
@cookie_value = cookie_value
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
154
163
|
def set_session(env, session_id, session, options)
|
155
164
|
session = session.merge("session_id" => session_id)
|
156
165
|
session_data = coder.encode(session)
|
@@ -163,7 +172,7 @@ module Rack
|
|
163
172
|
env["rack.errors"].puts("Warning! Rack::Session::Cookie data size exceeds 4K.")
|
164
173
|
nil
|
165
174
|
else
|
166
|
-
session_data
|
175
|
+
SessionId.new(session_id, session_data)
|
167
176
|
end
|
168
177
|
end
|
169
178
|
|
@@ -19,7 +19,7 @@ module Rack
|
|
19
19
|
# Note that memcache does drop data before it may be listed to expire. For
|
20
20
|
# a full description of behaviour, please see memcache's documentation.
|
21
21
|
|
22
|
-
class Memcache < Abstract::
|
22
|
+
class Memcache < Abstract::PersistedSecure
|
23
23
|
attr_reader :mutex, :pool
|
24
24
|
|
25
25
|
DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge \
|
@@ -42,15 +42,15 @@ module Rack
|
|
42
42
|
def generate_sid
|
43
43
|
loop do
|
44
44
|
sid = super
|
45
|
-
break sid unless @pool.get(sid, true)
|
45
|
+
break sid unless @pool.get(sid.private_id, true)
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
49
|
def get_session(env, sid)
|
50
50
|
with_lock(env) do
|
51
|
-
unless sid and session =
|
51
|
+
unless sid and session = get_session_with_fallback(sid)
|
52
52
|
sid, session = generate_sid, {}
|
53
|
-
unless /^STORED/ =~ @pool.add(sid, session)
|
53
|
+
unless /^STORED/ =~ @pool.add(sid.private_id, session)
|
54
54
|
raise "Session collision on '#{sid.inspect}'"
|
55
55
|
end
|
56
56
|
end
|
@@ -63,14 +63,15 @@ module Rack
|
|
63
63
|
expiry = expiry.nil? ? 0 : expiry + 1
|
64
64
|
|
65
65
|
with_lock(env) do
|
66
|
-
@pool.set session_id, new_session, expiry
|
66
|
+
@pool.set session_id.private_id, new_session, expiry
|
67
67
|
session_id
|
68
68
|
end
|
69
69
|
end
|
70
70
|
|
71
71
|
def destroy_session(env, session_id, options)
|
72
72
|
with_lock(env) do
|
73
|
-
@pool.delete(session_id)
|
73
|
+
@pool.delete(session_id.public_id)
|
74
|
+
@pool.delete(session_id.private_id)
|
74
75
|
generate_sid unless options[:drop]
|
75
76
|
end
|
76
77
|
end
|
@@ -88,6 +89,11 @@ module Rack
|
|
88
89
|
@mutex.unlock if @mutex.locked?
|
89
90
|
end
|
90
91
|
|
92
|
+
private
|
93
|
+
|
94
|
+
def get_session_with_fallback(sid)
|
95
|
+
@pool.get(sid.private_id) || @pool.get(sid.public_id)
|
96
|
+
end
|
91
97
|
end
|
92
98
|
end
|
93
99
|
end
|
data/lib/rack/session/pool.rb
CHANGED
@@ -24,7 +24,7 @@ module Rack
|
|
24
24
|
# )
|
25
25
|
# Rack::Handler::WEBrick.run sessioned
|
26
26
|
|
27
|
-
class Pool < Abstract::
|
27
|
+
class Pool < Abstract::PersistedSecure
|
28
28
|
attr_reader :mutex, :pool
|
29
29
|
DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge :drop => false
|
30
30
|
|
@@ -37,15 +37,15 @@ module Rack
|
|
37
37
|
def generate_sid
|
38
38
|
loop do
|
39
39
|
sid = super
|
40
|
-
break sid unless @pool.key? sid
|
40
|
+
break sid unless @pool.key? sid.private_id
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
44
|
def get_session(env, sid)
|
45
45
|
with_lock(env) do
|
46
|
-
unless sid and session =
|
46
|
+
unless sid and session = get_session_with_fallback(sid)
|
47
47
|
sid, session = generate_sid, {}
|
48
|
-
@pool.store sid, session
|
48
|
+
@pool.store sid.private_id, session
|
49
49
|
end
|
50
50
|
[sid, session]
|
51
51
|
end
|
@@ -53,14 +53,15 @@ module Rack
|
|
53
53
|
|
54
54
|
def set_session(env, session_id, new_session, options)
|
55
55
|
with_lock(env) do
|
56
|
-
@pool.store session_id, new_session
|
56
|
+
@pool.store session_id.private_id, new_session
|
57
57
|
session_id
|
58
58
|
end
|
59
59
|
end
|
60
60
|
|
61
61
|
def destroy_session(env, session_id, options)
|
62
62
|
with_lock(env) do
|
63
|
-
@pool.delete(session_id)
|
63
|
+
@pool.delete(session_id.public_id)
|
64
|
+
@pool.delete(session_id.private_id)
|
64
65
|
generate_sid unless options[:drop]
|
65
66
|
end
|
66
67
|
end
|
@@ -71,6 +72,12 @@ module Rack
|
|
71
72
|
ensure
|
72
73
|
@mutex.unlock if @mutex.locked?
|
73
74
|
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def get_session_with_fallback(sid)
|
79
|
+
@pool[sid.private_id] || @pool[sid.public_id]
|
80
|
+
end
|
74
81
|
end
|
75
82
|
end
|
76
83
|
end
|
data/lib/rack/showexceptions.rb
CHANGED
data/lib/rack.rb
CHANGED
data/rack.gemspec
CHANGED
data/test/spec_methodoverride.rb
CHANGED
@@ -8,7 +8,7 @@ describe Rack::MethodOverride do
|
|
8
8
|
[200, {"Content-Type" => "text/plain"}, []]
|
9
9
|
}))
|
10
10
|
end
|
11
|
-
|
11
|
+
|
12
12
|
should "not affect GET requests" do
|
13
13
|
env = Rack::MockRequest.env_for("/?_method=delete", :method => "GET")
|
14
14
|
app.call env
|
@@ -23,6 +23,22 @@ describe Rack::MethodOverride do
|
|
23
23
|
env["REQUEST_METHOD"].should.equal "PUT"
|
24
24
|
end
|
25
25
|
|
26
|
+
if RUBY_VERSION >= "1.9"
|
27
|
+
should "set rack.errors for invalid UTF8 _method values" do
|
28
|
+
errors = StringIO.new
|
29
|
+
env = Rack::MockRequest.env_for("/",
|
30
|
+
:method => "POST",
|
31
|
+
:input => "_method=\xBF".force_encoding("ASCII-8BIT"),
|
32
|
+
"rack.errors" => errors)
|
33
|
+
|
34
|
+
app.call env
|
35
|
+
|
36
|
+
errors.rewind
|
37
|
+
errors.read.should.equal "Invalid string for method\n"
|
38
|
+
env["REQUEST_METHOD"].should.equal "POST"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
26
42
|
should "modify REQUEST_METHOD for POST requests when X-HTTP-Method-Override is set" do
|
27
43
|
env = Rack::MockRequest.env_for("/",
|
28
44
|
:method => "POST",
|
@@ -65,14 +81,27 @@ EOF
|
|
65
81
|
"CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
|
66
82
|
"CONTENT_LENGTH" => input.size.to_s,
|
67
83
|
:method => "POST", :input => input)
|
68
|
-
|
69
|
-
app.call env
|
70
|
-
rescue EOFError
|
71
|
-
end
|
84
|
+
app.call env
|
72
85
|
|
73
86
|
env["REQUEST_METHOD"].should.equal "POST"
|
74
87
|
end
|
75
88
|
|
89
|
+
should "write error to RACK_ERRORS when given invalid multipart form data" do
|
90
|
+
input = <<EOF
|
91
|
+
--AaB03x\r
|
92
|
+
content-disposition: form-data; name="huge"; filename="huge"\r
|
93
|
+
EOF
|
94
|
+
env = Rack::MockRequest.env_for("/",
|
95
|
+
"CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
|
96
|
+
"CONTENT_LENGTH" => input.size.to_s,
|
97
|
+
"rack.errors" => StringIO.new,
|
98
|
+
:method => "POST", :input => input)
|
99
|
+
Rack::MethodOverride.new(proc { [200, {"Content-Type" => "text/plain"}, []] }).call env
|
100
|
+
|
101
|
+
env["rack.errors"].rewind
|
102
|
+
env["rack.errors"].read.should =~ /Bad request content body/
|
103
|
+
end
|
104
|
+
|
76
105
|
should "not modify REQUEST_METHOD for POST requests when the params are unparseable" do
|
77
106
|
env = Rack::MockRequest.env_for("/", :method => "POST", :input => "(%bad-params%)")
|
78
107
|
app.call env
|
data/test/spec_request.rb
CHANGED
@@ -425,6 +425,11 @@ describe Rack::Request do
|
|
425
425
|
request.should.be.ssl?
|
426
426
|
end
|
427
427
|
|
428
|
+
should "prevent scheme abuse" do
|
429
|
+
request = Rack::Request.new(Rack::MockRequest.env_for("/", 'HTTP_X_FORWARDED_SCHEME' => 'a."><script>alert(1)</script>'))
|
430
|
+
request.scheme.should.not.equal 'a."><script>alert(1)</script>'
|
431
|
+
end
|
432
|
+
|
428
433
|
should "parse cookies" do
|
429
434
|
req = Rack::Request.new \
|
430
435
|
Rack::MockRequest.env_for("", "HTTP_COOKIE" => "foo=bar;quux=h&m")
|
@@ -225,15 +225,52 @@ begin
|
|
225
225
|
req = Rack::MockRequest.new(pool)
|
226
226
|
|
227
227
|
res0 = req.get("/")
|
228
|
-
session_id = (cookie = res0["Set-Cookie"])[session_match, 1]
|
229
|
-
ses0 = pool.pool.get(session_id, true)
|
228
|
+
session_id = Rack::Session::SessionId.new (cookie = res0["Set-Cookie"])[session_match, 1]
|
229
|
+
ses0 = pool.pool.get(session_id.private_id, true)
|
230
230
|
|
231
231
|
req.get("/", "HTTP_COOKIE" => cookie)
|
232
|
-
ses1 = pool.pool.get(session_id, true)
|
232
|
+
ses1 = pool.pool.get(session_id.private_id, true)
|
233
233
|
|
234
234
|
ses1.should.not.equal ses0
|
235
235
|
end
|
236
236
|
|
237
|
+
it "can read the session with the legacy id" do
|
238
|
+
pool = Rack::Session::Memcache.new(incrementor)
|
239
|
+
req = Rack::MockRequest.new(pool)
|
240
|
+
|
241
|
+
res0 = req.get("/")
|
242
|
+
cookie = res0["Set-Cookie"]
|
243
|
+
session_id = Rack::Session::SessionId.new cookie[session_match, 1]
|
244
|
+
ses0 = pool.pool.get(session_id.private_id, true)
|
245
|
+
pool.pool.set(session_id.public_id, ses0, 0, true)
|
246
|
+
pool.pool.delete(session_id.private_id)
|
247
|
+
|
248
|
+
res1 = req.get("/", "HTTP_COOKIE" => cookie)
|
249
|
+
res1["Set-Cookie"].should.be.nil
|
250
|
+
res1.body.should.equal '{"counter"=>2}'
|
251
|
+
pool.pool.get(session_id.private_id, true).should.not.be.nil
|
252
|
+
end
|
253
|
+
|
254
|
+
it "drops the session in the legacy id as well" do
|
255
|
+
pool = Rack::Session::Memcache.new(incrementor)
|
256
|
+
req = Rack::MockRequest.new(pool)
|
257
|
+
drop = Rack::Utils::Context.new(pool, drop_session)
|
258
|
+
dreq = Rack::MockRequest.new(drop)
|
259
|
+
|
260
|
+
res0 = req.get("/")
|
261
|
+
cookie = res0["Set-Cookie"]
|
262
|
+
session_id = Rack::Session::SessionId.new cookie[session_match, 1]
|
263
|
+
ses0 = pool.pool.get(session_id.private_id, true)
|
264
|
+
pool.pool.set(session_id.public_id, ses0, 0, true)
|
265
|
+
pool.pool.delete(session_id.private_id)
|
266
|
+
|
267
|
+
res2 = dreq.get("/", "HTTP_COOKIE" => cookie)
|
268
|
+
res2["Set-Cookie"].should.be.nil
|
269
|
+
res2.body.should.equal '{"counter"=>2}'
|
270
|
+
pool.pool.get(session_id.private_id, true).should.be.nil
|
271
|
+
pool.pool.get(session_id.public_id, true).should.be.nil
|
272
|
+
end
|
273
|
+
|
237
274
|
# anyone know how to do this better?
|
238
275
|
it "cleanly merges sessions when multithreaded" do
|
239
276
|
unless $DEBUG
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'minitest/global_expectations/autorun'
|
4
|
+
require 'rack/session/abstract/id'
|
5
|
+
|
6
|
+
describe Rack::Session::Abstract::PersistedSecure::SecureSessionHash do
|
7
|
+
attr_reader :hash
|
8
|
+
|
9
|
+
def setup
|
10
|
+
super
|
11
|
+
@store = Class.new do
|
12
|
+
def load_session(req)
|
13
|
+
[Rack::Session::SessionId.new("id"), { foo: :bar, baz: :qux }]
|
14
|
+
end
|
15
|
+
def session_exists?(req)
|
16
|
+
true
|
17
|
+
end
|
18
|
+
end
|
19
|
+
@hash = Rack::Session::Abstract::PersistedSecure::SecureSessionHash.new(@store.new, nil)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "returns keys" do
|
23
|
+
assert_equal ["foo", "baz"], hash.keys
|
24
|
+
end
|
25
|
+
|
26
|
+
it "returns values" do
|
27
|
+
assert_equal [:bar, :qux], hash.values
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "#[]" do
|
31
|
+
it "returns value for a matching key" do
|
32
|
+
assert_equal :bar, hash[:foo]
|
33
|
+
end
|
34
|
+
|
35
|
+
it "returns value for a 'session_id' key" do
|
36
|
+
assert_equal "id", hash['session_id']
|
37
|
+
end
|
38
|
+
|
39
|
+
it "returns nil value for missing 'session_id' key" do
|
40
|
+
store = @store.new
|
41
|
+
def store.load_session(req)
|
42
|
+
[nil, {}]
|
43
|
+
end
|
44
|
+
@hash = Rack::Session::Abstract::PersistedSecure::SecureSessionHash.new(store, nil)
|
45
|
+
assert_nil hash['session_id']
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "#fetch" do
|
50
|
+
it "returns value for a matching key" do
|
51
|
+
assert_equal :bar, hash.fetch(:foo)
|
52
|
+
end
|
53
|
+
|
54
|
+
it "works with a default value" do
|
55
|
+
assert_equal :default, hash.fetch(:unknown, :default)
|
56
|
+
end
|
57
|
+
|
58
|
+
it "works with a block" do
|
59
|
+
assert_equal :default, hash.fetch(:unkown) { :default }
|
60
|
+
end
|
61
|
+
|
62
|
+
it "it raises when fetching unknown keys without defaults" do
|
63
|
+
lambda { hash.fetch(:unknown) }.must_raise KeyError
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "#stringify_keys" do
|
68
|
+
it "returns hash or session hash with keys stringified" do
|
69
|
+
assert_equal({ "foo" => :bar, "baz" => :qux }, hash.send(:stringify_keys, hash).to_h)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
data/test/spec_session_pool.rb
CHANGED
@@ -5,7 +5,7 @@ require 'rack/session/pool'
|
|
5
5
|
|
6
6
|
describe Rack::Session::Pool do
|
7
7
|
session_key = Rack::Session::Pool::DEFAULT_OPTIONS[:key]
|
8
|
-
session_match = /#{session_key}=[0-9a-fA-F]
|
8
|
+
session_match = /#{session_key}=([0-9a-fA-F]+);/
|
9
9
|
|
10
10
|
incrementor = lambda do |env|
|
11
11
|
env["rack.session"]["counter"] ||= 0
|
@@ -13,7 +13,7 @@ describe Rack::Session::Pool do
|
|
13
13
|
Rack::Response.new(env["rack.session"].inspect).to_a
|
14
14
|
end
|
15
15
|
|
16
|
-
|
16
|
+
get_session_id = Rack::Lint.new(lambda do |env|
|
17
17
|
Rack::Response.new(env["rack.session"].inspect).to_a
|
18
18
|
end)
|
19
19
|
|
@@ -142,6 +142,43 @@ describe Rack::Session::Pool do
|
|
142
142
|
pool.pool.size.should.equal 1
|
143
143
|
end
|
144
144
|
|
145
|
+
it "can read the session with the legacy id" do
|
146
|
+
pool = Rack::Session::Pool.new(incrementor)
|
147
|
+
req = Rack::MockRequest.new(pool)
|
148
|
+
|
149
|
+
res0 = req.get("/")
|
150
|
+
cookie = res0["Set-Cookie"]
|
151
|
+
session_id = Rack::Session::SessionId.new cookie[session_match, 1]
|
152
|
+
ses0 = pool.pool[session_id.private_id]
|
153
|
+
pool.pool[session_id.public_id] = ses0
|
154
|
+
pool.pool.delete(session_id.private_id)
|
155
|
+
|
156
|
+
res1 = req.get("/", "HTTP_COOKIE" => cookie)
|
157
|
+
res1["Set-Cookie"].should.be.nil
|
158
|
+
res1.body.should.equal '{"counter"=>2}'
|
159
|
+
pool.pool[session_id.private_id].should.not.be.nil
|
160
|
+
end
|
161
|
+
|
162
|
+
it "drops the session in the legacy id as well" do
|
163
|
+
pool = Rack::Session::Pool.new(incrementor)
|
164
|
+
req = Rack::MockRequest.new(pool)
|
165
|
+
drop = Rack::Utils::Context.new(pool, drop_session)
|
166
|
+
dreq = Rack::MockRequest.new(drop)
|
167
|
+
|
168
|
+
res0 = req.get("/")
|
169
|
+
cookie = res0["Set-Cookie"]
|
170
|
+
session_id = Rack::Session::SessionId.new cookie[session_match, 1]
|
171
|
+
ses0 = pool.pool[session_id.private_id]
|
172
|
+
pool.pool[session_id.public_id] = ses0
|
173
|
+
pool.pool.delete(session_id.private_id)
|
174
|
+
|
175
|
+
res2 = dreq.get("/", "HTTP_COOKIE" => cookie)
|
176
|
+
res2["Set-Cookie"].should.be.nil
|
177
|
+
res2.body.should.equal '{"counter"=>2}'
|
178
|
+
pool.pool[session_id.private_id].should.be.nil
|
179
|
+
pool.pool[session_id.public_id].should.be.nil
|
180
|
+
end
|
181
|
+
|
145
182
|
# anyone know how to do this better?
|
146
183
|
it "should merge sessions when multithreaded" do
|
147
184
|
unless $DEBUG
|
@@ -190,7 +227,7 @@ describe Rack::Session::Pool do
|
|
190
227
|
end
|
191
228
|
|
192
229
|
it "does not return a cookie if cookie was not written (only read)" do
|
193
|
-
app = Rack::Session::Pool.new(
|
230
|
+
app = Rack::Session::Pool.new(get_session_id)
|
194
231
|
res = Rack::MockRequest.new(app).get("/")
|
195
232
|
res["Set-Cookie"].should.be.nil
|
196
233
|
end
|
data/test/spec_showexceptions.rb
CHANGED
@@ -82,4 +82,17 @@ describe Rack::ShowExceptions do
|
|
82
82
|
res.should =~ /ShowExceptions/
|
83
83
|
res.should =~ /unknown location/
|
84
84
|
end
|
85
|
+
|
86
|
+
it "knows to prefer plaintext for non-html" do
|
87
|
+
# We don't need an app for this
|
88
|
+
exc = Rack::ShowExceptions.new(nil)
|
89
|
+
|
90
|
+
[
|
91
|
+
[{ "HTTP_ACCEPT" => "text/plain" }, true],
|
92
|
+
[{ "HTTP_ACCEPT" => "text/foo" }, true],
|
93
|
+
[{ "HTTP_ACCEPT" => "text/html" }, false]
|
94
|
+
].each do |env, expected|
|
95
|
+
expected.should.equal exc.prefers_plaintext?(env)
|
96
|
+
end
|
97
|
+
end
|
85
98
|
end
|
metadata
CHANGED
@@ -1,38 +1,38 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.6.
|
4
|
+
version: 1.6.13
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Christian Neukirchen
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-02-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
+
name: bacon
|
14
15
|
requirement: !ruby/object:Gem::Requirement
|
15
16
|
requirements:
|
16
17
|
- - ">="
|
17
18
|
- !ruby/object:Gem::Version
|
18
19
|
version: '0'
|
19
|
-
name: bacon
|
20
|
-
prerelease: false
|
21
20
|
type: :development
|
21
|
+
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
28
29
|
requirement: !ruby/object:Gem::Requirement
|
29
30
|
requirements:
|
30
31
|
- - ">="
|
31
32
|
- !ruby/object:Gem::Version
|
32
33
|
version: '0'
|
33
|
-
name: rake
|
34
|
-
prerelease: false
|
35
34
|
type: :development
|
35
|
+
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ">="
|
@@ -221,6 +221,7 @@ files:
|
|
221
221
|
- test/spec_session_abstract_id.rb
|
222
222
|
- test/spec_session_cookie.rb
|
223
223
|
- test/spec_session_memcache.rb
|
224
|
+
- test/spec_session_persisted_secure_secure_session_hash.rb
|
224
225
|
- test/spec_session_pool.rb
|
225
226
|
- test/spec_showexceptions.rb
|
226
227
|
- test/spec_showstatus.rb
|
@@ -240,7 +241,7 @@ homepage: http://rack.github.io/
|
|
240
241
|
licenses:
|
241
242
|
- MIT
|
242
243
|
metadata: {}
|
243
|
-
post_install_message:
|
244
|
+
post_install_message:
|
244
245
|
rdoc_options: []
|
245
246
|
require_paths:
|
246
247
|
- lib
|
@@ -255,59 +256,58 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
255
256
|
- !ruby/object:Gem::Version
|
256
257
|
version: '0'
|
257
258
|
requirements: []
|
258
|
-
|
259
|
-
|
260
|
-
signing_key:
|
259
|
+
rubygems_version: 3.1.2
|
260
|
+
signing_key:
|
261
261
|
specification_version: 4
|
262
262
|
summary: a modular Ruby webserver interface
|
263
263
|
test_files:
|
264
|
-
- test/
|
265
|
-
- test/spec_auth_digest.rb
|
266
|
-
- test/spec_body_proxy.rb
|
267
|
-
- test/spec_builder.rb
|
268
|
-
- test/spec_cascade.rb
|
269
|
-
- test/spec_cgi.rb
|
270
|
-
- test/spec_chunked.rb
|
271
|
-
- test/spec_commonlogger.rb
|
272
|
-
- test/spec_conditionalget.rb
|
273
|
-
- test/spec_config.rb
|
274
|
-
- test/spec_content_length.rb
|
275
|
-
- test/spec_content_type.rb
|
264
|
+
- test/spec_multipart.rb
|
276
265
|
- test/spec_deflater.rb
|
277
|
-
- test/
|
266
|
+
- test/spec_static.rb
|
267
|
+
- test/spec_session_cookie.rb
|
268
|
+
- test/spec_commonlogger.rb
|
269
|
+
- test/spec_session_pool.rb
|
270
|
+
- test/spec_methodoverride.rb
|
278
271
|
- test/spec_etag.rb
|
279
|
-
- test/
|
280
|
-
- test/spec_file.rb
|
272
|
+
- test/spec_version.rb
|
281
273
|
- test/spec_handler.rb
|
282
|
-
- test/
|
283
|
-
- test/
|
284
|
-
- test/
|
285
|
-
- test/spec_lock.rb
|
286
|
-
- test/spec_logger.rb
|
287
|
-
- test/spec_methodoverride.rb
|
274
|
+
- test/spec_thin.rb
|
275
|
+
- test/spec_showexceptions.rb
|
276
|
+
- test/spec_session_abstract_id.rb
|
288
277
|
- test/spec_mime.rb
|
289
|
-
- test/spec_mock.rb
|
290
|
-
- test/spec_mongrel.rb
|
291
|
-
- test/spec_multipart.rb
|
292
|
-
- test/spec_nulllogger.rb
|
293
278
|
- test/spec_recursive.rb
|
279
|
+
- test/spec_cgi.rb
|
280
|
+
- test/spec_content_type.rb
|
294
281
|
- test/spec_request.rb
|
295
|
-
- test/
|
296
|
-
- test/
|
282
|
+
- test/spec_showstatus.rb
|
283
|
+
- test/spec_chunked.rb
|
297
284
|
- test/spec_runtime.rb
|
285
|
+
- test/spec_session_persisted_secure_secure_session_hash.rb
|
286
|
+
- test/spec_fastcgi.rb
|
287
|
+
- test/spec_builder.rb
|
288
|
+
- test/spec_config.rb
|
289
|
+
- test/spec_mongrel.rb
|
290
|
+
- test/spec_utils.rb
|
298
291
|
- test/spec_sendfile.rb
|
299
|
-
- test/
|
300
|
-
- test/
|
301
|
-
- test/spec_session_cookie.rb
|
302
|
-
- test/spec_session_memcache.rb
|
303
|
-
- test/spec_session_pool.rb
|
304
|
-
- test/spec_showexceptions.rb
|
305
|
-
- test/spec_showstatus.rb
|
306
|
-
- test/spec_static.rb
|
292
|
+
- test/spec_lobster.rb
|
293
|
+
- test/spec_lint.rb
|
307
294
|
- test/spec_tempfile_reaper.rb
|
308
|
-
- test/
|
309
|
-
- test/
|
310
|
-
- test/
|
311
|
-
- test/
|
295
|
+
- test/spec_mock.rb
|
296
|
+
- test/spec_conditionalget.rb
|
297
|
+
- test/spec_server.rb
|
298
|
+
- test/spec_directory.rb
|
312
299
|
- test/spec_webrick.rb
|
313
|
-
|
300
|
+
- test/spec_response.rb
|
301
|
+
- test/spec_file.rb
|
302
|
+
- test/spec_body_proxy.rb
|
303
|
+
- test/spec_logger.rb
|
304
|
+
- test/spec_auth_digest.rb
|
305
|
+
- test/spec_urlmap.rb
|
306
|
+
- test/spec_nulllogger.rb
|
307
|
+
- test/spec_cascade.rb
|
308
|
+
- test/spec_auth_basic.rb
|
309
|
+
- test/spec_head.rb
|
310
|
+
- test/spec_lock.rb
|
311
|
+
- test/spec_rewindable_input.rb
|
312
|
+
- test/spec_session_memcache.rb
|
313
|
+
- test/spec_content_length.rb
|