mirror of
https://github.com/samsonjs/rack-attack.git
synced 2026-04-27 15:07:41 +00:00
feat: make blocklist/safelist name argument optional
This commit is contained in:
parent
b1558d022f
commit
6addaa11d0
3 changed files with 122 additions and 16 deletions
|
|
@ -29,24 +29,38 @@ class Rack::Attack
|
||||||
class << self
|
class << self
|
||||||
attr_accessor :notifier, :blocklisted_response, :throttled_response
|
attr_accessor :notifier, :blocklisted_response, :throttled_response
|
||||||
|
|
||||||
def safelist(name, &block)
|
def safelist(name = nil, &block)
|
||||||
self.safelists[name] = Safelist.new(name, block)
|
safelist = Safelist.new(name, block)
|
||||||
|
|
||||||
|
if name
|
||||||
|
self.safelists[name] = safelist
|
||||||
|
else
|
||||||
|
@anonymous_safelists ||= []
|
||||||
|
@anonymous_safelists << safelist
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def blocklist(name, &block)
|
def blocklist(name = nil, &block)
|
||||||
self.blocklists[name] = Blocklist.new(name, block)
|
blocklist = Blocklist.new(name, block)
|
||||||
|
|
||||||
|
if name
|
||||||
|
self.blocklists[name] = blocklist
|
||||||
|
else
|
||||||
|
@anonymous_blocklists ||= []
|
||||||
|
@anonymous_blocklists << blocklist
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def blocklist_ip(ip_address)
|
def blocklist_ip(ip_address)
|
||||||
@ip_blocklists ||= []
|
@anonymous_blocklists ||= []
|
||||||
ip_blocklist_proc = lambda { |request| IPAddr.new(ip_address).include?(IPAddr.new(request.ip)) }
|
ip_blocklist_proc = lambda { |request| IPAddr.new(ip_address).include?(IPAddr.new(request.ip)) }
|
||||||
@ip_blocklists << Blocklist.new(nil, ip_blocklist_proc)
|
@anonymous_blocklists << Blocklist.new(nil, ip_blocklist_proc)
|
||||||
end
|
end
|
||||||
|
|
||||||
def safelist_ip(ip_address)
|
def safelist_ip(ip_address)
|
||||||
@ip_safelists ||= []
|
@anonymous_safelists ||= []
|
||||||
ip_safelist_proc = lambda { |request| IPAddr.new(ip_address).include?(IPAddr.new(request.ip)) }
|
ip_safelist_proc = lambda { |request| IPAddr.new(ip_address).include?(IPAddr.new(request.ip)) }
|
||||||
@ip_safelists << Safelist.new(nil, ip_safelist_proc)
|
@anonymous_safelists << Safelist.new(nil, ip_safelist_proc)
|
||||||
end
|
end
|
||||||
|
|
||||||
def throttle(name, options, &block)
|
def throttle(name, options, &block)
|
||||||
|
|
@ -66,12 +80,12 @@ class Rack::Attack
|
||||||
def tracks; @tracks ||= {}; end
|
def tracks; @tracks ||= {}; end
|
||||||
|
|
||||||
def safelisted?(request)
|
def safelisted?(request)
|
||||||
ip_safelists.any? { |safelist| safelist.matched_by?(request) } ||
|
anonymous_safelists.any? { |safelist| safelist.matched_by?(request) } ||
|
||||||
safelists.any? { |_name, safelist| safelist.matched_by?(request) }
|
safelists.any? { |_name, safelist| safelist.matched_by?(request) }
|
||||||
end
|
end
|
||||||
|
|
||||||
def blocklisted?(request)
|
def blocklisted?(request)
|
||||||
ip_blocklists.any? { |blocklist| blocklist.matched_by?(request) } ||
|
anonymous_blocklists.any? { |blocklist| blocklist.matched_by?(request) } ||
|
||||||
blocklists.any? { |_name, blocklist| blocklist.matched_by?(request) }
|
blocklists.any? { |_name, blocklist| blocklist.matched_by?(request) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -97,8 +111,8 @@ class Rack::Attack
|
||||||
|
|
||||||
def clear_configuration
|
def clear_configuration
|
||||||
@safelists, @blocklists, @throttles, @tracks = {}, {}, {}, {}
|
@safelists, @blocklists, @throttles, @tracks = {}, {}, {}, {}
|
||||||
@ip_blocklists = []
|
@anonymous_blocklists = []
|
||||||
@ip_safelists = []
|
@anonymous_safelists = []
|
||||||
end
|
end
|
||||||
|
|
||||||
def clear!
|
def clear!
|
||||||
|
|
@ -108,12 +122,12 @@ class Rack::Attack
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def ip_blocklists
|
def anonymous_blocklists
|
||||||
@ip_blocklists ||= []
|
@anonymous_blocklists ||= []
|
||||||
end
|
end
|
||||||
|
|
||||||
def ip_safelists
|
def anonymous_safelists
|
||||||
@ip_safelists ||= []
|
@anonymous_safelists ||= []
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,46 @@
|
||||||
require_relative "../spec_helper"
|
require_relative "../spec_helper"
|
||||||
|
|
||||||
describe "#blocklist" do
|
describe "#blocklist" do
|
||||||
|
before do
|
||||||
|
Rack::Attack.blocklist do |request|
|
||||||
|
request.ip == "1.2.3.4"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "forbids request if blocklist condition is true" do
|
||||||
|
get "/", {}, "REMOTE_ADDR" => "1.2.3.4"
|
||||||
|
|
||||||
|
assert_equal 403, last_response.status
|
||||||
|
end
|
||||||
|
|
||||||
|
it "succeeds if blocklist condition is false" do
|
||||||
|
get "/", {}, "REMOTE_ADDR" => "5.6.7.8"
|
||||||
|
|
||||||
|
assert_equal 200, last_response.status
|
||||||
|
end
|
||||||
|
|
||||||
|
it "notifies when the request is blocked" do
|
||||||
|
notification_matched = nil
|
||||||
|
notification_type = nil
|
||||||
|
|
||||||
|
ActiveSupport::Notifications.subscribe("rack.attack") do |_name, _start, _finish, _id, payload|
|
||||||
|
notification_matched = payload[:request].env["rack.attack.matched"]
|
||||||
|
notification_type = payload[:request].env["rack.attack.match_type"]
|
||||||
|
end
|
||||||
|
|
||||||
|
get "/", {}, "REMOTE_ADDR" => "5.6.7.8"
|
||||||
|
|
||||||
|
assert_nil notification_matched
|
||||||
|
assert_nil notification_type
|
||||||
|
|
||||||
|
get "/", {}, "REMOTE_ADDR" => "1.2.3.4"
|
||||||
|
|
||||||
|
assert_nil notification_matched
|
||||||
|
assert_equal :blocklist, notification_type
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#blocklist with name" do
|
||||||
before do
|
before do
|
||||||
Rack::Attack.blocklist("block 1.2.3.4") do |request|
|
Rack::Attack.blocklist("block 1.2.3.4") do |request|
|
||||||
request.ip == "1.2.3.4"
|
request.ip == "1.2.3.4"
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,58 @@
|
||||||
require_relative "../spec_helper"
|
require_relative "../spec_helper"
|
||||||
|
|
||||||
describe "#safelist" do
|
describe "#safelist" do
|
||||||
|
before do
|
||||||
|
Rack::Attack.blocklist do |request|
|
||||||
|
request.ip == "1.2.3.4"
|
||||||
|
end
|
||||||
|
|
||||||
|
Rack::Attack.safelist do |request|
|
||||||
|
request.path == "/safe_space"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "forbids request if blocklist condition is true and safelist is false" do
|
||||||
|
get "/", {}, "REMOTE_ADDR" => "1.2.3.4"
|
||||||
|
|
||||||
|
assert_equal 403, last_response.status
|
||||||
|
end
|
||||||
|
|
||||||
|
it "succeeds if blocklist condition is false and safelist is false" do
|
||||||
|
get "/", {}, "REMOTE_ADDR" => "5.6.7.8"
|
||||||
|
|
||||||
|
assert_equal 200, last_response.status
|
||||||
|
end
|
||||||
|
|
||||||
|
it "succeeds request if blocklist condition is false and safelist is true" do
|
||||||
|
get "/safe_space", {}, "REMOTE_ADDR" => "5.6.7.8"
|
||||||
|
|
||||||
|
assert_equal 200, last_response.status
|
||||||
|
end
|
||||||
|
|
||||||
|
it "succeeds request if both blocklist and safelist conditions are true" do
|
||||||
|
get "/safe_space", {}, "REMOTE_ADDR" => "1.2.3.4"
|
||||||
|
|
||||||
|
assert_equal 200, last_response.status
|
||||||
|
end
|
||||||
|
|
||||||
|
it "notifies when the request is safe" do
|
||||||
|
notification_matched = nil
|
||||||
|
notification_type = nil
|
||||||
|
|
||||||
|
ActiveSupport::Notifications.subscribe("rack.attack") do |_name, _start, _finish, _id, payload|
|
||||||
|
notification_matched = payload[:request].env["rack.attack.matched"]
|
||||||
|
notification_type = payload[:request].env["rack.attack.match_type"]
|
||||||
|
end
|
||||||
|
|
||||||
|
get "/safe_space", {}, "REMOTE_ADDR" => "1.2.3.4"
|
||||||
|
|
||||||
|
assert_equal 200, last_response.status
|
||||||
|
assert_nil notification_matched
|
||||||
|
assert_equal :safelist, notification_type
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#safelist with name" do
|
||||||
before do
|
before do
|
||||||
Rack::Attack.blocklist("block 1.2.3.4") do |request|
|
Rack::Attack.blocklist("block 1.2.3.4") do |request|
|
||||||
request.ip == "1.2.3.4"
|
request.ip == "1.2.3.4"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue