rack-attack 5.4.1 → 6.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +78 -27
  3. data/Rakefile +3 -1
  4. data/lib/rack/attack.rb +138 -149
  5. data/lib/rack/attack/allow2ban.rb +2 -0
  6. data/lib/rack/attack/blocklist.rb +3 -1
  7. data/lib/rack/attack/cache.rb +9 -4
  8. data/lib/rack/attack/check.rb +5 -2
  9. data/lib/rack/attack/fail2ban.rb +2 -0
  10. data/lib/rack/attack/path_normalizer.rb +22 -18
  11. data/lib/rack/attack/railtie.rb +13 -0
  12. data/lib/rack/attack/request.rb +2 -0
  13. data/lib/rack/attack/safelist.rb +3 -1
  14. data/lib/rack/attack/store_proxy.rb +12 -14
  15. data/lib/rack/attack/store_proxy/active_support_redis_store_proxy.rb +39 -0
  16. data/lib/rack/attack/store_proxy/dalli_proxy.rb +27 -13
  17. data/lib/rack/attack/store_proxy/mem_cache_store_proxy.rb +3 -1
  18. data/lib/rack/attack/store_proxy/redis_cache_store_proxy.rb +23 -9
  19. data/lib/rack/attack/store_proxy/redis_proxy.rb +16 -10
  20. data/lib/rack/attack/store_proxy/redis_store_proxy.rb +5 -5
  21. data/lib/rack/attack/throttle.rb +12 -8
  22. data/lib/rack/attack/track.rb +9 -6
  23. data/lib/rack/attack/version.rb +3 -1
  24. data/spec/acceptance/allow2ban_spec.rb +2 -0
  25. data/spec/acceptance/blocking_ip_spec.rb +4 -2
  26. data/spec/acceptance/blocking_spec.rb +45 -3
  27. data/spec/acceptance/blocking_subnet_spec.rb +4 -2
  28. data/spec/acceptance/cache_store_config_for_allow2ban_spec.rb +50 -39
  29. data/spec/acceptance/cache_store_config_for_fail2ban_spec.rb +38 -29
  30. data/spec/acceptance/cache_store_config_for_throttle_spec.rb +2 -0
  31. data/spec/acceptance/cache_store_config_with_rails_spec.rb +2 -0
  32. data/spec/acceptance/customizing_blocked_response_spec.rb +2 -0
  33. data/spec/acceptance/customizing_throttled_response_spec.rb +2 -0
  34. data/spec/acceptance/extending_request_object_spec.rb +2 -0
  35. data/spec/acceptance/fail2ban_spec.rb +2 -0
  36. data/spec/acceptance/rails_middleware_spec.rb +35 -0
  37. data/spec/acceptance/safelisting_ip_spec.rb +4 -2
  38. data/spec/acceptance/safelisting_spec.rb +57 -3
  39. data/spec/acceptance/safelisting_subnet_spec.rb +4 -2
  40. data/spec/acceptance/stores/active_support_dalli_store_spec.rb +2 -0
  41. data/spec/acceptance/stores/active_support_mem_cache_store_pooled_spec.rb +1 -3
  42. data/spec/acceptance/stores/active_support_mem_cache_store_spec.rb +2 -0
  43. data/spec/acceptance/stores/active_support_memory_store_spec.rb +2 -0
  44. data/spec/acceptance/stores/active_support_redis_cache_store_pooled_spec.rb +9 -1
  45. data/spec/acceptance/stores/active_support_redis_cache_store_spec.rb +8 -1
  46. data/spec/acceptance/stores/active_support_redis_store_spec.rb +3 -1
  47. data/spec/acceptance/stores/connection_pool_dalli_client_spec.rb +5 -3
  48. data/spec/acceptance/stores/dalli_client_spec.rb +2 -0
  49. data/spec/acceptance/stores/redis_store_spec.rb +2 -0
  50. data/spec/acceptance/throttling_spec.rb +7 -5
  51. data/spec/acceptance/track_spec.rb +5 -3
  52. data/spec/acceptance/track_throttle_spec.rb +5 -3
  53. data/spec/allow2ban_spec.rb +20 -15
  54. data/spec/fail2ban_spec.rb +20 -17
  55. data/spec/integration/offline_spec.rb +3 -1
  56. data/spec/rack_attack_dalli_proxy_spec.rb +2 -0
  57. data/spec/rack_attack_instrumentation_spec.rb +42 -0
  58. data/spec/rack_attack_path_normalizer_spec.rb +4 -2
  59. data/spec/rack_attack_request_spec.rb +2 -0
  60. data/spec/rack_attack_spec.rb +38 -34
  61. data/spec/rack_attack_throttle_spec.rb +50 -19
  62. data/spec/rack_attack_track_spec.rb +12 -7
  63. data/spec/spec_helper.rb +12 -8
  64. data/spec/support/cache_store_helper.rb +2 -0
  65. metadata +44 -28
  66. 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: 21f1ecf9854b74958cacdb31211735c29b9a2f98f1ee848e0f60a3f409651cc6
