diff --git a/lib/rack/session/encryptor.rb b/lib/rack/session/encryptor.rb index a7f041a..8d861d5 100644 --- a/lib/rack/session/encryptor.rb +++ b/lib/rack/session/encryptor.rb @@ -32,12 +32,12 @@ module Serializable def serialize_payload(message) serialized_data = serializer.dump(message) - return "#{[0].pack('v')}#{serialized_data}" if @options[:pad_size].nil? + return "#{[0].pack('v')}#{serialized_data.force_encoding(Encoding::BINARY)}" if @options[:pad_size].nil? padding_bytes = @options[:pad_size] - (2 + serialized_data.size) % @options[:pad_size] padding_data = SecureRandom.random_bytes(padding_bytes) - "#{[padding_bytes].pack('v')}#{padding_data}#{serialized_data}" + "#{[padding_bytes].pack('v')}#{padding_data}#{serialized_data.force_encoding(Encoding::BINARY)}" end # Return the deserialized message. The first 2 bytes will be read as the @@ -103,7 +103,7 @@ def initialize(secret, opts = {}) serialize_json: false, pad_size: 32, purpose: nil }.update(opts) - @hmac_secret = secret.dup.force_encoding('BINARY') + @hmac_secret = secret.dup.force_encoding(Encoding::BINARY) @cipher_secret = @hmac_secret.slice!(0, 32) @hmac_secret.freeze @@ -250,7 +250,7 @@ def initialize(secret, opts = {}) }.update(opts) @options[:serialize_json] = true # Enforce JSON serialization - @cipher_secret = secret.dup.force_encoding('BINARY').slice!(0, 32) + @cipher_secret = secret.dup.force_encoding(Encoding::BINARY).slice!(0, 32) @cipher_secret.freeze end diff --git a/test/spec_session_cookie.rb b/test/spec_session_cookie.rb index bda4af3..219c286 100644 --- a/test/spec_session_cookie.rb +++ b/test/spec_session_cookie.rb @@ -212,7 +212,7 @@ def decode(str); @calls << :decode; JSON.parse(str); end response.body.must_equal '{"counter"=>1}' identity.calls.must_equal [:encode] - response = response_for(app: [incrementor, { coder: identity }], :cookie=>response["Set-Cookie"].split(';', 2).first) + response_for(app: [incrementor, { coder: identity }], cookie: response["Set-Cookie"].split(';', 2).first) identity.calls.must_equal [:encode, :decode, :encode] end @@ -223,8 +223,8 @@ def decode(str); @calls << :decode; JSON.parse(str); end end it "passes through same_site option to session cookie" do - response = response_for(app: [incrementor, same_site: :none]) - response["Set-Cookie"].must_include "SameSite=None" + response = response_for(app: [incrementor, { same_site: :none }]) + assert(response["Set-Cookie"].include?("SameSite=None") || response["Set-Cookie"].include?("samesite=none")) end it "allows using a lambda to specify same_site option, because some browsers require different settings" do @@ -232,11 +232,11 @@ def decode(str); @calls << :decode; JSON.parse(str); end # https://www.chromium.org/updates/same-site/incompatible-clients # https://gist.github.com/bnorton/7dee72023787f367c48b3f5c2d71540f - response = response_for(app: [incrementor, same_site: lambda { |req, res| :none }]) - response["Set-Cookie"].must_include "SameSite=None" + response = response_for(app: [incrementor, { same_site: lambda { |req, res| :none } }]) + assert(response["Set-Cookie"].include?("SameSite=None") || response["Set-Cookie"].include?("samesite=none")) - response = response_for(app: [incrementor, same_site: lambda { |req, res| :lax }]) - response["Set-Cookie"].must_include "SameSite=Lax" + response = response_for(app: [incrementor, { same_site: lambda { |req, res| :lax } }]) + assert(response["Set-Cookie"].include?("SameSite=Lax") || response["Set-Cookie"].include?("samesite=lax")) end it "loads from a cookie" do diff --git a/test/spec_session_encryptor.rb b/test/spec_session_encryptor.rb index 2288120..bc19373 100644 --- a/test/spec_session_encryptor.rb +++ b/test/spec_session_encryptor.rb @@ -58,6 +58,18 @@ def self.included(_base) encryptor.decrypt(message).must_equal({ 'foo' => 'bar' }) end + # The V1 encryptor defaults to the Marshal serializer, while the V2 + # encryptor always uses the JSON serializer. This means that we are + # indirectly covering both serializers. + it 'decrypts an encrypted message with UTF-8 data' do + encryptor = encryptor_class.new(@secret) + + encrypted_message = encryptor.encrypt({ 'foo' => 'bar 😀' }) + decrypted_message = encryptor.decrypt(encrypted_message) + + decrypted_message.must_equal({ 'foo' => 'bar 😀' }) + end + it 'decrypts raises InvalidSignature without purpose' do encryptor = encryptor_class.new(@secret, purpose: 'testing') other_encryptor = encryptor_class.new(@secret) diff --git a/test/spec_session_pool.rb b/test/spec_session_pool.rb index e0970b3..f046dc4 100644 --- a/test/spec_session_pool.rb +++ b/test/spec_session_pool.rb @@ -209,19 +209,19 @@ pool.same_site.must_equal :none req = Rack::MockRequest.new(pool) res = req.get("/") - res["Set-Cookie"].must_include "SameSite=None" + assert(res["Set-Cookie"].include?("SameSite=None") || res["Set-Cookie"].include?("samesite=none")) end it "allows using a lambda to specify same_site option, because some browsers require different settings" do pool = Rack::Session::Pool.new(incrementor, same_site: lambda { |req, res| :none }) req = Rack::MockRequest.new(pool) res = req.get("/") - res["Set-Cookie"].must_include "SameSite=None" + assert(res["Set-Cookie"].include?("SameSite=None") || res["Set-Cookie"].include?("samesite=none")) pool = Rack::Session::Pool.new(incrementor, same_site: lambda { |req, res| :lax }) req = Rack::MockRequest.new(pool) res = req.get("/") - res["Set-Cookie"].must_include "SameSite=Lax" + assert(res["Set-Cookie"].include?("SameSite=Lax") || res["Set-Cookie"].include?("samesite=lax")) end # anyone know how to do this better?