mirror of
https://github.com/samsonjs/rack-attack.git
synced 2026-04-27 15:07:41 +00:00
Merge branch 'master' into dalli3-and-rails7
This commit is contained in:
commit
8d9c884d40
8 changed files with 61 additions and 51 deletions
29
.github/workflows/build.yml
vendored
29
.github/workflows/build.yml
vendored
|
|
@ -17,9 +17,10 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
ruby:
|
ruby:
|
||||||
- 3.0.2
|
- 3.1.0
|
||||||
- 2.7.4
|
- 3.0.3
|
||||||
- 2.6.8
|
- 2.7.5
|
||||||
|
- 2.6.9
|
||||||
- 2.5.8
|
- 2.5.8
|
||||||
gemfile:
|
gemfile:
|
||||||
- rack_2
|
- rack_2
|
||||||
|
|
@ -40,19 +41,27 @@ jobs:
|
||||||
- active_support_redis_store
|
- active_support_redis_store
|
||||||
exclude:
|
exclude:
|
||||||
- gemfile: rack_1
|
- gemfile: rack_1
|
||||||
ruby: 3.0.2
|
ruby: 3.1.0
|
||||||
- gemfile: rails_5_2
|
- gemfile: rails_5_2
|
||||||
ruby: 3.0.2
|
ruby: 3.1.0
|
||||||
- gemfile: rails_4_2
|
- gemfile: rails_4_2
|
||||||
ruby: 3.0.2
|
ruby: 3.1.0
|
||||||
- gemfile: dalli2
|
- gemfile: dalli2
|
||||||
ruby: 3.0.2
|
ruby: 3.1.0
|
||||||
- gemfile: rack_1
|
- gemfile: rack_1
|
||||||
ruby: 2.7.4
|
ruby: 3.0.3
|
||||||
|
- gemfile: rails_5_2
|
||||||
|
ruby: 3.0.3
|
||||||
- gemfile: rails_4_2
|
- gemfile: rails_4_2
|
||||||
ruby: 2.7.4
|
ruby: 3.0.3
|
||||||
|
- gemfile: dalli2
|
||||||
|
ruby: 3.0.3
|
||||||
|
- gemfile: rack_1
|
||||||
|
ruby: 2.7.5
|
||||||
|
- gemfile: rails_4_2
|
||||||
|
ruby: 2.7.5
|
||||||
- gemfile: rails_7_0
|
- gemfile: rails_7_0
|
||||||
ruby: 2.6.8
|
ruby: 2.6.9
|
||||||
- gemfile: rails_7_0
|
- gemfile: rails_7_0
|
||||||
ruby: 2.5.8
|
ruby: 2.5.8
|
||||||
env:
|
env:
|
||||||
|
|
|
||||||
13
README.md
13
README.md
|
|
@ -312,16 +312,16 @@ Note that `Rack::Attack.cache` is only used for throttling, allow2ban and fail2b
|
||||||
|
|
||||||
## Customizing responses
|
## Customizing responses
|
||||||
|
|
||||||
Customize the response of blocklisted and throttled requests using an object that adheres to the [Rack app interface](http://www.rubydoc.info/github/rack/rack/file/SPEC).
|
Customize the response of blocklisted and throttled requests using an object that adheres to the [Rack app interface](http://www.rubydoc.info/github/rack/rack/file/SPEC.rdoc).
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
Rack::Attack.blocklisted_callback = lambda do |request|
|
Rack::Attack.blocklisted_responder = lambda do |request|
|
||||||
# Using 503 because it may make attacker think that they have successfully
|
# Using 503 because it may make attacker think that they have successfully
|
||||||
# DOSed the site. Rack::Attack returns 403 for blocklists by default
|
# DOSed the site. Rack::Attack returns 403 for blocklists by default
|
||||||
[ 503, {}, ['Blocked']]
|
[ 503, {}, ['Blocked']]
|
||||||
end
|
end
|
||||||
|
|
||||||
Rack::Attack.throttled_callback = lambda do |request|
|
Rack::Attack.throttled_responder = lambda do |request|
|
||||||
# NB: you have access to the name and other data about the matched throttle
|
# NB: you have access to the name and other data about the matched throttle
|
||||||
# request.env['rack.attack.matched'],
|
# request.env['rack.attack.matched'],
|
||||||
# request.env['rack.attack.match_type'],
|
# request.env['rack.attack.match_type'],
|
||||||
|
|
@ -407,7 +407,7 @@ for more on how to do this.
|
||||||
|
|
||||||
### Test case isolation
|
### Test case isolation
|
||||||
|
|
||||||
`Rack::Attack.reset!` can be used in your test suite to clear any Rack::Attack state between different test cases.
|
`Rack::Attack.reset!` can be used in your test suite to clear any Rack::Attack state between different test cases. If you're testing blocklist and safelist configurations, consider using `Rack::Attack.clear_configuration` to unset the values for those lists between test cases.
|
||||||
|
|
||||||
## How it works
|
## How it works
|
||||||
|
|
||||||
|
|
@ -427,10 +427,9 @@ def call(env)
|
||||||
if safelisted?(req)
|
if safelisted?(req)
|
||||||
@app.call(env)
|
@app.call(env)
|
||||||
elsif blocklisted?(req)
|
elsif blocklisted?(req)
|
||||||
self.class.blocklisted_callback.call(req)
|
self.class.blocklisted_responder.call(req)
|
||||||
elsif throttled?(req)
|
elsif throttled?(req)
|
||||||
self.class.throttled_response.call(env)
|
self.class.throttled_responder.call(req)
|
||||||
self.class.throttled_callback.call(req)
|
|
||||||
else
|
else
|
||||||
tracked?(req)
|
tracked?(req)
|
||||||
@app.call(env)
|
@app.call(env)
|
||||||
|
|
|
||||||
|
|
@ -66,10 +66,10 @@ module Rack
|
||||||
:safelist_ip,
|
:safelist_ip,
|
||||||
:throttle,
|
:throttle,
|
||||||
:track,
|
:track,
|
||||||
:throttled_callback,
|
:throttled_responder,
|
||||||
:throttled_callback=,
|
:throttled_responder=,
|
||||||
:blocklisted_callback,
|
:blocklisted_responder,
|
||||||
:blocklisted_callback=,
|
:blocklisted_responder=,
|
||||||
:blocklisted_response,
|
:blocklisted_response,
|
||||||
:blocklisted_response=,
|
:blocklisted_response=,
|
||||||
:throttled_response,
|
:throttled_response,
|
||||||
|
|
@ -113,14 +113,14 @@ module Rack
|
||||||
if configuration.blocklisted_response
|
if configuration.blocklisted_response
|
||||||
configuration.blocklisted_response.call(env)
|
configuration.blocklisted_response.call(env)
|
||||||
else
|
else
|
||||||
configuration.blocklisted_callback.call(request)
|
configuration.blocklisted_responder.call(request)
|
||||||
end
|
end
|
||||||
elsif configuration.throttled?(request)
|
elsif configuration.throttled?(request)
|
||||||
# Deprecated: Keeping throttled_response for backwards compatibility
|
# Deprecated: Keeping throttled_response for backwards compatibility
|
||||||
if configuration.throttled_response
|
if configuration.throttled_response
|
||||||
configuration.throttled_response.call(env)
|
configuration.throttled_response.call(env)
|
||||||
else
|
else
|
||||||
configuration.throttled_callback.call(request)
|
configuration.throttled_responder.call(request)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
configuration.tracked?(request)
|
configuration.tracked?(request)
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,9 @@ require "ipaddr"
|
||||||
module Rack
|
module Rack
|
||||||
class Attack
|
class Attack
|
||||||
class Configuration
|
class Configuration
|
||||||
DEFAULT_BLOCKLISTED_CALLBACK = lambda { |_req| [403, { 'Content-Type' => 'text/plain' }, ["Forbidden\n"]] }
|
DEFAULT_BLOCKLISTED_RESPONDER = lambda { |_req| [403, { 'Content-Type' => 'text/plain' }, ["Forbidden\n"]] }
|
||||||
|
|
||||||
DEFAULT_THROTTLED_CALLBACK = lambda do |req|
|
DEFAULT_THROTTLED_RESPONDER = lambda do |req|
|
||||||
if Rack::Attack.configuration.throttled_response_retry_after_header
|
if Rack::Attack.configuration.throttled_response_retry_after_header
|
||||||
match_data = req.env['rack.attack.match_data']
|
match_data = req.env['rack.attack.match_data']
|
||||||
now = match_data[:epoch_time]
|
now = match_data[:epoch_time]
|
||||||
|
|
@ -20,22 +20,20 @@ module Rack
|
||||||
end
|
end
|
||||||
|
|
||||||
attr_reader :safelists, :blocklists, :throttles, :anonymous_blocklists, :anonymous_safelists
|
attr_reader :safelists, :blocklists, :throttles, :anonymous_blocklists, :anonymous_safelists
|
||||||
attr_accessor :blocklisted_callback, :throttled_callback, :throttled_response_retry_after_header
|
attr_accessor :blocklisted_responder, :throttled_responder, :throttled_response_retry_after_header
|
||||||
|
|
||||||
attr_reader :blocklisted_response, :throttled_response # Keeping these for backwards compatibility
|
attr_reader :blocklisted_response, :throttled_response # Keeping these for backwards compatibility
|
||||||
|
|
||||||
def blocklisted_response=(callback)
|
def blocklisted_response=(responder)
|
||||||
# TODO: uncomment in 7.0
|
warn "[DEPRECATION] Rack::Attack.blocklisted_response is deprecated. "\
|
||||||
# warn "[DEPRECATION] Rack::Attack.blocklisted_response is deprecated. "\
|
"Please use Rack::Attack.blocklisted_responder instead."
|
||||||
# "Please use Rack::Attack.blocklisted_callback instead."
|
@blocklisted_response = responder
|
||||||
@blocklisted_response = callback
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def throttled_response=(callback)
|
def throttled_response=(responder)
|
||||||
# TODO: uncomment in 7.0
|
warn "[DEPRECATION] Rack::Attack.throttled_response is deprecated. "\
|
||||||
# warn "[DEPRECATION] Rack::Attack.throttled_response is deprecated. "\
|
"Please use Rack::Attack.throttled_responder instead"
|
||||||
# "Please use Rack::Attack.throttled_callback instead"
|
@throttled_response = responder
|
||||||
@throttled_response = callback
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
|
|
@ -115,8 +113,8 @@ module Rack
|
||||||
@anonymous_safelists = []
|
@anonymous_safelists = []
|
||||||
@throttled_response_retry_after_header = false
|
@throttled_response_retry_after_header = false
|
||||||
|
|
||||||
@blocklisted_callback = DEFAULT_BLOCKLISTED_CALLBACK
|
@blocklisted_responder = DEFAULT_BLOCKLISTED_RESPONDER
|
||||||
@throttled_callback = DEFAULT_THROTTLED_CALLBACK
|
@throttled_responder = DEFAULT_THROTTLED_RESPONDER
|
||||||
|
|
||||||
# Deprecated: Keeping these for backwards compatibility
|
# Deprecated: Keeping these for backwards compatibility
|
||||||
@blocklisted_response = nil
|
@blocklisted_response = nil
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ Gem::Specification.new do |s|
|
||||||
s.description = "A rack middleware for throttling and blocking abusive requests"
|
s.description = "A rack middleware for throttling and blocking abusive requests"
|
||||||
s.email = "aaron@ktheory.com"
|
s.email = "aaron@ktheory.com"
|
||||||
|
|
||||||
s.files = Dir.glob("{bin,lib}/**/*") + %w(Rakefile README.md)
|
s.files = Dir.glob("{bin,lib}/**/*") + %w(Rakefile README.md LICENSE)
|
||||||
s.homepage = 'https://github.com/rack/rack-attack'
|
s.homepage = 'https://github.com/rack/rack-attack'
|
||||||
s.rdoc_options = ["--charset=UTF-8"]
|
s.rdoc_options = ["--charset=UTF-8"]
|
||||||
s.require_paths = ["lib"]
|
s.require_paths = ["lib"]
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ describe "Customizing block responses" do
|
||||||
|
|
||||||
assert_equal 403, last_response.status
|
assert_equal 403, last_response.status
|
||||||
|
|
||||||
Rack::Attack.blocklisted_callback = lambda do |_req|
|
Rack::Attack.blocklisted_responder = lambda do |_req|
|
||||||
[503, {}, ["Blocked"]]
|
[503, {}, ["Blocked"]]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -28,7 +28,7 @@ describe "Customizing block responses" do
|
||||||
matched = nil
|
matched = nil
|
||||||
match_type = nil
|
match_type = nil
|
||||||
|
|
||||||
Rack::Attack.blocklisted_callback = lambda do |req|
|
Rack::Attack.blocklisted_responder = lambda do |req|
|
||||||
matched = req.env['rack.attack.matched']
|
matched = req.env['rack.attack.matched']
|
||||||
match_type = req.env['rack.attack.match_type']
|
match_type = req.env['rack.attack.match_type']
|
||||||
|
|
||||||
|
|
@ -46,9 +46,11 @@ describe "Customizing block responses" do
|
||||||
|
|
||||||
assert_equal 403, last_response.status
|
assert_equal 403, last_response.status
|
||||||
|
|
||||||
|
silence_warnings do
|
||||||
Rack::Attack.blocklisted_response = lambda do |_env|
|
Rack::Attack.blocklisted_response = lambda do |_env|
|
||||||
[503, {}, ["Blocked"]]
|
[503, {}, ["Blocked"]]
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
get "/", {}, "REMOTE_ADDR" => "1.2.3.4"
|
get "/", {}, "REMOTE_ADDR" => "1.2.3.4"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ describe "Customizing throttled response" do
|
||||||
|
|
||||||
assert_equal 429, last_response.status
|
assert_equal 429, last_response.status
|
||||||
|
|
||||||
Rack::Attack.throttled_callback = lambda do |_req|
|
Rack::Attack.throttled_responder = lambda do |_req|
|
||||||
[503, {}, ["Throttled"]]
|
[503, {}, ["Throttled"]]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -36,7 +36,7 @@ describe "Customizing throttled response" do
|
||||||
match_data = nil
|
match_data = nil
|
||||||
match_discriminator = nil
|
match_discriminator = nil
|
||||||
|
|
||||||
Rack::Attack.throttled_callback = lambda do |req|
|
Rack::Attack.throttled_responder = lambda do |req|
|
||||||
matched = req.env['rack.attack.matched']
|
matched = req.env['rack.attack.matched']
|
||||||
match_type = req.env['rack.attack.match_type']
|
match_type = req.env['rack.attack.match_type']
|
||||||
match_data = req.env['rack.attack.match_data']
|
match_data = req.env['rack.attack.match_data']
|
||||||
|
|
@ -68,9 +68,11 @@ describe "Customizing throttled response" do
|
||||||
|
|
||||||
assert_equal 429, last_response.status
|
assert_equal 429, last_response.status
|
||||||
|
|
||||||
|
silence_warnings do
|
||||||
Rack::Attack.throttled_response = lambda do |_req|
|
Rack::Attack.throttled_response = lambda do |_req|
|
||||||
[503, {}, ["Throttled"]]
|
[503, {}, ["Throttled"]]
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
get "/", {}, "REMOTE_ADDR" => "1.2.3.4"
|
get "/", {}, "REMOTE_ADDR" => "1.2.3.4"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,15 +64,15 @@ describe 'Rack::Attack' do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#blocklisted_callback' do
|
describe '#blocklisted_responder' do
|
||||||
it 'should exist' do
|
it 'should exist' do
|
||||||
_(Rack::Attack.blocklisted_callback).must_respond_to :call
|
_(Rack::Attack.blocklisted_responder).must_respond_to :call
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#throttled_callback' do
|
describe '#throttled_responder' do
|
||||||
it 'should exist' do
|
it 'should exist' do
|
||||||
_(Rack::Attack.throttled_callback).must_respond_to :call
|
_(Rack::Attack.throttled_responder).must_respond_to :call
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue