rack 2.0.7 → 2.0.9.1

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
- SHA1:
3
- metadata.gz: 116f4a51b2ae4bf5127f7609fe8bc0586647cd05
4
- data.tar.gz: aabc7fa3253cc8696e1d1f0e7b175656ef39b8a7
2
+ SHA256:
3
+ metadata.gz: 3d7540b6cecf9193ad7a6ca8f1be4e4a97cf56b3d9a54d420b21ca164af57b66
4
+ data.tar.gz: b966e0e74ffe6c9b813bbbb222ec3d5ff5b331878feb57440c23a74ebf93197d
5
5
  SHA512:
6
- metadata.gz: 1e2ecf2098113d2f435bd2e299cb207701960cd08225bd4bcc2953f46955f71225935c545c2efd35a0d288510435d56486ffc3e6c6cca9051695c70751447f85
7
- data.tar.gz: ff2e93c1e9989f628523a64693102ca05fdcd7749878710c2c4af44c84aa9eb1ac0af92817dbb251a5da929b09a35ea288ee20a0896d49771a41295917a18ca2
6
+ metadata.gz: e9d484bfb940bb4894a9c4be9cf7c88f5b7d13c55bbd1b7dfc110b6dee577d6aa724614ec7e9f3861f860e4d699933ee49b5e32fca1affdae6541f459176260a
7
+ data.tar.gz: 4ddec5784e6318979bfcefd89c12882f2da022ddb216e99df6d570871e35b9ba9f693872245f031a3fb8505361b966b41dae0d32b180fcb53a8bdec1f329f57f
data/HISTORY.md CHANGED
@@ -1,3 +1,8 @@
1
+ Fri May 27 08:27:04 2022 Aaron Patterson <tenderlove@ruby-lang.org>
2
+
3
+ * [CVE-2022-30123] Fix shell escaping issue in Common Logger
4
+ * [CVE-2022-30122] Restrict parsing of broken MIME attachments
5
+
1
6
  Sun Dec 4 18:48:03 2015 Jeremy Daer <jeremydaer@gmail.com>
2
7
 
3
8
  * First-party "SameSite" cookies. Browsers omit SameSite cookies
@@ -54,7 +54,10 @@ module Rack
54
54
  length,
55
55
  now - began_at ]
56
56
 
57
+ msg.gsub!(/[^[:print:]\n]/) { |c| "\\x#{c.ord}" }
58
+
57
59
  logger = @logger || env[RACK_ERRORS]
60
+
58
61
  # Standard library logger doesn't support write but it supports << which actually
59
62
  # calls to write on the log device without formatting
60
63
  if logger.respond_to?(:write)
data/lib/rack/lint.rb CHANGED
@@ -295,7 +295,7 @@ module Rack
295
295
  check_hijack env
296
296
 
297
297
  ## * The <tt>REQUEST_METHOD</tt> must be a valid token.
