rack-protection-maximum_cookie 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e0047e3e925b373963124935290d165fe6ace97a
4
+ data.tar.gz: 260e3fc72618ce92fd0e0bb9b880945bb754eee6
5
+ SHA512:
6
+ metadata.gz: 7027cac6bc57aeb935ecbd7e2cda7cddea6a71c19339edce7090d57fc08396be7f6758357b6b314982a49b1ee3dc8e03c41fa7dd7ab6e447130e167f2597bfb5
7
+ data.tar.gz: ee7e315fcc2652327321244d62e4de4797daa553b8a1698e202aa9a455203c9831338a311027a9c74a03eb10765d35b8c673a810915978e0fcbac7558c48bc00
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
@@ -0,0 +1,24 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - ruby-head
5
+ - 2.4.2
6
+ - 2.3.5
7
+ - 2.2.8
8
+ - 2.1.10
9
+ - jruby-head
10
+ - jruby-9.1.13.0
11
+ - jruby-9.0.5.0
12
+ env:
13
+ - RACK_VERSION='~> 2.0.0'
14
+ - RACK_VERSION='~> 1.6.8'
15
+ - RACK_VERSION='~> 1.5.5'
16
+ - RACK_VERSION='~> 1.4.7'
17
+ before_install: gem install bundler -v 1.15.4
18
+ matrix:
19
+ allow_failures:
20
+ - rvm: ruby-head
21
+ - rvm: jruby-head
22
+ exclude:
23
+ - rvm: 2.1.10
24
+ env: RACK_VERSION='~> 2.0.0'
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Mike Pastore
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,186 @@
1
+ # Rack::Protection::MaximumCookie
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/rack-protection-maximum_cookie.svg)](https://badge.fury.io/rb/rack-protection-maximum_cookie)
4
+ [![Build Status](https://travis-ci.org/mwpastore/rack-protection-maximum_cookie.svg?branch=master)](https://travis-ci.org/mwpastore/rack-protection-maximum_cookie)
5
+
6
+ Some bugs in Rack may cause cookies to be silently dropped—or worse,
7
+ truncated, leading to token leakage (i.e. transmission of a private session
8
+ over a insecure connection), cross-site scripting vulnerabilities, and/or
9
+ cross-site request forgery vulnerabilities. This gem provides a middleware that
10
+ tries to prevent these scenarios from occurring.
11
+
12
+ ### Caveats
13
+
14
+ 1. Most modern browsers no longer have a [per-domain cookie size limit][1],
15
+ so if you only care about modern browsers, go ahead and set `:per_domain?`
16
+ to false. You'll benefit from the per-domain cookie limit and per-cookie
17
+ bytesize limit (since most browsers still have some form of these limits,
18
+ and Rack's built-in check for them is nonexistent in the former case and
19
+ not implemented correctly in the latter).
20
+
21
+ 2. Browsers that do have per-domain cookie limits enforce these limits
22
+ client-side, cumulatively over time. If you were to set a new cookie, every
23
+ few minutes, one at a time, eventually you'd hit a limit. Or another
24
+ service could be setting cookies for the domain your service listens on. If
25
+ this is a concern for your app, see the `:stateful?` option below.
26
+
27
+ In practice, your service is most likely setting the same cookies, for the
28
+ same domains, every response, all in the same response, and it knows about
29
+ all the cookies being set, so the default behavior of this gem should be
30
+ appropriate in those cases.
31
+
32
+ ## Installation
33
+
34
+ Add this line to your app's Gemfile:
35
+
36
+ ```ruby
37
+ gem 'rack-protection-maximum_cookie'
38
+ ```
39
+
40
+ And then execute:
41
+
42
+ ```console
43
+ $ bundle
44
+ ```
45
+
46
+ Or install it yourself as:
47
+
48
+ ```console
49
+ $ gem install rack-protection-maximum_cookie
50
+ ```
51
+
52
+ ## Usage
53
+
54
+ Add this statement to your Rackup script or middleware-capable Rack app such as
55
+ Sinatra:
56
+
57
+ ```ruby
58
+ use Rack::Protection::MaximumCookie
59
+ ```
60
+
61
+ Or Rails:
62
+
63
+ > TODO: Somebody please tell me how to insert this and before or after what in
64
+ > Rails' middleware stack.
65
+
66
+ This middleware raises exceptions, so you'll want to use an error-reporting
67
+ service like Sentry, Rollbar, or Airbrake, and insert this middleware after it.
68
+
69
+ ### Advanced
70
+
71
+ Rack::Protection::MaximumCookie accepts the following `use`-time options:
72
+
73
+ * `:limit` *Integer*
74
+
75
+ Maximum number of cookies per domain. **50 cookies by default.** Set to a
76
+ negative number to disable.
77
+
78
+ * `:bytesize_limit` *Integer*
79
+
80
+ Maximum size—in bytes—of cookies per domain (if `:per_domain?` is
81
+ set to true), or the maximum size of each cookie (if `:per_domain?` is set to
82
+ false). **4,096 bytes by default.** Set to a negative number to disable.
83
+
84
+ * `:overhead` *Integer*
85
+
86
+ Overhead—in bytes—per cookie. **Three (3) bytes by default.** Set to
87
+ zero to disable.
88
+
89
+ * `:per_domain?` *Boolean*
90
+
91
+ If true, apply the bytesize limit (e.g. 4,096 bytes—minus any
92
+ per-cookie overhead) per domain. **This is the default behavior.**
93
+
94
+ If false, apply the bytesize limit (e.g. 4,096 bytes—minus any
95
+ overhead) per cookie.
96
+
97
+ * `:strict?` *Boolean*
98
+
99
+ If false, each sub-domain gets its own quota, separate from its second-level
100
+ domain. **This is the default behavior.**
101
+
102
+ If true, `:per_domain?` is forced to true, and each second-level domain's
103
+ cookies count towards its sub-domains' quotas. For example, if you have
104
+ cookies for example<i></i>.org totaling 4,000 bytes, you wouldn't be able to
105
+ set an additional 100-byte cookie on foo.example<i></i>.org in the same
106
+ request.
107
+
108
+ * `:stateful?` *Boolean*
109
+
110
+ If false, the cookies are evaluated for each response in isolation, i.e.
111
+ statelessly. **This is the default behavior.**
112
+
113
+ If true, `:strict?` is forced to true, and Rack::Protection::MaximumCookie
114
+ will attempt to compensate for cookies that were set for the domain in
115
+ previous responses (by this or other web services) but are not present in the
116
+ current response.
117
+
118
+ > This mechanism has its limits. First of all, browsers don't send cookie
119
+ > directives in request headers&mdash;only key=value pairs&mdash;so in order
120
+ > to compute the actual size of these cookies,
121
+ > Rack::Protection::MaximumCookie must estimate the upper bound of the
122
+ > bytesize of the directives in the original Set-Cookie headers. This
123
+ > *should* work reasonably well as long as your app doesn't rely on any
124
+ > non-standard directives or other quirky browser behavior.
125
+ >
126
+ > Secondly, browsers won't send cookies if their paths don't match the
127
+ > request path. This means that if you're setting two cookies, one for
128
+ > example<i></i>.org/foo and one for example<i></i>.org/bar, requests to
129
+ > either path won't include the other's cookie. That defeats this mechanism,
130
+ > as both cookies count toward example<i></i>.org's quota, but they aren't
131
+ > able to be properly accounted for in the request. The only "workaround" is
132
+ > to architect your app such that all cookies are set under a path common to
133
+ > all routes. Unfortunately, this may lead to other security issues.
134
+ >
135
+ > This is an ugly wart on HTTP and the reason why modern browsers don't have
136
+ > per-domain cookie size limits and drop cookies instead of truncating them.
137
+
138
+ #### Block Argument
139
+
140
+ If you don't want to raise exceptions, only want to raise exceptions under
141
+ certain conditions, or want to customize the exception, you can modify the
142
+ behavior by passing a block to the middleware initializer:
143
+
144
+ ```ruby
145
+ use Rack::Protection::MaximumCookie do |env|
146
+ raise MyCustomError, 'Someone broke the cookie jar!' if env['foo.bar']
147
+ end
148
+ ```
149
+
150
+ If the block returns a truthy value, the default exception will be raised:
151
+
152
+ ```ruby
153
+ use(Rack::Protection::MaximumCookie) { |env| env['foo.bar'] }
154
+ ```
155
+
156
+ Keep in mind that the block receives the mutated *response* `env`.
157
+
158
+ I'm interested in hearing use-cases for this feature and I'm open to passing
159
+ additional arguments to the block. Open a new issue to document and discuss.
160
+
161
+ ## Development
162
+
163
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run
164
+ `rake spec` to run the tests. You can also run `bin/console` for an interactive
165
+ prompt that will allow you to experiment.
166
+
167
+ To install this gem onto your local machine, run `bundle exec rake install`. To
168
+ release a new version, update the version number in `version.rb`, and then run
169
+ `bundle exec rake release`, which will create a git tag for the version, push
170
+ git commits and tags, and push the `.gem` file to
171
+ [rubygems.org](https://rubygems.org).
172
+
173
+ ## Contributing
174
+
175
+ Bug reports and pull requests are welcome on GitHub at
176
+ https://github.com/mwpastore/rack-protection-maximum_cookie.
177
+
178
+ Please let me know if I've made any invalid assumptions or conclusions about
179
+ the HTTP specification or older browser behavior.
180
+
181
+ ## License
182
+
183
+ The gem is available as open source under the terms of the [MIT
184
+ License](http://opensource.org/licenses/MIT).
185
+
186
+ [1]: http://browsercookielimits.squawky.net
@@ -0,0 +1,10 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << 'test'
6
+ t.libs << 'lib'
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task :default=>:test
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'irb'
5
+ require 'rack/protection/maximum_cookie'
6
+
7
+ IRB.start(__FILE__)
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
@@ -0,0 +1,2 @@
1
+ # frozen_string_literal: true
2
+ require 'rack/protection/maximum_cookie'
@@ -0,0 +1,259 @@
1
+ # frozen_string_literal: true
2
+ require 'rack/protection/maximum_cookie/version'
3
+
4
+ require 'date'
5
+ require 'public_suffix'
6
+ require 'rack'
7
+ require 'rack/request'
8
+ require 'resolv'
9
+ require 'set'
10
+
11
+ module Rack
12
+ SET_COOKIE = 'Set-Cookie'.freeze unless defined?(SET_COOKIE)
13
+
14
+ class Request
15
+ unless method_defined?(:hostname)
16
+ # TODO: Submit a PR to add this to Rack::Request a la URI#host vs. URI#hostname.
17
+ def hostname
18
+ host = host()
19
+ host[/\A\[([^\]]+)\]\z/, 1] || host
20
+ end
21
+ end
22
+ end
23
+
24
+ module Protection
25
+ class MaximumCookie
26
+ HEADER_SEP_RE = %r{\r?\n|\0}.freeze
27
+ COOKIE_DOMAIN_RE = %r{;\s*[Dd][Oo][Mm][Aa][Ii][Nn]=([^;]+)}.freeze
28
+ COOKIE_KEY_RE = %r{\A[^=]+}.freeze
29
+
30
+ attr_reader :app
31
+ attr_reader :handler
32
+ attr_reader :public_suffix_list
33
+
34
+ def limit
35
+ @options[:limit]
36
+ end
37
+
38
+ def limit?
39
+ @options[:limit] >= 0
40
+ end
41
+
42
+ def bytesize_limit
43
+ @options[:bytesize_limit]
44
+ end
45
+
46
+ def bytesize_limit?
47
+ @options[:bytesize_limit] >= 0
48
+ end
49
+
50
+ def overhead
51
+ @options[:overhead]
52
+ end
53
+
54
+ def per_domain?
55
+ @options[:per_domain?]
56
+ end
57
+
58
+ def strict?
59
+ @options[:strict?]
60
+ end
61
+
62
+ def stateful?
63
+ @options[:stateful?]
64
+ end
65
+
66
+ def initialize(app, options={}, &block)
67
+ @app = app
68
+ @handler = block
69
+
70
+ @options = {}.tap do |h|
71
+ h[:limit] = Integer(options.fetch(:limit, 50))
72
+ h[:bytesize_limit] = Integer(options.fetch(:bytesize_limit, 4_096))
73
+ h[:overhead] = Integer(options.fetch(:overhead, 3))
74
+ h[:stateful?] = !!options.fetch(:stateful?, options.fetch(:stateful, false))
75
+ h[:strict?] = h[:stateful?] || !!options.fetch(:strict?, options.fetch(:strict, false))
76
+ h[:per_domain?] = h[:strict?] || !!options.fetch(:per_domain?, options.fetch(:per_domain, true))
77
+
78
+ h.freeze
79
+ end
80
+
81
+ if strict?
82
+ # Allow non-ICANN domains to be handled the same as ICANN domains.
83
+ @public_suffix_list = PublicSuffix::List.parse(::File.read(PublicSuffix::List::DEFAULT_LIST_PATH), :private_domains=>false)
84
+ end
85
+
86
+ unless limit? || bytesize_limit?
87
+ abort 'No limits, nothing to do!'
88
+ end
89
+ end
90
+
91
+ def call(env)
92
+ status, headers, body = app.call(env)
93
+ if headers.key?(SET_COOKIE)
94
+ check_cookies env, Rack::Request.new(env),
95
+ normalize_cookie_header(headers[SET_COOKIE])
96
+ end
97
+ [status, headers, body]
98
+ end
99
+
100
+ private
101
+
102
+ def check_cookies(env, request, cookies)
103
+ default_subdomain = foldcase(request.hostname)
104
+
105
+ keys = Hash.new { |h, k| h[k] = Set.new } if stateful?
106
+ count = Hash.new { |h, k| h[k] = 0 }
107
+ bytesize = Hash.new { |h, k| h[k] = 0 } if per_domain?
108
+
109
+ cookies.each do |cookie|
110
+ # TODO: Skip "delete" cookies?
111
+
112
+ if (subdomain = cookie[COOKIE_DOMAIN_RE, 1])
113
+ foldcase!(subdomain)
114
+ else
115
+ subdomain = default_subdomain
116
+ end
117
+
118
+ keys[subdomain] << cookie[COOKIE_KEY_RE] if keys
119
+ count[subdomain] += 1
120
+ bytesize[subdomain] += cookie.bytesize + overhead if bytesize
121
+ end
122
+
123
+ if stateful?
124
+ # Fold the request cookies (that aren't also present in the response)
125
+ # into our totals.
126
+ fold(request, keys) do |domain, cookie_bytesize|
127
+ count[domain] += 1
128
+ bytesize[domain] += cookie_bytesize + overhead
129
+ end
130
+ end
131
+
132
+ if strict?
133
+ # Add the values for each second-level domain (e.g. example.com) to
134
+ # the values for its subdomains (e.g. foo. and bar.example.com).
135
+ propogate_values(count)
136
+ propogate_values(bytesize)
137
+ end
138
+
139
+ check_limit_per_domain(env, count, limit) if limit?
140
+
141
+ if bytesize_limit?
142
+ if per_domain?
143
+ check_bytesize_limit_per_domain(env, bytesize, bytesize_limit)
144
+ else
145
+ check_bytesize_limit_per_cookie(env, cookies, bytesize_limit - overhead)
146
+ end
147
+ end
148
+ end
149
+
150
+ def check_limit_per_domain(env, acc, limit)
151
+ bad_domains = acc
152
+ .keep_if { |_, value| value > limit }
153
+ .keys
154
+
155
+ if bad_domains.any? && handle(env)
156
+ raise_error "Too many cookies for domain(s): #{bad_domains.join(', ')}"
157
+ end
158
+ end
159
+
160
+ def check_bytesize_limit_per_domain(env, acc, limit)
161
+ bad_domains = acc
162
+ .keep_if { |_, value| value > limit }
163
+ .keys
164
+
165
+ if bad_domains.any? && handle(env)
166
+ raise_error "Too much cookie data for domain(s): #{bad_domains.join(', ')}"
167
+ end
168
+ end
169
+
170
+ def check_bytesize_limit_per_cookie(env, cookies, limit)
171
+ bad_cookies = cookies
172
+ .keep_if { |cookie| cookie.bytesize > limit }
173
+ .map! { |cookie| cookie[COOKIE_KEY_RE] }
174
+ .tap(&:uniq!)
175
+
176
+ if bad_cookies.any? && handle(env)
177
+ raise_error "Too much data for cookie(s): #{bad_cookies.join(', ')}"
178
+ end
179
+ end
180
+
181
+ def domain(hostname)
182
+ return hostname if hostname =~ Resolv::IPv4::Regex || hostname =~ Resolv::IPv6::Regex
183
+
184
+ PublicSuffix.domain(hostname, :list=>public_suffix_list) || hostname
185
+ end
186
+
187
+ def handle(env)
188
+ handler.nil? || handler.call(env)
189
+ end
190
+
191
+ def normalize_cookie_header(value)
192
+ Array(value)
193
+ .flat_map { |h| h.to_s.split(HEADER_SEP_RE) }
194
+ .tap(&:compact!)
195
+ end
196
+
197
+ if String.instance_method(:downcase).arity.abs > 0
198
+ def foldcase(str)
199
+ str.downcase(:fold)
200
+ end
201
+
202
+ def foldcase!(str)
203
+ str.downcase!(:fold)
204
+ end
205
+ else
206
+ def foldcase(str)
207
+ str.downcase
208
+ end
209
+
210
+ def foldcase!(str)
211
+ str.downcase!
212
+ end
213
+ end
214
+
215
+ def fold(request, response_cookie_keys)
216
+ # Assume that all request cookies have a domain of the default
217
+ # subdomain (e.g. foo.example.com) or its second-level domain (e.g.
218
+ # example.com).
219
+ domains = [foldcase(request.hostname)].tap do |a|
220
+ a.unshift(domain(a.first))
221
+ a.uniq!
222
+ end
223
+
224
+ request.cookies.each_pair do |key, value|
225
+ # *Try* to prevent double-counting cookies (i.e. on the response
226
+ # and the request).
227
+ next if domains.any? { |domain| response_cookie_keys[domain].include?(key) }
228
+
229
+ # *Try* to estimate the upper bound of the size of the cookie and its
230
+ # directives in the original Set-Cookie header.
231
+ # TODO: Replace this with a simpler byte count for efficiency?
232
+ mock_cookie = String.new("#{key}=#{value}").tap do |s|
233
+ s << "; Expires=#{Date.today.httpdate}"
234
+ s << '; Max-Age=123456'
235
+ s << "; Domain=#{domains.last}"
236
+ s << "; Path=#{request.script_name}"
237
+ s << '; Secure' if request.ssl?
238
+ s << '; HttpOnly; SameSite=strict'
239
+ end
240
+
241
+ yield domains.first, mock_cookie.bytesize
242
+ end
243
+ end
244
+
245
+ def propogate_values(hash)
246
+ hash.each_key do |subdomain|
247
+ sld = domain(subdomain)
248
+ next if sld == subdomain
249
+ next unless hash.key?(sld)
250
+ hash[subdomain] += hash[sld]
251
+ end
252
+ end
253
+
254
+ def raise_error(message)
255
+ fail message
256
+ end
257
+ end
258
+ end
259
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: false
2
+ module Rack
3
+ module Protection
4
+ class MaximumCookie
5
+ VERSION = '0.4.2'.freeze
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rack/protection/maximum_cookie/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'rack-protection-maximum_cookie'
8
+ spec.version = Rack::Protection::MaximumCookie::VERSION
9
+ spec.authors = ['Mike Pastore']
10
+ spec.email = ['mike@oobak.org']
11
+
12
+ spec.summary = %q{Properly enforce cookie limits in Rack responses}
13
+ spec.homepage = 'https://github.com/mwpastore/rack-protection-maximum_cookie#readme'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = %x{git ls-files -z}.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.bindir = 'exe'
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = %w[lib]
22
+
23
+ spec.required_ruby_version = '>= 2.1.10'
24
+
25
+ spec.add_dependency 'public_suffix', '~> 3.0'
26
+ spec.add_dependency 'rack', ENV.fetch('RACK_VERSION', ['>= 1.4.7', '< 2.1'])
27
+
28
+ spec.add_development_dependency 'bundler', '~> 1.15'
29
+ spec.add_development_dependency 'minitest', '~> 5.0'
30
+ spec.add_development_dependency 'rack-test', '~> 0.7.0'
31
+ spec.add_development_dependency 'rake', '~> 12.0'
32
+ end
metadata ADDED
@@ -0,0 +1,146 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack-protection-maximum_cookie
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.2
5
+ platform: ruby
6
+ authors:
7
+ - Mike Pastore
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-11-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: public_suffix
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rack
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.4.7
34
+ - - "<"
35
+ - !ruby/object:Gem::Version
36
+ version: '2.1'
37
+ type: :runtime
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 1.4.7
44
+ - - "<"
45
+ - !ruby/object:Gem::Version
46
+ version: '2.1'
47
+ - !ruby/object:Gem::Dependency
48
+ name: bundler
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '1.15'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '1.15'
61
+ - !ruby/object:Gem::Dependency
62
+ name: minitest
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '5.0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '5.0'
75
+ - !ruby/object:Gem::Dependency
76
+ name: rack-test
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: 0.7.0
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: 0.7.0
89
+ - !ruby/object:Gem::Dependency
90
+ name: rake
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '12.0'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '12.0'
103
+ description:
104
+ email:
105
+ - mike@oobak.org
106
+ executables: []
107
+ extensions: []
108
+ extra_rdoc_files: []
109
+ files:
110
+ - ".gitignore"
111
+ - ".travis.yml"
112
+ - Gemfile
113
+ - LICENSE.txt
114
+ - README.md
115
+ - Rakefile
116
+ - bin/console
117
+ - bin/setup
118
+ - lib/rack-protection-maximum_cookie.rb
119
+ - lib/rack/protection/maximum_cookie.rb
120
+ - lib/rack/protection/maximum_cookie/version.rb
121
+ - rack-protection-maximum_cookie.gemspec
122
+ homepage: https://github.com/mwpastore/rack-protection-maximum_cookie#readme
123
+ licenses:
124
+ - MIT
125
+ metadata: {}
126
+ post_install_message:
127
+ rdoc_options: []
128
+ require_paths:
129
+ - lib
130
+ required_ruby_version: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ version: 2.1.10
135
+ required_rubygems_version: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ requirements: []
141
+ rubyforge_project:
142
+ rubygems_version: 2.6.13
143
+ signing_key:
144
+ specification_version: 4
145
+ summary: Properly enforce cookie limits in Rack responses
146
+ test_files: []