rack-attack 6.2.2 → 6.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5479926deb14a57eebadf44bc0450c119c10c57844e6e887a6536a0dd79ddcc3
4
- data.tar.gz: a7c6d3162887c8d7e29b7ba390c281be9757c7f9a022c7e705e2029f3d51d091
3
+ metadata.gz: cba47e380843d184fd3df1af08b252aca3fc2411de7ccbd62a9b7da6ee933b72
4
+ data.tar.gz: 0dc5300a553830ca7e1cfa84de814fd743e608b9ec0140fd6b1145c421085ba7
5
5
  SHA512:
6
- metadata.gz: 1ff52d16e17933058af32dbe0aeeb704914fb28331f9df3b2bd90fc1820f32f7cd60c9f254506fea6bf1845f850fa72f77dff57fb4a5ecaf4ce619497688bb00
7
- data.tar.gz: 6f9a3f5adfddc7cd55274c24a7c9a4ff5e498324421abd3ef43cc5707f478a79824c0a5fb14d2a189128a22f42ffc1b144e06bd3bfbfb9e35de60f406edf6793
6
+ metadata.gz: 71fd5eace9c851dab06317f3f4f4f28a2cbc20dd20c663228fbd073a052b4f30c4de87a06109ce9cf6b7395fc547b0c99b6f4e579285a699be40a93a9511452f
7
+ data.tar.gz: 5139f0be932f94273dba2d8d59ccbcb0e14ca92656b68d126099a124f04160c2c426e72f330b9e3c5dd8ed560f2fb292aa68dde2c32f2238d36c8c7e95422d01
data/README.md CHANGED
@@ -342,6 +342,11 @@ end
342
342
  While Rack::Attack's primary focus is minimizing harm from abusive clients, it
343
343
  can also be used to return rate limit data that's helpful for well-behaved clients.
344
344
 
345
+ If you want to return to user how many seconds to wait until he can start sending requests again, this can be done through enabling `Retry-After` header:
346
+ ```ruby
347
+ Rack::Attack.throttled_response_retry_after_header = true
348
+ ```
349
+
345
350
  Here's an example response that includes conventional `RateLimit-*` headers:
346
351
 
