rack-attack 5.4.0 → 6.2.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.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +78 -27
  3. data/Rakefile +3 -1
  4. data/bin/setup +8 -0
  5. data/lib/rack/attack.rb +137 -148
  6. data/lib/rack/attack/allow2ban.rb +2 -0
  7. data/lib/rack/attack/blocklist.rb +3 -1
  8. data/lib/rack/attack/cache.rb +9 -4
  9. data/lib/rack/attack/check.rb +5 -2
  10. data/lib/rack/attack/fail2ban.rb +2 -0
  11. data/lib/rack/attack/path_normalizer.rb +22 -18
  12. data/lib/rack/attack/railtie.rb +21 -0
  13. data/lib/rack/attack/request.rb +2 -0
  14. data/lib/rack/attack/safelist.rb +3 -1
  15. data/lib/rack/attack/store_proxy.rb +12 -24
  16. data/lib/rack/attack/store_proxy/active_support_redis_store_proxy.rb +39 -0
  17. data/lib/rack/attack/store_proxy/dalli_proxy.rb +27 -13
  18. data/lib/rack/attack/store_proxy/mem_cache_store_proxy.rb +21 -0
  19. data/lib/rack/attack/store_proxy/redis_cache_store_proxy.rb +23 -9
  20. data/lib/rack/attack/store_proxy/redis_proxy.rb +16 -10
  21. data/lib/rack/attack/store_proxy/redis_store_proxy.rb +5 -5
  22. data/lib/rack/attack/throttle.rb +12 -8
  23. data/lib/rack/attack/track.rb +9 -6
  24. data/lib/rack/attack/version.rb +3 -1
  25. data/spec/acceptance/allow2ban_spec.rb +2 -0
  26. data/spec/acceptance/blocking_ip_spec.rb +4 -2
  27. data/spec/acceptance/blocking_spec.rb +45 -3
  28. data/spec/acceptance/blocking_subnet_spec.rb +4 -2
  29. data/spec/acceptance/cache_store_config_for_allow2ban_spec.rb +50 -39
  30. data/spec/acceptance/cache_store_config_for_fail2ban_spec.rb +38 -29
  31. data/spec/acceptance/cache_store_config_for_throttle_spec.rb +2 -0
  32. data/spec/acceptance/cache_store_config_with_rails_spec.rb +2 -0
  33. data/spec/acceptance/customizing_blocked_response_spec.rb +2 -0
  34. data/spec/acceptance/customizing_throttled_response_spec.rb +2 -0
  35. data/spec/acceptance/extending_request_object_spec.rb +2 -0
  36. data/spec/acceptance/fail2ban_spec.rb +2 -0
  37. data/spec/acceptance/rails_middleware_spec.rb +41 -0
  38. data/spec/acceptance/safelisting_ip_spec.rb +4 -2
  39. data/spec/acceptance/safelisting_spec.rb +57 -3
  40. data/spec/acceptance/safelisting_subnet_spec.rb +4 -2
  41. data/spec/acceptance/stores/active_support_dalli_store_spec.rb +3 -23
  42. data/spec/acceptance/stores/active_support_mem_cache_store_pooled_spec.rb +20 -0
  43. data/spec/acceptance/stores/active_support_mem_cache_store_spec.rb +4 -24
  44. data/spec/acceptance/stores/active_support_memory_store_spec.rb +3 -23
  45. data/spec/acceptance/stores/active_support_redis_cache_store_pooled_spec.rb +10 -24
  46. data/spec/acceptance/stores/active_support_redis_cache_store_spec.rb +9 -25
  47. data/spec/acceptance/stores/active_support_redis_store_spec.rb +4 -24
  48. data/spec/acceptance/stores/connection_pool_dalli_client_spec.rb +5 -23
  49. data/spec/acceptance/stores/dalli_client_spec.rb +3 -23
  50. data/spec/acceptance/stores/redis_spec.rb +1 -23
  51. data/spec/acceptance/stores/redis_store_spec.rb +3 -23
  52. data/spec/acceptance/throttling_spec.rb +7 -5
  53. data/spec/acceptance/track_spec.rb +5 -3
  54. data/spec/acceptance/track_throttle_spec.rb +5 -3
  55. data/spec/allow2ban_spec.rb +20 -15
  56. data/spec/fail2ban_spec.rb +20 -17
  57. data/spec/integration/offline_spec.rb +3 -1
  58. data/spec/rack_attack_dalli_proxy_spec.rb +2 -0
  59. data/spec/rack_attack_instrumentation_spec.rb +42 -0
  60. data/spec/rack_attack_path_normalizer_spec.rb +4 -2
  61. data/spec/rack_attack_request_spec.rb +2 -0
  62. data/spec/rack_attack_spec.rb +38 -34
  63. data/spec/rack_attack_throttle_spec.rb +50 -19
  64. data/spec/rack_attack_track_spec.rb +12 -7
  65. data/spec/spec_helper.rb +10 -8
  66. data/spec/support/cache_store_helper.rb +27 -1
  67. metadata +48 -28
  68. data/lib/rack/attack/store_proxy/mem_cache_proxy.rb +0 -50
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 123608043cbaa1604ab2d7b06c056010decd274dd4a5b1fe8f2175cec766fa4d
4
- data.tar.gz: 2bd3ca91293545b76221608f144c1a43a458fbd9e6f2e8ea2647bf561d8ea9a9
3
+ metadata.gz: ac5af22059fcc24c45b9732a806b13ef8a39b3ab425e713d22b7d0b1c9fbae11
4
+ data.tar.gz: fdd20e74080d4254d7910be3d1f0343580a2cedd79b18f2448fa753acd9259e2
5
5
  SHA512:
6
- metadata.gz: '06388ad68edc65019740c9ee77347f817b0769e782d0f8564ac5ac6b9c7fa819fe2157a10d89b570c78c14ef5a86fe080fac30aba8ebdfed281f2073785f5685'
7
- data.tar.gz: a15cc2cef9eebda52d662fe3eeee3f187d5b575c089a1fa2cf5e0179012499f6440889349a47108c4017f8b73dd18985655dc933baf8c031e08d73ea23d1dbba
6
+ metadata.gz: 1f2a7bd75ab8423dde30e482085b19cb5cfbf7347aed13c94da63d31784939075278cc0f891af450bd33e5ef3de4ea092441b26f2519e28ecb5cbe5c6a16d007
7
+ data.tar.gz: fbe8d0cc86c52be9a028a4fcb2f8f2399af143b6ccd77c7377cbe5f762bb344bdb80833c5e53221ffefad57d04ee132650b38cd5e9d44c5073e475ba894a1a3f
data/README.md CHANGED
@@ -1,3 +1,6 @@
1
+ __Note__: You are viewing the development version README.
2
+ For the README consistent with the latest released version see https://github.com/kickstarter/rack-attack/blob/6-stable/README.md.
3
+
1
4
  # Rack::Attack
2
5
 
3
6
  *Rack middleware for blocking & throttling abusive requests*