4
- data.tar.gz: 934f2ac2af420277f6d31052b56af24d2cbe93412a272aa753c25ce26228d7b1
3
+ metadata.gz: 55b137d5d1174ac14bcbc494ba7c6753652c23348fef73c5ac712884e299a8d0
4
+ data.tar.gz: 9aae493ec090c669ea43f7a5f9bc8a10e6786266477a4dd65b05495c7559e469
5
5
  SHA512:
6
- metadata.gz: 32f63613544602a11c8af12a5ac764adf44ba22beda1dc8840bfc5431a32edd8ea1075eab6933f423405e71959ed0c5b5f8668d72262c5fb4787f89105259f5a
7
- data.tar.gz: aad3b2d1ace4369f2605d863df03c86d0e008c934c5f9c6741b71d2a3e363bcf37c93685e436ff48c917a512d39b8fd059fb3db09f38250c609f5fa22244cda4
6
+ metadata.gz: 2e283d8c8b2e5ffcd99435561230c6019a7a1f9637927d4ca29e0df2c9af9522cb84e60dddd040ab3429b1cc697213d80adb4a9b4cb7587933060ad6e426fabd
7
+ data.tar.gz: 1ef81c28f633e8c146a54a0930c706a3158aab568328743e0a3b0953bcbcba0bfb4a32a3e589ea1292c001ec6fd8d52a9c6de3cc4063b466045b692e92d27318
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]
@@ -1,188 +1,177 @@
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 :MemCacheStoreProxy, 'rack/attack/store_proxy/mem_cache_store_proxy'
21
- autoload :RedisProxy, 'rack/attack/store_proxy/redis_proxy'
22
- autoload :RedisStoreProxy, 'rack/attack/store_proxy/redis_store_proxy'
23
- autoload :RedisCacheStoreProxy, 'rack/attack/store_proxy/redis_cache_store_proxy'
24
- autoload :Fail2Ban, 'rack/attack/fail2ban'
25
- autoload :Allow2Ban, 'rack/attack/allow2ban'
26
-
27
- class << self
28
- attr_accessor :notifier, :blocklisted_response, :throttled_response
29
-
30
- def safelist(name, &block)
31
- self.safelists[name] = Safelist.new(name, block)
32
- end
33
-
34
- def whitelist(name, &block)
35
- warn "[DEPRECATION] 'Rack::Attack.whitelist' is deprecated. Please use 'safelist' instead."
36
- safelist(name, &block)
37
- end
38
-
39
- def blocklist(name, &block)
40
- self.blocklists[name] = Blocklist.new(name, block)
41
- 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
42
46
 
43
- def blocklist_ip(ip_address)
44
- @ip_blocklists ||= []
45
- ip_blocklist_proc = lambda { |request| IPAddr.new(ip_address).include?(IPAddr.new(request.ip)) }
46
- @ip_blocklists << Blocklist.new(nil, ip_blocklist_proc)
47
- end
47
+ def blocklist(name = nil, &block)
48
+ blocklist = Blocklist.new(name, &block)
48
49
 