298
- assert("REQUEST_METHOD unknown: #{env[REQUEST_METHOD]}") {
298
+ assert("REQUEST_METHOD unknown: #{env[REQUEST_METHOD].dump}") {
299
299
  env[REQUEST_METHOD] =~ /\A[0-9A-Za-z!\#$%&'*+.^_`|~-]+\z/
300
300
  }
301
301
 
@@ -302,8 +302,9 @@ module Rack
302
302
  elsif filename = params['filename*']
303
303
  encoding, _, filename = filename.split("'", 3)
304
304
  end
305
- when BROKEN_QUOTED, BROKEN_UNQUOTED
305
+ when BROKEN
306
306
  filename = $1
307
+ filename = $1 if filename =~ /^"(.*)"$/
307
308
  end
308
309
 
309
310
  return unless filename
@@ -14,8 +14,7 @@ module Rack
14
14
  TOKEN = /[^\s()<>,;:\\"\/\[\]?=]+/
15
15
  CONDISP = /Content-Disposition:\s*#{TOKEN}\s*/i
16
16
  VALUE = /"(?:\\"|[^"])*"|#{TOKEN}/
17
- BROKEN_QUOTED = /^#{CONDISP}.*;\sfilename="(.*?)"(?:\s*$|\s*;\s*#{TOKEN}=)/i
18
- BROKEN_UNQUOTED = /^#{CONDISP}.*;\sfilename=(#{TOKEN})/i
17
+ BROKEN = /^#{CONDISP}.*;\s*filename=(#{VALUE})/i
19
18
  MULTIPART_CONTENT_TYPE = /Content-Type: (.*)#{EOL}/ni
20
19
  MULTIPART_CONTENT_DISPOSITION = /Content-Disposition:.*\s+name=(#{VALUE})/ni
21
20
  MULTIPART_CONTENT_ID = /Content-ID:\s*([^#{EOL}]*)/ni
@@ -6,11 +6,38 @@ require 'time'
6
6
  require 'rack/request'
7
7
  require 'rack/response'
8
8
  require 'securerandom'
9
+ require 'digest/sha2'
9
10
 
10
11
  module Rack
11
12
 
12
13
  module Session
13
14
 
15
+ class SessionId
16
+ ID_VERSION = 2
17
+
18
+ attr_reader :public_id
19
+
20
+ def initialize(public_id)
21
+ @public_id = public_id
22
+ end
23
+
24
+ def private_id
25
+ "#{ID_VERSION}::#{hash_sid(public_id)}"
26
+ end
27
+
28
+ alias :cookie_value :public_id
29
+ alias :to_s :public_id
30
+
31
+ def empty?; false; end
32
+ def inspect; public_id.inspect; end
33
+
34
+ private
35
+
36
+ def hash_sid(sid)
37
+ Digest::SHA256.hexdigest(sid)
38
+ end
39
+ end
40
+
14
41
  module Abstract
15
42
  # SessionHash is responsible to lazily load the session from store.
16
43
 
@@ -357,7 +384,7 @@ module Rack
357
384
  req.get_header(RACK_ERRORS).puts("Deferring cookie for #{session_id}") if $VERBOSE
358
385
  else
359
386
  cookie = Hash.new
360
- cookie[:value] = data
387
+ cookie[:value] = cookie_value(data)
361
388
  cookie[:expires] = Time.now + options[:expire_after] if options[:expire_after]
362
389
  cookie[:expires] = Time.now + options[:max_age] if options[:max_age]
363
390
  set_cookie(req, res, cookie.merge!(options))
@@ -365,6 +392,10 @@ module Rack
365
392
  end
366
393
  public :commit_session
367
394
 
395
+ def cookie_value(data)
396
+ data
397
+ end
398
+
368
399
  # Sets the cookie back to the client with session id. We skip the cookie
369
400
  # setting if the value didn't change (sid is the same) or expires was given.
370
401
 
@@ -406,6 +437,40 @@ module Rack
406
437
  end
407
438
  end
408
439
 
440
+ class PersistedSecure < Persisted
441
+ class SecureSessionHash < SessionHash
442
+ def [](key)
443
+ if key == "session_id"
444
+ load_for_read!
445
+ id.public_id if id
446
+ else
447
+ super
448
+ end
449
+ end
450
+ end
451
+
452
+ def generate_sid(*)
453
+ public_id = super
454
+
455
+ SessionId.new(public_id)
456
+ end
457
+
458
+ def extract_session_id(*)
459
+ public_id = super
460
+ public_id && SessionId.new(public_id)
461
+ end
462
+
463
+ private
464
+
465
+ def session_class
466
+ SecureSessionHash
467
+ end
468
+
469
+ def cookie_value(data)
470
+ data.cookie_value
471
+ end
472
+ end
473
+
409
474
  class ID < Persisted
410
475
  def self.inherited(klass)
411
476
  k = klass.ancestors.find { |kl| kl.respond_to?(:superclass) && kl.superclass == ID }
@@ -45,7 +45,7 @@ module Rack
45
45
  # })
46
46
  #
47
47
 
48
- class Cookie < Abstract::Persisted
48
+ class Cookie < Abstract::PersistedSecure
49
49
  # Encode session cookies as Base64
50
50
  class Base64
51
51
  def encode(str)
@@ -153,6 +153,15 @@ module Rack
153
153
  data
154
154
  end
155
155
 
156
+ class SessionId < DelegateClass(Session::SessionId)
157
+ attr_reader :cookie_value
158
+
159
+ def initialize(session_id, cookie_value)
160
+ super(session_id)
161
+ @cookie_value = cookie_value
162
+ end
163
+ end
164
+
156
165
  def write_session(req, session_id, session, options)
157
166
  session = session.merge("session_id" => session_id)
158
167
  session_data = coder.encode(session)
@@ -165,7 +174,7 @@ module Rack
165
174
  req.get_header(RACK_ERRORS).puts("Warning! Rack::Session::Cookie data size exceeds 4K.")
166
175
  nil
167
176
  else
168
- session_data
177
+ SessionId.new(session_id, session_data)
169
178
  end
170
179
  end
171
180
 
@@ -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
- def get_session(env, sid)
50
- with_lock(env) do
51
- unless sid and session = @pool.get(sid)
49
+ def find_session(req, sid)
50
+ with_lock(req) do
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
@@ -58,25 +58,26 @@ module Rack
58
58
  end
59
59
  end
60
60
 
61
- def set_session(env, session_id, new_session, options)
61
+ def write_session(req, session_id, new_session, options)
62
62
  expiry = options[:expire_after]
63
63
  expiry = expiry.nil? ? 0 : expiry + 1
64
64
 
65
- with_lock(env) do
66
- @pool.set session_id, new_session, expiry
65
+ with_lock(req) do
66
+ @pool.set session_id.private_id, new_session, expiry
67
67
  session_id
68
68
  end
69
69
  end
70
70
 
71
- def destroy_session(env, session_id, options)
72
- with_lock(env) do
73
- @pool.delete(session_id)
71
+ def delete_session(req, session_id, options)
72
+ with_lock(req) do
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
77
78
 
78
- def with_lock(env)
79
- @mutex.lock if env[RACK_MULTITHREAD]
79
+ def with_lock(req)
80
+ @mutex.lock if req.multithread?
80
81
  yield
81
82
  rescue MemCache::MemCacheError, Errno::ECONNREFUSED
82
83
  if $VERBOSE
@@ -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::Persisted
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 find_session(req, sid)
45
45
  with_lock(req) 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 write_session(req, session_id, new_session, options)
55
55
  with_lock(req) 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 delete_session(req, session_id, options)
62
62
  with_lock(req) 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/utils.rb CHANGED
@@ -252,6 +252,8 @@ module Rack
252
252
  case value[:same_site]
253
253
  when false, nil
254
254
  nil
255
+ when :none, 'None', :None
256
+ '; SameSite=None'.freeze
255
257
  when :lax, 'Lax', :Lax
256
258
  '; SameSite=Lax'.freeze
257
259
  when true, :strict, 'Strict', :Strict
data/lib/rack.rb CHANGED
@@ -18,7 +18,7 @@ module Rack
18
18
  VERSION.join(".")
19
19
  end
20
20
 
21
- RELEASE = "2.0.7"
21
+ RELEASE = "2.0.9.1"
22
22
 
23
23
  # Return the Rack release as a dotted string.
24
24
  def self.release
data/test/cgi/test.gz CHANGED
File without changes
@@ -1,6 +1,6 @@
1
1
  --AaB03x
2
2
  Content-Type: image/jpeg
3
- Content-Disposition: attachment; name="files"; filename=""human" genome.jpeg"; modification-date="Wed, 12 Feb 1997 16:29:51 -0500";
3
+ Content-Disposition: attachment; name="files"; filename="\"human\" genome.jpeg"; modification-date="Wed, 12 Feb 1997 16:29:51 -0500";
4
4
  Content-Description: a complete map of the human genome
5
5
 
6
6
  contents
@@ -21,6 +21,10 @@ describe Rack::CommonLogger do
21
21
  [200,
22
22
  {"Content-Type" => "text/html", "Content-Length" => "0"},
23
23
  []]}
24
+ app_without_lint = lambda { |env|
25
+ [200,
26
+ { "content-type" => "text/html", "content-length" => length.to_s },
27
+ [obj]]}
24
28
 
25
29
  it "log to rack.errors by default" do
26
30
  res = Rack::MockRequest.new(Rack::CommonLogger.new(app)).get("/")
@@ -85,6 +89,14 @@ describe Rack::CommonLogger do
85
89
  (0..1).must_include duration.to_f
86
90
  end
87
91
 
92
+ it "escapes non printable characters except newline" do
93
+ logdev = StringIO.new
94
+ log = Logger.new(logdev)
95
+ Rack::MockRequest.new(Rack::CommonLogger.new(app_without_lint, log)).request("GET\b", "/hello")
96
+
97
+ logdev.string.must_match(/GET\\x8 \/hello/)
98
+ end
99
+
88
100
  def length
89
101
  123
90
102
  end
data/test/spec_lint.rb CHANGED
@@ -96,6 +96,11 @@ describe Rack::Lint do
96
96
  }.must_raise(Rack::Lint::LintError).
97
97
  message.must_match(/REQUEST_METHOD/)
98
98
 
99
+ lambda {
100
+ Rack::Lint.new(nil).call(env("REQUEST_METHOD" => "OOPS?\b!"))
101
+ }.must_raise(Rack::Lint::LintError).
102
+ message.must_match(/OOPS\?\\/)
103
+
99
104
  lambda {
100
105
  Rack::Lint.new(nil).call(env("SCRIPT_NAME" => "howdy"))
101
106
  }.must_raise(Rack::Lint::LintError).
@@ -381,19 +381,6 @@ describe Rack::Multipart do
381
381
  params["files"][:tempfile].read.must_equal "contents"
382
382
  end
383
383
 
384
- it "parse filename with unescaped quotes" do
385
- env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_unescaped_quotes))
386
- params = Rack::Multipart.parse_multipart(env)
387
- params["files"][:type].must_equal "application/octet-stream"
388
- params["files"][:filename].must_equal "escape \"quotes"
389
- params["files"][:head].must_equal "Content-Disposition: form-data; " +
390
- "name=\"files\"; " +
391
- "filename=\"escape \"quotes\"\r\n" +
392
- "Content-Type: application/octet-stream\r\n"
393
- params["files"][:name].must_equal "files"
394
- params["files"][:tempfile].read.must_equal "contents"
395
- end
396
-
397
384
  it "parse filename with escaped quotes and modification param" do
398
385
  env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_escaped_quotes_and_modification_param))
399
386
  params = Rack::Multipart.parse_multipart(env)
@@ -402,7 +389,7 @@ describe Rack::Multipart do
402
389
  params["files"][:head].must_equal "Content-Type: image/jpeg\r\n" +
403
390
  "Content-Disposition: attachment; " +
404
391
  "name=\"files\"; " +
405
- "filename=\"\"human\" genome.jpeg\"; " +
392
+ "filename=\"\\\"human\\\" genome.jpeg\"; " +
406
393
  "modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\";\r\n" +
407
394
  "Content-Description: a complete map of the human genome\r\n"
408
395
  params["files"][:name].must_equal "files"
@@ -115,6 +115,24 @@ describe Rack::Response do
115
115
  response["Set-Cookie"].must_equal "foo=bar"
116
116
  end
117
117
 
118
+ it "can set SameSite cookies with symbol value :none" do
119
+ response = Rack::Response.new
120
+ response.set_cookie "foo", { value: "bar", same_site: :none }
121
+ response["Set-Cookie"].must_equal "foo=bar; SameSite=None"
122
+ end
123
+
124
+ it "can set SameSite cookies with symbol value :None" do
125
+ response = Rack::Response.new
126
+ response.set_cookie "foo", { value: "bar", same_site: :None }
127
+ response["Set-Cookie"].must_equal "foo=bar; SameSite=None"
128
+ end
129
+
130
+ it "can set SameSite cookies with string value 'None'" do
131
+ response = Rack::Response.new
132
+ response.set_cookie "foo", { value: "bar", same_site: "None" }
133
+ response["Set-Cookie"].must_equal "foo=bar; SameSite=None"
134
+ end
135
+
118
136
  it "can set SameSite cookies with symbol value :lax" do
119
137
  response = Rack::Response.new
120
138
  response.set_cookie "foo", {:value => "bar", :same_site => :lax}
@@ -226,15 +226,52 @@ begin
226
226
  req = Rack::MockRequest.new(pool)
227
227
 
228
228
  res0 = req.get("/")
229
- session_id = (cookie = res0["Set-Cookie"])[session_match, 1]
230
- ses0 = pool.pool.get(session_id, true)
229
+ session_id = Rack::Session::SessionId.new (cookie = res0["Set-Cookie"])[session_match, 1]
230
+ ses0 = pool.pool.get(session_id.private_id, true)
231
231
 
232
232
  req.get("/", "HTTP_COOKIE" => cookie)
233
- ses1 = pool.pool.get(session_id, true)
233
+ ses1 = pool.pool.get(session_id.private_id, true)
234
234
 
235
235
  ses1.wont_equal ses0
236
236
  end
237
237
 
238
+ it "can read the session with the legacy id" do
239
+ pool = Rack::Session::Memcache.new(incrementor)
240
+ req = Rack::MockRequest.new(pool)
241
+
242
+ res0 = req.get("/")
243
+ cookie = res0["Set-Cookie"]
244
+ session_id = Rack::Session::SessionId.new cookie[session_match, 1]
245
+ ses0 = pool.pool.get(session_id.private_id, true)
246
+ pool.pool.set(session_id.public_id, ses0, 0, true)
247
+ pool.pool.delete(session_id.private_id)
248
+
249
+ res1 = req.get("/", "HTTP_COOKIE" => cookie)
250
+ res1["Set-Cookie"].must_be_nil
251
+ res1.body.must_equal '{"counter"=>2}'
252
+ pool.pool.get(session_id.private_id, true).wont_be_nil
253
+ end
254
+
255
+ it "drops the session in the legacy id as well" do
256
+ pool = Rack::Session::Memcache.new(incrementor)
257
+ req = Rack::MockRequest.new(pool)
258
+ drop = Rack::Utils::Context.new(pool, drop_session)
259
+ dreq = Rack::MockRequest.new(drop)
260
+
261
+ res0 = req.get("/")
262
+ cookie = res0["Set-Cookie"]
263
+ session_id = Rack::Session::SessionId.new cookie[session_match, 1]
264
+ ses0 = pool.pool.get(session_id.private_id, true)
265
+ pool.pool.set(session_id.public_id, ses0, 0, true)
266
+ pool.pool.delete(session_id.private_id)
267
+
268
+ res2 = dreq.get("/", "HTTP_COOKIE" => cookie)
269
+ res2["Set-Cookie"].must_be_nil
270
+ res2.body.must_equal '{"counter"=>2}'
271
+ pool.pool.get(session_id.private_id, true).must_be_nil
272
+ pool.pool.get(session_id.public_id, true).must_be_nil
273
+ end
274
+
238
275
  # anyone know how to do this better?
239
276
  it "cleanly merges sessions when multithreaded" do
240
277
  skip 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
+
@@ -6,7 +6,7 @@ require 'rack/session/pool'
6
6
 
7
7
  describe Rack::Session::Pool do
8
8
  session_key = Rack::Session::Pool::DEFAULT_OPTIONS[:key]
9
- session_match = /#{session_key}=[0-9a-fA-F]+;/
9
+ session_match = /#{session_key}=([0-9a-fA-F]+);/
10
10
 
11
11
  incrementor = lambda do |env|
12
12
  env["rack.session"]["counter"] ||= 0
@@ -14,7 +14,7 @@ describe Rack::Session::Pool do
14
14
  Rack::Response.new(env["rack.session"].inspect).to_a
15
15
  end
16
16
 
17
- session_id = Rack::Lint.new(lambda do |env|
17
+ get_session_id = Rack::Lint.new(lambda do |env|
18
18
  Rack::Response.new(env["rack.session"].inspect).to_a
19
19
  end)
20
20
 
@@ -143,6 +143,43 @@ describe Rack::Session::Pool do
143
143
  pool.pool.size.must_equal 1
144
144
  end
145
145
 
146
+ it "can read the session with the legacy id" do
147
+ pool = Rack::Session::Pool.new(incrementor)
148
+ req = Rack::MockRequest.new(pool)
149
+
150
+ res0 = req.get("/")
151
+ cookie = res0["Set-Cookie"]
152
+ session_id = Rack::Session::SessionId.new cookie[session_match, 1]
153
+ ses0 = pool.pool[session_id.private_id]
154
+ pool.pool[session_id.public_id] = ses0
155
+ pool.pool.delete(session_id.private_id)
156
+
157
+ res1 = req.get("/", "HTTP_COOKIE" => cookie)
158
+ res1["Set-Cookie"].must_be_nil
159
+ res1.body.must_equal '{"counter"=>2}'
160
+ pool.pool[session_id.private_id].wont_be_nil
161
+ end
162
+
163
+ it "drops the session in the legacy id as well" do
164
+ pool = Rack::Session::Pool.new(incrementor)
165
+ req = Rack::MockRequest.new(pool)
166
+ drop = Rack::Utils::Context.new(pool, drop_session)
167
+ dreq = Rack::MockRequest.new(drop)
168
+
169
+ res0 = req.get("/")
170
+ cookie = res0["Set-Cookie"]
171
+ session_id = Rack::Session::SessionId.new cookie[session_match, 1]
172
+ ses0 = pool.pool[session_id.private_id]
173
+ pool.pool[session_id.public_id] = ses0
174
+ pool.pool.delete(session_id.private_id)
175
+
176
+ res2 = dreq.get("/", "HTTP_COOKIE" => cookie)
177
+ res2["Set-Cookie"].must_be_nil
178
+ res2.body.must_equal '{"counter"=>2}'
179
+ pool.pool[session_id.private_id].must_be_nil
180
+ pool.pool[session_id.public_id].must_be_nil
181
+ end
182
+
146
183
  # anyone know how to do this better?
147
184
  it "should merge sessions when multithreaded" do
148
185
  unless $DEBUG
@@ -191,7 +228,7 @@ describe Rack::Session::Pool do
191
228
  end
192
229
 
193
230
  it "does not return a cookie if cookie was not written (only read)" do
194
- app = Rack::Session::Pool.new(session_id)
231
+ app = Rack::Session::Pool.new(get_session_id)
195
232
  res = Rack::MockRequest.new(app).get("/")
196
233
  res["Set-Cookie"].must_be_nil
197
234
  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: 2.0.7
4
+ version: 2.0.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Leah Neukirchen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-04-02 00:00:00.000000000 Z
11
+ date: 2022-05-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -239,6 +239,7 @@ files:
239
239
  - test/spec_session_abstract_session_hash.rb
240
240
  - test/spec_session_cookie.rb
241
241
  - test/spec_session_memcache.rb
242
+ - test/spec_session_persisted_secure_secure_session_hash.rb
242
243
  - test/spec_session_pool.rb
243
244
  - test/spec_show_exceptions.rb
244
245
  - test/spec_show_status.rb
@@ -274,60 +275,60 @@ required_rubygems_version: !ruby/object:Gem::Requirement
274
275
  - !ruby/object:Gem::Version
275
276
  version: '0'
276
277
  requirements: []
277
- rubyforge_project:
278
- rubygems_version: 2.6.13
278
+ rubygems_version: 3.0.3.1
279
279
  signing_key:
280
280
  specification_version: 4
281
281
  summary: a modular Ruby webserver interface
282
282
  test_files:
283
- - test/spec_auth_basic.rb
284
- - test/spec_auth_digest.rb
285
- - test/spec_body_proxy.rb
286
- - test/spec_builder.rb
287
- - test/spec_cascade.rb
288
- - test/spec_cgi.rb
289
- - test/spec_chunked.rb
290
- - test/spec_common_logger.rb
291
- - test/spec_conditional_get.rb
292
- - test/spec_config.rb
293
- - test/spec_content_length.rb
294
- - test/spec_content_type.rb
283
+ - test/spec_multipart.rb
295
284
  - test/spec_deflater.rb
296
- - test/spec_directory.rb
285
+ - test/spec_static.rb
286
+ - test/spec_session_cookie.rb
287
+ - test/spec_session_pool.rb
297
288
  - test/spec_etag.rb
298
- - test/spec_events.rb
299
- - test/spec_fastcgi.rb
300
- - test/spec_file.rb
289
+ - test/spec_version.rb
301
290
  - test/spec_handler.rb
302
- - test/spec_head.rb
303
- - test/spec_lint.rb
304
- - test/spec_lobster.rb
305
- - test/spec_lock.rb
306
- - test/spec_logger.rb
307
- - test/spec_media_type.rb
308
- - test/spec_method_override.rb
291
+ - test/spec_thin.rb
292
+ - test/spec_session_abstract_id.rb
309
293
  - test/spec_mime.rb
310
- - test/spec_mock.rb
311
- - test/spec_multipart.rb
312
- - test/spec_null_logger.rb
313
294
  - test/spec_recursive.rb
295
+ - test/spec_null_logger.rb
296
+ - test/spec_media_type.rb
297
+ - test/spec_cgi.rb
298
+ - test/spec_method_override.rb
299
+ - test/spec_content_type.rb
300
+ - test/spec_session_abstract_session_hash.rb
314
301
  - test/spec_request.rb
315
- - test/spec_response.rb
316
- - test/spec_rewindable_input.rb
302
+ - test/spec_chunked.rb
303
+ - test/spec_show_exceptions.rb
317
304
  - test/spec_runtime.rb
305
+ - test/spec_session_persisted_secure_secure_session_hash.rb
306
+ - test/spec_fastcgi.rb
307
+ - test/spec_common_logger.rb
308
+ - test/spec_builder.rb
309
+ - test/spec_config.rb
310
+ - test/spec_utils.rb
318
311
  - test/spec_sendfile.rb
312
+ - test/spec_lobster.rb
313
+ - test/spec_lint.rb
314
+ - test/spec_conditional_get.rb
315
+ - test/spec_tempfile_reaper.rb
316
+ - test/spec_mock.rb
319
317
  - test/spec_server.rb
320
- - test/spec_session_abstract_id.rb
321
- - test/spec_session_abstract_session_hash.rb
322
- - test/spec_session_cookie.rb
323
- - test/spec_session_memcache.rb
324
- - test/spec_session_pool.rb
325
- - test/spec_show_exceptions.rb
318
+ - test/spec_directory.rb
319
+ - test/spec_webrick.rb
320
+ - test/spec_response.rb
321
+ - test/spec_file.rb
326
322
  - test/spec_show_status.rb
327
- - test/spec_static.rb
328
- - test/spec_tempfile_reaper.rb
329
- - test/spec_thin.rb
323
+ - test/spec_body_proxy.rb
324
+ - test/spec_logger.rb
325
+ - test/spec_auth_digest.rb
330
326
  - test/spec_urlmap.rb
331
- - test/spec_utils.rb
332
- - test/spec_version.rb
333
- - test/spec_webrick.rb
327
+ - test/spec_events.rb
328
+ - test/spec_cascade.rb
329
+ - test/spec_auth_basic.rb
330
+ - test/spec_head.rb
331
+ - test/spec_lock.rb
332
+ - test/spec_rewindable_input.rb
333
+ - test/spec_session_memcache.rb
334
+ - test/spec_content_length.rb