rack-attack 2.2.1 → 2.3.0

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.

Potentially problematic release.


This version of rack-attack might be problematic. Click here for more details.

@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b294cc1911f079c5df74d5325e01fabd2a207498
4
+ data.tar.gz: 99306d1db94cfeab978bef7573e1455c0e2cc428
5
+ SHA512:
6
+ metadata.gz: 12a543ddffa7cc24893c3ee302071738e8f9e39fe063e9b6610492c41d03ba5179c43bffe50ff182e880fd936a1d970ab9f6a0d6c536c2db77e89b555dd253d2
7
+ data.tar.gz: b439faf86fd536d941327aa03c0d4e2d344a527857f120e09e0ab0bdec9fbbcef686d716da9c56867d4ca277c20edf7d8313c6c79d7f01c4e09ff0d929092a7a
data/README.md CHANGED
@@ -4,7 +4,14 @@
4
4
  Rack::Attack is a rack middleware to protect your web app from bad clients.
5
5
  It allows *whitelisting*, *blacklisting*, *throttling*, and *tracking* based on arbitrary properties of the request.
6
6
 
7
- Throttle state is stored in a configurable cache (e.g. `Rails.cache`), presumably backed by memcached or redis (at least v3.0.0).
7
+ Throttle state is stored in a configurable cache (e.g. `Rails.cache`), presumably backed by memcached or redis ([at least gem v3.0.0](https://rubygems.org/gems/redis)).
8
+
9
+ See the [Backing & Hacking blog post](http://www.kickstarter.com/backing-and-hacking/rack-attack-protection-from-abusive-clients) introducing Rack::Attack.
10
+
11
+ [![Gem Version](https://badge.fury.io/rb/rack-attack.png)](http://badge.fury.io/rb/rack-attack)
12
+ [![Build Status](https://travis-ci.org/kickstarter/rack-attack.png?branch=master)](https://travis-ci.org/kickstarter/rack-attack)
13
+ [![Code Climate](https://codeclimate.com/github/kickstarter/rack-attack.png)](https://codeclimate.com/github/kickstarter/rack-attack)
14
+
8
15
 
9
16
  ## Installation
10
17
 
@@ -15,7 +22,7 @@ Install the [rack-attack](http://rubygems.org/gems/rack-attack) gem; or add it t
15
22
  gem 'rack-attack'
16
23
  ```
17
24
  Tell your app to use the Rack::Attack middleware.
18
- For Rails 3 apps:
25
+ For Rails 3+ apps:
19
26
 
20
27
  ```ruby
21
28
  # In config/application.rb
@@ -43,7 +50,7 @@ The Rack::Attack middleware compares each request against *whitelists*, *blackli
43
50
 
44
51
  * If the request matches any **whitelist**, it is allowed.
45
52
  * Otherwise, if the request matches any **blacklist**, it is blocked.
46
- * Otherwise, if the request matches any **throttle**, a counter is incremented in the Rack::Attack.cache. If the throttle limit is exceeded, the request is blocked.
53
+ * Otherwise, if the request matches any **throttle**, a counter is incremented in the Rack::Attack.cache. If any throttle's limit is exceeded, the request is blocked.
47
54
  * Otherwise, all **tracks** are checked, and the request is allowed.
48
55
 
49
56
  The algorithm is actually more concise in code: See [Rack::Attack.call](https://github.com/kickstarter/rack-attack/blob/master/lib/rack/attack.rb):
@@ -71,7 +78,7 @@ The algorithm is actually more concise in code: See [Rack::Attack.call](https://
71
78
 
72
79
  ## Usage
73
80
 
74
- Define whitelists, blacklists, throttles, and tracks as blocks that return truthy values if matched, falsy otherwise. In a Rails app
81
+ Define whitelists, blacklists, throttles, and tracks as blocks that return truthy values if matched, falsy otherwise. In a Rails app
75
82
  these go in an initializer in `config/initializers/`.
76
83
  A [Rack::Request](http://rack.rubyforge.org/doc/classes/Rack/Request.html) object is passed to the block (named 'req' in the examples).
77
84
 
@@ -121,6 +128,24 @@ how the parameters work.
121
128
  end
122
129
  ```
123
130
 
131
+ #### Allow2Ban
132
+ `Allow2Ban.filter` works the same way as the `Fail2Ban.filter` except that it *allows* requests from misbehaving
133
+ clients until such time as they reach maxretry at which they are cut off as per normal.
134
+ ```ruby
135
+ # Lockout IP addresses that are hammering your login page.
136
+ # After 20 requests in 1 minute, block all requests from that IP for 1 hour.
137
+ Rack::Attack.blacklist('allow2ban login scrapers') do |req|
138
+ # `filter` returns false value if request is to your login page (but still
139
+ # increments the count) so request below the limit are not blocked until
140
+ # they hit the limit. At that point, filter will return true and block.
141
+ Rack::Attack::Allow2Ban.filter(req.ip, :maxretry => 20, :findtime => 1.minute, :bantime => 1.hour) do
142
+ # The count for the IP is incremented if the return value is truthy.
143
+ req.path = '/login' and req.method == 'post'
144
+ end
145
+ end
146
+ ```
147
+
148
+
124
149
  ### Throttles
125
150
 
126
151
  ```ruby
@@ -140,6 +165,13 @@ how the parameters work.
140
165
  Rack::Attack.throttle('logins/email', :limit => 6, :period => 60.seconds) do |req|
141
166
  req.params['email'] if req.path == '/login' && req.post?
142
167
  end
168
+
169
+ # You can also set a limit using a proc instead of a number. For
170
+ # instance, after Rack::Auth::Basic has authenticated the user:
171
+ limit_based_on_proc = proc {|req| req.env["REMOTE_USER"] == "admin" ? 100 : 1}
172
+ Rack::Attack.throttle('req/ip', :limit => limit_based_on_proc, :period => 1.second) do |req|
173
+ req.ip
174
+ end
143
175
  ```
144
176
 
145
177
  ### Tracks
@@ -204,9 +236,9 @@ You can subscribe to 'rack.attack' events and log it, graph it, etc:
204
236
 
205
237
  ## Testing
206
238
 
207
- A note on developing and testing apps using Rack::Attack - if you are using throttling in particular, you will
208
- need to enable the cache in your development environment. See [Caching with Rails](http://guides.rubyonrails.org/caching_with_rails.html)
209
- for more on how to do this.
239
+ A note on developing and testing apps using Rack::Attack - if you are using throttling in particular, you will
240
+ need to enable the cache in your development environment. See [Caching with Rails](http://guides.rubyonrails.org/caching_with_rails.html)
241
+ for more on how to do this.
210
242
 
211
243
  ## Performance
212
244
 
@@ -230,10 +262,12 @@ It is impractical if not impossible to block abusive clients completely.
230
262
  Rack::Attack aims to let developers quickly mitigate abusive requests and rely
231
263
  less on short-term, one-off hacks to block a particular attack.
232
264
 
233
- See also: the [Backing & Hacking blog post](http://www.kickstarter.com/backing-and-hacking/rack-attack-protection-from-abusive-clients) introducing Rack::Attack.
265
+ ## Mailing list
234
266
 
235
- [![Build Status](https://travis-ci.org/kickstarter/rack-attack.png?branch=master)](https://travis-ci.org/kickstarter/rack-attack)
236
- [![Code Climate](https://codeclimate.com/github/kickstarter/rack-attack.png)](https://codeclimate.com/github/kickstarter/rack-attack)
267
+ New releases of Rack::Attack are announced on
268
+ <rack.attack.announce@librelist.com>. To subscribe, just send an email to
269
+ <rack.attack.announce@librelist.com>. See the
270
+ [archives](http://librelist.com/browser/rack.attack.announce/).
237
271
 
238
272
  ## License
239
273
 
@@ -8,6 +8,7 @@ module Rack::Attack
8
8
  autoload :Track, 'rack/attack/track'
9
9
  autoload :StoreProxy,'rack/attack/store_proxy'
10
10
  autoload :Fail2Ban, 'rack/attack/fail2ban'
11
+ autoload :Allow2Ban, 'rack/attack/allow2ban'
11
12
 
12
13
  class << self
13
14
 
@@ -0,0 +1,23 @@
1
+ module Rack
2
+ module Attack
3
+ class Allow2Ban < Fail2Ban
4
+ class << self
5
+ protected
6
+ def key_prefix
7
+ 'allow2ban'
8
+ end
9
+
10
+ # everything the same here except we return only return true
11
+ # (blocking the request) if they have tripped the limit.
12
+ def fail!(discriminator, bantime, findtime, maxretry)
13
+ count = cache.count("#{key_prefix}:count:#{discriminator}", findtime)
14
+ if count >= maxretry
15
+ ban!(discriminator, bantime)
16
+ end
17
+ # we may not block them this time, but they're banned for next time
18
+ false
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -15,23 +15,28 @@ module Rack
15
15
  end
16
16
  end
17
17
 
18
- private
18
+ protected
19
+ def key_prefix
20
+ 'fail2ban'
21
+ end
22
+
19
23
  def fail!(discriminator, bantime, findtime, maxretry)
20
- count = cache.count("fail2ban:count:#{discriminator}", findtime)
24
+ count = cache.count("#{key_prefix}:count:#{discriminator}", findtime)
21
25
  if count >= maxretry
22
26
  ban!(discriminator, bantime)
23
27
  end
24
28
 
25
- # Return true for blacklist
26
29
  true
27
30
  end
28
31
 
32
+
33
+ private
29
34
  def ban!(discriminator, bantime)
30
- cache.write("fail2ban:ban:#{discriminator}", 1, bantime)
35
+ cache.write("#{key_prefix}:ban:#{discriminator}", 1, bantime)
31
36
  end
32
37
 
33
38
  def banned?(discriminator)
34
- cache.read("fail2ban:ban:#{discriminator}")
39
+ cache.read("#{key_prefix}:ban:#{discriminator}")
35
40
  end
36
41
 
37
42
  def cache
@@ -22,14 +22,15 @@ module Rack
22
22
 
23
23
  key = "#{name}:#{discriminator}"
24
24
  count = cache.count(key, period)
25
+ current_limit = limit.respond_to?(:call) ? limit.call(req) : limit
25
26
  data = {
26
27
  :count => count,
27
28
  :period => period,
28
- :limit => limit
29
+ :limit => current_limit
29
30
  }
30
31
  (req.env['rack.attack.throttle_data'] ||= {})[name] = data
31
32
 
32
- (count > limit).tap do |throttled|
33
+ (count > current_limit).tap do |throttled|
33
34
  if throttled
34
35
  req.env['rack.attack.matched'] = name
35
36
  req.env['rack.attack.match_type'] = :throttle
@@ -38,7 +39,6 @@ module Rack
38
39
  end
39
40
  end
40
41
  end
41
-
42
42
  end
43
43
  end
44
44
  end
@@ -1,5 +1,5 @@
1
1
  module Rack
2
2
  module Attack
3
- VERSION = '2.2.1'
3
+ VERSION = '2.3.0'
4
4
  end
5
5
  end
@@ -0,0 +1,121 @@
1
+ require_relative 'spec_helper'
2
+ describe 'Rack::Attack.Allow2Ban' do
3
+ before do
4
+ # Use a long findtime; failures due to cache key rotation less likely
5
+ @cache = Rack::Attack.cache
6
+ @findtime = 60
7
+ @bantime = 60
8
+ Rack::Attack.cache.store = ActiveSupport::Cache::MemoryStore.new
9
+ @f2b_options = {:bantime => @bantime, :findtime => @findtime, :maxretry => 2}
10
+ Rack::Attack.blacklist('pentest') do |req|
11
+ Rack::Attack::Allow2Ban.filter(req.ip, @f2b_options){req.query_string =~ /OMGHAX/}
12
+ end
13
+ end
14
+
15
+ describe 'discriminator has not been banned' do
16
+ describe 'making ok request' do
17
+ it 'succeeds' do
18
+ get '/', {}, 'REMOTE_ADDR' => '1.2.3.4'
19
+ last_response.status.must_equal 200
20
+ end
21
+ end
22
+
23
+ describe 'making qualifying request' do
24
+ describe 'when not at maxretry' do
25
+ before { get '/?foo=OMGHAX', {}, 'REMOTE_ADDR' => '1.2.3.4' }
26
+ it 'succeeds' do
27
+ last_response.status.must_equal 200
28
+ end
29
+
30
+ it 'increases fail count' do
31
+ key = "rack::attack:#{Time.now.to_i/@findtime}:allow2ban:count:1.2.3.4"
32
+ @cache.store.read(key).must_equal 1
33
+ end
34
+
35
+ it 'is not banned' do
36
+ key = "rack::attack:allow2ban:1.2.3.4"
37
+ @cache.store.read(key).must_be_nil
38
+ end
39
+ end
40
+
41
+ describe 'when at maxretry' do
42
+ before do
43
+ # maxretry is 2 - so hit with an extra failed request first
44
+ get '/?test=OMGHAX', {}, 'REMOTE_ADDR' => '1.2.3.4'
45
+ get '/?foo=OMGHAX', {}, 'REMOTE_ADDR' => '1.2.3.4'
46
+ end
47
+
48
+ it 'succeeds' do
49
+ last_response.status.must_equal 200
50
+ end
51
+
52
+ it 'increases fail count' do
53
+ key = "rack::attack:#{Time.now.to_i/@findtime}:allow2ban:count:1.2.3.4"
54
+ @cache.store.read(key).must_equal 2
55
+ end
56
+
57
+ it 'is banned' do
58
+ key = "rack::attack:allow2ban:ban:1.2.3.4"
59
+ @cache.store.read(key).must_equal 1
60
+ end
61
+
62
+ end
63
+ end
64
+ end
65
+
66
+ describe 'discriminator has been banned' do
67
+ before do
68
+ # maxretry is 2 - so hit enough times to get banned
69
+ get '/?test=OMGHAX', {}, 'REMOTE_ADDR' => '1.2.3.4'
70
+ get '/?foo=OMGHAX', {}, 'REMOTE_ADDR' => '1.2.3.4'
71
+ end
72
+
73
+ describe 'making request for other discriminator' do
74
+ it 'succeeds' do
75
+ get '/', {}, 'REMOTE_ADDR' => '2.2.3.4'
76
+ last_response.status.must_equal 200
77
+ end
78
+ end
79
+
80
+ describe 'making ok request' do
81
+ before do
82
+ get '/', {}, 'REMOTE_ADDR' => '1.2.3.4'
83
+ end
84
+
85
+ it 'fails' do
86
+ last_response.status.must_equal 401
87
+ end
88
+
89
+ it 'does not increase fail count' do
90
+ key = "rack::attack:#{Time.now.to_i/@findtime}:allow2ban:count:1.2.3.4"
91
+ @cache.store.read(key).must_equal 2
92
+ end
93
+
94
+ it 'is still banned' do
95
+ key = "rack::attack:allow2ban:ban:1.2.3.4"
96
+ @cache.store.read(key).must_equal 1
97
+ end
98
+ end
99
+
100
+ describe 'making failing request' do
101
+ before do
102
+ get '/?foo=OMGHAX', {}, 'REMOTE_ADDR' => '1.2.3.4'
103
+ end
104
+
105
+ it 'fails' do
106
+ last_response.status.must_equal 401
107
+ end
108
+
109
+ it 'does not increase fail count' do
110
+ key = "rack::attack:#{Time.now.to_i/@findtime}:allow2ban:count:1.2.3.4"
111
+ @cache.store.read(key).must_equal 2
112
+ end
113
+
114
+ it 'is still banned' do
115
+ key = "rack::attack:allow2ban:ban:1.2.3.4"
116
+ @cache.store.read(key).must_equal 1
117
+ end
118
+ end
119
+
120
+ end
121
+ end
@@ -37,7 +37,27 @@ describe 'Rack::Attack.throttle' do
37
37
  last_response.headers['Retry-After'].must_equal @period.to_s
38
38
  end
39
39
  end
40
-
41
40
  end
42
41
 
42
+ describe 'Rack::Attack.throttle with limit as proc' do
43
+ before do
44
+ @period = 60 # Use a long period; failures due to cache key rotation less likely
45
+ Rack::Attack.cache.store = ActiveSupport::Cache::MemoryStore.new
46
+ Rack::Attack.throttle('ip/sec', :limit => lambda {|req| 1}, :period => @period) { |req| req.ip }
47
+ end
48
+
49
+ allow_ok_requests
50
+
51
+ describe 'a single request' do
52
+ before { get '/', {}, 'REMOTE_ADDR' => '1.2.3.4' }
53
+ it 'should set the counter for one request' do
54
+ key = "rack::attack:#{Time.now.to_i/@period}:ip/sec:1.2.3.4"
55
+ Rack::Attack.cache.store.read(key).must_equal 1
56
+ end
43
57
 
58
+ it 'should populate throttle data' do
59
+ data = { :count => 1, :limit => 1, :period => @period }
60
+ last_request.env['rack.attack.throttle_data']['ip/sec'].must_equal data
61
+ end
62
+ end
63
+ end
@@ -13,7 +13,7 @@ rescue LoadError
13
13
  #nothing to do here
14
14
  end
15
15
 
16
- class Minitest::Spec
16
+ class MiniTest::Spec
17
17
 
18
18
  include Rack::Test::Methods
19
19
 
metadata CHANGED
@@ -1,110 +1,134 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-attack
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.1
5
- prerelease:
4
+ version: 2.3.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Aaron Suggs
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-08-13 00:00:00.000000000 Z
11
+ date: 2013-10-11 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: rack
16
- requirement: &1 !ruby/object:Gem::Requirement
17
- none: false
15
+ requirement: !ruby/object:Gem::Requirement
18
16
  requirements:
19
17
  - - '>='
20
18
  - !ruby/object:Gem::Version
21
19
  version: '0'
22
20
  type: :runtime
23
21
  prerelease: false
24
- version_requirements: *1
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
25
27
  - !ruby/object:Gem::Dependency
26
28
  name: minitest
27
- requirement: &2 !ruby/object:Gem::Requirement
28
- none: false
29
+ requirement: !ruby/object:Gem::Requirement
29
30
  requirements:
30
31
  - - '>='
31
32
  - !ruby/object:Gem::Version
32
33
  version: '0'
33
34
  type: :development
34
35
  prerelease: false
35
- version_requirements: *2
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
36
41
  - !ruby/object:Gem::Dependency
37
42
  name: rack-test
38
- requirement: &3 !ruby/object:Gem::Requirement
39
- none: false
43
+ requirement: !ruby/object:Gem::Requirement
40
44
  requirements:
41
45
  - - '>='
42
46
  - !ruby/object:Gem::Version
43
47
  version: '0'
44
48
  type: :development
45
49
  prerelease: false
46
- version_requirements: *3
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
47
55
  - !ruby/object:Gem::Dependency
48
56
  name: rake
49
- requirement: &4 !ruby/object:Gem::Requirement
50
- none: false
57
+ requirement: !ruby/object:Gem::Requirement
51
58
  requirements:
52
59
  - - '>='
53
60
  - !ruby/object:Gem::Version
54
61
  version: '0'
55
62
  type: :development
56
63
  prerelease: false
57
- version_requirements: *4
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
58
69
  - !ruby/object:Gem::Dependency
59
70
  name: activesupport
60
- requirement: &5 !ruby/object:Gem::Requirement
61
- none: false
71
+ requirement: !ruby/object:Gem::Requirement
62
72
  requirements:
63
73
  - - '>='
64
74
  - !ruby/object:Gem::Version
65
75
  version: 3.0.0
66
76
  type: :development
67
77
  prerelease: false
68
- version_requirements: *5
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: 3.0.0
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: debugger
71
- requirement: &6 !ruby/object:Gem::Requirement
72
- none: false
85
+ requirement: !ruby/object:Gem::Requirement
73
86
  requirements:
74
87
  - - ~>
75
88
  - !ruby/object:Gem::Version
76
89
  version: '1.5'
77
90
  type: :development
78
91
  prerelease: false
79
- version_requirements: *6
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ~>
95
+ - !ruby/object:Gem::Version
96
+ version: '1.5'
80
97
  - !ruby/object:Gem::Dependency
81
98
  name: redis-activesupport
82
- requirement: &7 !ruby/object:Gem::Requirement
83
- none: false
99
+ requirement: !ruby/object:Gem::Requirement
84
100
  requirements:
85
101
  - - '>='
86
102
  - !ruby/object:Gem::Version
87
103
  version: '0'
88
104
  type: :development
89
105
  prerelease: false
90
- version_requirements: *7
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
91
111
  - !ruby/object:Gem::Dependency
92
112
  name: dalli
93
- requirement: &8 !ruby/object:Gem::Requirement
94
- none: false
113
+ requirement: !ruby/object:Gem::Requirement
95
114
  requirements:
96
115
  - - '>='
97
116
  - !ruby/object:Gem::Version
98
117
  version: '0'
99
118
  type: :development
100
119
  prerelease: false
101
- version_requirements: *8
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
102
125
  description: A rack middleware for throttling and blocking abusive requests
103
126
  email: aaron@ktheory.com
104
127
  executables: []
105
128
  extensions: []
106
129
  extra_rdoc_files: []
107
130
  files:
131
+ - lib/rack/attack/allow2ban.rb
108
132
  - lib/rack/attack/blacklist.rb
109
133
  - lib/rack/attack/cache.rb
110
134
  - lib/rack/attack/check.rb
@@ -117,6 +141,7 @@ files:
117
141
  - lib/rack/attack.rb
118
142
  - Rakefile
119
143
  - README.md
144
+ - spec/allow2ban_spec.rb
120
145
  - spec/fail2ban_spec.rb
121
146
  - spec/rack_attack_cache_spec.rb
122
147
  - spec/rack_attack_spec.rb
@@ -126,30 +151,30 @@ files:
126
151
  homepage: http://github.com/kickstarter/rack-attack
127
152
  licenses:
128
153
  - MIT
154
+ metadata: {}
129
155
  post_install_message:
130
156
  rdoc_options:
131
157
  - --charset=UTF-8
132
158
  require_paths:
133
159
  - lib
134
160
  required_ruby_version: !ruby/object:Gem::Requirement
135
- none: false
136
161
  requirements:
137
162
  - - '>='
138
163
  - !ruby/object:Gem::Version
139
164
  version: 1.9.2
140
165
  required_rubygems_version: !ruby/object:Gem::Requirement
141
- none: false
142
166
  requirements:
143
167
  - - '>='
144
168
  - !ruby/object:Gem::Version
145
169
  version: '0'
146
170
  requirements: []
147
171
  rubyforge_project:
148
- rubygems_version: 1.8.3
172
+ rubygems_version: 2.1.6
149
173
  signing_key:
150
- specification_version: 3
174
+ specification_version: 4
151
175
  summary: Block & throttle abusive requests
152
176
  test_files:
177
+ - spec/allow2ban_spec.rb
153
178
  - spec/fail2ban_spec.rb
154
179
  - spec/rack_attack_cache_spec.rb
155
180
  - spec/rack_attack_spec.rb