49
- def safelist_ip(ip_address)
50
- @ip_safelists ||= []
51
- ip_safelist_proc = lambda { |request| IPAddr.new(ip_address).include?(IPAddr.new(request.ip)) }
52
- @ip_safelists << Safelist.new(nil, ip_safelist_proc)
53
- end
50
+ if name
51
+ blocklists[name] = blocklist
52
+ else
53
+ anonymous_blocklists << blocklist
54
+ end
55
+ end
54
56
 
55
- def blacklist(name, &block)
56
- warn "[DEPRECATION] 'Rack::Attack.blacklist' is deprecated. Please use 'blocklist' instead."
57
- blocklist(name, &block)
58
- end
57
+ def blocklist_ip(ip_address)
58
+ anonymous_blocklists << Blocklist.new { |request| IPAddr.new(ip_address).include?(IPAddr.new(request.ip)) }
59
+ end
59
60
 
60
- def throttle(name, options, &block)
61
- self.throttles[name] = Throttle.new(name, options, block)
62
- end
61
+ def safelist_ip(ip_address)
62
+ anonymous_safelists << Safelist.new { |request| IPAddr.new(ip_address).include?(IPAddr.new(request.ip)) }
63
+ end
63
64
 
64
- def track(name, options = {}, &block)
65
- self.tracks[name] = Track.new(name, options, block)
66
- end
65
+ def throttle(name, options, &block)
66
+ throttles[name] = Throttle.new(name, options, &block)
67
+ end
67
68
 
68
- def safelists; @safelists ||= {}; end
69
+ def track(name, options = {}, &block)
70
+ tracks[name] = Track.new(name, options, &block)
71
+ end
69
72
 
70
- def blocklists; @blocklists ||= {}; end
73
+ def safelists
74
+ @safelists ||= {}
75
+ end
71
76
 
72
- def throttles; @throttles ||= {}; end
77
+ def blocklists
78
+ @blocklists ||= {}
79
+ end
73
80
 
74
- def tracks; @tracks ||= {}; end
81
+ def throttles
82
+ @throttles ||= {}
83
+ end
75
84
 
76
- def whitelists
77
- warn "[DEPRECATION] 'Rack::Attack.whitelists' is deprecated. Please use 'safelists' instead."
78
- safelists
79
- end
85
+ def tracks
86
+ @tracks ||= {}
87
+ end
80
88
 
81
- def blacklists
82
- warn "[DEPRECATION] 'Rack::Attack.blacklists' is deprecated. Please use 'blocklists' instead."
83
- blocklists
84
- 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
85
93
 
86
- def safelisted?(request)
87
- ip_safelists.any? { |safelist| safelist.matched_by?(request) } ||
88
- safelists.any? { |_name, safelist| safelist.matched_by?(request) }
89
- 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
90
98
 
91
- def whitelisted?(request)
92
- warn "[DEPRECATION] 'Rack::Attack.whitelisted?' is deprecated. Please use 'safelisted?' instead."
93
- safelisted?(request)
94
- end
99
+ def throttled?(request)
100
+ throttles.any? do |_name, throttle|
101
+ throttle.matched_by?(request)
102
+ end
103
+ end
95
104
 
96
- def blocklisted?(request)
97
- ip_blocklists.any? { |blocklist| blocklist.matched_by?(request) } ||
98
- blocklists.any? { |_name, blocklist| blocklist.matched_by?(request) }
99
- end
105
+ def tracked?(request)
106
+ tracks.each_value do |track|
107
+ track.matched_by?(request)
108
+ end
109
+ end
100
110
 
101
- def blacklisted?(request)
102
- warn "[DEPRECATION] 'Rack::Attack.blacklisted?' is deprecated. Please use 'blocklisted?' instead."
103
- blocklisted?(request)
104
- 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)
105
115
 
106
- def throttled?(request)
107
- throttles.any? do |_name, throttle|
108
- throttle.matched_by?(request)
116
+ # Deprecated: Keeping just for backwards compatibility
117
+ notifier.instrument("rack.attack", request: request)
118
+ end
109
119
  end
