rack-attack 5.1.0 → 5.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +164 -79
- data/lib/rack/attack.rb +30 -8
- data/lib/rack/attack/cache.rb +24 -10
- data/lib/rack/attack/check.rb +1 -0
- data/lib/rack/attack/version.rb +1 -1
- data/spec/acceptance/allow2ban_spec.rb +71 -0
- data/spec/acceptance/blocking_ip_spec.rb +38 -0
- data/spec/acceptance/blocking_spec.rb +20 -0
- data/spec/acceptance/blocking_subnet_spec.rb +44 -0
- data/spec/acceptance/cache_store_config_for_allow2ban_spec.rb +111 -0
- data/spec/acceptance/cache_store_config_for_fail2ban_spec.rb +108 -0
- data/spec/acceptance/cache_store_config_for_throttle_spec.rb +48 -0
- data/spec/acceptance/cache_store_config_with_rails_spec.rb +31 -0
- data/spec/acceptance/customizing_blocked_response_spec.rb +41 -0
- data/spec/acceptance/customizing_throttled_response_spec.rb +59 -0
- data/spec/acceptance/extending_request_object_spec.rb +34 -0
- data/spec/acceptance/fail2ban_spec.rb +76 -0
- data/spec/acceptance/safelisting_ip_spec.rb +49 -0
- data/spec/acceptance/safelisting_spec.rb +16 -0
- data/spec/acceptance/safelisting_subnet_spec.rb +48 -0
- data/spec/acceptance/throttling_spec.rb +130 -1
- data/spec/acceptance/track_spec.rb +27 -0
- data/spec/acceptance/track_throttle_spec.rb +53 -0
- data/spec/spec_helper.rb +12 -0
- metadata +60 -4
- data/spec/rack_attack_store_config_spec.rb +0 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 21a52aca7aa6592b23e6a3e99f0b06cf6d4d9eedb8366ec57fe4f9cfe804ea82
|
4
|
+
data.tar.gz: 32e0db149bc10308fb8b5ae737147e1c11c9f854e08b9f453d9a7066911308fe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 560d951d375a9114752b37a2858f48e85af9edcdeabb0073c6e6f8d179b6d10a0331a7abee625df4c0171f9c993bb105cc8e54fed9170fd47d288fb2bf29617a
|
7
|
+
data.tar.gz: b3993951744e2755873cc7ce0c6810c3780ad44c6c84491bda5277a46536589d15b26bd25e7849e7b20a4e28aebc86ed874cb0b61e0d988da54252a3b3e024dc
|
data/README.md
CHANGED
@@ -1,10 +1,8 @@
|
|
1
|
-
# Rack::Attack
|
2
|
-
*Rack middleware for blocking & throttling abusive requests*
|
1
|
+
# Rack::Attack
|
3
2
|
|
4
|
-
Rack
|
5
|
-
It allows *safelisting*, *blocklisting*, *throttling*, and *tracking* based on arbitrary properties of the request.
|
3
|
+
*Rack middleware for blocking & throttling abusive requests*
|
6
4
|
|
7
|
-
|
5
|
+
Protect your Rails and Rack apps from bad clients. Rack::Attack lets you easily decide when to *allow*, *block* and *throttle* based on properties of the request.
|
8
6
|
|
9
7
|
See the [Backing & Hacking blog post](http://www.kickstarter.com/backing-and-hacking/rack-attack-protection-from-abusive-clients) introducing Rack::Attack.
|
10
8
|
|
@@ -12,98 +10,97 @@ See the [Backing & Hacking blog post](http://www.kickstarter.com/backing-and-hac
|
|
12
10
|
[![Build Status](https://travis-ci.org/kickstarter/rack-attack.svg?branch=master)](https://travis-ci.org/kickstarter/rack-attack)
|
13
11
|
[![Code Climate](https://codeclimate.com/github/kickstarter/rack-attack.svg)](https://codeclimate.com/github/kickstarter/rack-attack)
|
14
12
|
|
15
|
-
## Looking for maintainers
|
16
|
-
|
17
|
-
I'm looking for new maintainers to help me support Rack::Attack. Check out
|
18
|
-
[issue #219 for details](https://github.com/kickstarter/rack-attack/issues/219).
|
19
|
-
|
20
13
|
## Getting started
|
21
14
|
|
22
|
-
|
15
|
+
### 1. Installing
|
16
|
+
|
17
|
+
Add this line to your application's Gemfile:
|
23
18
|
|
24
19
|
```ruby
|
25
20
|
# In your Gemfile
|
21
|
+
|
26
22
|
gem 'rack-attack'
|
27
23
|
```
|
28
|
-
|
29
|
-
|
24
|
+
|
25
|
+
And then execute:
|
26
|
+
|
27
|
+
$ bundle
|
28
|
+
|
29
|
+
Or install it yourself as:
|
30
|
+
|
31
|
+
$ gem install rack-attack
|
32
|
+
|
33
|
+
### 2. Plugging into the application
|
34
|
+
|
35
|
+
Then tell your ruby web application to use rack-attack as a middleware.
|
36
|
+
|
37
|
+
a) For __rails__ applications:
|
30
38
|
|
31
39
|
```ruby
|
32
40
|
# In config/application.rb
|
41
|
+
|
33
42
|
config.middleware.use Rack::Attack
|
34
43
|
```
|
35
44
|
|
36
|
-
|
45
|
+
b) For __rack__ applications:
|
37
46
|
|
38
47
|
```ruby
|
39
48
|
# In config.ru
|
49
|
+
|
50
|
+
require "rack/attack"
|
40
51
|
use Rack::Attack
|
41
52
|
```
|
42
53
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
class Rack::Attack
|
47
|
-
# your custom configuration...
|
48
|
-
end
|
49
|
-
```
|
54
|
+
__IMPORTANT__: By default, rack-attack won't perform any blocking or throttling, until you specifically tell it what to protect against by configuring some rules.
|
55
|
+
|
56
|
+
## Usage
|
50
57
|
|
51
58
|
*Tip:* The example in the wiki is a great way to get started:
|
52
59
|
[Example Configuration](https://github.com/kickstarter/rack-attack/wiki/Example-Configuration)
|
53
60
|
|
54
|
-
|
61
|
+
Define rules by calling `Rack::Attack` public methods, in any file that runs when your application is being initialized. For rails applications this means creating a new file named `config/initializers/rack_attack.rb` and writing your rules there.
|
55
62
|
|
56
|
-
|
57
|
-
Rack::Attack.cache.store = ActiveSupport::Cache::MemoryStore.new # defaults to Rails.cache
|
58
|
-
```
|
63
|
+
### Safelisting
|
59
64
|
|
60
|
-
|
65
|
+
Safelists have the most precedence, so any request matching a safelist would be allowed despite matching any number of blocklists or throttles.
|
61
66
|
|
62
|
-
|
67
|
+
#### `safelist_ip(ip_address_string)`
|
63
68
|
|
64
|
-
|
69
|
+
E.g.
|
65
70
|
|
66
|
-
|
67
|
-
|
68
|
-
* 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.
|
69
|
-
* Otherwise, all **tracks** are checked, and the request is allowed.
|
71
|
+
```ruby
|
72
|
+
# config/initializers/rack_attack.rb (for rails app)
|
70
73
|
|
71
|
-
|
74
|
+
Rack::Attack.safelist_ip("5.6.7.8")
|
75
|
+
```
|
76
|
+
|
77
|
+
#### `safelist_ip(ip_subnet_string)`
|
78
|
+
|
79
|
+
E.g.
|
72
80
|
|
73
81
|
```ruby
|
74
|
-
|
75
|
-
req = Rack::Attack::Request.new(env)
|
82
|
+
# config/initializers/rack_attack.rb (for rails app)
|
76
83
|
|
77
|
-
|
78
|
-
@app.call(env)
|
79
|
-
elsif blocklisted?(req)
|
80
|
-
self.class.blocklisted_response.call(env)
|
81
|
-
elsif throttled?(req)
|
82
|
-
self.class.throttled_response.call(env)
|
83
|
-
else
|
84
|
-
tracked?(req)
|
85
|
-
@app.call(env)
|
86
|
-
end
|
87
|
-
end
|
84
|
+
Rack::Attack.safelist_ip("5.6.7.0/24")
|
88
85
|
```
|
89
86
|
|
90
|
-
|
91
|
-
can cleanly monkey patch helper methods onto the
|
92
|
-
[request object](https://github.com/kickstarter/rack-attack/blob/master/lib/rack/attack/request.rb).
|
87
|
+
#### `safelist(name, &block)`
|
93
88
|
|
94
|
-
|
89
|
+
Name your custom safelist and make your ruby-block argument return a truthy value if you want the request to be blocked, and falsy otherwise.
|
95
90
|
|
96
|
-
|
91
|
+
The request object is a [Rack::Request](http://www.rubydoc.info/gems/rack/Rack/Request).
|
97
92
|
|
98
|
-
|
93
|
+
E.g.
|
99
94
|
|
100
|
-
|
101
|
-
|
102
|
-
A [Rack::Request](http://www.rubydoc.info/gems/rack/Rack/Request) object is passed to the block (named 'req' in the examples).
|
95
|
+
```ruby
|
96
|
+
# config/initializers/rack_attack.rb (for rails apps)
|
103
97
|
|
104
|
-
|
98
|
+
# Provided that trusted users use an HTTP request header named APIKey
|
99
|
+
Rack::Attack.safelist("mark any authenticated access safe") do |request|
|
100
|
+
# Requests are allowed if the return value is truthy
|
101
|
+
request.env["APIKey"] == "secret-string"
|
102
|
+
end
|
105
103
|
|
106
|
-
```ruby
|
107
104
|
# Always allow requests from localhost
|
108
105
|
# (blocklist & throttles are skipped)
|
109
106
|
Rack::Attack.safelist('allow from localhost') do |req|
|
@@ -112,16 +109,44 @@ Rack::Attack.safelist('allow from localhost') do |req|
|
|
112
109
|
end
|
113
110
|
```
|
114
111
|
|
115
|
-
###
|
112
|
+
### Blocking
|
113
|
+
|
114
|
+
#### `blocklist_ip(ip_address_string)`
|
115
|
+
|
116
|
+
E.g.
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
# config/initializers/rack_attack.rb (for rails apps)
|
120
|
+
|
121
|
+
Rack::Attack.blocklist_ip("1.2.3.4")
|
122
|
+
```
|
123
|
+
|
124
|
+
#### `blocklist_ip(ip_subnet_string)`
|
125
|
+
|
126
|
+
E.g.
|
116
127
|
|
117
128
|
```ruby
|
118
|
-
#
|
119
|
-
|
129
|
+
# config/initializers/rack_attack.rb (for rails apps)
|
130
|
+
|
131
|
+
Rack::Attack.blocklist_ip("1.2.0.0/16")
|
132
|
+
```
|
133
|
+
|
134
|
+
#### `blocklist(name, &block)`
|
135
|
+
|
136
|
+
Name your custom blocklist and make your ruby-block argument returna a truthy value if you want the request to be blocked, and falsy otherwise.
|
137
|
+
|
138
|
+
The request object is a [Rack::Request](http://www.rubydoc.info/gems/rack/Rack/Request).
|
139
|
+
|
140
|
+
E.g.
|
141
|
+
|
142
|
+
```ruby
|
143
|
+
# config/initializers/rack_attack.rb (for rails apps)
|
144
|
+
|
145
|
+
Rack::Attack.blocklist("block all access to admin") do |request|
|
120
146
|
# Requests are blocked if the return value is truthy
|
121
|
-
|
147
|
+
request.path.start_with?("/admin")
|
122
148
|
end
|
123
149
|
|
124
|
-
# Block logins from a bad user agent
|
125
150
|
Rack::Attack.blocklist('block bad UA logins') do |req|
|
126
151
|
req.path == '/login' && req.post? && req.user_agent == 'BadUA'
|
127
152
|
end
|
@@ -134,6 +159,8 @@ This pattern is inspired by [fail2ban](http://www.fail2ban.org/wiki/index.php/Ma
|
|
134
159
|
See the [fail2ban documentation](http://www.fail2ban.org/wiki/index.php/MANUAL_0_8#Jail_Options) for more details on
|
135
160
|
how the parameters work. For multiple filters, be sure to put each filter in a separate blocklist and use a unique discriminator for each fail2ban filter.
|
136
161
|
|
162
|
+
Fail2ban state is stored in a [configurable cache](#cache-store-configuration) (which defaults to `Rails.cache` if present).
|
163
|
+
|
137
164
|
```ruby
|
138
165
|
# Block suspicious requests for '/etc/password' or wordpress specific paths.
|
139
166
|
# After 3 blocked requests in 10 minutes, block all requests from that IP for 5 minutes.
|
@@ -154,8 +181,12 @@ end
|
|
154
181
|
Note that `Fail2Ban` filters are not automatically scoped to the blocklist, so when using multiple filters in an application the scoping must be added to the discriminator e.g. `"pentest:#{req.ip}"`.
|
155
182
|
|
156
183
|
#### Allow2Ban
|
184
|
+
|
157
185
|
`Allow2Ban.filter` works the same way as the `Fail2Ban.filter` except that it *allows* requests from misbehaving
|
158
186
|
clients until such time as they reach maxretry at which they are cut off as per normal.
|
187
|
+
|
188
|
+
Allow2ban state is stored in a [configurable cache](#cache-store-configuration) (which defaults to `Rails.cache` if present).
|
189
|
+
|
159
190
|
```ruby
|
160
191
|
# Lockout IP addresses that are hammering your login page.
|
161
192
|
# After 20 requests in 1 minute, block all requests from that IP for 1 hour.
|
@@ -170,33 +201,40 @@ Rack::Attack.blocklist('allow2ban login scrapers') do |req|
|
|
170
201
|
end
|
171
202
|
```
|
172
203
|
|
204
|
+
### Throttling
|
205
|
+
|
206
|
+
Throttle state is stored in a [configurable cache](#cache-store-configuration) (which defaults to `Rails.cache` if present).
|
207
|
+
|
208
|
+
#### `throttle(name, options, &block)`
|
209
|
+
|
210
|
+
Name your custom throttle, provide `limit` and `period` as options, and make your ruby-block argument return the __discriminator__. This discriminator is how you tell rack-attack whether you're limiting per IP address, per user email or any other.
|
173
211
|
|
174
|
-
|
212
|
+
The request object is a [Rack::Request](http://www.rubydoc.info/gems/rack/Rack/Request).
|
213
|
+
|
214
|
+
E.g.
|
175
215
|
|
176
216
|
```ruby
|
177
|
-
#
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
# "rack::attack:#{Time.now.to_i/1.second}:req/ip:#{req.ip}"
|
182
|
-
#
|
183
|
-
# If falsy, the cache key is neither incremented nor checked.
|
184
|
-
|
185
|
-
req.ip
|
217
|
+
# config/initializers/rack_attack.rb (for rails apps)
|
218
|
+
|
219
|
+
Rack::Attack.throttle("requests by ip", limit: 5, period: 2) do |request|
|
220
|
+
request.ip
|
186
221
|
end
|
187
222
|
|
188
223
|
# Throttle login attempts for a given email parameter to 6 reqs/minute
|
189
224
|
# Return the email as a discriminator on POST /login requests
|
190
|
-
Rack::Attack.throttle('logins
|
191
|
-
|
225
|
+
Rack::Attack.throttle('limit logins per email', limit: 6, period: 60) do |req|
|
226
|
+
if req.path == '/login' && req.post?
|
227
|
+
req.params['email']
|
228
|
+
end
|
192
229
|
end
|
193
230
|
|
194
231
|
# You can also set a limit and period using a proc. For instance, after
|
195
232
|
# Rack::Auth::Basic has authenticated the user:
|
196
|
-
limit_proc = proc {|req| req.env["REMOTE_USER"] == "admin" ? 100 : 1}
|
197
|
-
period_proc = proc {|req| req.env["REMOTE_USER"] == "admin" ? 1
|
198
|
-
|
199
|
-
|
233
|
+
limit_proc = proc { |req| req.env["REMOTE_USER"] == "admin" ? 100 : 1 }
|
234
|
+
period_proc = proc { |req| req.env["REMOTE_USER"] == "admin" ? 1 : 60 }
|
235
|
+
|
236
|
+
Rack::Attack.throttle('request per ip', limit: limit_proc, period: period_proc) do |request|
|
237
|
+
request.ip
|
200
238
|
end
|
201
239
|
```
|
202
240
|
|
@@ -222,7 +260,17 @@ ActiveSupport::Notifications.subscribe("rack.attack") do |name, start, finish, r
|
|
222
260
|
end
|
223
261
|
```
|
224
262
|
|
225
|
-
|
263
|
+
### Cache store configuration
|
264
|
+
|
265
|
+
Throttle, allow2ban and fail2ban state is stored in a configurable cache (which defaults to `Rails.cache` if present), presumably backed by memcached or redis ([at least gem v3.0.0](https://rubygems.org/gems/redis)).
|
266
|
+
|
267
|
+
```ruby
|
268
|
+
Rack::Attack.cache.store = ActiveSupport::Cache::MemoryStore.new # defaults to Rails.cache
|
269
|
+
```
|
270
|
+
|
271
|
+
Note that `Rack::Attack.cache` is only used for throttling, allow2ban and fail2ban filtering; not blocklisting and safelisting. Your cache store must implement `increment` and `write` like [ActiveSupport::Cache::Store](http://api.rubyonrails.org/classes/ActiveSupport/Cache/Store.html).
|
272
|
+
|
273
|
+
## Customizing responses
|
226
274
|
|
227
275
|
Customize the response of blocklisted and throttled requests using an object that adheres to the [Rack app interface](http://rack.rubyforge.org/doc/SPEC.html).
|
228
276
|
|
@@ -287,6 +335,43 @@ ActiveSupport::Notifications.subscribe('rack.attack') do |name, start, finish, r
|
|
287
335
|
end
|
288
336
|
```
|
289
337
|
|
338
|
+
## How it works
|
339
|
+
|
340
|
+
The Rack::Attack middleware compares each request against *safelists*, *blocklists*, *throttles*, and *tracks* that you define. There are none by default.
|
341
|
+
|
342
|
+
* If the request matches any **safelist**, it is allowed.
|
343
|
+
* Otherwise, if the request matches any **blocklist**, it is blocked.
|
344
|
+
* 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.
|
345
|
+
* Otherwise, all **tracks** are checked, and the request is allowed.
|
346
|
+
|
347
|
+
The algorithm is actually more concise in code: See [Rack::Attack.call](https://github.com/kickstarter/rack-attack/blob/master/lib/rack/attack.rb):
|
348
|
+
|
349
|
+
```ruby
|
350
|
+
def call(env)
|
351
|
+
req = Rack::Attack::Request.new(env)
|
352
|
+
|
353
|
+
if safelisted?(req)
|
354
|
+
@app.call(env)
|
355
|
+
elsif blocklisted?(req)
|
356
|
+
self.class.blocklisted_response.call(env)
|
357
|
+
elsif throttled?(req)
|
358
|
+
self.class.throttled_response.call(env)
|
359
|
+
else
|
360
|
+
tracked?(req)
|
361
|
+
@app.call(env)
|
362
|
+
end
|
363
|
+
end
|
364
|
+
```
|
365
|
+
|
366
|
+
Note: `Rack::Attack::Request` is just a subclass of `Rack::Request` so that you
|
367
|
+
can cleanly monkey patch helper methods onto the
|
368
|
+
[request object](https://github.com/kickstarter/rack-attack/blob/master/lib/rack/attack/request.rb).
|
369
|
+
|
370
|
+
### About Tracks
|
371
|
+
|
372
|
+
`Rack::Attack.track` doesn't affect request processing. Tracks are an easy way to log and measure requests matching arbitrary attributes.
|
373
|
+
|
374
|
+
|
290
375
|
## Testing
|
291
376
|
|
292
377
|
A note on developing and testing apps using Rack::Attack - if you are using throttling in particular, you will
|
data/lib/rack/attack.rb
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
require 'rack'
|
2
2
|
require 'forwardable'
|
3
|
+
require 'rack/attack/path_normalizer'
|
4
|
+
require 'rack/attack/request'
|
5
|
+
require "ipaddr"
|
3
6
|
|
4
7
|
class Rack::Attack
|
5
8
|
class MisconfiguredStoreError < StandardError; end
|
6
9
|
class MissingStoreError < StandardError; end
|
7
10
|
|
8
11
|
autoload :Cache, 'rack/attack/cache'
|
9
|
-
autoload :PathNormalizer, 'rack/attack/path_normalizer'
|
10
12
|
autoload :Check, 'rack/attack/check'
|
11
13
|
autoload :Throttle, 'rack/attack/throttle'
|
12
14
|
autoload :Safelist, 'rack/attack/safelist'
|
@@ -18,7 +20,6 @@ class Rack::Attack
|
|
18
20
|
autoload :RedisStoreProxy, 'rack/attack/store_proxy/redis_store_proxy'
|
19
21
|
autoload :Fail2Ban, 'rack/attack/fail2ban'
|
20
22
|
autoload :Allow2Ban, 'rack/attack/allow2ban'
|
21
|
-
autoload :Request, 'rack/attack/request'
|
22
23
|
|
23
24
|
class << self
|
24
25
|
|
@@ -37,6 +38,18 @@ class Rack::Attack
|
|
37
38
|
self.blocklists[name] = Blocklist.new(name, block)
|
38
39
|
end
|
39
40
|
|
41
|
+
def blocklist_ip(ip)
|
42
|
+
@ip_blocklists ||= []
|
43
|
+
ip_blocklist_proc = lambda { |request| IPAddr.new(ip).include?(IPAddr.new(request.ip)) }
|
44
|
+
@ip_blocklists << Blocklist.new(nil, ip_blocklist_proc)
|
45
|
+
end
|
46
|
+
|
47
|
+
def safelist_ip(ip)
|
48
|
+
@ip_safelists ||= []
|
49
|
+
ip_safelist_proc = lambda { |request| IPAddr.new(ip).include?(IPAddr.new(request.ip)) }
|
50
|
+
@ip_safelists << Safelist.new(nil, ip_safelist_proc)
|
51
|
+
end
|
52
|
+
|
40
53
|
def blacklist(name, &block)
|
41
54
|
warn "[DEPRECATION] 'Rack::Attack.blacklist' is deprecated. Please use 'blocklist' instead."
|
42
55
|
blocklist(name, &block)
|
@@ -66,9 +79,8 @@ class Rack::Attack
|
|
66
79
|
end
|
67
80
|
|
68
81
|
def safelisted?(req)
|
69
|
-
|
70
|
-
safelist
|
71
|
-
end
|
82
|
+
ip_safelists.any? { |safelist| safelist.match?(req) } ||
|
83
|
+
safelists.any? { |_name, safelist| safelist.match?(req) }
|
72
84
|
end
|
73
85
|
|
74
86
|
def whitelisted?(req)
|
@@ -77,9 +89,8 @@ class Rack::Attack
|
|
77
89
|
end
|
78
90
|
|
79
91
|
def blocklisted?(req)
|
80
|
-
|
81
|
-
blocklist
|
82
|
-
end
|
92
|
+
ip_blocklists.any? { |blocklist| blocklist.match?(req) } ||
|
93
|
+
blocklists.any? { |_name, blocklist| blocklist.match?(req) }
|
83
94
|
end
|
84
95
|
|
85
96
|
def blacklisted?(req)
|
@@ -109,6 +120,8 @@ class Rack::Attack
|
|
109
120
|
|
110
121
|
def clear!
|
111
122
|
@safelists, @blocklists, @throttles, @tracks = {}, {}, {}, {}
|
123
|
+
@ip_blocklists = []
|
124
|
+
@ip_safelists = []
|
112
125
|
end
|
113
126
|
|
114
127
|
def blacklisted_response=(res)
|
@@ -121,6 +134,15 @@ class Rack::Attack
|
|
121
134
|
blocklisted_response
|
122
135
|
end
|
123
136
|
|
137
|
+
private
|
138
|
+
|
139
|
+
def ip_blocklists
|
140
|
+
@ip_blocklists ||= []
|
141
|
+
end
|
142
|
+
|
143
|
+
def ip_safelists
|
144
|
+
@ip_safelists ||= []
|
145
|
+
end
|
124
146
|
end
|
125
147
|
|
126
148
|
# Set defaults
|