rack-protection 2.0.3 → 3.0.6

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.
@@ -0,0 +1,273 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'openssl'
4
+ require 'zlib'
5
+ require 'json'
6
+ require 'rack/request'
7
+ require 'rack/response'
8
+ require 'rack/session/abstract/id'
9
+
10
+ module Rack
11
+ module Protection
12
+ # Rack::Protection::EncryptedCookie provides simple cookie based session management.
13
+ # By default, the session is a Ruby Hash stored as base64 encoded marshalled
14
+ # data set to :key (default: rack.session). The object that encodes the
15
+ # session data is configurable and must respond to +encode+ and +decode+.
16
+ # Both methods must take a string and return a string.
17
+ #
18
+ # When the secret key is set, cookie data is checked for data integrity.
19
+ # The old_secret key is also accepted and allows graceful secret rotation.
20
+ # A legacy_hmac_secret is also accepted and is used to upgrade existing
21
+ # sessions to the new encryption scheme.
22
+ #
23
+ # There is also a legacy_hmac_coder option which can be set if a non-default
24
+ # coder was used for legacy session cookies.
25
+ #
26
+ # Example:
27
+ #
28
+ # use Rack::Protection::EncryptedCookie,
29
+ # :key => 'rack.session',
30
+ # :domain => 'foo.com',
31
+ # :path => '/',
32
+ # :expire_after => 2592000,
33
+ # :secret => 'change_me',
34
+ # :old_secret => 'old_secret'
35
+ #
36
+ # All parameters are optional.
37
+ #
38
+ # Example using legacy HMAC options
39
+ #
40
+ # Rack::Protection:EncryptedCookie.new(application, {
41
+ # # The secret used for legacy HMAC cookies
42
+ # legacy_hmac_secret: 'legacy secret',
43
+ # # legacy_hmac_coder will default to Rack::Protection::EncryptedCookie::Base64::Marshal
44
+ # legacy_hmac_coder: Rack::Protection::EncryptedCookie::Identity.new,
45
+ # # legacy_hmac will default to OpenSSL::Digest::SHA1
46
+ # legacy_hmac: OpenSSL::Digest::SHA256
47
+ # })
48
+ #
49
+ # Example of a cookie with no encoding:
50
+ #
51
+ # Rack::Protection::EncryptedCookie.new(application, {
52
+ # :coder => Rack::Protection::EncryptedCookie::Identity.new
53
+ # })
54
+ #
55
+ # Example of a cookie with custom encoding:
56
+ #
57
+ # Rack::Protection::EncryptedCookie.new(application, {
58
+ # :coder => Class.new {
59
+ # def encode(str); str.reverse; end
60
+ # def decode(str); str.reverse; end
61
+ # }.new
62
+ # })
63
+ #
64
+ class EncryptedCookie < Rack::Session::Abstract::Persisted
65
+ # Encode session cookies as Base64
66
+ class Base64
67
+ def encode(str)
68
+ [str].pack('m0')
69
+ end
70
+
71
+ def decode(str)
72
+ str.unpack1('m')
73
+ end
74
+
75
+ # Encode session cookies as Marshaled Base64 data
76
+ class Marshal < Base64
77
+ def encode(str)
78
+ super(::Marshal.dump(str))
79
+ end
80
+
81
+ def decode(str)
82
+ return unless str
83
+
84
+ begin
85
+ ::Marshal.load(super(str))
86
+ rescue StandardError
87
+ nil
88
+ end
89
+ end
90
+ end
91
+
92
+ # N.B. Unlike other encoding methods, the contained objects must be a
93
+ # valid JSON composite type, either a Hash or an Array.
94
+ class JSON < Base64
95
+ def encode(obj)
96
+ super(::JSON.dump(obj))
97
+ end
98
+
99
+ def decode(str)
100
+ return unless str
101
+
102
+ begin
103
+ ::JSON.parse(super(str))
104
+ rescue StandardError
105
+ nil
106
+ end
107
+ end
108
+ end
109
+
110
+ class ZipJSON < Base64
111
+ def encode(obj)
112
+ super(Zlib::Deflate.deflate(::JSON.dump(obj)))
113
+ end
114
+
115
+ def decode(str)
116
+ return unless str
117
+
118
+ ::JSON.parse(Zlib::Inflate.inflate(super(str)))
119
+ rescue StandardError
120
+ nil
121
+ end
122
+ end
123
+ end
124
+
125
+ # Use no encoding for session cookies
126
+ class Identity
127
+ def encode(str); str; end
128
+ def decode(str); str; end
129
+ end
130
+
131
+ class Marshal
132
+ def encode(str)
133
+ ::Marshal.dump(str)
134
+ end
135
+
136
+ def decode(str)
137
+ ::Marshal.load(str) if str
138
+ end
139
+ end
140
+
141
+ attr_reader :coder
142
+
143
+ def initialize(app, options = {})
144
+ # Assume keys are hex strings and convert them to raw byte strings for
145
+ # actual key material
146
+ @secrets = options.values_at(:secret, :old_secret).compact.map do |secret|
147
+ [secret].pack('H*')
148
+ end
149
+
150
+ warn <<-MSG unless secure?(options)
151
+ SECURITY WARNING: No secret option provided to Rack::Protection::EncryptedCookie.
152
+ This poses a security threat. It is strongly recommended that you
153
+ provide a secret to prevent exploits that may be possible from crafted
154
+ cookies. This will not be supported in future versions of Rack, and
155
+ future versions will even invalidate your existing user cookies.
156
+
157
+ Called from: #{caller[0]}.
158
+ MSG
159
+
160
+ warn <<-MSG if @secrets.first && @secrets.first.length < 32
161
+ SECURITY WARNING: Your secret is not long enough. It must be at least
162
+ 32 bytes long and securely random. To generate such a key for use
163
+ you can run the following command:
164
+
165
+ ruby -rsecurerandom -e 'p SecureRandom.hex(32)'
166
+
167
+ Called from: #{caller[0]}.
168
+ MSG
169
+
170
+ if options.key?(:legacy_hmac_secret)
171
+ @legacy_hmac = options.fetch(:legacy_hmac, OpenSSL::Digest::SHA1)
172
+
173
+ # Multiply the :digest_length: by 2 because this value is the length of
174
+ # the digest in bytes but session digest strings are encoded as hex
175
+ # strings
176
+ @legacy_hmac_length = @legacy_hmac.new.digest_length * 2
177
+ @legacy_hmac_secret = options[:legacy_hmac_secret]
178
+ @legacy_hmac_coder = (options[:legacy_hmac_coder] ||= Base64::Marshal.new)
179
+ else
180
+ @legacy_hmac = false
181
+ end
182
+
183
+ # If encryption is used we can just use a default Marshal encoder
184
+ # without Base64 encoding the results.
185
+ #
186
+ # If no encryption is used, rely on the previous default (Base64::Marshal)
187
+ @coder = (options[:coder] ||= (@secrets.any? ? Marshal.new : Base64::Marshal.new))
188
+
189
+ super(app, options.merge!(cookie_only: true))
190
+ end
191
+
192
+ private
193
+
194
+ def find_session(req, _sid)
195
+ data = unpacked_cookie_data(req)
196
+ data = persistent_session_id!(data)
197
+ [data['session_id'], data]
198
+ end
199
+
200
+ def extract_session_id(request)
201
+ unpacked_cookie_data(request)['session_id']
202
+ end
203
+
204
+ def unpacked_cookie_data(request)
205
+ request.fetch_header(RACK_SESSION_UNPACKED_COOKIE_DATA) do |k|
206
+ session_data = cookie_data = request.cookies[@key]
207
+
208
+ # Try to decrypt with the first secret, if that returns nil, try
209
+ # with old_secret
210
+ unless @secrets.empty?
211
+ session_data = Rack::Protection::Encryptor.decrypt_message(cookie_data, @secrets.first)
212
+ session_data ||= Rack::Protection::Encryptor.decrypt_message(cookie_data, @secrets[1]) if @secrets.size > 1
213
+ end
214
+
215
+ # If session_data is still nil, are there is a legacy HMAC
216
+ # configured, try verify and parse the cookie that way
217
+ if !session_data && @legacy_hmac
218
+ digest = cookie_data.slice!(-@legacy_hmac_length..-1)
219
+ cookie_data.slice!(-2..-1) # remove double dash
220
+ session_data = cookie_data if digest_match?(cookie_data, digest)
221
+
222
+ # Decode using legacy HMAC decoder
223
+ request.set_header(k, @legacy_hmac_coder.decode(session_data) || {})
224
+ else
225
+ request.set_header(k, coder.decode(session_data) || {})
226
+ end
227
+ end
228
+ end
229
+
230
+ def persistent_session_id!(data, sid = nil)
231
+ data ||= {}
232
+ data['session_id'] ||= sid || generate_sid
233
+ data
234
+ end
235
+
236
+ def write_session(req, session_id, session, _options)
237
+ session = session.merge('session_id' => session_id)
238
+ session_data = coder.encode(session)
239
+
240
+ unless @secrets.empty?
241
+ session_data = Rack::Protection::Encryptor.encrypt_message(session_data, @secrets.first)
242
+ end
243
+
244
+ if session_data.size > (4096 - @key.size)
245
+ req.get_header(RACK_ERRORS).puts('Warning! Rack::Protection::EncryptedCookie data size exceeds 4K.')
246
+ nil
247
+ else
248
+ session_data
249
+ end
250
+ end
251
+
252
+ def delete_session(_req, _session_id, options)
253
+ # Nothing to do here, data is in the client
254
+ generate_sid unless options[:drop]
255
+ end
256
+
257
+ def digest_match?(data, digest)
258
+ return false unless data && digest
259
+
260
+ Rack::Utils.secure_compare(digest, generate_hmac(data))
261
+ end
262
+
263
+ def generate_hmac(data)
264
+ OpenSSL::HMAC.hexdigest(@legacy_hmac.new, @legacy_hmac_secret, data)
265
+ end
266
+
267
+ def secure?(options)
268
+ @secrets.size >= 1 ||
269
+ (options[:coder] && options[:let_coder_handle_secure_encoding])
270
+ end
271
+ end
272
+ end
273
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'openssl'
4
+
5
+ module Rack
6
+ module Protection
7
+ module Encryptor
8
+ CIPHER = 'aes-256-gcm'
9
+ DELIMITER = '--'
10
+
11
+ def self.base64_encode(str)
12
+ [str].pack('m0')
13
+ end
14
+
15
+ def self.base64_decode(str)
16
+ str.unpack1('m0')
17
+ end
18
+
19
+ def self.encrypt_message(data, secret, auth_data = '')
20
+ raise ArgumentError, 'data cannot be nil' if data.nil?
21
+
22
+ cipher = OpenSSL::Cipher.new(CIPHER)
23
+ cipher.encrypt
24
+ cipher.key = secret[0, cipher.key_len]
25
+
26
+ # Rely on OpenSSL for the initialization vector
27
+ iv = cipher.random_iv
28
+
29
+ # This must be set to properly use AES GCM for the OpenSSL module
30
+ cipher.auth_data = auth_data
31
+
32
+ cipher_text = cipher.update(data)
33
+ cipher_text << cipher.final
34
+
35
+ "#{base64_encode cipher_text}#{DELIMITER}#{base64_encode iv}#{DELIMITER}#{base64_encode cipher.auth_tag}"
36
+ end
37
+
38
+ def self.decrypt_message(data, secret)
39
+ return unless data
40
+
41
+ cipher = OpenSSL::Cipher.new(CIPHER)
42
+ cipher_text, iv, auth_tag = data.split(DELIMITER, 3).map! { |v| base64_decode(v) }
43
+
44
+ # This check is from ActiveSupport::MessageEncryptor
45
+ # see: https://github.com/ruby/openssl/issues/63
46
+ return if auth_tag.nil? || auth_tag.bytes.length != 16
47
+
48
+ cipher.decrypt
49
+ cipher.key = secret[0, cipher.key_len]
50
+ cipher.iv = iv
51
+ cipher.auth_tag = auth_tag
52
+ cipher.auth_data = ''
53
+
54
+ decrypted_data = cipher.update(cipher_text)
55
+ decrypted_data << cipher.final
56
+ decrypted_data
57
+ rescue OpenSSL::Cipher::CipherError, TypeError, ArgumentError
58
+ nil
59
+ end
60
+ end
61
+ end
62
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack/protection'
2
4
  require 'rack/utils'