347
352
  ```ruby
@@ -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
@@ -2,9 +2,10 @@
2
2
 
3
3
  require 'rack'
4
4
  require 'forwardable'
5
+ require 'rack/attack/cache'
6
+ require 'rack/attack/configuration'
5
7
  require 'rack/attack/path_normalizer'
6
8
  require 'rack/attack/request'
7
- require "ipaddr"
8
9
 
9
10
  require 'rack/attack/railtie' if defined?(::Rails)
10
11
 
@@ -13,8 +14,8 @@ module Rack
13
14
  class Error < StandardError; end
14
15
  class MisconfiguredStoreError < Error; end
15
16
  class MissingStoreError < Error; end
17
+ class IncompatibleStoreError < Error; end
16
18
 
17
- autoload :Cache, 'rack/attack/cache'
18
19
  autoload :Check, 'rack/attack/check'
19
20
  autoload :Throttle, 'rack/attack/throttle'
20
21
  autoload :Safelist, 'rack/attack/safelist'
@@ -31,82 +32,8 @@ module Rack
31
32
  autoload :Allow2Ban, 'rack/attack/allow2ban'
32
33
 
33
34
  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
46
-
47
- def blocklist(name = nil, &block)
48
- blocklist = Blocklist.new(name, &block)
49
-
50
- if name
51
- blocklists[name] = blocklist
52
- else
53
- anonymous_blocklists << blocklist
54
- end
55
- end
56
-
57
- def blocklist_ip(ip_address)
58
- anonymous_blocklists << Blocklist.new { |request| IPAddr.new(ip_address).include?(IPAddr.new(request.ip)) }
59
- end
60
-
61
- def safelist_ip(ip_address)
62
- anonymous_safelists << Safelist.new { |request| IPAddr.new(ip_address).include?(IPAddr.new(request.ip)) }
63
- end
64
-
65
- def throttle(name, options, &block)
66
- throttles[name] = Throttle.new(name, options, &block)
67
- end
68
-
69
- def track(name, options = {}, &block)
70
- tracks[name] = Track.new(name, options, &block)
71
- end
72
-
73
- def safelists
74
- @safelists ||= {}
75
- end
76
-
77
- def blocklists
78
- @blocklists ||= {}
79
- end
80
-
81
- def throttles
82
- @throttles ||= {}
83
- end
84
-
85
- def tracks
86
- @tracks ||= {}
87
- end
88
-
89
- def safelisted?(request)
90
- anonymous_safelists.any? { |safelist| safelist.matched_by?(request) } ||
91
- safelists.any? { |_name, safelist| safelist.matched_by?(request) }
92
- end
93
-
94
- def blocklisted?(request)
95
- anonymous_blocklists.any? { |blocklist| blocklist.matched_by?(request) } ||
96
- blocklists.any? { |_name, blocklist| blocklist.matched_by?(request) }
97
- end
98
-
99
- def throttled?(request)
100
- throttles.any? do |_name, throttle|
101
- throttle.matched_by?(request)
102
- end
103
- end
104
-
105
- def tracked?(request)
106
- tracks.each_value do |track|
107
- track.matched_by?(request)
108
- end
109
- end
35
+ attr_accessor :enabled, :notifier
36
+ attr_reader :configuration
110
37
 
111
38
  def instrument(request)
112
39
  if notifier
@@ -122,34 +49,48 @@ module Rack
122
49
  @cache ||= Cache.new
123
50
  end
124
51
 
125
- def clear_configuration
126
- @safelists = {}
127
- @blocklists = {}
128
- @throttles = {}
129
- @tracks = {}
130
- self.anonymous_blocklists = []
131
- self.anonymous_safelists = []
132
- end
133
-
134
52
  def clear!
135
53
  warn "[DEPRECATION] Rack::Attack.clear! is deprecated. Please use Rack::Attack.clear_configuration instead"
136
- clear_configuration
137
- end
54
+ @configuration.clear_configuration
55
+ end
56
+
57
+ def reset!
58
+ cache.reset!
59
+ end
60
+
61
+ extend Forwardable
62
+ def_delegators(
63
+ :@configuration,
64
+ :safelist,
65
+ :blocklist,
66
+ :blocklist_ip,
67
+ :safelist_ip,
68
+ :throttle,
69
+ :track,
70
+ :blocklisted_response,
71
+ :blocklisted_response=,
72
+ :throttled_response,
73
+ :throttled_response=,
74
+ :throttled_response_retry_after_header,
75
+ :throttled_response_retry_after_header=,
76
+ :clear_configuration,
77
+ :safelists,
78
+ :blocklists,
79
+ :throttles,
80
+ :tracks
81
+ )
138
82
  end
139
83
 
140
84
  # Set defaults
141
85
  @enabled = true
142
- @anonymous_blocklists = []
143
- @anonymous_safelists = []
144
86
  @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"]]
149
- end
87
+ @configuration = Configuration.new
88
+
89
+ attr_reader :configuration
150
90
 
151
91
  def initialize(app)
152
92
  @app = app
93
+ @configuration = self.class.configuration
153
94
  end
154
95
 
155
96
  def call(env)
@@ -159,19 +100,16 @@ module Rack
159
100
  env['PATH_INFO'] = PathNormalizer.normalize_path(env['PATH_INFO'])
160
101
  request = Rack::Attack::Request.new(env)
161
102
 
162
- if safelisted?(request)
103
+ if configuration.safelisted?(request)
163
104
  @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)
105
+ elsif configuration.blocklisted?(request)
106
+ configuration.blocklisted_response.call(env)
107
+ elsif configuration.throttled?(request)
108
+ configuration.throttled_response.call(env)
168
109
  else
169
- tracked?(request)
110
+ configuration.tracked?(request)
170
111
  @app.call(env)
171
112
  end
172
113
  end
173
-
174
- extend Forwardable
175
- def_delegators self, :safelisted?, :blocklisted?, :throttled?, :tracked?
176
114
  end
177
115
  end
@@ -41,6 +41,17 @@ module Rack
41
41
  store.delete("#{prefix}:#{unprefixed_key}")
42
42
  end
43
43
 
44
+ def reset!
45
+ if store.respond_to?(:delete_matched)
46
+ store.delete_matched("#{prefix}*")
47
+ else
48
+ raise(
49
+ Rack::Attack::IncompatibleStoreError,
50
+ "Configured store #{store.class.name} doesn't respond to #delete_matched method"
51
+ )
52
+ end
53
+ end
54
+
44
55
  private
45
56
 
46
57
  def key_and_expiry(unprefixed_key, period)
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ipaddr"
4
+
5
+ module Rack
6
+ class Attack
7
+ class Configuration
8
+ DEFAULT_BLOCKLISTED_RESPONSE = lambda { |_env| [403, { 'Content-Type' => 'text/plain' }, ["Forbidden\n"]] }
9
+
10
+ DEFAULT_THROTTLED_RESPONSE = lambda do |env|
11
+ if Rack::Attack.configuration.throttled_response_retry_after_header
12
+ match_data = env['rack.attack.match_data']
13
+ now = match_data[:epoch_time]
14
+ retry_after = match_data[:period] - (now % match_data[:period])
15
+
16
+ [429, { 'Content-Type' => 'text/plain', 'Retry-After' => retry_after.to_s }, ["Retry later\n"]]
17
+ else
18
+ [429, { 'Content-Type' => 'text/plain' }, ["Retry later\n"]]
19
+ end
20
+ end
21
+
22
+ attr_reader :safelists, :blocklists, :throttles, :anonymous_blocklists, :anonymous_safelists
23
+ attr_accessor :blocklisted_response, :throttled_response, :throttled_response_retry_after_header
24
+
25
+ def initialize
26
+ set_defaults
27
+ end
28
+
29
+ def safelist(name = nil, &block)
30
+ safelist = Safelist.new(name, &block)
31
+
32
+ if name
33
+ @safelists[name] = safelist
34
+ else
35
+ @anonymous_safelists << safelist
36
+ end
37
+ end
38
+
39
+ def blocklist(name = nil, &block)
40
+ blocklist = Blocklist.new(name, &block)
41
+
42
+ if name
43
+ @blocklists[name] = blocklist
44
+ else
45
+ @anonymous_blocklists << blocklist
46
+ end
47
+ end
48
+
49
+ def blocklist_ip(ip_address)
50
+ @anonymous_blocklists << Blocklist.new { |request| IPAddr.new(ip_address).include?(IPAddr.new(request.ip)) }
51
+ end
52
+
53
+ def safelist_ip(ip_address)
54
+ @anonymous_safelists << Safelist.new { |request| IPAddr.new(ip_address).include?(IPAddr.new(request.ip)) }
55
+ end
56
+
57
+ def throttle(name, options, &block)
58
+ @throttles[name] = Throttle.new(name, options, &block)
59
+ end
60
+
61
+ def track(name, options = {}, &block)
62
+ @tracks[name] = Track.new(name, options, &block)
63
+ end
64
+
65
+ def safelisted?(request)
66
+ @anonymous_safelists.any? { |safelist| safelist.matched_by?(request) } ||
67
+ @safelists.any? { |_name, safelist| safelist.matched_by?(request) }
68
+ end
69
+
70
+ def blocklisted?(request)
71
+ @anonymous_blocklists.any? { |blocklist| blocklist.matched_by?(request) } ||
72
+ @blocklists.any? { |_name, blocklist| blocklist.matched_by?(request) }
73
+ end
74
+
75
+ def throttled?(request)
76
+ @throttles.any? do |_name, throttle|
77
+ throttle.matched_by?(request)
78
+ end
79
+ end
80
+
81
+ def tracked?(request)
82
+ @tracks.each_value do |track|
83
+ track.matched_by?(request)
84
+ end
85
+ end
86
+
87
+ def clear_configuration
88
+ set_defaults
89
+ end
90
+
91
+ private
92
+
93
+ def set_defaults
94
+ @safelists = {}
95
+ @blocklists = {}
96
+ @throttles = {}
97
+ @tracks = {}
98
+ @anonymous_blocklists = []
99
+ @anonymous_safelists = []
100
+ @throttled_response_retry_after_header = false
101
+
102
+ @blocklisted_response = DEFAULT_BLOCKLISTED_RESPONSE
103
+ @throttled_response = DEFAULT_THROTTLED_RESPONSE
104
+ end
105
+ end
106
+ end
107
+ end
@@ -15,33 +15,17 @@ module Rack
15
15
  #
16
16
  # So in order to workaround this we use RedisCacheStore#write (which sets expiration) to initialize
17
17
  # the counter. After that we continue using the original RedisCacheStore#increment.
18
- rescuing do
19
- if options[:expires_in] && !read(name)
20
- write(name, amount, options)
18
+ if options[:expires_in] && !read(name)
19
+ write(name, amount, options)
21
20
 
22
- amount
23
- else
24
- super
25
- end
21
+ amount
22
+ else
23
+ super
26
24
  end
27
25
  end
28
26
 
29
- def read(*_args)
30
- rescuing { super }
31
- end
32
-
33
27
  def write(name, value, options = {})
34
- rescuing do
35
- super(name, value, options.merge!(raw: true))
36
- end
37
- end
38
-
39
- private
40
-
41
- def rescuing
42
- yield
43
- rescue Redis::BaseError
44
- nil
28
+ super(name, value, options.merge!(raw: true))
45
29
  end
46
30
  end
47
31
  end
@@ -43,11 +43,24 @@ module Rack
43
43
  rescuing { del(key) }
44
44
  end
45
45
 
46
+ def delete_matched(matcher, _options = nil)
47
+ cursor = "0"
48
+
49
+ rescuing do
50
+ # Fetch keys in batches using SCAN to avoid blocking the Redis server.
51
+ loop do
52
+ cursor, keys = scan(cursor, match: matcher, count: 1000)
53
+ del(*keys) unless keys.empty?
54
+ break if cursor == "0"
55
+ end
56
+ end
57
+ end
58
+
46
59
  private
47
60
 
48
61
  def rescuing
49
62
  yield
50
- rescue Redis::BaseError
63
+ rescue Redis::BaseConnectionError
51
64
  nil
52
65
  end
53
66
  end
@@ -23,34 +23,50 @@ module Rack
23
23
 
24
24
  def matched_by?(request)
25
25
  discriminator = block.call(request)
26
+
26
27
  return false unless discriminator
27
28
 
28
- current_period = period.respond_to?(:call) ? period.call(request) : period
29
- current_limit = limit.respond_to?(:call) ? limit.call(request) : limit
30
- key = "#{name}:#{discriminator}"
31
- count = cache.count(key, current_period)
32
- epoch_time = cache.last_epoch_time
29
+ current_period = period_for(request)
30
+ current_limit = limit_for(request)
31
+ count = cache.count("#{name}:#{discriminator}", current_period)
33
32
 
34
33
  data = {
35
34
  discriminator: discriminator,
36
35
  count: count,
37
36
  period: current_period,
38
37
  limit: current_limit,
39
- epoch_time: epoch_time
38
+ epoch_time: cache.last_epoch_time
40
39
  }
41
40
 
42
- (request.env['rack.attack.throttle_data'] ||= {})[name] = data
43
-
44
41
  (count > current_limit).tap do |throttled|
42
+ annotate_request_with_throttle_data(request, data)
45
43
  if throttled
46
- request.env['rack.attack.matched'] = name
47
- request.env['rack.attack.match_discriminator'] = discriminator
48
- request.env['rack.attack.match_type'] = type
49
- request.env['rack.attack.match_data'] = data
44
+ annotate_request_with_matched_data(request, data)
50
45
  Rack::Attack.instrument(request)
51
46
  end
52
47
  end
53
48
  end
49
+
50
+ private
51
+
52
+ def period_for(request)
53
+ period.respond_to?(:call) ? period.call(request) : period
54
+ end
55
+
56
+ def limit_for(request)
57
+ limit.respond_to?(:call) ? limit.call(request) : limit
58
+ end
59
+
60
+ def annotate_request_with_throttle_data(request, data)
61
+ (request.env['rack.attack.throttle_data'] ||= {})[name] = data
62
+ end
63
+
64
+ def annotate_request_with_matched_data(request, data)
65
+ request.env['rack.attack.matched'] = name
66
+ request.env['rack.attack.match_discriminator'] = data[:discriminator]
67
+ request.env['rack.attack.match_type'] = type
68
+ request.env['rack.attack.match_data'] = data
69
+ end
54
70
  end
55
71
  end
56
72
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Rack
4
4
  class Attack
5
- VERSION = '6.2.2'
5
+ VERSION = '6.3.0'
6
6
  end
7
7
  end
@@ -20,7 +20,7 @@ describe "#throttle" do
20
20
  get "/", {}, "REMOTE_ADDR" => "1.2.3.4"
21
21
 
22
22
  assert_equal 429, last_response.status
23
- assert_equal "60", last_response.headers["Retry-After"]
23
+ assert_nil last_response.headers["Retry-After"]
24
24
  assert_equal "Retry later\n", last_response.body
25
25
 
26
26
  get "/", {}, "REMOTE_ADDR" => "5.6.7.8"
@@ -34,6 +34,24 @@ describe "#throttle" do
34
34
  end
35
35
  end
36
36
 
37
+ it "returns correct Retry-After header if enabled" do
38
+ Rack::Attack.throttled_response_retry_after_header = true
39
+
40
+ Rack::Attack.throttle("by ip", limit: 1, period: 60) do |request|
41
+ request.ip
42
+ end
43
+
44
+ Timecop.freeze(Time.at(0)) do
45
+ get "/", {}, "REMOTE_ADDR" => "1.2.3.4"
46
+ assert_equal 200, last_response.status
47
+ end
48
+
49
+ Timecop.freeze(Time.at(25)) do
50
+ get "/", {}, "REMOTE_ADDR" => "1.2.3.4"
51
+ assert_equal "35", last_response.headers["Retry-After"]
52
+ end
53
+ end
54
+
37
55
  it "supports limit to be dynamic" do
38
56
  # Could be used to have different rate limits for authorized
39
57
  # vs general requests
@@ -13,7 +13,11 @@ OfflineExamples = Minitest::SharedExamples.new do
13
13
  end
14
14
 
15
15
  it 'should count' do
16
- @cache.send(:do_count, 'rack::attack::cache-test-key', 1)
16
+ @cache.count('cache-test-key', 1)
17
+ end
18
+
19
+ it 'should delete' do
20
+ @cache.delete('cache-test-key')
17
21
  end
18
22
  end
19
23
 
@@ -29,6 +33,18 @@ if defined?(::ActiveSupport::Cache::RedisStore)
29
33
  end
30
34
  end
31
35
 
36
+ if defined?(Redis) && defined?(ActiveSupport::Cache::RedisCacheStore) && Redis::VERSION >= '4'
37
+ describe 'when Redis is offline' do
38
+ include OfflineExamples
39
+
40
+ before do
41
+ @cache = Rack::Attack::Cache.new
42
+ # Use presumably unused port for Redis client
43
+ @cache.store = ActiveSupport::Cache::RedisCacheStore.new(host: '127.0.0.1', port: 3333)
44
+ end
45
+ end
46
+ end
47
+
32
48
  if defined?(::Dalli)
33
49
  describe 'when Memcached is offline' do
34
50
  include OfflineExamples
@@ -46,6 +62,23 @@ if defined?(::Dalli)
46
62
  end
47
63
  end
48
64
 
65
+ if defined?(::Dalli) && defined?(::ActiveSupport::Cache::MemCacheStore)
66
+ describe 'when Memcached is offline' do
67
+ include OfflineExamples
68
+
69
+ before do
70
+ Dalli.logger.level = Logger::FATAL
71
+
72
+ @cache = Rack::Attack::Cache.new
73
+ @cache.store = ActiveSupport::Cache::MemCacheStore.new('127.0.0.1:22122')
74
+ end
75
+
76
+ after do
77
+ Dalli.logger.level = Logger::INFO
78
+ end
79
+ end
80
+ end
81
+
49
82
  if defined?(Redis)
50
83
  describe 'when Redis is offline' do
51
84
  include OfflineExamples
@@ -99,4 +99,26 @@ describe 'Rack::Attack' do
99
99
  end
100
100
  end
101
101
  end
102
+
103
+ describe 'reset!' do
104
+ it 'raises an error when is not supported by cache store' do
105
+ Rack::Attack.cache.store = Class.new
106
+ assert_raises(Rack::Attack::IncompatibleStoreError) do
107
+ Rack::Attack.reset!
108
+ end
109
+ end
110
+
111
+ if defined?(Redis)
112
+ it 'should delete rack attack keys' do
113
+ redis = Redis.new
114
+ redis.set('key', 'value')
115
+ redis.set("#{Rack::Attack.cache.prefix}::key", 'value')
116
+ Rack::Attack.cache.store = redis
117
+ Rack::Attack.reset!
118
+
119
+ _(redis.get('key')).must_equal 'value'
120
+ _(redis.get("#{Rack::Attack.cache.prefix}::key")).must_be_nil
121
+ end
122
+ end
123
+ end
102
124
  end
@@ -57,10 +57,6 @@ describe 'Rack::Attack.throttle' do
57
57
 
58
58
  _(last_request.env['rack.attack.match_discriminator']).must_equal('1.2.3.4')
59
59
  end
60
-
61
- it 'should set a Retry-After header' do
62
- _(last_response.headers['Retry-After']).must_equal @period.to_s
63
- end
64
60
  end
65
61
  end
66
62
 
@@ -30,16 +30,11 @@ class MiniTest::Spec
30
30
 
31
31
  before do
32
32
  Rails.cache = nil
33
- @_original_throttled_response = Rack::Attack.throttled_response
34
- @_original_blocklisted_response = Rack::Attack.blocklisted_response
35
33
  end
36
34
 
37
35
  after do
38
36
  Rack::Attack.clear_configuration
39
37
  Rack::Attack.instance_variable_set(:@cache, nil)
40
-
41
- Rack::Attack.throttled_response = @_original_throttled_response
42
- Rack::Attack.blocklisted_response = @_original_blocklisted_response
43
38
  end
44
39
 
45
40
  def app
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-attack
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.2.2
4
+ version: 6.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aaron Suggs
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-12-18 00:00:00.000000000 Z
11
+ date: 2020-04-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -126,14 +126,14 @@ dependencies:
126
126
  requirements:
127
127
  - - '='
128
128
  - !ruby/object:Gem::Version
129
- version: 0.75.0
129
+ version: 0.78.0
130
130
  type: :development
131
131
  prerelease: false
132
132
  version_requirements: !ruby/object:Gem::Requirement
133
133
  requirements:
134
134
  - - '='
135
135
  - !ruby/object:Gem::Version
136
- version: 0.75.0
136
+ version: 0.78.0
137
137
  - !ruby/object:Gem::Dependency
138
138
  name: rubocop-performance
139
139
  requirement: !ruby/object:Gem::Requirement
@@ -204,11 +204,13 @@ extra_rdoc_files: []
204
204
  files:
205
205
  - README.md
206
206
  - Rakefile
207
+ - bin/setup
207
208
  - lib/rack/attack.rb
208
209
  - lib/rack/attack/allow2ban.rb
209
210
  - lib/rack/attack/blocklist.rb
210
211
  - lib/rack/attack/cache.rb
211
212
  - lib/rack/attack/check.rb
213
+ - lib/rack/attack/configuration.rb
212
214
  - lib/rack/attack/fail2ban.rb
213
215
  - lib/rack/attack/path_normalizer.rb
214
216
  - lib/rack/attack/railtie.rb
@@ -289,50 +291,50 @@ required_rubygems_version: !ruby/object:Gem::Requirement
289
291
  - !ruby/object:Gem::Version
290
292
  version: '0'
291
293
  requirements: []
292
- rubygems_version: 3.1.1
294
+ rubygems_version: 3.1.2
293
295
  signing_key:
294
296
  specification_version: 4
295
297
  summary: Block & throttle abusive requests
296
298
  test_files:
297
- - spec/rack_attack_spec.rb
298
- - spec/fail2ban_spec.rb
299
- - spec/allow2ban_spec.rb
300
- - spec/support/cache_store_helper.rb
301
- - spec/rack_attack_instrumentation_spec.rb
302
- - spec/rack_attack_throttle_spec.rb
303
299
  - spec/integration/offline_spec.rb
304
- - spec/rack_attack_dalli_proxy_spec.rb
305
- - spec/acceptance/fail2ban_spec.rb
306
- - spec/acceptance/allow2ban_spec.rb
300
+ - spec/rack_attack_path_normalizer_spec.rb
301
+ - spec/acceptance/safelisting_subnet_spec.rb
307
302
  - spec/acceptance/rails_middleware_spec.rb
308
- - spec/acceptance/throttling_spec.rb
309
303
  - spec/acceptance/track_throttle_spec.rb
304
+ - spec/acceptance/cache_store_config_for_fail2ban_spec.rb
305
+ - spec/acceptance/cache_store_config_with_rails_spec.rb
306
+ - spec/acceptance/cache_store_config_for_allow2ban_spec.rb
307
+ - spec/acceptance/safelisting_ip_spec.rb
308
+ - spec/acceptance/track_spec.rb
310
309
  - spec/acceptance/blocking_subnet_spec.rb
311
310
  - spec/acceptance/blocking_ip_spec.rb
312
- - spec/acceptance/cache_store_config_with_rails_spec.rb
313
- - spec/acceptance/cache_store_config_for_fail2ban_spec.rb
314
- - spec/acceptance/safelisting_subnet_spec.rb
311
+ - spec/acceptance/allow2ban_spec.rb
312
+ - spec/acceptance/throttling_spec.rb
313
+ - spec/acceptance/blocking_spec.rb
314
+ - spec/acceptance/customizing_throttled_response_spec.rb
315
315
  - spec/acceptance/extending_request_object_spec.rb
316
316
  - spec/acceptance/safelisting_spec.rb
317
- - spec/acceptance/customizing_throttled_response_spec.rb
318
- - spec/acceptance/safelisting_ip_spec.rb
319
- - spec/acceptance/cache_store_config_for_allow2ban_spec.rb
320
- - spec/acceptance/customizing_blocked_response_spec.rb
321
317
  - spec/acceptance/cache_store_config_for_throttle_spec.rb
322
- - spec/acceptance/blocking_spec.rb
323
- - spec/acceptance/stores/redis_spec.rb
324
- - spec/acceptance/stores/active_support_redis_cache_store_pooled_spec.rb
318
+ - spec/acceptance/fail2ban_spec.rb
319
+ - spec/acceptance/stores/active_support_mem_cache_store_pooled_spec.rb
320
+ - spec/acceptance/stores/active_support_redis_cache_store_spec.rb
325
321
  - spec/acceptance/stores/active_support_memory_store_spec.rb
322
+ - spec/acceptance/stores/active_support_redis_store_spec.rb
323
+ - spec/acceptance/stores/active_support_mem_cache_store_spec.rb
324
+ - spec/acceptance/stores/active_support_redis_cache_store_pooled_spec.rb
326
325
  - spec/acceptance/stores/connection_pool_dalli_client_spec.rb
327
- - spec/acceptance/stores/active_support_redis_cache_store_spec.rb
328
326
  - spec/acceptance/stores/active_support_dalli_store_spec.rb
329
- - spec/acceptance/stores/active_support_mem_cache_store_pooled_spec.rb
330
- - spec/acceptance/stores/active_support_mem_cache_store_spec.rb
331
- - spec/acceptance/stores/dalli_client_spec.rb
332
327
  - spec/acceptance/stores/redis_store_spec.rb
333
- - spec/acceptance/stores/active_support_redis_store_spec.rb
334
- - spec/acceptance/track_spec.rb
335
- - spec/rack_attack_path_normalizer_spec.rb
328
+ - spec/acceptance/stores/dalli_client_spec.rb
329
+ - spec/acceptance/stores/redis_spec.rb
330
+ - spec/acceptance/customizing_blocked_response_spec.rb
331
+ - spec/spec_helper.rb
332
+ - spec/allow2ban_spec.rb
333
+ - spec/rack_attack_instrumentation_spec.rb
334
+ - spec/rack_attack_dalli_proxy_spec.rb
335
+ - spec/rack_attack_spec.rb
336
+ - spec/rack_attack_throttle_spec.rb
336
337
  - spec/rack_attack_request_spec.rb
338
+ - spec/fail2ban_spec.rb
337
339
  - spec/rack_attack_track_spec.rb
338
- - spec/spec_helper.rb
340
+ - spec/support/cache_store_helper.rb