rack-attack 4.3.1 → 5.4.2
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 +5 -5
- data/README.md +230 -113
- data/Rakefile +11 -3
- data/bin/setup +8 -0
- data/lib/rack/attack.rb +121 -48
- data/lib/rack/attack/allow2ban.rb +2 -1
- data/lib/rack/attack/{whitelist.rb → blocklist.rb} +2 -3
- data/lib/rack/attack/cache.rb +24 -5
- data/lib/rack/attack/check.rb +6 -8
- data/lib/rack/attack/fail2ban.rb +3 -2
- data/lib/rack/attack/path_normalizer.rb +6 -11
- data/lib/rack/attack/request.rb +1 -1
- data/lib/rack/attack/{blacklist.rb → safelist.rb} +2 -4
- data/lib/rack/attack/store_proxy.rb +13 -12
- data/lib/rack/attack/store_proxy/dalli_proxy.rb +2 -3
- data/lib/rack/attack/store_proxy/mem_cache_proxy.rb +50 -0
- data/lib/rack/attack/store_proxy/mem_cache_store_proxy.rb +19 -0
- data/lib/rack/attack/store_proxy/redis_cache_store_proxy.rb +35 -0
- data/lib/rack/attack/store_proxy/redis_proxy.rb +54 -0
- data/lib/rack/attack/store_proxy/redis_store_proxy.rb +5 -24
- data/lib/rack/attack/throttle.rb +16 -12
- data/lib/rack/attack/track.rb +3 -3
- 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 +41 -0
- data/spec/acceptance/blocking_subnet_spec.rb +44 -0
- data/spec/acceptance/cache_store_config_for_allow2ban_spec.rb +126 -0
- data/spec/acceptance/cache_store_config_for_fail2ban_spec.rb +121 -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 +48 -0
- data/spec/acceptance/safelisting_spec.rb +53 -0
- data/spec/acceptance/safelisting_subnet_spec.rb +48 -0
- data/spec/acceptance/stores/active_support_dalli_store_spec.rb +19 -0
- data/spec/acceptance/stores/active_support_mem_cache_store_pooled_spec.rb +22 -0
- data/spec/acceptance/stores/active_support_mem_cache_store_spec.rb +18 -0
- data/spec/acceptance/stores/active_support_memory_store_spec.rb +16 -0
- data/spec/acceptance/stores/active_support_redis_cache_store_pooled_spec.rb +18 -0
- data/spec/acceptance/stores/active_support_redis_cache_store_spec.rb +18 -0
- data/spec/acceptance/stores/active_support_redis_store_spec.rb +18 -0
- data/spec/acceptance/stores/connection_pool_dalli_client_spec.rb +22 -0
- data/spec/acceptance/stores/dalli_client_spec.rb +19 -0
- data/spec/acceptance/stores/redis_spec.rb +20 -0
- data/spec/acceptance/stores/redis_store_spec.rb +18 -0
- data/spec/acceptance/throttling_spec.rb +159 -0
- data/spec/acceptance/track_spec.rb +27 -0
- data/spec/acceptance/track_throttle_spec.rb +53 -0
- data/spec/allow2ban_spec.rb +10 -9
- data/spec/fail2ban_spec.rb +12 -10
- data/spec/integration/offline_spec.rb +21 -23
- data/spec/rack_attack_dalli_proxy_spec.rb +0 -2
- data/spec/rack_attack_request_spec.rb +2 -2
- data/spec/rack_attack_spec.rb +53 -18
- data/spec/rack_attack_throttle_spec.rb +45 -13
- data/spec/rack_attack_track_spec.rb +11 -8
- data/spec/spec_helper.rb +35 -14
- data/spec/support/cache_store_helper.rb +82 -0
- metadata +161 -61
- data/spec/integration/rack_attack_cache_spec.rb +0 -119
data/spec/fail2ban_spec.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require_relative 'spec_helper'
|
|
2
|
+
|
|
2
3
|
describe 'Rack::Attack.Fail2Ban' do
|
|
3
4
|
before do
|
|
4
5
|
# Use a long findtime; failures due to cache key rotation less likely
|
|
@@ -6,9 +7,10 @@ describe 'Rack::Attack.Fail2Ban' do
|
|
|
6
7
|
@findtime = 60
|
|
7
8
|
@bantime = 60
|
|
8
9
|
Rack::Attack.cache.store = ActiveSupport::Cache::MemoryStore.new
|
|
9
|
-
@f2b_options = {:bantime => @bantime, :findtime => @findtime, :maxretry => 2}
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
@f2b_options = { :bantime => @bantime, :findtime => @findtime, :maxretry => 2 }
|
|
11
|
+
|
|
12
|
+
Rack::Attack.blocklist('pentest') do |req|
|
|
13
|
+
Rack::Attack::Fail2Ban.filter(req.ip, @f2b_options) { req.query_string =~ /OMGHAX/ }
|
|
12
14
|
end
|
|
13
15
|
end
|
|
14
16
|
|
|
@@ -23,12 +25,13 @@ describe 'Rack::Attack.Fail2Ban' do
|
|
|
23
25
|
describe 'making failing request' do
|
|
24
26
|
describe 'when not at maxretry' do
|
|
25
27
|
before { get '/?foo=OMGHAX', {}, 'REMOTE_ADDR' => '1.2.3.4' }
|
|
28
|
+
|
|
26
29
|
it 'fails' do
|
|
27
30
|
last_response.status.must_equal 403
|
|
28
31
|
end
|
|
29
32
|
|
|
30
33
|
it 'increases fail count' do
|
|
31
|
-
key = "rack::attack:#{Time.now.to_i
|
|
34
|
+
key = "rack::attack:#{Time.now.to_i / @findtime}:fail2ban:count:1.2.3.4"
|
|
32
35
|
@cache.store.read(key).must_equal 1
|
|
33
36
|
end
|
|
34
37
|
|
|
@@ -50,7 +53,7 @@ describe 'Rack::Attack.Fail2Ban' do
|
|
|
50
53
|
end
|
|
51
54
|
|
|
52
55
|
it 'increases fail count' do
|
|
53
|
-
key = "rack::attack:#{Time.now.to_i
|
|
56
|
+
key = "rack::attack:#{Time.now.to_i / @findtime}:fail2ban:count:1.2.3.4"
|
|
54
57
|
@cache.store.read(key).must_equal 2
|
|
55
58
|
end
|
|
56
59
|
|
|
@@ -72,8 +75,8 @@ describe 'Rack::Attack.Fail2Ban' do
|
|
|
72
75
|
end
|
|
73
76
|
|
|
74
77
|
it 'resets fail count' do
|
|
75
|
-
key = "rack::attack:#{Time.now.to_i
|
|
76
|
-
@cache.store.read(key)
|
|
78
|
+
key = "rack::attack:#{Time.now.to_i / @findtime}:fail2ban:count:1.2.3.4"
|
|
79
|
+
assert_nil @cache.store.read(key)
|
|
77
80
|
end
|
|
78
81
|
|
|
79
82
|
it 'IP is not banned' do
|
|
@@ -107,7 +110,7 @@ describe 'Rack::Attack.Fail2Ban' do
|
|
|
107
110
|
end
|
|
108
111
|
|
|
109
112
|
it 'does not increase fail count' do
|
|
110
|
-
key = "rack::attack:#{Time.now.to_i
|
|
113
|
+
key = "rack::attack:#{Time.now.to_i / @findtime}:fail2ban:count:1.2.3.4"
|
|
111
114
|
@cache.store.read(key).must_equal 2
|
|
112
115
|
end
|
|
113
116
|
|
|
@@ -127,7 +130,7 @@ describe 'Rack::Attack.Fail2Ban' do
|
|
|
127
130
|
end
|
|
128
131
|
|
|
129
132
|
it 'does not increase fail count' do
|
|
130
|
-
key = "rack::attack:#{Time.now.to_i
|
|
133
|
+
key = "rack::attack:#{Time.now.to_i / @findtime}:fail2ban:count:1.2.3.4"
|
|
131
134
|
@cache.store.read(key).must_equal 2
|
|
132
135
|
end
|
|
133
136
|
|
|
@@ -136,6 +139,5 @@ describe 'Rack::Attack.Fail2Ban' do
|
|
|
136
139
|
@cache.store.read(key).must_equal 1
|
|
137
140
|
end
|
|
138
141
|
end
|
|
139
|
-
|
|
140
142
|
end
|
|
141
143
|
end
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
require 'active_support/cache'
|
|
2
|
-
require 'active_support/cache/redis_store'
|
|
3
|
-
require 'dalli'
|
|
4
2
|
require_relative '../spec_helper'
|
|
5
3
|
|
|
6
4
|
OfflineExamples = Minitest::SharedExamples.new do
|
|
7
|
-
|
|
8
5
|
it 'should write' do
|
|
9
6
|
@cache.write('cache-test-key', 'foobar', 1)
|
|
10
7
|
end
|
|
@@ -16,32 +13,33 @@ OfflineExamples = Minitest::SharedExamples.new do
|
|
|
16
13
|
it 'should count' do
|
|
17
14
|
@cache.send(:do_count, 'rack::attack::cache-test-key', 1)
|
|
18
15
|
end
|
|
19
|
-
|
|
20
16
|
end
|
|
21
17
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
before {
|
|
26
|
-
@cache = Rack::Attack::Cache.new
|
|
27
|
-
# Use presumably unused port for Redis client
|
|
28
|
-
@cache.store = ActiveSupport::Cache::RedisStore.new(:host => '127.0.0.1', :port => 3333)
|
|
29
|
-
}
|
|
18
|
+
if defined?(::ActiveSupport::Cache::RedisStore)
|
|
19
|
+
describe 'when Redis is offline' do
|
|
20
|
+
include OfflineExamples
|
|
30
21
|
|
|
22
|
+
before do
|
|
23
|
+
@cache = Rack::Attack::Cache.new
|
|
24
|
+
# Use presumably unused port for Redis client
|
|
25
|
+
@cache.store = ActiveSupport::Cache::RedisStore.new(:host => '127.0.0.1', :port => 3333)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
31
28
|
end
|
|
32
29
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
before {
|
|
37
|
-
Dalli.logger.level = Logger::FATAL
|
|
30
|
+
if defined?(::Dalli)
|
|
31
|
+
describe 'when Memcached is offline' do
|
|
32
|
+
include OfflineExamples
|
|
38
33
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
34
|
+
before do
|
|
35
|
+
Dalli.logger.level = Logger::FATAL
|
|
42
36
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
37
|
+
@cache = Rack::Attack::Cache.new
|
|
38
|
+
@cache.store = Dalli::Client.new('127.0.0.1:22122')
|
|
39
|
+
end
|
|
46
40
|
|
|
41
|
+
after do
|
|
42
|
+
Dalli.logger.level = Logger::INFO
|
|
43
|
+
end
|
|
44
|
+
end
|
|
47
45
|
end
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
require_relative 'spec_helper'
|
|
2
2
|
|
|
3
3
|
describe Rack::Attack::StoreProxy::DalliProxy do
|
|
4
|
-
|
|
5
4
|
it 'should stub Dalli::Client#with on older clients' do
|
|
6
5
|
proxy = Rack::Attack::StoreProxy::DalliProxy.new(Class.new)
|
|
7
6
|
proxy.with {} # will not raise an error
|
|
8
7
|
end
|
|
9
|
-
|
|
10
8
|
end
|
data/spec/rack_attack_spec.rb
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
require_relative 'spec_helper'
|
|
2
2
|
|
|
3
3
|
describe 'Rack::Attack' do
|
|
4
|
-
|
|
4
|
+
it_allows_ok_requests
|
|
5
5
|
|
|
6
6
|
describe 'normalizing paths' do
|
|
7
7
|
before do
|
|
8
|
-
Rack::Attack.
|
|
8
|
+
Rack::Attack.blocklist("banned_path") { |req| req.path == '/foo' }
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
it 'blocks requests with trailing slash' do
|
|
@@ -14,50 +14,85 @@ describe 'Rack::Attack' do
|
|
|
14
14
|
end
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
describe '
|
|
17
|
+
describe 'blocklist' do
|
|
18
18
|
before do
|
|
19
19
|
@bad_ip = '1.2.3.4'
|
|
20
|
-
Rack::Attack.
|
|
20
|
+
Rack::Attack.blocklist("ip #{@bad_ip}") { |req| req.ip == @bad_ip }
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
it('has a
|
|
24
|
-
Rack::Attack.
|
|
23
|
+
it('has a blocklist') {
|
|
24
|
+
Rack::Attack.blocklists.key?("ip #{@bad_ip}").must_equal true
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
it('has a blacklist with a deprication warning') {
|
|
28
|
+
_, stderror = capture_io do
|
|
29
|
+
Rack::Attack.blacklists.key?("ip #{@bad_ip}").must_equal true
|
|
30
|
+
end
|
|
31
|
+
assert_match "[DEPRECATION] 'Rack::Attack.blacklists' is deprecated. Please use 'blocklists' instead.", stderror
|
|
25
32
|
}
|
|
26
33
|
|
|
27
34
|
describe "a bad request" do
|
|
28
35
|
before { get '/', {}, 'REMOTE_ADDR' => @bad_ip }
|
|
29
|
-
|
|
30
|
-
|
|
36
|
+
|
|
37
|
+
it "should return a blocklist response" do
|
|
31
38
|
last_response.status.must_equal 403
|
|
32
39
|
last_response.body.must_equal "Forbidden\n"
|
|
33
40
|
end
|
|
41
|
+
|
|
34
42
|
it "should tag the env" do
|
|
35
43
|
last_request.env['rack.attack.matched'].must_equal "ip #{@bad_ip}"
|
|
36
|
-
last_request.env['rack.attack.match_type'].must_equal :
|
|
44
|
+
last_request.env['rack.attack.match_type'].must_equal :blocklist
|
|
37
45
|
end
|
|
38
46
|
|
|
39
|
-
|
|
47
|
+
it_allows_ok_requests
|
|
40
48
|
end
|
|
41
49
|
|
|
42
|
-
describe "and
|
|
50
|
+
describe "and safelist" do
|
|
43
51
|
before do
|
|
44
52
|
@good_ua = 'GoodUA'
|
|
45
|
-
Rack::Attack.
|
|
53
|
+
Rack::Attack.safelist("good ua") { |req| req.user_agent == @good_ua }
|
|
46
54
|
end
|
|
47
55
|
|
|
48
|
-
it('has a
|
|
49
|
-
|
|
56
|
+
it('has a safelist') { Rack::Attack.safelists.key?("good ua") }
|
|
57
|
+
|
|
58
|
+
it('has a whitelist with a deprication warning') {
|
|
59
|
+
_, stderror = capture_io do
|
|
60
|
+
Rack::Attack.whitelists.key?("good ua")
|
|
61
|
+
end
|
|
62
|
+
assert_match "[DEPRECATION] 'Rack::Attack.whitelists' is deprecated. Please use 'safelists' instead.", stderror
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
describe "with a request match both safelist & blocklist" do
|
|
50
66
|
before { get '/', {}, 'REMOTE_ADDR' => @bad_ip, 'HTTP_USER_AGENT' => @good_ua }
|
|
51
|
-
|
|
52
|
-
|
|
67
|
+
|
|
68
|
+
it "should allow safelists before blocklists" do
|
|
53
69
|
last_response.status.must_equal 200
|
|
54
70
|
end
|
|
71
|
+
|
|
55
72
|
it "should tag the env" do
|
|
56
73
|
last_request.env['rack.attack.matched'].must_equal 'good ua'
|
|
57
|
-
last_request.env['rack.attack.match_type'].must_equal :
|
|
74
|
+
last_request.env['rack.attack.match_type'].must_equal :safelist
|
|
58
75
|
end
|
|
59
76
|
end
|
|
60
77
|
end
|
|
61
|
-
end
|
|
62
78
|
|
|
79
|
+
describe '#blocklisted_response' do
|
|
80
|
+
it 'should exist' do
|
|
81
|
+
Rack::Attack.blocklisted_response.must_respond_to :call
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
it 'should give a deprication warning for blacklisted_response' do
|
|
85
|
+
_, stderror = capture_io do
|
|
86
|
+
Rack::Attack.blacklisted_response
|
|
87
|
+
end
|
|
88
|
+
assert_match "[DEPRECATION] 'Rack::Attack.blacklisted_response' is deprecated. Please use 'blocklisted_response' instead.", stderror
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
describe '#throttled_response' do
|
|
93
|
+
it 'should exist' do
|
|
94
|
+
Rack::Attack.throttled_response.must_respond_to :call
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
63
98
|
end
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require_relative 'spec_helper'
|
|
2
|
+
|
|
2
3
|
describe 'Rack::Attack.throttle' do
|
|
3
4
|
before do
|
|
4
5
|
@period = 60 # Use a long period; failures due to cache key rotation less likely
|
|
@@ -6,34 +7,40 @@ describe 'Rack::Attack.throttle' do
|
|
|
6
7
|
Rack::Attack.throttle('ip/sec', :limit => 1, :period => @period) { |req| req.ip }
|
|
7
8
|
end
|
|
8
9
|
|
|
9
|
-
it('should have a throttle'){ Rack::Attack.throttles.key?('ip/sec') }
|
|
10
|
-
|
|
10
|
+
it('should have a throttle') { Rack::Attack.throttles.key?('ip/sec') }
|
|
11
|
+
|
|
12
|
+
it_allows_ok_requests
|
|
11
13
|
|
|
12
14
|
describe 'a single request' do
|
|
13
15
|
before { get '/', {}, 'REMOTE_ADDR' => '1.2.3.4' }
|
|
16
|
+
|
|
14
17
|
it 'should set the counter for one request' do
|
|
15
|
-
key = "rack::attack:#{Time.now.to_i
|
|
18
|
+
key = "rack::attack:#{Time.now.to_i / @period}:ip/sec:1.2.3.4"
|
|
16
19
|
Rack::Attack.cache.store.read(key).must_equal 1
|
|
17
20
|
end
|
|
18
21
|
|
|
19
22
|
it 'should populate throttle data' do
|
|
20
|
-
data = { :count => 1, :limit => 1, :period => @period }
|
|
23
|
+
data = { :count => 1, :limit => 1, :period => @period, epoch_time: Rack::Attack.cache.last_epoch_time.to_i }
|
|
21
24
|
last_request.env['rack.attack.throttle_data']['ip/sec'].must_equal data
|
|
22
25
|
end
|
|
23
26
|
end
|
|
27
|
+
|
|
24
28
|
describe "with 2 requests" do
|
|
25
29
|
before do
|
|
26
30
|
2.times { get '/', {}, 'REMOTE_ADDR' => '1.2.3.4' }
|
|
27
31
|
end
|
|
32
|
+
|
|
28
33
|
it 'should block the last request' do
|
|
29
34
|
last_response.status.must_equal 429
|
|
30
35
|
end
|
|
36
|
+
|
|
31
37
|
it 'should tag the env' do
|
|
32
38
|
last_request.env['rack.attack.matched'].must_equal 'ip/sec'
|
|
33
39
|
last_request.env['rack.attack.match_type'].must_equal :throttle
|
|
34
|
-
last_request.env['rack.attack.match_data'].must_equal(
|
|
40
|
+
last_request.env['rack.attack.match_data'].must_equal(:count => 2, :limit => 1, :period => @period, epoch_time: Rack::Attack.cache.last_epoch_time.to_i)
|
|
35
41
|
last_request.env['rack.attack.match_discriminator'].must_equal('1.2.3.4')
|
|
36
42
|
end
|
|
43
|
+
|
|
37
44
|
it 'should set a Retry-After header' do
|
|
38
45
|
last_response.headers['Retry-After'].must_equal @period.to_s
|
|
39
46
|
end
|
|
@@ -44,20 +51,21 @@ describe 'Rack::Attack.throttle with limit as proc' do
|
|
|
44
51
|
before do
|
|
45
52
|
@period = 60 # Use a long period; failures due to cache key rotation less likely
|
|
46
53
|
Rack::Attack.cache.store = ActiveSupport::Cache::MemoryStore.new
|
|
47
|
-
Rack::Attack.throttle('ip/sec', :limit => lambda { |
|
|
54
|
+
Rack::Attack.throttle('ip/sec', :limit => lambda { |_req| 1 }, :period => @period) { |req| req.ip }
|
|
48
55
|
end
|
|
49
56
|
|
|
50
|
-
|
|
57
|
+
it_allows_ok_requests
|
|
51
58
|
|
|
52
59
|
describe 'a single request' do
|
|
53
60
|
before { get '/', {}, 'REMOTE_ADDR' => '1.2.3.4' }
|
|
61
|
+
|
|
54
62
|
it 'should set the counter for one request' do
|
|
55
|
-
key = "rack::attack:#{Time.now.to_i
|
|
63
|
+
key = "rack::attack:#{Time.now.to_i / @period}:ip/sec:1.2.3.4"
|
|
56
64
|
Rack::Attack.cache.store.read(key).must_equal 1
|
|
57
65
|
end
|
|
58
66
|
|
|
59
67
|
it 'should populate throttle data' do
|
|
60
|
-
data = { :count => 1, :limit => 1, :period => @period }
|
|
68
|
+
data = { :count => 1, :limit => 1, :period => @period, epoch_time: Rack::Attack.cache.last_epoch_time.to_i }
|
|
61
69
|
last_request.env['rack.attack.throttle_data']['ip/sec'].must_equal data
|
|
62
70
|
end
|
|
63
71
|
end
|
|
@@ -67,21 +75,45 @@ describe 'Rack::Attack.throttle with period as proc' do
|
|
|
67
75
|
before do
|
|
68
76
|
@period = 60 # Use a long period; failures due to cache key rotation less likely
|
|
69
77
|
Rack::Attack.cache.store = ActiveSupport::Cache::MemoryStore.new
|
|
70
|
-
Rack::Attack.throttle('ip/sec', :limit => lambda { |
|
|
78
|
+
Rack::Attack.throttle('ip/sec', :limit => lambda { |_req| 1 }, :period => lambda { |_req| @period }) { |req| req.ip }
|
|
71
79
|
end
|
|
72
80
|
|
|
73
|
-
|
|
81
|
+
it_allows_ok_requests
|
|
74
82
|
|
|
75
83
|
describe 'a single request' do
|
|
76
84
|
before { get '/', {}, 'REMOTE_ADDR' => '1.2.3.4' }
|
|
85
|
+
|
|
77
86
|
it 'should set the counter for one request' do
|
|
78
|
-
key = "rack::attack:#{Time.now.to_i
|
|
87
|
+
key = "rack::attack:#{Time.now.to_i / @period}:ip/sec:1.2.3.4"
|
|
79
88
|
Rack::Attack.cache.store.read(key).must_equal 1
|
|
80
89
|
end
|
|
81
90
|
|
|
82
91
|
it 'should populate throttle data' do
|
|
83
|
-
data = { :count => 1, :limit => 1, :period => @period }
|
|
92
|
+
data = { :count => 1, :limit => 1, :period => @period, epoch_time: Rack::Attack.cache.last_epoch_time.to_i }
|
|
84
93
|
last_request.env['rack.attack.throttle_data']['ip/sec'].must_equal data
|
|
85
94
|
end
|
|
86
95
|
end
|
|
87
96
|
end
|
|
97
|
+
|
|
98
|
+
describe 'Rack::Attack.throttle with block retuning nil' do
|
|
99
|
+
before do
|
|
100
|
+
@period = 60
|
|
101
|
+
Rack::Attack.cache.store = ActiveSupport::Cache::MemoryStore.new
|
|
102
|
+
Rack::Attack.throttle('ip/sec', :limit => 1, :period => @period) { |_| nil }
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
it_allows_ok_requests
|
|
106
|
+
|
|
107
|
+
describe 'a single request' do
|
|
108
|
+
before { get '/', {}, 'REMOTE_ADDR' => '1.2.3.4' }
|
|
109
|
+
|
|
110
|
+
it 'should not set the counter' do
|
|
111
|
+
key = "rack::attack:#{Time.now.to_i / @period}:ip/sec:1.2.3.4"
|
|
112
|
+
assert_nil Rack::Attack.cache.store.read(key)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
it 'should not populate throttle data' do
|
|
116
|
+
assert_nil last_request.env['rack.attack.throttle_data']
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
@@ -16,9 +16,11 @@ describe 'Rack::Attack.track' do
|
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
before do
|
|
19
|
-
Rack::Attack.track("everything"){ |
|
|
19
|
+
Rack::Attack.track("everything") { |_req| true }
|
|
20
20
|
end
|
|
21
|
-
|
|
21
|
+
|
|
22
|
+
it_allows_ok_requests
|
|
23
|
+
|
|
22
24
|
it "should tag the env" do
|
|
23
25
|
get '/'
|
|
24
26
|
last_request.env['rack.attack.matched'].must_equal 'everything'
|
|
@@ -29,11 +31,12 @@ describe 'Rack::Attack.track' do
|
|
|
29
31
|
before do
|
|
30
32
|
Counter.reset
|
|
31
33
|
# A second track
|
|
32
|
-
Rack::Attack.track("homepage"){ |req| req.path == "/"}
|
|
34
|
+
Rack::Attack.track("homepage") { |req| req.path == "/" }
|
|
33
35
|
|
|
34
|
-
ActiveSupport::Notifications.subscribe("rack.attack") do |*
|
|
36
|
+
ActiveSupport::Notifications.subscribe("rack.attack") do |*_args|
|
|
35
37
|
Counter.incr
|
|
36
38
|
end
|
|
39
|
+
|
|
37
40
|
get "/"
|
|
38
41
|
end
|
|
39
42
|
|
|
@@ -44,15 +47,15 @@ describe 'Rack::Attack.track' do
|
|
|
44
47
|
|
|
45
48
|
describe "without limit and period options" do
|
|
46
49
|
it "should assign the track filter to a Check instance" do
|
|
47
|
-
|
|
48
|
-
|
|
50
|
+
track = Rack::Attack.track("homepage") { |req| req.path == "/" }
|
|
51
|
+
track.filter.class.must_equal Rack::Attack::Check
|
|
49
52
|
end
|
|
50
53
|
end
|
|
51
54
|
|
|
52
55
|
describe "with limit and period options" do
|
|
53
56
|
it "should assign the track filter to a Throttle instance" do
|
|
54
|
-
|
|
55
|
-
|
|
57
|
+
track = Rack::Attack.track("homepage", :limit => 10, :period => 10) { |req| req.path == "/" }
|
|
58
|
+
track.filter.class.must_equal Rack::Attack::Throttle
|
|
56
59
|
end
|
|
57
60
|
end
|
|
58
61
|
end
|