3
5
  require 'tempfile'
@@ -15,8 +17,7 @@ module Rack
15
17
  # More infos:: http://en.wikipedia.org/wiki/Cross-site_scripting
16
18
  #
17
19
  # Automatically escapes Rack::Request#params so they can be embedded in HTML
18
- # or JavaScript without any further issues. Calls +html_safe+ on the escaped
19
- # strings if defined, to avoid double-escaping in Rails.
20
+ # or JavaScript without any further issues.
20
21
  #
21
22
  # Options:
22
23
  # escape:: What escaping modes to use, should be Symbol or Array of Symbols.
@@ -29,8 +30,8 @@ module Rack
29
30
  public :escape_html
30
31
  end
31
32
 
32
- default_options :escape => :html,
33
- :escaper => defined?(EscapeUtils) ? EscapeUtils : self
33
+ default_options escape: :html,
34
+ escaper: defined?(EscapeUtils) ? EscapeUtils : self
34
35
 
35
36
  def initialize(*)
36
37
  super
@@ -41,15 +42,19 @@ module Rack
41
42
  @javascript = modes.include? :javascript
42
43
  @url = modes.include? :url
43
44
 
44
- if @javascript and not @escaper.respond_to? :escape_javascript
45
- fail("Use EscapeUtils for JavaScript escaping.")
46
- end
45
+ return unless @javascript && (!@escaper.respond_to? :escape_javascript)
46
+
47
+ raise('Use EscapeUtils for JavaScript escaping.')
47
48
  end
48
49
 
49
50
  def call(env)
50
51
  request = Request.new(env)
51
52
  get_was = handle(request.GET)
52
- post_was = handle(request.POST) rescue nil
53
+ post_was = begin
54
+ handle(request.POST)
55
+ rescue StandardError
56
+ nil
57
+ end
53
58
  app.call env
54
59
  ensure
55
60
  request.GET.replace get_was if get_was
@@ -68,13 +73,12 @@ module Rack
68
73
  when Array then object.map { |o| escape(o) }
69
74
  when String then escape_string(object)
70
75
  when Tempfile then object
71
- else nil
72
76
  end
73
77
  end
74
78
 
75
79
  def escape_hash(hash)
76
80
  hash = hash.dup
77
- hash.each { |k,v| hash[k] = escape(v) }
81
+ hash.each { |k, v| hash[k] = escape(v) }
78
82
  hash
79
83
  end
80
84
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack/protection'
2
4
 
3
5
  module Rack
@@ -16,7 +18,7 @@ module Rack
16
18
  # Compatible with rack-csrf.
17
19
  class FormToken < AuthenticityToken
18
20
  def accepts?(env)
19
- env["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest" or super
21
+ env['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest' or super
20
22
  end
21
23
  end
22
24
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack/protection'
2
4
 
3
5
  module Rack
@@ -17,7 +19,7 @@ module Rack
17
19
  # frame. Use :deny to forbid any embedding, :sameorigin
18
20
  # to allow embedding from the same origin (default).
19
21
  class FrameOptions < Base
20
- default_options :frame_options => :sameorigin
22
+ default_options frame_options: :sameorigin
21
23
 
22
24
  def frame_options
23
25
  @frame_options ||= begin
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack/protection'
2
4
 
3
5
  module Rack
@@ -9,17 +11,17 @@ module Rack
9
11
  # http://tools.ietf.org/html/draft-abarth-origin
10
12
  #
11
13
  # Does not accept unsafe HTTP requests when value of Origin HTTP request header
12
- # does not match default or whitelisted URIs.
14
+ # does not match default or permitted URIs.
13
15
  #
14
- # If you want to whitelist a specific domain, you can pass in as the `:origin_whitelist` option:
16
+ # If you want to permit a specific domain, you can pass in as the `:permitted_origins` option:
15
17
  #
16
- # use Rack::Protection, origin_whitelist: ["http://localhost:3000", "http://127.0.01:3000"]
18
+ # use Rack::Protection, permitted_origins: ["http://localhost:3000", "http://127.0.01:3000"]
17
19
  #
18
20
  # The `:allow_if` option can also be set to a proc to use custom allow/deny logic.
19
21
  class HttpOrigin < Base
20
22
  DEFAULT_PORTS = { 'http' => 80, 'https' => 443, 'coffee' => 80 }
21
23
  default_reaction :deny
22
- default_options :allow_if => nil
24
+ default_options allow_if: nil
23
25
 
24
26
  def base_url(env)
25
27
  request = Rack::Request.new(env)
@@ -29,12 +31,13 @@ module Rack
29
31
 
30
32
  def accepts?(env)
31
33
  return true if safe? env
32
- return true unless origin = env['HTTP_ORIGIN']
34
+ return true unless (origin = env['HTTP_ORIGIN'])
33
35
  return true if base_url(env) == origin
34
- return true if options[:allow_if] && options[:allow_if].call(env)
35
- Array(options[:origin_whitelist]).include? origin
36
- end
36
+ return true if options[:allow_if]&.call(env)
37
37
 
38
+ permitted_origins = options[:permitted_origins]
39
+ Array(permitted_origins).include? origin
40
+ end
38
41
  end
39
42
  end
40
43
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack/protection'
2
4
 
3
5
  module Rack
@@ -13,9 +15,11 @@ module Rack
13
15
 
14
16
  def accepts?(env)
15
17
  return true unless env.include? 'HTTP_X_FORWARDED_FOR'
16
- ips = env['HTTP_X_FORWARDED_FOR'].split(/\s*,\s*/)
17
- return false if env.include? 'HTTP_CLIENT_IP' and not ips.include? env['HTTP_CLIENT_IP']
18
- return false if env.include? 'HTTP_X_REAL_IP' and not ips.include? env['HTTP_X_REAL_IP']
18
+
19
+ ips = env['HTTP_X_FORWARDED_FOR'].split(',').map(&:strip)
20
+ return false if env.include?('HTTP_CLIENT_IP') && (!ips.include? env['HTTP_CLIENT_IP'])
21
+ return false if env.include?('HTTP_X_REAL_IP') && (!ips.include? env['HTTP_X_REAL_IP'])
22
+
19
23
  true
20
24
  end
21
25
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack/protection'
2
4
 
3
5
  module Rack
@@ -17,7 +19,7 @@ module Rack
17
19
  #
18
20
  # The `:allow_if` option can be set to a proc to use custom allow/deny logic.
19
21
  class JsonCsrf < Base
20
- default_options :allow_if => nil
22
+ default_options allow_if: nil
21
23
 
22
24
  alias react deny
23
25
 
@@ -36,8 +38,9 @@ module Rack
36
38
 
37
39
  def has_vector?(request, headers)
38
40
  return false if request.xhr?
39
- return false if options[:allow_if] && options[:allow_if].call(request.env)
40
- return false unless headers['Content-Type'].to_s.split(';', 2).first =~ /^\s*application\/json\s*$/
41
+ return false if options[:allow_if]&.call(request.env)
42
+ return false unless headers['Content-Type'].to_s.split(';', 2).first =~ %r{^\s*application/json\s*$}
43
+
41
44
  origin(request.env).nil? and referrer(request.env) != request.host
42
45
  end
43
46
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack/protection'
2
4
 
3
5
  module Rack
@@ -11,38 +13,31 @@ module Rack
11
13
  # Thus <tt>GET /foo/%2e%2e%2fbar</tt> becomes <tt>GET /bar</tt>.
12
14
  class PathTraversal < Base
13
15
  def call(env)
14
- path_was = env["PATH_INFO"]
15
- env["PATH_INFO"] = cleanup path_was if path_was && !path_was.empty?
16
+ path_was = env['PATH_INFO']
17
+ env['PATH_INFO'] = cleanup path_was if path_was && !path_was.empty?
16
18
  app.call env
17
19
  ensure
18
- env["PATH_INFO"] = path_was
20
+ env['PATH_INFO'] = path_was
19
21
  end
20
22
 
21
23
  def cleanup(path)
22
- if path.respond_to?(:encoding)
23
- # Ruby 1.9+ M17N
24
- encoding = path.encoding
25
- dot = '.'.encode(encoding)
26
- slash = '/'.encode(encoding)
27
- backslash = '\\'.encode(encoding)
28
- else
29
- # Ruby 1.8
30
- dot = '.'
31
- slash = '/'
32
- backslash = '\\'
33
- end
24
+ encoding = path.encoding
25
+ dot = '.'.encode(encoding)
26
+ slash = '/'.encode(encoding)
27
+ backslash = '\\'.encode(encoding)
34
28
 
35
29
  parts = []
36
30
  unescaped = path.gsub(/%2e/i, dot).gsub(/%2f/i, slash).gsub(/%5c/i, backslash)
37
31
  unescaped = unescaped.gsub(backslash, slash)
38
32
 
39
33
  unescaped.split(slash).each do |part|
40
- next if part.empty? or part == dot
34
+ next if part.empty? || (part == dot)
35
+
41
36
  part == '..' ? parts.pop : parts << part
42
37
  end
43
38
 
44
39
  cleaned = slash + parts.join(slash)
45
- cleaned << slash if parts.any? and unescaped =~ %r{/\.{0,2}$}
40
+ cleaned << slash if parts.any? && unescaped =~ (%r{/\.{0,2}$})
46
41
  cleaned
47
42
  end
48
43
  end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rack/protection'
4
+
5
+ module Rack
6
+ module Protection
7
+ ##
8
+ # Prevented attack:: Secret leakage, third party tracking
9
+ # Supported browsers:: mixed support
10
+ # More infos:: https://www.w3.org/TR/referrer-policy/
11
+ # https://caniuse.com/#search=referrer-policy
12
+ #
13
+ # Sets Referrer-Policy header to tell the browser to limit the Referer header.
14
+ #
15
+ # Options:
16
+ # referrer_policy:: The policy to use (default: 'strict-origin-when-cross-origin')
17
+ class ReferrerPolicy < Base
18
+ default_options referrer_policy: 'strict-origin-when-cross-origin'
19
+
20
+ def call(env)
21
+ status, headers, body = @app.call(env)
22
+ headers['Referrer-Policy'] ||= options[:referrer_policy]
23
+ [status, headers, body]
24
+ end
25
+ end
26
+ end
27
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack/protection'
2
4
 
3
5
  module Rack
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack/protection'
2
4
 
3
5
  module Rack
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack/protection'
2
4
 
3
5
  module Rack
@@ -13,23 +15,22 @@ module Rack
13
15
  # spoofed, too, this will not prevent determined hijacking attempts.
14
16
  class SessionHijacking < Base
15
17
  default_reaction :drop_session
16
- default_options :tracking_key => :tracking, :encrypt_tracking => true,
17
- :track => %w[HTTP_USER_AGENT HTTP_ACCEPT_LANGUAGE]
18
+ default_options tracking_key: :tracking,
19
+ track: %w[HTTP_USER_AGENT]
18
20
 
19
21
  def accepts?(env)
20
22
  session = session env
21
23
  key = options[:tracking_key]
22
24
  if session.include? key
23
- session[key].all? { |k,v| v == encrypt(env[k]) }
25
+ session[key].all? { |k, v| v == encode(env[k]) }
24
26
  else
25
27
  session[key] = {}
26
- options[:track].each { |k| session[key][k] = encrypt(env[k]) }
28
+ options[:track].each { |k| session[key][k] = encode(env[k]) }
27
29
  end
28
30
  end
29
31
 
30
- def encrypt(value)
31
- value = value.to_s.downcase
32
- options[:encrypt_tracking] ? super(value) : value
32
+ def encode(value)
33
+ value.to_s.downcase
33
34
  end
34
35
  end
35
36
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack/protection'
2
4
 
3
5
  module Rack
@@ -18,11 +20,11 @@ module Rack
18
20
  # preload:: Allow this domain to be included in browsers HSTS preload list. See https://hstspreload.appspot.com/
19
21
 
20
22
  class StrictTransport < Base
21
- default_options :max_age => 31_536_000, :include_subdomains => false, :preload => false
23
+ default_options max_age: 31_536_000, include_subdomains: false, preload: false
22
24
 
23
25
  def strict_transport
24
26
  @strict_transport ||= begin
25
- strict_transport = 'max-age=' + options[:max_age].to_s
27
+ strict_transport = "max-age=#{options[:max_age]}"
26
28
  strict_transport += '; includeSubDomains' if options[:include_subdomains]
27
29
  strict_transport += '; preload' if options[:preload]
28
30
  strict_transport.to_str