@@ -9,10 +12,44 @@ See the [Backing & Hacking blog post](https://www.kickstarter.com/backing-and-ha
9
12
  [![Gem Version](https://badge.fury.io/rb/rack-attack.svg)](https://badge.fury.io/rb/rack-attack)
10
13
  [![Build Status](https://travis-ci.org/kickstarter/rack-attack.svg?branch=master)](https://travis-ci.org/kickstarter/rack-attack)
11
14
  [![Code Climate](https://codeclimate.com/github/kickstarter/rack-attack.svg)](https://codeclimate.com/github/kickstarter/rack-attack)
15
+ [![Join the chat at https://gitter.im/rack-attack/rack-attack](https://badges.gitter.im/rack-attack/rack-attack.svg)](https://gitter.im/rack-attack/rack-attack)
16
+
17
+ ## Table of contents
18
+
19
+ - [Getting started](#getting-started)
20
+ - [Installing](#installing)
21
+ - [Plugging into the application](#plugging-into-the-application)
22
+ - [Usage](#usage)
23
+ - [Safelisting](#safelisting)
24
+ - [`safelist_ip(ip_address_string)`](#safelist_ipip_address_string)
25
+ - [`safelist_ip(ip_subnet_string)`](#safelist_ipip_subnet_string)
26
+ - [`safelist(name, &block)`](#safelistname-block)
27
+ - [Blocking](#blocking)
28
+ - [`blocklist_ip(ip_address_string)`](#blocklist_ipip_address_string)
29
+ - [`blocklist_ip(ip_subnet_string)`](#blocklist_ipip_subnet_string)
30
+ - [`blocklist(name, &block)`](#blocklistname-block)
31
+ - [Fail2Ban](#fail2ban)
32
+ - [Allow2Ban](#allow2ban)
33
+ - [Throttling](#throttling)
34
+ - [`throttle(name, options, &block)`](#throttlename-options-block)
35
+ - [Tracks](#tracks)
36
+ - [Cache store configuration](#cache-store-configuration)
37
+ - [Customizing responses](#customizing-responses)
38
+ - [RateLimit headers for well-behaved clients](#ratelimit-headers-for-well-behaved-clients)
39
+ - [Logging & Instrumentation](#logging--instrumentation)
40
+ - [How it works](#how-it-works)
41
+ - [About Tracks](#about-tracks)
42
+ - [Testing](#testing)
43
+ - [Performance](#performance)
44
+ - [Motivation](#motivation)
45
+ - [Contributing](#contributing)
46
+ - [Code of Conduct](#code-of-conduct)
47
+ - [Development setup](#development-setup)
48
+ - [License](#license)
12
49
 
13
50
  ## Getting started
14
51
 
15
- ### 1. Installing
52
+ ### Installing
16
53
 
17
54
  Add this line to your application's Gemfile:
18
55
 
@@ -30,18 +67,23 @@ Or install it yourself as:
30
67
 
31
68
  $ gem install rack-attack
32
69
 
33
- ### 2. Plugging into the application
70
+ ### Plugging into the application
34
71
 
35
72
  Then tell your ruby web application to use rack-attack as a middleware.
36
73
 
37
- a) For __rails__ applications:
38
-
74
+ a) For __rails__ applications with versions >= 5.1 it is used by default. For older rails versions you should enable it explicitly:
39
75
  ```ruby
40
76
  # In config/application.rb
41
77
 
42
78
  config.middleware.use Rack::Attack
43
79
  ```
44
80
 
81
+ You can disable it permanently (like for specific environment) or temporarily (can be useful for specific test cases) by writing:
82
+
83
+ ```ruby
84
+ Rack::Attack.enabled = false
85
+ ```
86
+
45
87
  b) For __rack__ applications:
46
88
 
47
89
  ```ruby
@@ -55,8 +97,8 @@ __IMPORTANT__: By default, rack-attack won't perform any blocking or throttling,
55
97
 
56
98
  ## Usage
57
99
 
58
- *Tip:* The example in the wiki is a great way to get started:
59
- [Example Configuration](https://github.com/kickstarter/rack-attack/wiki/Example-Configuration)
100
+ *Tip:* If you just want to get going asap, then you can take our [example configuration](docs/example_configuration.md)
101
+ and tailor it to your needs, or check out the [advanced configuration](docs/advanced_configuration.md) examples.
60
102
 
61
103
  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.
62
104
 
@@ -86,7 +128,7 @@ Rack::Attack.safelist_ip("5.6.7.0/24")
86
128
 
87
129
  #### `safelist(name, &block)`
88
130
 
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.
131
+ Name your custom safelist and make your ruby-block argument return a truthy value if you want the request to be allowed, and falsy otherwise.
90
132
 
91
133
  The request object is a [Rack::Request](http://www.rubydoc.info/gems/rack/Rack/Request).
92
134
 
@@ -252,8 +294,9 @@ Rack::Attack.track("special_agent", limit: 6, period: 60) do |req|
252
294
  end
253
295
 
254
296
  # Track it using ActiveSupport::Notification
255
- ActiveSupport::Notifications.subscribe("rack.attack") do |name, start, finish, request_id, req|
256
- if req.env['rack.attack.matched'] == "special_agent" && req.env['rack.attack.match_type'] == :track
297
+ ActiveSupport::Notifications.subscribe("track.rack_attack") do |name, start, finish, request_id, payload|
298
+ req = payload[:request]
299
+ if req.env['rack.attack.matched'] == "special_agent"
257
300
  Rails.logger.info "special_agent: #{req.path}"
258
301
  STATSD.increment("special_agent")
259
302
  end
@@ -294,12 +337,12 @@ Rack::Attack.throttled_response = lambda do |env|
294
337
  end
295
338
  ```
296
339
 
297
- ### X-RateLimit headers for well-behaved clients
340
+ ### RateLimit headers for well-behaved clients
298
341
 
299
342
  While Rack::Attack's primary focus is minimizing harm from abusive clients, it
300
343
  can also be used to return rate limit data that's helpful for well-behaved clients.
301
344
 
302
- Here's an example response that includes conventional `X-RateLimit-*` headers:
345
+ Here's an example response that includes conventional `RateLimit-*` headers:
303
346
 
304
347
  ```ruby
305
348
  Rack::Attack.throttled_response = lambda do |env|
@@ -307,9 +350,9 @@ Rack::Attack.throttled_response = lambda do |env|
307
350
  now = match_data[:epoch_time]
308
351
 
309
352
  headers = {
310
- 'X-RateLimit-Limit' => match_data[:limit].to_s,
311
- 'X-RateLimit-Remaining' => '0',
312
- 'X-RateLimit-Reset' => (now + (match_data[:period] - now % match_data[:period])).to_s
353
+ 'RateLimit-Limit' => match_data[:limit].to_s,
354
+ 'RateLimit-Remaining' => '0',
355
+ 'RateLimit-Reset' => (now + (match_data[:period] - now % match_data[:period])).to_s
313
356
  }
314
357
 
315
358
  [ 429, headers, ["Throttled\n"]]
@@ -320,18 +363,33 @@ end
320
363
  For responses that did not exceed a throttle limit, Rack::Attack annotates the env with match data:
321
364
 
322
365
  ```ruby
323
- request.env['rack.attack.throttle_data'][name] # => { :count => n, :period => p, :limit => l, :epoch_time => t }
366
+ request.env['rack.attack.throttle_data'][name] # => { discriminator: d, count: n, period: p, limit: l, epoch_time: t }
324
367
  ```
325
368
 
326
369
  ## Logging & Instrumentation
327
370
 
328
371
  Rack::Attack uses the [ActiveSupport::Notifications](http://api.rubyonrails.org/classes/ActiveSupport/Notifications.html) API if available.
329
372
 
330
- You can subscribe to 'rack.attack' events and log it, graph it, etc:
373
+ You can subscribe to `rack_attack` events and log it, graph it, etc.
374
+
375
+ To get notified about specific type of events, subscribe to the event name followed by the `rack_attack` namesapce.
376
+ E.g. for throttles use:
377
+
378
+ ```ruby
379
+ ActiveSupport::Notifications.subscribe("throttle.rack_attack") do |name, start, finish, request_id, payload|
380
+ # request object available in payload[:request]
381
+
382
+ # Your code here
383
+ end
384
+ ```
385
+
386
+ If you want to subscribe to every `rack_attack` event, use:
331
387
 
332
388
  ```ruby
333
- ActiveSupport::Notifications.subscribe('rack.attack') do |name, start, finish, request_id, req|
334
- puts req.inspect
389
+ ActiveSupport::Notifications.subscribe(/rack_attack/) do |name, start, finish, request_id, payload|
390
+ # request object available in payload[:request]
391
+
392
+ # Your code here
335
393
  end
336
394
  ```
337
395
 
@@ -344,7 +402,7 @@ The Rack::Attack middleware compares each request against *safelists*, *blocklis
344
402
  * 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
403
  * Otherwise, all **tracks** are checked, and the request is allowed.
346
404
 
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):
405
+ The algorithm is actually more concise in code: See [Rack::Attack.call](lib/rack/attack.rb):
348
406
 
349
407
  ```ruby
350
408
  def call(env)
@@ -365,7 +423,7 @@ end
365
423
 
366
424
  Note: `Rack::Attack::Request` is just a subclass of `Rack::Request` so that you
367
425
  can cleanly monkey patch helper methods onto the
368
- [request object](https://github.com/kickstarter/rack-attack/blob/master/lib/rack/attack/request.rb).
426
+ [request object](lib/rack/attack/request.rb).
369
427
 
370
428
  ### About Tracks
371
429
 
@@ -412,13 +470,6 @@ This project is intended to be a safe, welcoming space for collaboration, and co
412
470
 
413
471
  Check out the [Development guide](docs/development.md).
414
472
 
415
- ## Mailing list
416
-
417
- New releases of Rack::Attack are announced on
418
- <rack.attack.announce@librelist.com>. To subscribe, just send an email to
419
- <rack.attack.announce@librelist.com>. See the
420
- [archives](http://librelist.com/browser/rack.attack.announce/).
421
-
422
473
  ## License
423
474
 
424
475
  Copyright Kickstarter, PBC.
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "rubygems"
2
4
  require "bundler/setup"
3
5
  require 'bundler/gem_tasks'
@@ -24,4 +26,4 @@ Rake::TestTask.new(:test) do |t|
24
26
  t.pattern = "spec/**/*_spec.rb"
25
27
  end
26
28
 
27
- task :default => [:rubocop, :test]
29
+ task default: [:rubocop, :test]
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -1,187 +1,176 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack'
2
4
  require 'forwardable'
3
5
  require 'rack/attack/path_normalizer'
4
6
  require 'rack/attack/request'
5
7
  require "ipaddr"
6
8
 
7
- class Rack::Attack
8
- class MisconfiguredStoreError < StandardError; end
9
- class MissingStoreError < StandardError; end
10
-
11
- autoload :Cache, 'rack/attack/cache'
12
- autoload :Check, 'rack/attack/check'
13
- autoload :Throttle, 'rack/attack/throttle'
14
- autoload :Safelist, 'rack/attack/safelist'
15
- autoload :Blocklist, 'rack/attack/blocklist'
16
- autoload :Track, 'rack/attack/track'
17
- autoload :StoreProxy, 'rack/attack/store_proxy'
18
- autoload :DalliProxy, 'rack/attack/store_proxy/dalli_proxy'
19
- autoload :MemCacheProxy, 'rack/attack/store_proxy/mem_cache_proxy'
20
- autoload :RedisProxy, 'rack/attack/store_proxy/redis_proxy'
21
- autoload :RedisStoreProxy, 'rack/attack/store_proxy/redis_store_proxy'
22
- autoload :RedisCacheStoreProxy, 'rack/attack/store_proxy/redis_cache_store_proxy'
23
- autoload :Fail2Ban, 'rack/attack/fail2ban'
24
- autoload :Allow2Ban, 'rack/attack/allow2ban'
25
-
26
- class << self
27
- attr_accessor :notifier, :blocklisted_response, :throttled_response
28
-
29
- def safelist(name, &block)
30
- self.safelists[name] = Safelist.new(name, block)
31
- end
32
-
33
- def whitelist(name, &block)
34
- warn "[DEPRECATION] 'Rack::Attack.whitelist' is deprecated. Please use 'safelist' instead."
35
- safelist(name, &block)
36
- end
37
-
38
- def blocklist(name, &block)
39
- self.blocklists[name] = Blocklist.new(name, block)
40
- end
9
+ require 'rack/attack/railtie' if defined?(::Rails)
10
+
11
+ module Rack
12
+ class Attack
13
+ class Error < StandardError; end
14
+ class MisconfiguredStoreError < Error; end
15
+ class MissingStoreError < Error; end
16
+
17
+ autoload :Cache, 'rack/attack/cache'
18
+ autoload :Check, 'rack/attack/check'
19
+ autoload :Throttle, 'rack/attack/throttle'
20
+ autoload :Safelist, 'rack/attack/safelist'
21
+ autoload :Blocklist, 'rack/attack/blocklist'
22
+ autoload :Track, 'rack/attack/track'
23
+ autoload :StoreProxy, 'rack/attack/store_proxy'
24
+ autoload :DalliProxy, 'rack/attack/store_proxy/dalli_proxy'
25
+ autoload :MemCacheStoreProxy, 'rack/attack/store_proxy/mem_cache_store_proxy'
26
+ autoload :RedisProxy, 'rack/attack/store_proxy/redis_proxy'
27
+ autoload :RedisStoreProxy, 'rack/attack/store_proxy/redis_store_proxy'
28
+ autoload :RedisCacheStoreProxy, 'rack/attack/store_proxy/redis_cache_store_proxy'
29
+ autoload :ActiveSupportRedisStoreProxy, 'rack/attack/store_proxy/active_support_redis_store_proxy'
30
+ autoload :Fail2Ban, 'rack/attack/fail2ban'
31
+ autoload :Allow2Ban, 'rack/attack/allow2ban'
32
+
33
+ class << self
34
+ attr_accessor :enabled, :notifier, :blocklisted_response, :throttled_response,
35
+ :anonymous_blocklists, :anonymous_safelists
36
+
37
+ def safelist(name = nil, &block)
38
+ safelist = Safelist.new(name, &block)
39
+
40
+ if name
41
+ safelists[name] = safelist
42
+ else
43
+ anonymous_safelists << safelist
44
+ end
45
+ end
41
46
 
42
- def blocklist_ip(ip_address)
43
- @ip_blocklists ||= []
44
- ip_blocklist_proc = lambda { |request| IPAddr.new(ip_address).include?(IPAddr.new(request.ip)) }
45
- @ip_blocklists << Blocklist.new(nil, ip_blocklist_proc)
46
- end
47
+ def blocklist(name = nil, &block)
48
+ blocklist = Blocklist.new(name, &block)
47
49
 
48
- def safelist_ip(ip_address)
49
- @ip_safelists ||= []
50
- ip_safelist_proc = lambda { |request| IPAddr.new(ip_address).include?(IPAddr.new(request.ip)) }
51
- @ip_safelists << Safelist.new(nil, ip_safelist_proc)
52
- end
50
+ if name
51
+ blocklists[name] = blocklist
52
+ else
53
+ anonymous_blocklists << blocklist
54
+ end
55
+ end
53
56
 
54
- def blacklist(name, &block)
55
- warn "[DEPRECATION] 'Rack::Attack.blacklist' is deprecated. Please use 'blocklist' instead."
56
- blocklist(name, &block)
57
- end
57
+ def blocklist_ip(ip_address)
58
+ anonymous_blocklists << Blocklist.new { |request| IPAddr.new(ip_address).include?(IPAddr.new(request.ip)) }
59
+ end
58
60
 
59
- def throttle(name, options, &block)
60
- self.throttles[name] = Throttle.new(name, options, block)
61
- end
61
+ def safelist_ip(ip_address)
62
+ anonymous_safelists << Safelist.new { |request| IPAddr.new(ip_address).include?(IPAddr.new(request.ip)) }
63
+ end
62
64
 
63
- def track(name, options = {}, &block)
64
- self.tracks[name] = Track.new(name, options, block)
65
- end
65
+ def throttle(name, options, &block)
66
+ throttles[name] = Throttle.new(name, options, &block)
67
+ end
66
68
 
67
- def safelists; @safelists ||= {}; end
69
+ def track(name, options = {}, &block)
70
+ tracks[name] = Track.new(name, options, &block)
71
+ end
68
72
 
69
- def blocklists; @blocklists ||= {}; end
73
+ def safelists
74
+ @safelists ||= {}
75
+ end
70
76
 
71
- def throttles; @throttles ||= {}; end
77
+ def blocklists
78
+ @blocklists ||= {}
79
+ end
72
80
 
73
- def tracks; @tracks ||= {}; end
81
+ def throttles
82
+ @throttles ||= {}
83
+ end
74
84
 
75
- def whitelists
76
- warn "[DEPRECATION] 'Rack::Attack.whitelists' is deprecated. Please use 'safelists' instead."
77
- safelists
78
- end
85
+ def tracks
86
+ @tracks ||= {}
87
+ end
79
88
 
80
- def blacklists
81
- warn "[DEPRECATION] 'Rack::Attack.blacklists' is deprecated. Please use 'blocklists' instead."
82
- blocklists
83
- end
89
+ def safelisted?(request)
90
+ anonymous_safelists.any? { |safelist| safelist.matched_by?(request) } ||
91
+ safelists.any? { |_name, safelist| safelist.matched_by?(request) }
92
+ end
84
93
 
85
- def safelisted?(request)
86
- ip_safelists.any? { |safelist| safelist.matched_by?(request) } ||
87
- safelists.any? { |_name, safelist| safelist.matched_by?(request) }
88
- end
94
+ def blocklisted?(request)
95
+ anonymous_blocklists.any? { |blocklist| blocklist.matched_by?(request) } ||
96
+ blocklists.any? { |_name, blocklist| blocklist.matched_by?(request) }
97
+ end
89
98
 
90
- def whitelisted?(request)
91
- warn "[DEPRECATION] 'Rack::Attack.whitelisted?' is deprecated. Please use 'safelisted?' instead."
92
- safelisted?(request)
93
- end
99
+ def throttled?(request)
100
+ throttles.any? do |_name, throttle|
101
+ throttle.matched_by?(request)
102
+ end
103
+ end
94
104
 
95
- def blocklisted?(request)
96
- ip_blocklists.any? { |blocklist| blocklist.matched_by?(request) } ||
97
- blocklists.any? { |_name, blocklist| blocklist.matched_by?(request) }
98
- end
105
+ def tracked?(request)
106
+ tracks.each_value do |track|
107
+ track.matched_by?(request)
108
+ end
109
+ end
99
110
 
100
- def blacklisted?(request)
101
- warn "[DEPRECATION] 'Rack::Attack.blacklisted?' is deprecated. Please use 'blocklisted?' instead."
102
- blocklisted?(request)
103
- end
111
+ def instrument(request)
112
+ if notifier
113
+ event_type = request.env["rack.attack.match_type"]
114
+ notifier.instrument("#{event_type}.rack_attack", request: request)
104
115
 
105
- def throttled?(request)
106
- throttles.any? do |_name, throttle|
107
- throttle.matched_by?(request)
116
+ # Deprecated: Keeping just for backwards compatibility
117
+ notifier.instrument("rack.attack", request: request)
118
+ end
108
119
  end
109
- end
110
120
 
111
- def tracked?(request)
112
- tracks.each_value do |track|
113
- track.matched_by?(request)
121
+ def cache
122
+ @cache ||= Cache.new
114
123
  end
115
- end
116
-
117
- def instrument(request)
118
- notifier.instrument('rack.attack', request) if notifier
119
- end
120
-
121
- def cache
122
- @cache ||= Cache.new
123
- end
124
124
 
125
- def clear_configuration
126
- @safelists, @blocklists, @throttles, @tracks = {}, {}, {}, {}
127
- @ip_blocklists = []
128
- @ip_safelists = []
129
- end
125
+ def clear_configuration
126
+ @safelists = {}
127
+ @blocklists = {}
128
+ @throttles = {}
129
+ @tracks = {}
130
+ self.anonymous_blocklists = []
131
+ self.anonymous_safelists = []
132
+ end
130
133
 
131
- def clear!
132
- warn "[DEPRECATION] Rack::Attack.clear! is deprecated. Please use Rack::Attack.clear_configuration instead"
133
- clear_configuration
134
+ def clear!
135
+ warn "[DEPRECATION] Rack::Attack.clear! is deprecated. Please use Rack::Attack.clear_configuration instead"
136
+ clear_configuration
137
+ end
134
138
  end
135
139
 
136
- def blacklisted_response=(res)
137
- warn "[DEPRECATION] 'Rack::Attack.blacklisted_response=' is deprecated. Please use 'blocklisted_response=' instead."
138
- self.blocklisted_response = res
140
+ # Set defaults
141
+ @enabled = true
142
+ @anonymous_blocklists = []
143
+ @anonymous_safelists = []
144
+ @notifier = ActiveSupport::Notifications if defined?(ActiveSupport::Notifications)
145
+ @blocklisted_response = lambda { |_env| [403, { 'Content-Type' => 'text/plain' }, ["Forbidden\n"]] }
146
+ @throttled_response = lambda do |env|
147
+ retry_after = (env['rack.attack.match_data'] || {})[:period]
148
+ [429, { 'Content-Type' => 'text/plain', 'Retry-After' => retry_after.to_s }, ["Retry later\n"]]
139
149
  end
140
150
 
141
- def blacklisted_response
142
- warn "[DEPRECATION] 'Rack::Attack.blacklisted_response' is deprecated. Please use 'blocklisted_response' instead."
143
- blocklisted_response
151
+ def initialize(app)
152
+ @app = app
144
153
  end
145
154
 
146
- private
155
+ def call(env)
156
+ return @app.call(env) unless self.class.enabled
147
157
 
148
- def ip_blocklists
149
- @ip_blocklists ||= []
150
- end
158
+ env['PATH_INFO'] = PathNormalizer.normalize_path(env['PATH_INFO'])
159
+ request = Rack::Attack::Request.new(env)
151
160
 
152
- def ip_safelists
153
- @ip_safelists ||= []
161
+ if safelisted?(request)
162
+ @app.call(env)
163
+ elsif blocklisted?(request)
164
+ self.class.blocklisted_response.call(env)
165
+ elsif throttled?(request)
166
+ self.class.throttled_response.call(env)
167
+ else
168
+ tracked?(request)
169
+ @app.call(env)
170
+ end
154
171
  end
155
- end
156
-
157
- # Set defaults
158
- @notifier = ActiveSupport::Notifications if defined?(ActiveSupport::Notifications)
159
- @blocklisted_response = lambda { |_env| [403, { 'Content-Type' => 'text/plain' }, ["Forbidden\n"]] }
160
- @throttled_response = lambda { |env|
161
- retry_after = (env['rack.attack.match_data'] || {})[:period]
162
- [429, { 'Content-Type' => 'text/plain', 'Retry-After' => retry_after.to_s }, ["Retry later\n"]]
163
- }
164
-
165
- def initialize(app)
166
- @app = app
167
- end
168
172
 
169
- def call(env)
170
- env['PATH_INFO'] = PathNormalizer.normalize_path(env['PATH_INFO'])
171
- request = Rack::Attack::Request.new(env)
172
-
173
- if safelisted?(request)
174
- @app.call(env)
175
- elsif blocklisted?(request)
176
- self.class.blocklisted_response.call(env)
177
- elsif throttled?(request)
178
- self.class.throttled_response.call(env)
179
- else
180
- tracked?(request)
181
- @app.call(env)
182
- end
173
+ extend Forwardable
174
+ def_delegators self, :safelisted?, :blocklisted?, :throttled?, :tracked?
183
175
  end
184
-
185
- extend Forwardable
186
- def_delegators self, :safelisted?, :blocklisted?, :throttled?, :tracked?
187
176
  end