rack 1.6.11 → 1.6.12

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b455a83d19e7b00bb4feb2287b28116434155cf52ea772cb9b532495f49938cc
4
- data.tar.gz: 99a947eaf73e0207a642c92398e26062d5dc508455c72447e865a6aaec86dc5b
3
+ metadata.gz: 8f95a801d50bb534494f0a7c7bbddb845d4e3448e65041642e6dea7102ca1c3d
4
+ data.tar.gz: 41df2cb86bafdb3ea49aa84382700982451396babe777cd33331b8cf0373a413
5
5
  SHA512:
6
- metadata.gz: ecc84a2788ac063238c547ea118a1e14624ace7ebc683cbf34842e57f5d4ac6fd843c4be0c3e717d7351da297a5e664f7b93255c2b37f3a73dc0939a4eb5596f
7
- data.tar.gz: ff727aec584e743839a3a9c3fbe9f88d3c6c481b3f9fdf6f472a182b6caef19de7835c9a5222359fe5bb296eab79030df880e220756693fbc244b7fd5f05756c
6
+ metadata.gz: af0085562b2835171e9fd23d746c91c036d2031966f16f81c7c16cc82410d5414f9215b3e40a89021b5dfd1c1c76344afd22a966cbba4fc8d22ba61ae49b22cf
7
+ data.tar.gz: e992b79e53e683527b06d7129ff07faa0c1b0754287b3f485673de13f231a83628168dde620e89a897bb4fd407d5c0c6fc329962c62b8c845fb54a2ff1359d29
@@ -20,7 +20,7 @@ module Rack
20
20
 
21
21
  # Return the Rack release as a dotted string.
22
22
  def self.release
23
- "1.6.11"
23
+ "1.6.12"
24
24
  end
25
25
  PATH_INFO = 'PATH_INFO'.freeze
26
26
  REQUEST_METHOD = 'REQUEST_METHOD'.freeze
@@ -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
+
33
+ def empty?; false; end
34
+ def to_s; raise; 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 ID
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
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
@@ -44,7 +44,7 @@ module Rack
44
44
  # })
45
45
  #
46
46
 
47
- class Cookie < Abstract::ID
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::ID
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 = @pool.get(sid)
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
@@ -24,7 +24,7 @@ module Rack
24
24
  # )
25
25
  # Rack::Handler::WEBrick.run sessioned
26
26
 
27
- class Pool < Abstract::ID
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 = @pool[sid]
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
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "rack"
3
- s.version = "1.6.11"
3
+ s.version = "1.6.12"
4
4
  s.platform = Gem::Platform::RUBY
5
5
  s.summary = "a modular Ruby webserver interface"
6
6
  s.license = "MIT"
@@ -47,7 +47,7 @@ describe Rack::Session::Abstract::ID do
47
47
  end
48
48
  end
49
49
  id = Rack::Session::Abstract::ID.new nil, :secure_random => secure_random.new
50
- id.send(:generate_sid).should.eql 'fake_hex'
50
+ id.send(:generate_sid).should.equal 'fake_hex'
51
51
  end
52
52
 
53
53
  end
@@ -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
@@ -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
- session_id = Rack::Lint.new(lambda do |env|
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(session_id)
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
@@ -92,7 +92,7 @@ describe Rack::ShowExceptions do
92
92
  [{ "HTTP_ACCEPT" => "text/foo" }, true],
93
93
  [{ "HTTP_ACCEPT" => "text/html" }, false]
94
94
  ].each do |env, expected|
95
- assert_equal(expected, exc.prefers_plaintext?(env))
95
+ expected.should.equal exc.prefers_plaintext?(env)
96
96
  end
97
97
  end
98
98
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.11
4
+ version: 1.6.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Christian Neukirchen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-11-05 00:00:00.000000000 Z
11
+ date: 2019-12-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bacon
@@ -255,8 +255,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
255
255
  - !ruby/object:Gem::Version
256
256
  version: '0'
257
257
  requirements: []
258
- rubyforge_project: rack
259
- rubygems_version: 2.7.6
258
+ rubygems_version: 3.0.3
260
259
  signing_key:
261
260
  specification_version: 4
262
261
  summary: a modular Ruby webserver interface