rack-attack 4.2.0 → 4.3.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rack-attack might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e99e6a7757d11626b6b7d078abe43e9fe123cc36
4
- data.tar.gz: 77b5a17a9de1acd9692805e2e634d165d66f1506
3
+ metadata.gz: da0016c3e3d7fee696a96f8f1e3a493b0b197518
4
+ data.tar.gz: 6b337c7d2ed9c48dbfd4cfc031ab6157fbceda3d
5
5
  SHA512:
6
- metadata.gz: 75e2f1b7c760cc33323618edf03dd6fb3f661b73d2ed2d2ce67e80f632942c369d4e4336c479ae68e99805c2c2910a51b54dc4fab545b54d295536c28811ca20
7
- data.tar.gz: 5da774caa95cfe83eaeb1c42d759ac0761fc76c5c6137681101f658680b3bba9edde35862f9a84cd0a1f530ae294142663a619c3940fedc761345b0be5cd3542
6
+ metadata.gz: 1f84ef0262ee5f64ed98d745bedf49f7e8ef38693a3bc8e0eddb643dc7165e881ee11642944cf75d52769e13f6da934be69bf3156c6945e938caa139bf886252
7
+ data.tar.gz: b952d3bd6061eaf8c7b5172c0b6620d4e3797d383c7a00d9d6d80eef78d470ba3ce8dbb1c57f801f3685164f7076a587236fc5bc3550b433c3771bddbfe589ef
data/README.md CHANGED
@@ -15,7 +15,7 @@ See the [Backing & Hacking blog post](http://www.kickstarter.com/backing-and-hac
15
15
 
16
16
  ## Getting started
17
17
 
18
- Install the [rack-attack](http://rubygems.org/gems/rack-attack) gem; or add it to you Gemfile with bundler:
18
+ Install the [rack-attack](http://rubygems.org/gems/rack-attack) gem; or add it to your Gemfile with bundler:
19
19
 
20
20
  ```ruby
21
21
  # In your Gemfile
@@ -36,7 +36,7 @@ Or for Rackup files:
36
36
  use Rack::Attack
37
37
  ```
38
38
 
39
- Add a `rack-attack.rb` file to `config/initalizers/`:
39
+ Add a `rack-attack.rb` file to `config/initializers/`:
40
40
  ```ruby
41
41
  # In config/initializers/rack-attack.rb
42
42
  class Rack::Attack
@@ -95,7 +95,7 @@ can cleanly monkey patch helper methods onto the
95
95
 
96
96
  Define whitelists, blacklists, throttles, and tracks as blocks that return truthy values if matched, falsy otherwise. In a Rails app
97
97
  these go in an initializer in `config/initializers/`.
98
- A [Rack::Request](http://rack.rubyforge.org/doc/classes/Rack/Request.html) object is passed to the block (named 'req' in the examples).
98
+ A [Rack::Request](http://www.rubydoc.info/gems/rack/Rack/Request) object is passed to the block (named 'req' in the examples).
99
99
 
100
100
  ### Whitelists
101
101
 
@@ -199,7 +199,7 @@ Rack::Attack.track("special_agent") do |req|
199
199
  end
200
200
 
201
201
  # Supports optional limit and period, triggers the notification only when the limit is reached.
202
- Rack::Attack.track("special_agent", :limit 6, :period => 60.seconds) do |req|
202
+ Rack::Attack.track("special_agent", :limit => 6, :period => 60.seconds) do |req|
203
203
  req.user_agent == "SpecialAgent"
204
204
  end
205
205
 
data/Rakefile CHANGED
@@ -1,5 +1,6 @@
1
1
  require "rubygems"
2
2
  require "bundler/setup"
3
+ require 'bundler/gem_tasks'
3
4
  require 'rake/testtask'
4
5
 
5
6
  namespace :test do
@@ -15,10 +15,7 @@ module Rack
15
15
  end
16
16
 
17
17
  def count(unprefixed_key, period)
18
- epoch_time = Time.now.to_i
19
- # Add 1 to expires_in to avoid timing error: http://git.io/i1PHXA
20
- expires_in = period - (epoch_time % period) + 1
21
- key = "#{prefix}:#{(epoch_time/period).to_i}:#{unprefixed_key}"
18
+ key, expires_in = key_and_expiry(unprefixed_key, period)
22
19
  do_count(key, expires_in)
23
20
  end
24
21
 
@@ -30,7 +27,24 @@ module Rack
30
27
  store.write("#{prefix}:#{unprefixed_key}", value, :expires_in => expires_in)
31
28
  end
32
29
 
30
+ def reset_count(unprefixed_key, period)
31
+ key, _ = key_and_expiry(unprefixed_key, period)
32
+ store.delete(key)
33
+ end
34
+
35
+ def delete(unprefixed_key)
36
+ store.delete("#{prefix}:#{unprefixed_key}")
37
+ end
38
+
33
39
  private
40
+
41
+ def key_and_expiry(unprefixed_key, period)
42
+ epoch_time = Time.now.to_i
43
+ # Add 1 to expires_in to avoid timing error: http://git.io/i1PHXA
44
+ expires_in = (period - (epoch_time % period) + 1).to_i
45
+ ["#{prefix}:#{(epoch_time / period).to_i}:#{unprefixed_key}", expires_in]
46
+ end
47
+
34
48
  def do_count(key, expires_in)
35
49
  result = store.increment(key, 1, :expires_in => expires_in)
36
50
 
@@ -15,6 +15,17 @@ module Rack
15
15
  end
16
16
  end
17
17
 
18
+ def reset(discriminator, options)
19
+ findtime = options[:findtime] or raise ArgumentError, "Must pass findtime option"
20
+ cache.reset_count("#{key_prefix}:count:#{discriminator}", findtime)
21
+ # Clear ban flag just in case it's there
22
+ cache.delete("#{key_prefix}:ban:#{discriminator}")
23
+ end
24
+
25
+ def banned?(discriminator)
26
+ cache.read("#{key_prefix}:ban:#{discriminator}") ? true : false
27
+ end
28
+
18
29
  protected
19
30
  def key_prefix
20
31
  'fail2ban'
@@ -35,10 +46,6 @@ module Rack
35
46
  cache.write("#{key_prefix}:ban:#{discriminator}", 1, bantime)
36
47
  end
37
48
 
38
- def banned?(discriminator)
39
- cache.read("#{key_prefix}:ban:#{discriminator}")
40
- end
41
-
42
49
  def cache
43
50
  Rack::Attack.cache
44
51
  end
@@ -13,15 +13,15 @@ module Rack
13
13
  end
14
14
 
15
15
  def read(key)
16
- self.get(key)
16
+ self.get(key, raw: true)
17
17
  rescue Redis::BaseError
18
18
  end
19
19
 
20
20
  def write(key, value, options={})
21
21
  if (expires_in = options[:expires_in])
22
- self.setex(key, expires_in, value)
22
+ self.setex(key, expires_in, value, raw: true)
23
23
  else
24
- self.set(key, value)
24
+ self.set(key, value, raw: true)
25
25
  end
26
26
  rescue Redis::BaseError
27
27
  end
@@ -36,6 +36,10 @@ module Rack
36
36
  rescue Redis::BaseError
37
37
  end
38
38
 
39
+ def delete(key, options={})
40
+ self.del(key)
41
+ rescue Redis::BaseError
42
+ end
39
43
  end
40
44
  end
41
45
  end
@@ -1,5 +1,5 @@
1
1
  module Rack
2
2
  class Attack
3
- VERSION = '4.2.0'
3
+ VERSION = '4.3.0'
4
4
  end
5
5
  end
@@ -58,7 +58,27 @@ describe 'Rack::Attack.Fail2Ban' do
58
58
  key = "rack::attack:fail2ban:ban:1.2.3.4"
59
59
  @cache.store.read(key).must_equal 1
60
60
  end
61
+ end
62
+
63
+ describe 'reset after success' do
64
+ before do
65
+ get '/?test=OMGHAX', {}, 'REMOTE_ADDR' => '1.2.3.4'
66
+ Rack::Attack::Fail2Ban.reset('1.2.3.4', @f2b_options)
67
+ get '/', {}, 'REMOTE_ADDR' => '1.2.3.4'
68
+ end
61
69
 
70
+ it 'succeeds' do
71
+ last_response.status.must_equal 200
72
+ end
73
+
74
+ it 'resets fail count' do
75
+ key = "rack::attack:#{Time.now.to_i/@findtime}:fail2ban:count:1.2.3.4"
76
+ @cache.store.read(key).must_equal nil
77
+ end
78
+
79
+ it 'IP is not banned' do
80
+ Rack::Attack::Fail2Ban.banned?('1.2.3.4').must_equal false
81
+ end
62
82
  end
63
83
  end
64
84
  end
@@ -1,6 +1,9 @@
1
1
  require_relative '../spec_helper'
2
2
 
3
3
  describe Rack::Attack::Cache do
4
+
5
+ # A convenience method for deleting a key from cache.
6
+ # Slightly differnet than @cache.delete, which adds a prefix.
4
7
  def delete(key)
5
8
  if @cache.store.respond_to?(:delete)
6
9
  @cache.store.delete(key)
@@ -80,6 +83,36 @@ describe Rack::Attack::Cache do
80
83
  @cache.read("cache-test-key").must_equal "foobar"
81
84
  end
82
85
  end
86
+
87
+ describe "delete" do
88
+ it "must delete the value" do
89
+ store.write(@key, "foobar", :expires_in => @expires_in)
90
+ @cache.read('cache-test-key').must_equal "foobar"
91
+ store.delete(@key)
92
+ @cache.read('cache-test-key').must_equal nil
93
+ end
94
+ end
95
+
96
+ describe "cache#delete" do
97
+ it "must delete the value" do
98
+ @cache.write("cache-test-key", "foobar", 1)
99
+ store.read(@key).must_equal "foobar"
100
+ @cache.delete('cache-test-key')
101
+ store.read(@key).must_be :nil?
102
+ end
103
+ end
104
+
105
+ describe "reset_count" do
106
+ it "must delete the value" do
107
+ period = 1.minute
108
+ unprefixed_key = 'cache-test-key'
109
+ @cache.count(unprefixed_key, period)
110
+ period_key, _ = @cache.send(:key_and_expiry, 'cache-test-key', period)
111
+ store.read(period_key).to_i.must_equal 1
112
+ @cache.reset_count(unprefixed_key, period)
113
+ store.read(period_key).must_equal nil
114
+ end
115
+ end
83
116
  end
84
117
 
85
118
  end
@@ -9,7 +9,9 @@ describe 'Rack::Attack' do
9
9
  Rack::Attack.blacklist("ip #{@bad_ip}") {|req| req.ip == @bad_ip }
10
10
  end
11
11
 
12
- it('has a blacklist') { Rack::Attack.blacklists.key?("ip #{@bad_ip}") }
12
+ it('has a blacklist') {
13
+ Rack::Attack.blacklists.key?("ip #{@bad_ip}").must_equal true
14
+ }
13
15
 
14
16
  describe "a bad request" do
15
17
  before { get '/', {}, 'REMOTE_ADDR' => @bad_ip }
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: 4.2.0
4
+ version: 4.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: 2014-10-26 00:00:00.000000000 Z
11
+ date: 2015-05-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -189,7 +189,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
189
189
  version: '0'
190
190
  requirements: []
191
191
  rubyforge_project:
192
- rubygems_version: 2.2.2
192
+ rubygems_version: 2.4.5
193
193
  signing_key:
194
194
  specification_version: 4
195
195
  summary: Block & throttle abusive requests
@@ -204,4 +204,3 @@ test_files:
204
204
  - spec/rack_attack_throttle_spec.rb
205
205
  - spec/rack_attack_track_spec.rb
206
206
  - spec/spec_helper.rb
207
- has_rdoc: