rack-protection-maximum_cookie 0.4.2

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,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: []