110
- end
111
120
 
112
- def tracked?(request)
113
- tracks.each_value do |track|
114
- track.matched_by?(request)
121
+ def cache
122
+ @cache ||= Cache.new
115
123
  end
116
- end
117
-
118
- def instrument(request)
119
- notifier.instrument('rack.attack', request) if notifier
120
- end
121
-
122
- def cache
123
- @cache ||= Cache.new
124
- end
125
124
 
126
- def clear_configuration
127
- @safelists, @blocklists, @throttles, @tracks = {}, {}, {}, {}
128
- @ip_blocklists = []
129
- @ip_safelists = []
130
- end
125
+ def clear_configuration
126
+ @safelists = {}
127
+ @blocklists = {}
128
+ @throttles = {}
129
+ @tracks = {}
130
+ self.anonymous_blocklists = []
131
+ self.anonymous_safelists = []
132
+ end
131
133
 
132
- def clear!
133
- warn "[DEPRECATION] Rack::Attack.clear! is deprecated. Please use Rack::Attack.clear_configuration instead"
134
- 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
135
138
  end
136
139
 
137
- def blacklisted_response=(res)
138
- warn "[DEPRECATION] 'Rack::Attack.blacklisted_response=' is deprecated. Please use 'blocklisted_response=' instead."
139
- 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"]]
140
149
  end
141
150
 
142
- def blacklisted_response
143
- warn "[DEPRECATION] 'Rack::Attack.blacklisted_response' is deprecated. Please use 'blocklisted_response' instead."
144
- blocklisted_response
151
+ def initialize(app)
152
+ @app = app
145
153
  end
146
154
 
147
- private
155
+ def call(env)
156
+ return @app.call(env) if !self.class.enabled || env["rack.attack.called"]
148
157
 
149
- def ip_blocklists
150
- @ip_blocklists ||= []
151
- end
158
+ env["rack.attack.called"] = true
159
+ env['PATH_INFO'] = PathNormalizer.normalize_path(env['PATH_INFO'])
160
+ request = Rack::Attack::Request.new(env)
152
161
 
153
- def ip_safelists
154
- @ip_safelists ||= []
162
+ if safelisted?(request)
163
+ @app.call(env)
164
+ elsif blocklisted?(request)
165
+ self.class.blocklisted_response.call(env)
166
+ elsif throttled?(request)
167
+ self.class.throttled_response.call(env)
168
+ else
169
+ tracked?(request)
170
+ @app.call(env)
171
+ end
155
172
  end
156
- end
157
-
158
- # Set defaults
159
- @notifier = ActiveSupport::Notifications if defined?(ActiveSupport::Notifications)
160
- @blocklisted_response = lambda { |_env| [403, { 'Content-Type' => 'text/plain' }, ["Forbidden\n"]] }
161
- @throttled_response = lambda { |env|
162
- retry_after = (env['rack.attack.match_data'] || {})[:period]
163
- [429, { 'Content-Type' => 'text/plain', 'Retry-After' => retry_after.to_s }, ["Retry later\n"]]
164
- }
165
-
166
- def initialize(app)
167
- @app = app
168
- end
169
173
 
170
- def call(env)
171
- env['PATH_INFO'] = PathNormalizer.normalize_path(env['PATH_INFO'])
172
- request = Rack::Attack::Request.new(env)
173
-
174
- if safelisted?(request)
175
- @app.call(env)
176
- elsif blocklisted?(request)
177
- self.class.blocklisted_response.call(env)
178
- elsif throttled?(request)
179
- self.class.throttled_response.call(env)
180
- else
181
- tracked?(request)
182
- @app.call(env)
183
- end
174
+ extend Forwardable
175
+ def_delegators self, :safelisted?, :blocklisted?, :throttled?, :tracked?
184
176
  end
185
-
186
- extend Forwardable
187
- def_delegators self, :safelisted?, :blocklisted?, :throttled?, :tracked?
188
177
  end