mirror of
https://github.com/samsonjs/rack-attack.git
synced 2026-04-27 15:07:41 +00:00
commit
935b91815b
14 changed files with 110 additions and 83 deletions
27
.rubocop.yml
27
.rubocop.yml
|
|
@ -41,16 +41,35 @@ Style/BlockDelimiters:
|
||||||
Style/BracesAroundHashParameters:
|
Style/BracesAroundHashParameters:
|
||||||
Enabled: true
|
Enabled: true
|
||||||
|
|
||||||
|
Style/Encoding:
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
|
Style/EmptyMethod:
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
Style/FrozenStringLiteralComment:
|
Style/FrozenStringLiteralComment:
|
||||||
Enabled: true
|
Enabled: true
|
||||||
|
|
||||||
Style/HashSyntax:
|
Style/HashSyntax:
|
||||||
Enabled: true
|
Enabled: true
|
||||||
|
|
||||||
|
Style/OptionalArguments:
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
|
Style/RaiseArgs:
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
|
Style/RedundantBegin:
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
Style/RedundantFreeze:
|
Style/RedundantFreeze:
|
||||||
Enabled: true
|
Enabled: true
|
||||||
|
|
||||||
# TODO
|
Style/RedundantSelf:
|
||||||
# Remove cop disabling and fix offenses
|
Enabled: true
|
||||||
Lint/HandleExceptions:
|
|
||||||
Enabled: false
|
Style/Semicolon:
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
|
Style/SingleLineMethods:
|
||||||
|
Enabled: true
|
||||||
|
|
|
||||||
|
|
@ -30,50 +30,56 @@ class Rack::Attack
|
||||||
attr_accessor :notifier, :blocklisted_response, :throttled_response, :anonymous_blocklists, :anonymous_safelists
|
attr_accessor :notifier, :blocklisted_response, :throttled_response, :anonymous_blocklists, :anonymous_safelists
|
||||||
|
|
||||||
def safelist(name = nil, &block)
|
def safelist(name = nil, &block)
|
||||||
safelist = Safelist.new(name, block)
|
safelist = Safelist.new(name, &block)
|
||||||
|
|
||||||
if name
|
if name
|
||||||
self.safelists[name] = safelist
|
safelists[name] = safelist
|
||||||
else
|
else
|
||||||
anonymous_safelists << safelist
|
anonymous_safelists << safelist
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def blocklist(name = nil, &block)
|
def blocklist(name = nil, &block)
|
||||||
blocklist = Blocklist.new(name, block)
|
blocklist = Blocklist.new(name, &block)
|
||||||
|
|
||||||
if name
|
if name
|
||||||
self.blocklists[name] = blocklist
|
blocklists[name] = blocklist
|
||||||
else
|
else
|
||||||
anonymous_blocklists << blocklist
|
anonymous_blocklists << blocklist
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def blocklist_ip(ip_address)
|
def blocklist_ip(ip_address)
|
||||||
ip_blocklist_proc = lambda { |request| IPAddr.new(ip_address).include?(IPAddr.new(request.ip)) }
|
anonymous_blocklists << Blocklist.new { |request| IPAddr.new(ip_address).include?(IPAddr.new(request.ip)) }
|
||||||
anonymous_blocklists << Blocklist.new(nil, ip_blocklist_proc)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def safelist_ip(ip_address)
|
def safelist_ip(ip_address)
|
||||||
ip_safelist_proc = lambda { |request| IPAddr.new(ip_address).include?(IPAddr.new(request.ip)) }
|
anonymous_safelists << Safelist.new { |request| IPAddr.new(ip_address).include?(IPAddr.new(request.ip)) }
|
||||||
anonymous_safelists << Safelist.new(nil, ip_safelist_proc)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def throttle(name, options, &block)
|
def throttle(name, options, &block)
|
||||||
self.throttles[name] = Throttle.new(name, options, block)
|
throttles[name] = Throttle.new(name, options, &block)
|
||||||
end
|
end
|
||||||
|
|
||||||
def track(name, options = {}, &block)
|
def track(name, options = {}, &block)
|
||||||
self.tracks[name] = Track.new(name, options, block)
|
tracks[name] = Track.new(name, options, &block)
|
||||||
end
|
end
|
||||||
|
|
||||||
def safelists; @safelists ||= {}; end
|
def safelists
|
||||||
|
@safelists ||= {}
|
||||||
|
end
|
||||||
|
|
||||||
def blocklists; @blocklists ||= {}; end
|
def blocklists
|
||||||
|
@blocklists ||= {}
|
||||||
|
end
|
||||||
|
|
||||||
def throttles; @throttles ||= {}; end
|
def throttles
|
||||||
|
@throttles ||= {}
|
||||||
|
end
|
||||||
|
|
||||||
def tracks; @tracks ||= {}; end
|
def tracks
|
||||||
|
@tracks ||= {}
|
||||||
|
end
|
||||||
|
|
||||||
def safelisted?(request)
|
def safelisted?(request)
|
||||||
anonymous_safelists.any? { |safelist| safelist.matched_by?(request) } ||
|
anonymous_safelists.any? { |safelist| safelist.matched_by?(request) } ||
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
module Rack
|
module Rack
|
||||||
class Attack
|
class Attack
|
||||||
class Blocklist < Check
|
class Blocklist < Check
|
||||||
def initialize(name, block)
|
def initialize(name = nil, &block)
|
||||||
super
|
super
|
||||||
@type = :blocklist
|
@type = :blocklist
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ module Rack
|
||||||
class Attack
|
class Attack
|
||||||
class Check
|
class Check
|
||||||
attr_reader :name, :block, :type
|
attr_reader :name, :block, :type
|
||||||
def initialize(name, options = {}, block)
|
def initialize(name, options = {}, &block)
|
||||||
@name, @block = name, block
|
@name, @block = name, block
|
||||||
@type = options.fetch(:type, nil)
|
@type = options.fetch(:type, nil)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
module Rack
|
module Rack
|
||||||
class Attack
|
class Attack
|
||||||
class Safelist < Check
|
class Safelist < Check
|
||||||
def initialize(name, block)
|
def initialize(name = nil, &block)
|
||||||
super
|
super
|
||||||
@type = :safelist
|
@type = :safelist
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -24,31 +24,35 @@ module Rack
|
||||||
end
|
end
|
||||||
|
|
||||||
def read(key)
|
def read(key)
|
||||||
with do |client|
|
rescuing do
|
||||||
client.get(key)
|
with do |client|
|
||||||
|
client.get(key)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
rescue Dalli::DalliError
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def write(key, value, options = {})
|
def write(key, value, options = {})
|
||||||
with do |client|
|
rescuing do
|
||||||
client.set(key, value, options.fetch(:expires_in, 0), raw: true)
|
with do |client|
|
||||||
|
client.set(key, value, options.fetch(:expires_in, 0), raw: true)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
rescue Dalli::DalliError
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def increment(key, amount, options = {})
|
def increment(key, amount, options = {})
|
||||||
with do |client|
|
rescuing do
|
||||||
client.incr(key, amount, options.fetch(:expires_in, 0), amount)
|
with do |client|
|
||||||
|
client.incr(key, amount, options.fetch(:expires_in, 0), amount)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
rescue Dalli::DalliError
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete(key)
|
def delete(key)
|
||||||
with do |client|
|
rescuing do
|
||||||
client.delete(key)
|
with do |client|
|
||||||
|
client.delete(key)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
rescue Dalli::DalliError
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
@ -56,10 +60,18 @@ module Rack
|
||||||
def stub_with_if_missing
|
def stub_with_if_missing
|
||||||
unless __getobj__.respond_to?(:with)
|
unless __getobj__.respond_to?(:with)
|
||||||
class << self
|
class << self
|
||||||
def with; yield __getobj__; end
|
def with
|
||||||
|
yield __getobj__
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def rescuing
|
||||||
|
yield
|
||||||
|
rescue Dalli::DalliError
|
||||||
|
nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -19,34 +19,40 @@ module Rack
|
||||||
end
|
end
|
||||||
|
|
||||||
def read(key)
|
def read(key)
|
||||||
get(key)
|
rescuing { get(key) }
|
||||||
rescue Redis::BaseError
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def write(key, value, options = {})
|
def write(key, value, options = {})
|
||||||
if (expires_in = options[:expires_in])
|
if (expires_in = options[:expires_in])
|
||||||
setex(key, expires_in, value)
|
rescuing { setex(key, expires_in, value) }
|
||||||
else
|
else
|
||||||
set(key, value)
|
rescuing { set(key, value) }
|
||||||
end
|
end
|
||||||
rescue Redis::BaseError
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def increment(key, amount, options = {})
|
def increment(key, amount, options = {})
|
||||||
count = nil
|
count = nil
|
||||||
|
|
||||||
pipelined do
|
rescuing do
|
||||||
count = incrby(key, amount)
|
pipelined do
|
||||||
expire(key, options[:expires_in]) if options[:expires_in]
|
count = incrby(key, amount)
|
||||||
|
expire(key, options[:expires_in]) if options[:expires_in]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
count.value if count
|
count.value if count
|
||||||
rescue Redis::BaseError
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete(key, _options = {})
|
def delete(key, _options = {})
|
||||||
del(key)
|
rescuing { del(key) }
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def rescuing
|
||||||
|
yield
|
||||||
rescue Redis::BaseError
|
rescue Redis::BaseError
|
||||||
|
nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -11,17 +11,15 @@ module Rack
|
||||||
end
|
end
|
||||||
|
|
||||||
def read(key)
|
def read(key)
|
||||||
get(key, raw: true)
|
rescuing { get(key, raw: true) }
|
||||||
rescue Redis::BaseError
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def write(key, value, options = {})
|
def write(key, value, options = {})
|
||||||
if (expires_in = options[:expires_in])
|
if (expires_in = options[:expires_in])
|
||||||
setex(key, expires_in, value, raw: true)
|
rescuing { setex(key, expires_in, value, raw: true) }
|
||||||
else
|
else
|
||||||
set(key, value, raw: true)
|
rescuing { set(key, value, raw: true) }
|
||||||
end
|
end
|
||||||
rescue Redis::BaseError
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,10 @@ module Rack
|
||||||
MANDATORY_OPTIONS = [:limit, :period].freeze
|
MANDATORY_OPTIONS = [:limit, :period].freeze
|
||||||
|
|
||||||
attr_reader :name, :limit, :period, :block, :type
|
attr_reader :name, :limit, :period, :block, :type
|
||||||
def initialize(name, options, block)
|
def initialize(name, options, &block)
|
||||||
@name, @block = name, block
|
@name, @block = name, block
|
||||||
MANDATORY_OPTIONS.each do |opt|
|
MANDATORY_OPTIONS.each do |opt|
|
||||||
raise ArgumentError.new("Must pass #{opt.inspect} option") unless options[opt]
|
raise ArgumentError, "Must pass #{opt.inspect} option" unless options[opt]
|
||||||
end
|
end
|
||||||
@limit = options[:limit]
|
@limit = options[:limit]
|
||||||
@period = options[:period].respond_to?(:call) ? options[:period] : options[:period].to_i
|
@period = options[:period].respond_to?(:call) ? options[:period] : options[:period].to_i
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,13 @@ module Rack
|
||||||
class Track
|
class Track
|
||||||
attr_reader :filter
|
attr_reader :filter
|
||||||
|
|
||||||
def initialize(name, options = {}, block)
|
def initialize(name, options = {}, &block)
|
||||||
options[:type] = :track
|
options[:type] = :track
|
||||||
|
|
||||||
if options[:limit] && options[:period]
|
if options[:limit] && options[:period]
|
||||||
@filter = Throttle.new(name, options, block)
|
@filter = Throttle.new(name, options, &block)
|
||||||
else
|
else
|
||||||
@filter = Check.new(name, options, block)
|
@filter = Check.new(name, options, &block)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
# -*- encoding: utf-8 -*-
|
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
lib = File.expand_path('../lib/', __FILE__)
|
lib = File.expand_path('../lib/', __FILE__)
|
||||||
|
|
|
||||||
|
|
@ -22,11 +22,9 @@ describe "Cache store config when using allow2ban" do
|
||||||
raised_exception = nil
|
raised_exception = nil
|
||||||
|
|
||||||
fake_store_class = Class.new do
|
fake_store_class = Class.new do
|
||||||
def write(key, value)
|
def write(key, value); end
|
||||||
end
|
|
||||||
|
|
||||||
def increment(key, count, options = {})
|
def increment(key, count, options = {}); end
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
Object.stub_const(:FakeStore, fake_store_class) do
|
Object.stub_const(:FakeStore, fake_store_class) do
|
||||||
|
|
@ -44,11 +42,9 @@ describe "Cache store config when using allow2ban" do
|
||||||
raised_exception = nil
|
raised_exception = nil
|
||||||
|
|
||||||
fake_store_class = Class.new do
|
fake_store_class = Class.new do
|
||||||
def read(key)
|
def read(key); end
|
||||||
end
|
|
||||||
|
|
||||||
def increment(key, count, options = {})
|
def increment(key, count, options = {}); end
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
Object.stub_const(:FakeStore, fake_store_class) do
|
Object.stub_const(:FakeStore, fake_store_class) do
|
||||||
|
|
@ -66,11 +62,9 @@ describe "Cache store config when using allow2ban" do
|
||||||
raised_exception = nil
|
raised_exception = nil
|
||||||
|
|
||||||
fake_store_class = Class.new do
|
fake_store_class = Class.new do
|
||||||
def read(key)
|
def read(key); end
|
||||||
end
|
|
||||||
|
|
||||||
def write(key, value)
|
def write(key, value); end
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
Object.stub_const(:FakeStore, fake_store_class) do
|
Object.stub_const(:FakeStore, fake_store_class) do
|
||||||
|
|
|
||||||
|
|
@ -22,11 +22,9 @@ describe "Cache store config when using fail2ban" do
|
||||||
raised_exception = nil
|
raised_exception = nil
|
||||||
|
|
||||||
fake_store_class = Class.new do
|
fake_store_class = Class.new do
|
||||||
def write(key, value)
|
def write(key, value); end
|
||||||
end
|
|
||||||
|
|
||||||
def increment(key, count, options = {})
|
def increment(key, count, options = {}); end
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
Object.stub_const(:FakeStore, fake_store_class) do
|
Object.stub_const(:FakeStore, fake_store_class) do
|
||||||
|
|
@ -44,11 +42,9 @@ describe "Cache store config when using fail2ban" do
|
||||||
raised_exception = nil
|
raised_exception = nil
|
||||||
|
|
||||||
fake_store_class = Class.new do
|
fake_store_class = Class.new do
|
||||||
def read(key)
|
def read(key); end
|
||||||
end
|
|
||||||
|
|
||||||
def increment(key, count, options = {})
|
def increment(key, count, options = {}); end
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
Object.stub_const(:FakeStore, fake_store_class) do
|
Object.stub_const(:FakeStore, fake_store_class) do
|
||||||
|
|
@ -66,11 +62,9 @@ describe "Cache store config when using fail2ban" do
|
||||||
raised_exception = nil
|
raised_exception = nil
|
||||||
|
|
||||||
fake_store_class = Class.new do
|
fake_store_class = Class.new do
|
||||||
def read(key)
|
def read(key); end
|
||||||
end
|
|
||||||
|
|
||||||
def write(key, value)
|
def write(key, value); end
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
Object.stub_const(:FakeStore, fake_store_class) do
|
Object.stub_const(:FakeStore, fake_store_class) do
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,9 @@ if RUBY_ENGINE == "ruby"
|
||||||
end
|
end
|
||||||
|
|
||||||
def safe_require(name)
|
def safe_require(name)
|
||||||
begin
|
require name
|
||||||
require name
|
rescue LoadError
|
||||||
rescue LoadError
|
nil
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
safe_require "connection_pool"
|
safe_require "connection_pool"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue