mirror of
https://github.com/samsonjs/rack-attack.git
synced 2026-03-25 09:25:49 +00:00
125 lines
3.8 KiB
Ruby
125 lines
3.8 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require "ipaddr"
|
|
|
|
module Rack
|
|
class Attack
|
|
class Configuration
|
|
DEFAULT_BLOCKLISTED_RESPONDER = lambda { |_req| [403, { 'content-type' => 'text/plain' }, ["Forbidden\n"]] }
|
|
|
|
DEFAULT_THROTTLED_RESPONDER = lambda do |req|
|
|
if Rack::Attack.configuration.throttled_response_retry_after_header
|
|
match_data = req.env['rack.attack.match_data']
|
|
now = match_data[:epoch_time]
|
|
retry_after = match_data[:period] - (now % match_data[:period])
|
|
|
|
[429, { 'content-type' => 'text/plain', 'retry-after' => retry_after.to_s }, ["Retry later\n"]]
|
|
else
|
|
[429, { 'content-type' => 'text/plain' }, ["Retry later\n"]]
|
|
end
|
|
end
|
|
|
|
attr_reader :safelists, :blocklists, :throttles, :anonymous_blocklists, :anonymous_safelists
|
|
attr_accessor :blocklisted_responder, :throttled_responder, :throttled_response_retry_after_header
|
|
|
|
attr_reader :blocklisted_response, :throttled_response # Keeping these for backwards compatibility
|
|
|
|
def blocklisted_response=(responder)
|
|
warn "[DEPRECATION] Rack::Attack.blocklisted_response is deprecated. "\
|
|
"Please use Rack::Attack.blocklisted_responder instead."
|
|
@blocklisted_response = responder
|
|
end
|
|
|
|
def throttled_response=(responder)
|
|
warn "[DEPRECATION] Rack::Attack.throttled_response is deprecated. "\
|
|
"Please use Rack::Attack.throttled_responder instead"
|
|
@throttled_response = responder
|
|
end
|
|
|
|
def initialize
|
|
set_defaults
|
|
end
|
|
|
|
def safelist(name = nil, &block)
|
|
safelist = Safelist.new(name, &block)
|
|
|
|
if name
|
|
@safelists[name] = safelist
|
|
else
|
|
@anonymous_safelists << safelist
|
|
end
|
|
end
|
|
|
|
def blocklist(name = nil, &block)
|
|
blocklist = Blocklist.new(name, &block)
|
|
|
|
if name
|
|
@blocklists[name] = blocklist
|
|
else
|
|
@anonymous_blocklists << blocklist
|
|
end
|
|
end
|
|
|
|
def blocklist_ip(ip_address)
|
|
@anonymous_blocklists << Blocklist.new { |request| IPAddr.new(ip_address).include?(IPAddr.new(request.ip)) }
|
|
end
|
|
|
|
def safelist_ip(ip_address)
|
|
@anonymous_safelists << Safelist.new { |request| IPAddr.new(ip_address).include?(IPAddr.new(request.ip)) }
|
|
end
|
|
|
|
def throttle(name, options, &block)
|
|
@throttles[name] = Throttle.new(name, options, &block)
|
|
end
|
|
|
|
def track(name, options = {}, &block)
|
|
@tracks[name] = Track.new(name, options, &block)
|
|
end
|
|
|
|
def safelisted?(request)
|
|
@anonymous_safelists.any? { |safelist| safelist.matched_by?(request) } ||
|
|
@safelists.any? { |_name, safelist| safelist.matched_by?(request) }
|
|
end
|
|
|
|
def blocklisted?(request)
|
|
@anonymous_blocklists.any? { |blocklist| blocklist.matched_by?(request) } ||
|
|
@blocklists.any? { |_name, blocklist| blocklist.matched_by?(request) }
|
|
end
|
|
|
|
def throttled?(request)
|
|
@throttles.any? do |_name, throttle|
|
|
throttle.matched_by?(request)
|
|
end
|
|
end
|
|
|
|
def tracked?(request)
|
|
@tracks.each_value do |track|
|
|
track.matched_by?(request)
|
|
end
|
|
end
|
|
|
|
def clear_configuration
|
|
set_defaults
|
|
end
|
|
|
|
private
|
|
|
|
def set_defaults
|
|
@safelists = {}
|
|
@blocklists = {}
|
|
@throttles = {}
|
|
@tracks = {}
|
|
@anonymous_blocklists = []
|
|
@anonymous_safelists = []
|
|
@throttled_response_retry_after_header = false
|
|
|
|
@blocklisted_responder = DEFAULT_BLOCKLISTED_RESPONDER
|
|
@throttled_responder = DEFAULT_THROTTLED_RESPONDER
|
|
|
|
# Deprecated: Keeping these for backwards compatibility
|
|
@blocklisted_response = nil
|
|
@throttled_response = nil
|
|
end
|
|
end
|
|
end
|
|
end
|