From 55cb6def03574eb3b111bfd83a0b6778f5e29c75 Mon Sep 17 00:00:00 2001 From: Gonzalo Rodriguez Date: Thu, 17 Oct 2019 14:26:22 -0300 Subject: [PATCH 001/113] feat: clear custom response when clearing configuration --- lib/rack/attack/configuration.rb | 30 +++++++++++++++++++----------- spec/spec_helper.rb | 5 ----- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/lib/rack/attack/configuration.rb b/lib/rack/attack/configuration.rb index 5a15c745..b7287e77 100644 --- a/lib/rack/attack/configuration.rb +++ b/lib/rack/attack/configuration.rb @@ -3,6 +3,20 @@ module Rack class Attack class Configuration + DEFAULT_BLOCKLISTED_RESPONSE = lambda { |_env| [403, { 'Content-Type' => 'text/plain' }, ["Forbidden\n"]] } + + DEFAULT_THROTTLED_RESPONSE = lambda do |env| + if Rack::Attack.configuration.throttled_response_retry_after_header + match_data = 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_response, :throttled_response, :throttled_response_retry_after_header @@ -15,17 +29,8 @@ def initialize @anonymous_safelists = [] @throttled_response_retry_after_header = false - @blocklisted_response = lambda { |_env| [403, { 'Content-Type' => 'text/plain' }, ["Forbidden\n"]] } - @throttled_response = lambda do |env| - if throttled_response_retry_after_header - match_data = 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 + @blocklisted_response = DEFAULT_BLOCKLISTED_RESPONSE + @throttled_response = DEFAULT_THROTTLED_RESPONSE end def safelist(name = nil, &block) @@ -94,6 +99,9 @@ def clear_configuration @anonymous_blocklists = [] @anonymous_safelists = [] @throttled_response_retry_after_header = false + + @blocklisted_response = DEFAULT_BLOCKLISTED_RESPONSE + @throttled_response = DEFAULT_THROTTLED_RESPONSE end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index a1728cb6..9649be81 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -30,16 +30,11 @@ class MiniTest::Spec before do Rails.cache = nil - @_original_throttled_response = Rack::Attack.throttled_response - @_original_blocklisted_response = Rack::Attack.blocklisted_response end after do Rack::Attack.clear_configuration Rack::Attack.instance_variable_set(:@cache, nil) - - Rack::Attack.throttled_response = @_original_throttled_response - Rack::Attack.blocklisted_response = @_original_blocklisted_response end def app From 0188a90ab26c01a859f093318bab9aecc348d68c Mon Sep 17 00:00:00 2001 From: Gonzalo Rodriguez Date: Thu, 17 Oct 2019 14:27:32 -0300 Subject: [PATCH 002/113] refactor: DRY setting config defaults --- lib/rack/attack/configuration.rb | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/lib/rack/attack/configuration.rb b/lib/rack/attack/configuration.rb index b7287e77..160886ed 100644 --- a/lib/rack/attack/configuration.rb +++ b/lib/rack/attack/configuration.rb @@ -21,16 +21,7 @@ class Configuration attr_accessor :blocklisted_response, :throttled_response, :throttled_response_retry_after_header def initialize - @safelists = {} - @blocklists = {} - @throttles = {} - @tracks = {} - @anonymous_blocklists = [] - @anonymous_safelists = [] - @throttled_response_retry_after_header = false - - @blocklisted_response = DEFAULT_BLOCKLISTED_RESPONSE - @throttled_response = DEFAULT_THROTTLED_RESPONSE + set_defaults end def safelist(name = nil, &block) @@ -92,6 +83,12 @@ def tracked?(request) end def clear_configuration + set_defaults + end + + private + + def set_defaults @safelists = {} @blocklists = {} @throttles = {} From 56361ab56b96c909d6796f05e05bf224dd05d050 Mon Sep 17 00:00:00 2001 From: fatkodima Date: Fri, 18 Oct 2019 01:23:51 +0300 Subject: [PATCH 003/113] Remove support for ruby 2.3 --- .rubocop.yml | 2 +- .travis.yml | 3 --- rack-attack.gemspec | 2 +- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 28f8502a..478b1fc1 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -6,7 +6,7 @@ inherit_mode: - Exclude AllCops: - TargetRubyVersion: 2.3 + TargetRubyVersion: 2.4 DisabledByDefault: true Exclude: - "examples/instrumentation.rb" diff --git a/.travis.yml b/.travis.yml index ddfaf94a..52a9bb9d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,6 @@ rvm: - 2.6.5 - 2.5.7 - 2.4.9 - - 2.3.8 before_install: - gem update --system @@ -35,8 +34,6 @@ matrix: exclude: - gemfile: gemfiles/rails_6_0.gemfile rvm: 2.4.9 - - gemfile: gemfiles/rails_6_0.gemfile - rvm: 2.3.8 fast_finish: true services: diff --git a/rack-attack.gemspec b/rack-attack.gemspec index 0bebfa36..65a74444 100644 --- a/rack-attack.gemspec +++ b/rack-attack.gemspec @@ -27,7 +27,7 @@ Gem::Specification.new do |s| "source_code_uri" => "https://github.com/kickstarter/rack-attack" } - s.required_ruby_version = '>= 2.3' + s.required_ruby_version = '>= 2.4' s.add_runtime_dependency 'rack', ">= 1.0", "< 3" From 20ec4d31db201a7f6fc37167c1bbba871582d6ea Mon Sep 17 00:00:00 2001 From: fatkodima Date: Fri, 18 Oct 2019 02:31:05 +0300 Subject: [PATCH 004/113] Do not rescue all errors for redis backed stores --- .../store_proxy/redis_cache_store_proxy.rb | 28 ++++----------- lib/rack/attack/store_proxy/redis_proxy.rb | 2 +- spec/integration/offline_spec.rb | 35 ++++++++++++++++++- 3 files changed, 41 insertions(+), 24 deletions(-) diff --git a/lib/rack/attack/store_proxy/redis_cache_store_proxy.rb b/lib/rack/attack/store_proxy/redis_cache_store_proxy.rb index f4081bee..78807f86 100644 --- a/lib/rack/attack/store_proxy/redis_cache_store_proxy.rb +++ b/lib/rack/attack/store_proxy/redis_cache_store_proxy.rb @@ -15,33 +15,17 @@ def increment(name, amount = 1, options = {}) # # So in order to workaround this we use RedisCacheStore#write (which sets expiration) to initialize # the counter. After that we continue using the original RedisCacheStore#increment. - rescuing do - if options[:expires_in] && !read(name) - write(name, amount, options) + if options[:expires_in] && !read(name) + write(name, amount, options) - amount - else - super - end + amount + else + super end end - def read(*_args) - rescuing { super } - end - def write(name, value, options = {}) - rescuing do - super(name, value, options.merge!(raw: true)) - end - end - - private - - def rescuing - yield - rescue Redis::BaseError - nil + super(name, value, options.merge!(raw: true)) end end end diff --git a/lib/rack/attack/store_proxy/redis_proxy.rb b/lib/rack/attack/store_proxy/redis_proxy.rb index 1bcb4b48..d4e6f3a1 100644 --- a/lib/rack/attack/store_proxy/redis_proxy.rb +++ b/lib/rack/attack/store_proxy/redis_proxy.rb @@ -47,7 +47,7 @@ def delete(key, _options = {}) def rescuing yield - rescue Redis::BaseError + rescue Redis::BaseConnectionError nil end end diff --git a/spec/integration/offline_spec.rb b/spec/integration/offline_spec.rb index f9bacab8..d4960633 100644 --- a/spec/integration/offline_spec.rb +++ b/spec/integration/offline_spec.rb @@ -13,7 +13,11 @@ end it 'should count' do - @cache.send(:do_count, 'rack::attack::cache-test-key', 1) + @cache.count('cache-test-key', 1) + end + + it 'should delete' do + @cache.delete('cache-test-key') end end @@ -29,6 +33,18 @@ end end +if defined?(Redis) && defined?(ActiveSupport::Cache::RedisCacheStore) && Redis::VERSION >= '4' + describe 'when Redis is offline' do + include OfflineExamples + + before do + @cache = Rack::Attack::Cache.new + # Use presumably unused port for Redis client + @cache.store = ActiveSupport::Cache::RedisCacheStore.new(host: '127.0.0.1', port: 3333) + end + end +end + if defined?(::Dalli) describe 'when Memcached is offline' do include OfflineExamples @@ -46,6 +62,23 @@ end end +if defined?(::Dalli) && defined?(::ActiveSupport::Cache::MemCacheStore) + describe 'when Memcached is offline' do + include OfflineExamples + + before do + Dalli.logger.level = Logger::FATAL + + @cache = Rack::Attack::Cache.new + @cache.store = ActiveSupport::Cache::MemCacheStore.new('127.0.0.1:22122') + end + + after do + Dalli.logger.level = Logger::INFO + end + end +end + if defined?(Redis) describe 'when Redis is offline' do include OfflineExamples From 1f216e12e78171f7d8755be2113b72dad270efe7 Mon Sep 17 00:00:00 2001 From: Gonzalo Rodriguez Date: Fri, 18 Oct 2019 17:29:58 -0300 Subject: [PATCH 005/113] refactor: move require statement to correct file --- lib/rack/attack.rb | 1 - lib/rack/attack/configuration.rb | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/rack/attack.rb b/lib/rack/attack.rb index f533ca6d..1a5c50d6 100644 --- a/lib/rack/attack.rb +++ b/lib/rack/attack.rb @@ -6,7 +6,6 @@ require 'rack/attack/configuration' require 'rack/attack/path_normalizer' require 'rack/attack/request' -require "ipaddr" require 'rack/attack/railtie' if defined?(::Rails) diff --git a/lib/rack/attack/configuration.rb b/lib/rack/attack/configuration.rb index 160886ed..f2d0189b 100644 --- a/lib/rack/attack/configuration.rb +++ b/lib/rack/attack/configuration.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require "ipaddr" + module Rack class Attack class Configuration From a3dff705bb32244f161d9a7728ea8993dd2b0241 Mon Sep 17 00:00:00 2001 From: fatkodima Date: Fri, 11 Oct 2019 16:17:07 +0300 Subject: [PATCH 006/113] Make discriminators case-insensitive by default --- lib/rack/attack.rb | 5 +++- lib/rack/attack/throttle.rb | 11 ++++++-- spec/rack_attack_throttle_spec.rb | 44 +++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 3 deletions(-) diff --git a/lib/rack/attack.rb b/lib/rack/attack.rb index 1a5c50d6..a2342f5b 100644 --- a/lib/rack/attack.rb +++ b/lib/rack/attack.rb @@ -31,7 +31,7 @@ class MissingStoreError < Error; end autoload :Allow2Ban, 'rack/attack/allow2ban' class << self - attr_accessor :enabled, :notifier + attr_accessor :enabled, :notifier, :discriminator_normalizer attr_reader :configuration def instrument(request) @@ -79,6 +79,9 @@ def clear! # Set defaults @enabled = true @notifier = ActiveSupport::Notifications if defined?(ActiveSupport::Notifications) + @discriminator_normalizer = lambda do |discriminator| + discriminator.to_s.strip.downcase + end @configuration = Configuration.new attr_reader :configuration diff --git a/lib/rack/attack/throttle.rb b/lib/rack/attack/throttle.rb index 3b80d9e7..a1e21a7c 100644 --- a/lib/rack/attack/throttle.rb +++ b/lib/rack/attack/throttle.rb @@ -22,8 +22,7 @@ def cache end def matched_by?(request) - discriminator = block.call(request) - + discriminator = discriminator_for(request) return false unless discriminator current_period = period_for(request) @@ -49,6 +48,14 @@ def matched_by?(request) private + def discriminator_for(request) + discriminator = block.call(request) + if discriminator && Rack::Attack.discriminator_normalizer + discriminator = Rack::Attack.discriminator_normalizer.call(discriminator) + end + discriminator + end + def period_for(request) period.respond_to?(:call) ? period.call(request) : period end diff --git a/spec/rack_attack_throttle_spec.rb b/spec/rack_attack_throttle_spec.rb index dcb1e414..feb599cb 100644 --- a/spec/rack_attack_throttle_spec.rb +++ b/spec/rack_attack_throttle_spec.rb @@ -144,3 +144,47 @@ end end end + +describe 'Rack::Attack.throttle with discriminator_normalizer' do + before do + @period = 60 + @emails = [ + "person@example.com", + "PERSON@example.com ", + " person@example.com\r\n ", + ] + Rack::Attack.cache.store = ActiveSupport::Cache::MemoryStore.new + Rack::Attack.throttle('logins/email', limit: 4, period: @period) do |req| + if req.path == '/login' && req.post? + req.params['email'] + end + end + end + + it 'should not differentiate requests when discriminator_normalizer is enabled' do + post_logins + key = "rack::attack:#{Time.now.to_i / @period}:logins/email:person@example.com" + _(Rack::Attack.cache.store.read(key)).must_equal 3 + end + + it 'should differentiate requests when discriminator_normalizer is disabled' do + begin + prev = Rack::Attack.discriminator_normalizer + Rack::Attack.discriminator_normalizer = nil + + post_logins + @emails.each do |email| + key = "rack::attack:#{Time.now.to_i / @period}:logins/email:#{email}" + _(Rack::Attack.cache.store.read(key)).must_equal 1 + end + ensure + Rack::Attack.discriminator_normalizer = prev + end + end + + def post_logins + @emails.each do |email| + post '/login', email: email + end + end +end From 18e637aea8dd4c7b19b68fb58c945480376a682a Mon Sep 17 00:00:00 2001 From: fatkodima Date: Thu, 10 Oct 2019 13:32:04 +0300 Subject: [PATCH 007/113] Allow to reset state between tests --- lib/rack/attack.rb | 5 +++++ lib/rack/attack/cache.rb | 11 +++++++++++ lib/rack/attack/store_proxy/redis_proxy.rb | 13 +++++++++++++ spec/rack_attack_spec.rb | 22 ++++++++++++++++++++++ 4 files changed, 51 insertions(+) diff --git a/lib/rack/attack.rb b/lib/rack/attack.rb index a2342f5b..740df4f6 100644 --- a/lib/rack/attack.rb +++ b/lib/rack/attack.rb @@ -14,6 +14,7 @@ class Attack class Error < StandardError; end class MisconfiguredStoreError < Error; end class MissingStoreError < Error; end + class IncompatibleStoreError < Error; end autoload :Check, 'rack/attack/check' autoload :Throttle, 'rack/attack/throttle' @@ -53,6 +54,10 @@ def clear! @configuration.clear_configuration end + def reset! + cache.reset! + end + extend Forwardable def_delegators( :@configuration, diff --git a/lib/rack/attack/cache.rb b/lib/rack/attack/cache.rb index cfa2efa4..0defa646 100644 --- a/lib/rack/attack/cache.rb +++ b/lib/rack/attack/cache.rb @@ -41,6 +41,17 @@ def delete(unprefixed_key) store.delete("#{prefix}:#{unprefixed_key}") end + def reset! + if store.respond_to?(:delete_matched) + store.delete_matched("#{prefix}*") + else + raise( + Rack::Attack::IncompatibleStoreError, + "Configured store #{store.class.name} doesn't respond to #delete_matched method" + ) + end + end + private def key_and_expiry(unprefixed_key, period) diff --git a/lib/rack/attack/store_proxy/redis_proxy.rb b/lib/rack/attack/store_proxy/redis_proxy.rb index d4e6f3a1..e51b3e92 100644 --- a/lib/rack/attack/store_proxy/redis_proxy.rb +++ b/lib/rack/attack/store_proxy/redis_proxy.rb @@ -43,6 +43,19 @@ def delete(key, _options = {}) rescuing { del(key) } end + def delete_matched(matcher, _options = nil) + cursor = "0" + + rescuing do + # Fetch keys in batches using SCAN to avoid blocking the Redis server. + loop do + cursor, keys = scan(cursor, match: matcher, count: 1000) + del(*keys) unless keys.empty? + break if cursor == "0" + end + end + end + private def rescuing diff --git a/spec/rack_attack_spec.rb b/spec/rack_attack_spec.rb index 647a08d7..d9bab4d7 100644 --- a/spec/rack_attack_spec.rb +++ b/spec/rack_attack_spec.rb @@ -99,4 +99,26 @@ end end end + + describe 'reset!' do + it 'raises an error when is not supported by cache store' do + Rack::Attack.cache.store = Class.new + assert_raises(Rack::Attack::IncompatibleStoreError) do + Rack::Attack.reset! + end + end + + if defined?(Redis) + it 'should delete rack attack keys' do + redis = Redis.new + redis.set('key', 'value') + redis.set("#{Rack::Attack.cache.prefix}::key", 'value') + Rack::Attack.cache.store = redis + Rack::Attack.reset! + + _(redis.get('key')).must_equal 'value' + _(redis.get("#{Rack::Attack.cache.prefix}::key")).must_be_nil + end + end + end end From edaa6c6a918f596a19af333f48a0746a65f095e7 Mon Sep 17 00:00:00 2001 From: fatkodima Date: Thu, 24 Oct 2019 02:48:32 +0300 Subject: [PATCH 008/113] Make store proxies lookup dynamic --- lib/rack/attack.rb | 13 +++++---- lib/rack/attack/base_proxy.rb | 27 +++++++++++++++++++ lib/rack/attack/cache.rb | 7 ++++- lib/rack/attack/store_proxy.rb | 21 --------------- .../active_support_redis_store_proxy.rb | 4 +-- lib/rack/attack/store_proxy/dalli_proxy.rb | 4 +-- .../store_proxy/mem_cache_store_proxy.rb | 4 +-- .../store_proxy/redis_cache_store_proxy.rb | 4 +-- lib/rack/attack/store_proxy/redis_proxy.rb | 6 ++--- .../attack/store_proxy/redis_store_proxy.rb | 2 +- 10 files changed, 51 insertions(+), 41 deletions(-) create mode 100644 lib/rack/attack/base_proxy.rb delete mode 100644 lib/rack/attack/store_proxy.rb diff --git a/lib/rack/attack.rb b/lib/rack/attack.rb index 740df4f6..ad668756 100644 --- a/lib/rack/attack.rb +++ b/lib/rack/attack.rb @@ -6,6 +6,12 @@ require 'rack/attack/configuration' require 'rack/attack/path_normalizer' require 'rack/attack/request' +require 'rack/attack/store_proxy/dalli_proxy' +require 'rack/attack/store_proxy/mem_cache_store_proxy' +require 'rack/attack/store_proxy/redis_proxy' +require 'rack/attack/store_proxy/redis_store_proxy' +require 'rack/attack/store_proxy/redis_cache_store_proxy' +require 'rack/attack/store_proxy/active_support_redis_store_proxy' require 'rack/attack/railtie' if defined?(::Rails) @@ -21,13 +27,6 @@ class IncompatibleStoreError < Error; end autoload :Safelist, 'rack/attack/safelist' autoload :Blocklist, 'rack/attack/blocklist' autoload :Track, 'rack/attack/track' - autoload :StoreProxy, 'rack/attack/store_proxy' - autoload :DalliProxy, 'rack/attack/store_proxy/dalli_proxy' - autoload :MemCacheStoreProxy, 'rack/attack/store_proxy/mem_cache_store_proxy' - autoload :RedisProxy, 'rack/attack/store_proxy/redis_proxy' - autoload :RedisStoreProxy, 'rack/attack/store_proxy/redis_store_proxy' - autoload :RedisCacheStoreProxy, 'rack/attack/store_proxy/redis_cache_store_proxy' - autoload :ActiveSupportRedisStoreProxy, 'rack/attack/store_proxy/active_support_redis_store_proxy' autoload :Fail2Ban, 'rack/attack/fail2ban' autoload :Allow2Ban, 'rack/attack/allow2ban' diff --git a/lib/rack/attack/base_proxy.rb b/lib/rack/attack/base_proxy.rb new file mode 100644 index 00000000..3e3c28af --- /dev/null +++ b/lib/rack/attack/base_proxy.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'delegate' + +module Rack + class Attack + class BaseProxy < SimpleDelegator + class << self + def proxies + @@proxies ||= [] + end + + def inherited(klass) + proxies << klass + end + + def lookup(store) + proxies.find { |proxy| proxy.handle?(store) } + end + + def handle?(_store) + raise NotImplementedError + end + end + end + end +end diff --git a/lib/rack/attack/cache.rb b/lib/rack/attack/cache.rb index 0defa646..f27259e1 100644 --- a/lib/rack/attack/cache.rb +++ b/lib/rack/attack/cache.rb @@ -13,7 +13,12 @@ def initialize attr_reader :store def store=(store) - @store = StoreProxy.build(store) + @store = + if (proxy = BaseProxy.lookup(store)) + proxy.new(store) + else + store + end end def count(unprefixed_key, period) diff --git a/lib/rack/attack/store_proxy.rb b/lib/rack/attack/store_proxy.rb deleted file mode 100644 index 55c63e21..00000000 --- a/lib/rack/attack/store_proxy.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -module Rack - class Attack - module StoreProxy - PROXIES = [ - DalliProxy, - MemCacheStoreProxy, - RedisStoreProxy, - RedisProxy, - RedisCacheStoreProxy, - ActiveSupportRedisStoreProxy - ].freeze - - def self.build(store) - klass = PROXIES.find { |proxy| proxy.handle?(store) } - klass ? klass.new(store) : store - end - end - end -end diff --git a/lib/rack/attack/store_proxy/active_support_redis_store_proxy.rb b/lib/rack/attack/store_proxy/active_support_redis_store_proxy.rb index 68f0326f..d2c0e3b2 100644 --- a/lib/rack/attack/store_proxy/active_support_redis_store_proxy.rb +++ b/lib/rack/attack/store_proxy/active_support_redis_store_proxy.rb @@ -1,11 +1,11 @@ # frozen_string_literal: true -require 'delegate' +require 'rack/attack/base_proxy' module Rack class Attack module StoreProxy - class ActiveSupportRedisStoreProxy < SimpleDelegator + class ActiveSupportRedisStoreProxy < BaseProxy def self.handle?(store) defined?(::Redis) && defined?(::ActiveSupport::Cache::RedisStore) && diff --git a/lib/rack/attack/store_proxy/dalli_proxy.rb b/lib/rack/attack/store_proxy/dalli_proxy.rb index 360e2198..48198bb2 100644 --- a/lib/rack/attack/store_proxy/dalli_proxy.rb +++ b/lib/rack/attack/store_proxy/dalli_proxy.rb @@ -1,11 +1,11 @@ # frozen_string_literal: true -require 'delegate' +require 'rack/attack/base_proxy' module Rack class Attack module StoreProxy - class DalliProxy < SimpleDelegator + class DalliProxy < BaseProxy def self.handle?(store) return false unless defined?(::Dalli) diff --git a/lib/rack/attack/store_proxy/mem_cache_store_proxy.rb b/lib/rack/attack/store_proxy/mem_cache_store_proxy.rb index f8c036c9..122037f0 100644 --- a/lib/rack/attack/store_proxy/mem_cache_store_proxy.rb +++ b/lib/rack/attack/store_proxy/mem_cache_store_proxy.rb @@ -1,11 +1,11 @@ # frozen_string_literal: true -require 'delegate' +require 'rack/attack/base_proxy' module Rack class Attack module StoreProxy - class MemCacheStoreProxy < SimpleDelegator + class MemCacheStoreProxy < BaseProxy def self.handle?(store) defined?(::Dalli) && defined?(::ActiveSupport::Cache::MemCacheStore) && diff --git a/lib/rack/attack/store_proxy/redis_cache_store_proxy.rb b/lib/rack/attack/store_proxy/redis_cache_store_proxy.rb index 78807f86..380215f7 100644 --- a/lib/rack/attack/store_proxy/redis_cache_store_proxy.rb +++ b/lib/rack/attack/store_proxy/redis_cache_store_proxy.rb @@ -1,11 +1,11 @@ # frozen_string_literal: true -require 'delegate' +require 'rack/attack/base_proxy' module Rack class Attack module StoreProxy - class RedisCacheStoreProxy < SimpleDelegator + class RedisCacheStoreProxy < BaseProxy def self.handle?(store) store.class.name == "ActiveSupport::Cache::RedisCacheStore" end diff --git a/lib/rack/attack/store_proxy/redis_proxy.rb b/lib/rack/attack/store_proxy/redis_proxy.rb index e51b3e92..3127de63 100644 --- a/lib/rack/attack/store_proxy/redis_proxy.rb +++ b/lib/rack/attack/store_proxy/redis_proxy.rb @@ -1,11 +1,11 @@ # frozen_string_literal: true -require 'delegate' +require 'rack/attack/base_proxy' module Rack class Attack module StoreProxy - class RedisProxy < SimpleDelegator + class RedisProxy < BaseProxy def initialize(*args) if Gem::Version.new(Redis::VERSION) < Gem::Version.new("3") warn 'RackAttack requires Redis gem >= 3.0.0.' @@ -15,7 +15,7 @@ def initialize(*args) end def self.handle?(store) - defined?(::Redis) && store.is_a?(::Redis) + defined?(::Redis) && store.class == ::Redis end def read(key) diff --git a/lib/rack/attack/store_proxy/redis_store_proxy.rb b/lib/rack/attack/store_proxy/redis_store_proxy.rb index 6be54128..28557bcb 100644 --- a/lib/rack/attack/store_proxy/redis_store_proxy.rb +++ b/lib/rack/attack/store_proxy/redis_store_proxy.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'delegate' +require 'rack/attack/store_proxy/redis_proxy' module Rack class Attack From e3056e737f68a9e15df0ec578d2d7f5e6dec9100 Mon Sep 17 00:00:00 2001 From: Gonzalo Rodriguez Date: Tue, 29 Oct 2019 15:45:26 -0300 Subject: [PATCH 009/113] fix: avoid unintended effects on load_config_initializers and other gems load order Because of the sort algorithm rails uses to satisfy `after` and `before` constraints, gems can have unintended effects on others. See https://github.com/rails/rails/commit/0a120a818d413c64ff9867125f0b03788fc306f8 Prefer making rack-attack middleware idempotent instead of relying on the load order and the contents of the middleware stack too much. closes #452 closes #456 --- lib/rack/attack.rb | 3 ++- lib/rack/attack/railtie.rb | 12 ++---------- spec/acceptance/rails_middleware_spec.rb | 6 ------ spec/spec_helper.rb | 2 ++ 4 files changed, 6 insertions(+), 17 deletions(-) diff --git a/lib/rack/attack.rb b/lib/rack/attack.rb index b33092d4..add91c7e 100644 --- a/lib/rack/attack.rb +++ b/lib/rack/attack.rb @@ -153,8 +153,9 @@ def initialize(app) end def call(env) - return @app.call(env) unless self.class.enabled + return @app.call(env) if !self.class.enabled || env["rack.attack.called"] + env["rack.attack.called"] = true env['PATH_INFO'] = PathNormalizer.normalize_path(env['PATH_INFO']) request = Rack::Attack::Request.new(env) diff --git a/lib/rack/attack/railtie.rb b/lib/rack/attack/railtie.rb index ceed9f7d..398ac6c2 100644 --- a/lib/rack/attack/railtie.rb +++ b/lib/rack/attack/railtie.rb @@ -3,17 +3,9 @@ module Rack class Attack class Railtie < ::Rails::Railtie - initializer 'rack.attack.middleware', after: :load_config_initializers, before: :build_middleware_stack do |app| + initializer "rack-attack.middleware" do |app| if Gem::Version.new(::Rails::VERSION::STRING) >= Gem::Version.new("5.1") - middlewares = app.config.middleware - operations = middlewares.send(:operations) + middlewares.send(:delete_operations) - - use_middleware = operations.none? do |operation| - middleware = operation[1] - middleware.include?(Rack::Attack) - end - - middlewares.use(Rack::Attack) if use_middleware + app.middleware.use(Rack::Attack) end end end diff --git a/spec/acceptance/rails_middleware_spec.rb b/spec/acceptance/rails_middleware_spec.rb index 2c69b7e1..8e7b014a 100644 --- a/spec/acceptance/rails_middleware_spec.rb +++ b/spec/acceptance/rails_middleware_spec.rb @@ -18,12 +18,6 @@ assert_equal 1, @app.middleware.count(Rack::Attack) end - it "is not added when it was added explicitly" do - @app.config.middleware.use(Rack::Attack) - @app.initialize! - assert_equal 1, @app.middleware.count(Rack::Attack) - end - it "is not added when it was explicitly deleted" do @app.config.middleware.delete(Rack::Attack) @app.initialize! diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index a1728cb6..0389bd01 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -46,6 +46,8 @@ def app Rack::Builder.new do # Use Rack::Lint to test that rack-attack is complying with the rack spec use Rack::Lint + # Intentionally added twice to test idempotence property + use Rack::Attack use Rack::Attack use Rack::Lint From 3654293085f6ac0401d0046cb32529ea0ac5519d Mon Sep 17 00:00:00 2001 From: Gonzalo Rodriguez Date: Wed, 30 Oct 2019 15:26:58 -0300 Subject: [PATCH 010/113] Bump gem version to v6.2.1 --- CHANGELOG.md | 7 +++++++ lib/rack/attack/version.rb | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f125d166..a9d7cd3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to this project will be documented in this file. +## [6.2.1] - 2019-10-30 + +### Fixed + +- Remove unintended side-effects on Rails app initialization order. It was potentially affecting the order of `config/initializers/*` in respect to gems initializers (#457) + ## [6.2.0] - 2019-10-12 ### Added @@ -189,6 +195,7 @@ so your custom code is less prone to race conditions ([#282](https://github.com/ - Remove unused variable - Extract mandatory options to constants +[6.2.1]: https://github.com/kickstarter/rack-attack/compare/v6.2.0...v6.2.1/ [6.2.0]: https://github.com/kickstarter/rack-attack/compare/v6.1.0...v6.2.0/ [6.1.0]: https://github.com/kickstarter/rack-attack/compare/v6.0.0...v6.1.0/ [6.0.0]: https://github.com/kickstarter/rack-attack/compare/v5.4.2...v6.0.0/ diff --git a/lib/rack/attack/version.rb b/lib/rack/attack/version.rb index 715c7836..d6ea4e72 100644 --- a/lib/rack/attack/version.rb +++ b/lib/rack/attack/version.rb @@ -2,6 +2,6 @@ module Rack class Attack - VERSION = '6.2.0' + VERSION = '6.2.1' end end From 2527b37221c20a1aa5ce29940cccd29d166b69c4 Mon Sep 17 00:00:00 2001 From: fatkodima Date: Thu, 31 Oct 2019 14:35:29 +0200 Subject: [PATCH 011/113] Auto include middleware for older railses --- README.md | 7 +------ lib/rack/attack/railtie.rb | 4 +--- spec/acceptance/rails_middleware_spec.rb | 21 +++------------------ 3 files changed, 5 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 866cf354..22f89e46 100644 --- a/README.md +++ b/README.md @@ -71,12 +71,7 @@ Or install it yourself as: Then tell your ruby web application to use rack-attack as a middleware. -a) For __rails__ applications with versions >= 5.1 it is used by default. For older rails versions you should enable it explicitly: -```ruby -# In config/application.rb - -config.middleware.use Rack::Attack -``` +a) For __rails__ applications it is used by default. You can disable it permanently (like for specific environment) or temporarily (can be useful for specific test cases) by writing: diff --git a/lib/rack/attack/railtie.rb b/lib/rack/attack/railtie.rb index 398ac6c2..234e1267 100644 --- a/lib/rack/attack/railtie.rb +++ b/lib/rack/attack/railtie.rb @@ -4,9 +4,7 @@ module Rack class Attack class Railtie < ::Rails::Railtie initializer "rack-attack.middleware" do |app| - if Gem::Version.new(::Rails::VERSION::STRING) >= Gem::Version.new("5.1") - app.middleware.use(Rack::Attack) - end + app.middleware.use(Rack::Attack) end end end diff --git a/spec/acceptance/rails_middleware_spec.rb b/spec/acceptance/rails_middleware_spec.rb index 8e7b014a..0e14e895 100644 --- a/spec/acceptance/rails_middleware_spec.rb +++ b/spec/acceptance/rails_middleware_spec.rb @@ -12,24 +12,9 @@ end end - if Gem::Version.new(Rails::VERSION::STRING) >= Gem::Version.new("5.1") - it "is used by default" do - @app.initialize! - assert_equal 1, @app.middleware.count(Rack::Attack) - end - - it "is not added when it was explicitly deleted" do - @app.config.middleware.delete(Rack::Attack) - @app.initialize! - refute @app.middleware.include?(Rack::Attack) - end - end - - if Gem::Version.new(Rails::VERSION::STRING) < Gem::Version.new("5.1") - it "is not used by default" do - @app.initialize! - assert_equal 0, @app.middleware.count(Rack::Attack) - end + it "is used by default" do + @app.initialize! + assert @app.middleware.include?(Rack::Attack) end end end From 58b4042e3576bd704ea46b6808f4a4822db71464 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Spie=C3=9F?= Date: Thu, 12 Dec 2019 18:13:13 +0100 Subject: [PATCH 012/113] Use gender-neutral pronoun in Readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 22f89e46..85ea134a 100644 --- a/README.md +++ b/README.md @@ -337,7 +337,7 @@ end While Rack::Attack's primary focus is minimizing harm from abusive clients, it can also be used to return rate limit data that's helpful for well-behaved clients. -If you want to return to user how many seconds to wait until he can start sending requests again, this can be done through enabling `Retry-After` header: +If you want to return to user how many seconds to wait until they can start sending requests again, this can be done through enabling `Retry-After` header: ```ruby Rack::Attack.throttled_response_retry_after_header = true ``` From f79759717a86c31eae80bfaefb4fe7d6ff4d1478 Mon Sep 17 00:00:00 2001 From: Nikolay Rys Date: Tue, 17 Dec 2019 18:44:08 +0100 Subject: [PATCH 013/113] Feature proposal: Request instead of Env in callbacks (#419) feat: allow easy access to the request object in the callbacks --- README.md | 15 +++++---- docs/development.md | 4 +++ lib/rack/attack.rb | 12 +++++-- lib/rack/attack/configuration.rb | 32 +++++++++++++++---- .../customizing_blocked_response_spec.rb | 23 ++++++++++--- .../customizing_throttled_response_spec.rb | 31 ++++++++++++++---- spec/rack_attack_spec.rb | 8 ++--- 7 files changed, 96 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 85ea134a..21a4c399 100644 --- a/README.md +++ b/README.md @@ -313,18 +313,18 @@ Note that `Rack::Attack.cache` is only used for throttling, allow2ban and fail2b 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). ```ruby -Rack::Attack.blocklisted_response = lambda do |env| +Rack::Attack.blocklisted_callback = lambda do |request| # Using 503 because it may make attacker think that they have successfully # DOSed the site. Rack::Attack returns 403 for blocklists by default [ 503, {}, ['Blocked']] end -Rack::Attack.throttled_response = lambda do |env| +Rack::Attack.throttled_callback = lambda do |request| # NB: you have access to the name and other data about the matched throttle - # env['rack.attack.matched'], - # env['rack.attack.match_type'], - # env['rack.attack.match_data'], - # env['rack.attack.match_discriminator'] + # request.env['rack.attack.matched'], + # request.env['rack.attack.match_type'], + # request.env['rack.attack.match_data'], + # request.env['rack.attack.match_discriminator'] # Using 503 because it may make attacker think that they have successfully # DOSed the site. Rack::Attack returns 429 for throttling by default @@ -411,9 +411,10 @@ def call(env) if safelisted?(req) @app.call(env) elsif blocklisted?(req) - self.class.blocklisted_response.call(env) + self.class.blocklisted_callback.call(req) elsif throttled?(req) self.class.throttled_response.call(env) + self.class.throttled_callback.call(req) else tracked?(req) @app.call(env) diff --git a/docs/development.md b/docs/development.md index c3fec28f..b32cecf8 100644 --- a/docs/development.md +++ b/docs/development.md @@ -8,6 +8,10 @@ Install dependencies by running $ bundle install +Install test dependencies by running: + + $ bundle exec appraisal install + Then run the test suite by running $ bundle exec appraisal rake test diff --git a/lib/rack/attack.rb b/lib/rack/attack.rb index afb5b088..d29bf687 100644 --- a/lib/rack/attack.rb +++ b/lib/rack/attack.rb @@ -66,6 +66,10 @@ def reset! :safelist_ip, :throttle, :track, + :throttled_callback, + :throttled_callback=, + :blocklisted_callback, + :blocklisted_callback=, :blocklisted_response, :blocklisted_response=, :throttled_response, @@ -105,9 +109,13 @@ def call(env) if configuration.safelisted?(request) @app.call(env) elsif configuration.blocklisted?(request) - configuration.blocklisted_response.call(env) + # Deprecated: Keeping blocklisted_response for backwards compatibility + configuration.blocklisted_response ? configuration.blocklisted_response.call(env) : + configuration.blocklisted_callback.call(request) elsif configuration.throttled?(request) - configuration.throttled_response.call(env) + # Deprecated: Keeping throttled_response for backwards compatibility + configuration.throttled_response ? configuration.throttled_response.call(env) : + configuration.throttled_callback.call(request) else configuration.tracked?(request) @app.call(env) diff --git a/lib/rack/attack/configuration.rb b/lib/rack/attack/configuration.rb index f2d0189b..c6093106 100644 --- a/lib/rack/attack/configuration.rb +++ b/lib/rack/attack/configuration.rb @@ -5,11 +5,11 @@ module Rack class Attack class Configuration - DEFAULT_BLOCKLISTED_RESPONSE = lambda { |_env| [403, { 'Content-Type' => 'text/plain' }, ["Forbidden\n"]] } + DEFAULT_BLOCKLISTED_CALLBACK = lambda { |_req| [403, { 'Content-Type' => 'text/plain' }, ["Forbidden\n"]] } - DEFAULT_THROTTLED_RESPONSE = lambda do |env| + DEFAULT_THROTTLED_CALLBACK = lambda do |req| if Rack::Attack.configuration.throttled_response_retry_after_header - match_data = env['rack.attack.match_data'] + match_data = req.env['rack.attack.match_data'] now = match_data[:epoch_time] retry_after = match_data[:period] - (now % match_data[:period]) @@ -20,7 +20,23 @@ class Configuration end attr_reader :safelists, :blocklists, :throttles, :anonymous_blocklists, :anonymous_safelists - attr_accessor :blocklisted_response, :throttled_response, :throttled_response_retry_after_header + attr_accessor :blocklisted_callback, :throttled_callback, :throttled_response_retry_after_header + + attr_reader :blocklisted_response, :throttled_response # Keeping these for backwards compatibility + + def blocklisted_response=(callback) + # TODO: uncomment in 7.0 + # warn "[DEPRECATION] Rack::Attack.blocklisted_response is deprecated. "\ + # "Please use Rack::Attack.blocklisted_callback instead." + @blocklisted_response = callback + end + + def throttled_response=(callback) + # TODO: uncomment in 7.0 + # warn "[DEPRECATION] Rack::Attack.throttled_response is deprecated. "\ + # "Please use Rack::Attack.throttled_callback instead" + @throttled_response = callback + end def initialize set_defaults @@ -99,8 +115,12 @@ def set_defaults @anonymous_safelists = [] @throttled_response_retry_after_header = false - @blocklisted_response = DEFAULT_BLOCKLISTED_RESPONSE - @throttled_response = DEFAULT_THROTTLED_RESPONSE + @blocklisted_callback = DEFAULT_BLOCKLISTED_CALLBACK + @throttled_callback = DEFAULT_THROTTLED_CALLBACK + + # Deprecated: Keeping these for backwards compatibility + @blocklisted_response = nil + @throttled_response = nil end end end diff --git a/spec/acceptance/customizing_blocked_response_spec.rb b/spec/acceptance/customizing_blocked_response_spec.rb index fd830c26..9a538620 100644 --- a/spec/acceptance/customizing_blocked_response_spec.rb +++ b/spec/acceptance/customizing_blocked_response_spec.rb @@ -14,7 +14,7 @@ assert_equal 403, last_response.status - Rack::Attack.blocklisted_response = lambda do |_env| + Rack::Attack.blocklisted_callback = lambda do |_req| [503, {}, ["Blocked"]] end @@ -28,9 +28,9 @@ matched = nil match_type = nil - Rack::Attack.blocklisted_response = lambda do |env| - matched = env['rack.attack.matched'] - match_type = env['rack.attack.match_type'] + Rack::Attack.blocklisted_callback = lambda do |req| + matched = req.env['rack.attack.matched'] + match_type = req.env['rack.attack.match_type'] [503, {}, ["Blocked"]] end @@ -40,4 +40,19 @@ assert_equal "block 1.2.3.4", matched assert_equal :blocklist, match_type end + + it "supports old style" do + get "/", {}, "REMOTE_ADDR" => "1.2.3.4" + + assert_equal 403, last_response.status + + Rack::Attack.blocklisted_response = lambda do |_env| + [503, {}, ["Blocked"]] + end + + get "/", {}, "REMOTE_ADDR" => "1.2.3.4" + + assert_equal 503, last_response.status + assert_equal "Blocked", last_response.body + end end diff --git a/spec/acceptance/customizing_throttled_response_spec.rb b/spec/acceptance/customizing_throttled_response_spec.rb index 5c849790..df875ecc 100644 --- a/spec/acceptance/customizing_throttled_response_spec.rb +++ b/spec/acceptance/customizing_throttled_response_spec.rb @@ -20,7 +20,7 @@ assert_equal 429, last_response.status - Rack::Attack.throttled_response = lambda do |_env| + Rack::Attack.throttled_callback = lambda do |_req| [503, {}, ["Throttled"]] end @@ -36,11 +36,11 @@ match_data = nil match_discriminator = nil - Rack::Attack.throttled_response = lambda do |env| - matched = env['rack.attack.matched'] - match_type = env['rack.attack.match_type'] - match_data = env['rack.attack.match_data'] - match_discriminator = env['rack.attack.match_discriminator'] + Rack::Attack.throttled_callback = lambda do |req| + matched = req.env['rack.attack.matched'] + match_type = req.env['rack.attack.match_type'] + match_data = req.env['rack.attack.match_data'] + match_discriminator = req.env['rack.attack.match_discriminator'] [429, {}, ["Throttled"]] end @@ -58,4 +58,23 @@ get "/", {}, "REMOTE_ADDR" => "1.2.3.4" assert_equal 3, match_data[:count] end + + it "supports old style" do + get "/", {}, "REMOTE_ADDR" => "1.2.3.4" + + assert_equal 200, last_response.status + + get "/", {}, "REMOTE_ADDR" => "1.2.3.4" + + assert_equal 429, last_response.status + + Rack::Attack.throttled_response = lambda do |_req| + [503, {}, ["Throttled"]] + end + + get "/", {}, "REMOTE_ADDR" => "1.2.3.4" + + assert_equal 503, last_response.status + assert_equal "Throttled", last_response.body + end end diff --git a/spec/rack_attack_spec.rb b/spec/rack_attack_spec.rb index d9bab4d7..c55fa206 100644 --- a/spec/rack_attack_spec.rb +++ b/spec/rack_attack_spec.rb @@ -64,15 +64,15 @@ end end - describe '#blocklisted_response' do + describe '#blocklisted_callback' do it 'should exist' do - _(Rack::Attack.blocklisted_response).must_respond_to :call + _(Rack::Attack.blocklisted_callback).must_respond_to :call end end - describe '#throttled_response' do + describe '#throttled_callback' do it 'should exist' do - _(Rack::Attack.throttled_response).must_respond_to :call + _(Rack::Attack.throttled_callback).must_respond_to :call end end end From 6cca2467d2400a2c2a42d5fbb278af9c76704169 Mon Sep 17 00:00:00 2001 From: fatkodima Date: Mon, 14 Oct 2019 23:16:52 +0300 Subject: [PATCH 014/113] Fix rescuing errors in RedisProxy#increment --- lib/rack/attack/store_proxy/redis_proxy.rb | 8 ++------ spec/integration/offline_spec.rb | 12 ++++++++++++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/lib/rack/attack/store_proxy/redis_proxy.rb b/lib/rack/attack/store_proxy/redis_proxy.rb index 9fe2bc8c..1bcb4b48 100644 --- a/lib/rack/attack/store_proxy/redis_proxy.rb +++ b/lib/rack/attack/store_proxy/redis_proxy.rb @@ -31,16 +31,12 @@ def write(key, value, options = {}) end def increment(key, amount, options = {}) - count = nil - rescuing do pipelined do - count = incrby(key, amount) + incrby(key, amount) expire(key, options[:expires_in]) if options[:expires_in] - end + end.first end - - count.value if count end def delete(key, _options = {}) diff --git a/spec/integration/offline_spec.rb b/spec/integration/offline_spec.rb index 26779a85..f9bacab8 100644 --- a/spec/integration/offline_spec.rb +++ b/spec/integration/offline_spec.rb @@ -45,3 +45,15 @@ end end end + +if defined?(Redis) + describe 'when Redis is offline' do + include OfflineExamples + + before do + @cache = Rack::Attack::Cache.new + # Use presumably unused port for Redis client + @cache.store = Redis.new(host: '127.0.0.1', port: 3333) + end + end +end From 30898d8cafe27e2c510c393be981af6a896c193d Mon Sep 17 00:00:00 2001 From: Gonzalo Rodriguez Date: Tue, 17 Dec 2019 18:24:38 -0300 Subject: [PATCH 015/113] ci: fix rubygems install step --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 52a9bb9d..7fbcd20d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ rvm: - 2.4.9 before_install: - - gem update --system + - yes | gem update --system - gem install bundler -v "~> 2.0" gemfile: From d7b67011b7c5bb72b6c74ed511630a3b2a7d7f3a Mon Sep 17 00:00:00 2001 From: Gonzalo Rodriguez Date: Tue, 17 Dec 2019 18:24:38 -0300 Subject: [PATCH 016/113] ci: fix rubygems install step --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ddfaf94a..05069073 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,7 @@ rvm: - 2.3.8 before_install: - - gem update --system + - yes | gem update --system - gem install bundler -v "~> 2.0" gemfile: From da418806638d0556fe15f20cea20194d4bbb85b3 Mon Sep 17 00:00:00 2001 From: Gonzalo Rodriguez Date: Tue, 17 Dec 2019 18:03:17 -0300 Subject: [PATCH 017/113] Bump gem version to v6.2.2 --- CHANGELOG.md | 7 +++++++ lib/rack/attack/version.rb | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a9d7cd3e..f7f382d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to this project will be documented in this file. +## [6.2.2] - 2019-12-18 + +### Fixed + +- Fixed occasional `Redis::FutureNotReady` error (#445) (@fatkodima) + ## [6.2.1] - 2019-10-30 ### Fixed @@ -195,6 +201,7 @@ so your custom code is less prone to race conditions ([#282](https://github.com/ - Remove unused variable - Extract mandatory options to constants +[6.2.2]: https://github.com/kickstarter/rack-attack/compare/v6.2.1...v6.2.2/ [6.2.1]: https://github.com/kickstarter/rack-attack/compare/v6.2.0...v6.2.1/ [6.2.0]: https://github.com/kickstarter/rack-attack/compare/v6.1.0...v6.2.0/ [6.1.0]: https://github.com/kickstarter/rack-attack/compare/v6.0.0...v6.1.0/ diff --git a/lib/rack/attack/version.rb b/lib/rack/attack/version.rb index d6ea4e72..a4b448c6 100644 --- a/lib/rack/attack/version.rb +++ b/lib/rack/attack/version.rb @@ -2,6 +2,6 @@ module Rack class Attack - VERSION = '6.2.1' + VERSION = '6.2.2' end end From f413efc7960effd9ca9e4d1c717effabe23bec4c Mon Sep 17 00:00:00 2001 From: Gonzalo Date: Wed, 18 Dec 2019 13:35:08 -0300 Subject: [PATCH 018/113] ci: test against ruby 2.7.0 - don't test ruby 2.7.0 with incompatible rails versions --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index 7fbcd20d..676be42b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ cache: bundler rvm: - ruby-head + - 2.7.0-preview3 - 2.6.5 - 2.5.7 - 2.4.9 @@ -32,6 +33,10 @@ matrix: allow_failures: - rvm: ruby-head exclude: + - gemfile: gemfiles/rack_1_6.gemfile + rvm: 2.7.0-preview3 + - gemfile: gemfiles/rails_4_2.gemfile + rvm: 2.7.0-preview3 - gemfile: gemfiles/rails_6_0.gemfile rvm: 2.4.9 fast_finish: true From fa5ef552d379d7b300124779d405909cc833cf0e Mon Sep 17 00:00:00 2001 From: Gonzalo Rodriguez Date: Tue, 22 Oct 2019 14:28:34 -0300 Subject: [PATCH 019/113] ci: update Travis dist to bionic --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 676be42b..8986f7b3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -dist: xenial +dist: bionic language: ruby cache: bundler From addadf6b31f991fea6c7d8694fa0fb9b43521917 Mon Sep 17 00:00:00 2001 From: Gonzalo Date: Mon, 6 Jan 2020 13:09:04 -0300 Subject: [PATCH 020/113] ci: update to final ruby 2.7 --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8986f7b3..f06c8f7d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ cache: bundler rvm: - ruby-head - - 2.7.0-preview3 + - 2.7.0 - 2.6.5 - 2.5.7 - 2.4.9 @@ -34,9 +34,9 @@ matrix: - rvm: ruby-head exclude: - gemfile: gemfiles/rack_1_6.gemfile - rvm: 2.7.0-preview3 + rvm: 2.7.0 - gemfile: gemfiles/rails_4_2.gemfile - rvm: 2.7.0-preview3 + rvm: 2.7.0 - gemfile: gemfiles/rails_6_0.gemfile rvm: 2.4.9 fast_finish: true From 0a80e30f4685e5534042589b4fc990549d8d797a Mon Sep 17 00:00:00 2001 From: Gonzalo Date: Mon, 27 Jan 2020 16:00:31 -0300 Subject: [PATCH 021/113] docs: help users write more clear bug reports with a template --- .github/ISSUE_TEMPLATE/bug_report.md | 46 ++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..00bcd47e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,46 @@ +**Describe the bug** + +A clear and concise description of what the bug is. + +**To Reproduce** + +Steps to reproduce the behavior: + +1. Have a rack-attack config as follows: + +```ruby + # E.g. + + Rack::Attack.cache.store = " ... " + + Rack::Attack.throttle(... + + # And so on + +``` + +2. Start the app server +3. Visit the page with path '....' +4. See error + +**Expected behavior** + +A clear and concise description of what you expected to happen. + +**Screenshots** + +If applicable, add screenshots to help explain your problem. + +**Environment information (please complete the following information):** + + - rack-attack version: + - ruby version: + - rack version: + - rails version (if using rails): + - rails environment (if using rails): [e.g. development, production, all] + - redis gem version (if reporting redis-related issue): + - redis server version (if reporting redis-related issue): + +**Additional context** + +Add any other context about the problem you think can help here. From 9f93d34492dcbad3277a139015311b4fbf79b59c Mon Sep 17 00:00:00 2001 From: Gonzalo Date: Tue, 4 Feb 2020 14:15:07 -0300 Subject: [PATCH 022/113] ci: test against latest rack minor versions --- .travis.yml | 6 +++--- Appraisals | 8 ++++---- gemfiles/{rack_1_6.gemfile => rack_1.gemfile} | 2 +- gemfiles/{rack_2_0.gemfile => rack_2.gemfile} | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) rename gemfiles/{rack_1_6.gemfile => rack_1.gemfile} (88%) rename gemfiles/{rack_2_0.gemfile => rack_2.gemfile} (80%) diff --git a/.travis.yml b/.travis.yml index f06c8f7d..53d497ec 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,8 +14,8 @@ before_install: - gem install bundler -v "~> 2.0" gemfile: - - gemfiles/rack_2_0.gemfile - - gemfiles/rack_1_6.gemfile + - gemfiles/rack_2.gemfile + - gemfiles/rack_1.gemfile - gemfiles/rails_6_0.gemfile - gemfiles/rails_5_2.gemfile - gemfiles/rails_5_1.gemfile @@ -33,7 +33,7 @@ matrix: allow_failures: - rvm: ruby-head exclude: - - gemfile: gemfiles/rack_1_6.gemfile + - gemfile: gemfiles/rack_1.gemfile rvm: 2.7.0 - gemfile: gemfiles/rails_4_2.gemfile rvm: 2.7.0 diff --git a/Appraisals b/Appraisals index 834d2018..ef44d14e 100644 --- a/Appraisals +++ b/Appraisals @@ -1,16 +1,16 @@ # frozen_string_literal: true -appraise "rack_2_0" do - gem "rack", "~> 2.0.4" +appraise "rack_2" do + gem "rack", "~> 2.0" end -appraise "rack_1_6" do +appraise "rack_1" do # Override activesupport and actionpack version constraints by making # it more loose so it's compatible with rack 1.6.x gem "actionpack", ">= 4.2" gem "activesupport", ">= 4.2" - gem "rack", "~> 1.6.9" + gem "rack", "~> 1.6" # Override rack-test version constraint by making it more loose # so it's compatible with actionpack 4.2.x diff --git a/gemfiles/rack_1_6.gemfile b/gemfiles/rack_1.gemfile similarity index 88% rename from gemfiles/rack_1_6.gemfile rename to gemfiles/rack_1.gemfile index 0cc00506..2390b859 100644 --- a/gemfiles/rack_1_6.gemfile +++ b/gemfiles/rack_1.gemfile @@ -4,7 +4,7 @@ source "https://rubygems.org" gem "actionpack", ">= 4.2" gem "activesupport", ">= 4.2" -gem "rack", "~> 1.6.9" +gem "rack", "~> 1.6" gem "rack-test", ">= 0.6" gemspec path: "../" diff --git a/gemfiles/rack_2_0.gemfile b/gemfiles/rack_2.gemfile similarity index 80% rename from gemfiles/rack_2_0.gemfile rename to gemfiles/rack_2.gemfile index 9915a10d..964e087d 100644 --- a/gemfiles/rack_2_0.gemfile +++ b/gemfiles/rack_2.gemfile @@ -2,6 +2,6 @@ source "https://rubygems.org" -gem "rack", "~> 2.0.4" +gem "rack", "~> 2.0" gemspec path: "../" From 688fee30468046c59d6d2dd2e0a2ae7fe916cd01 Mon Sep 17 00:00:00 2001 From: Gonzalo Rodriguez Date: Wed, 25 Dec 2019 18:10:42 -0300 Subject: [PATCH 023/113] build: update rubocop to v0.78 --- .rubocop.yml | 12 ++++++------ rack-attack.gemspec | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 478b1fc1..b12c0b4b 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -21,6 +21,9 @@ Gemspec: Layout: Enabled: true +Layout/LineLength: + Max: 120 + Lint: Enabled: true @@ -29,9 +32,6 @@ Naming: Exclude: - "lib/rack/attack/path_normalizer.rb" -Metrics/LineLength: - Max: 120 - Performance: Enabled: true @@ -83,6 +83,9 @@ Style/RedundantBegin: Style/RedundantFreeze: Enabled: true +Style/RedundantPercentQ: + Enabled: true + Style/RedundantSelf: Enabled: true @@ -94,6 +97,3 @@ Style/SingleLineMethods: Style/SpecialGlobalVars: Enabled: true - -Style/UnneededPercentQ: - Enabled: true diff --git a/rack-attack.gemspec b/rack-attack.gemspec index 65a74444..28dd3f8d 100644 --- a/rack-attack.gemspec +++ b/rack-attack.gemspec @@ -37,7 +37,7 @@ Gem::Specification.new do |s| s.add_development_dependency "minitest-stub-const", "~> 0.6" s.add_development_dependency 'rack-test', "~> 1.0" s.add_development_dependency 'rake', "~> 13.0" - s.add_development_dependency "rubocop", "0.75.0" + s.add_development_dependency "rubocop", "0.78.0" s.add_development_dependency "rubocop-performance", "~> 1.5.0" s.add_development_dependency "timecop", "~> 0.9.1" From 1c0c232a63a40e05800c38435a010967576d6362 Mon Sep 17 00:00:00 2001 From: Gonzalo Rodriguez Date: Sat, 25 Apr 2020 15:29:45 -0300 Subject: [PATCH 024/113] Revert "docs: help users write more clear bug reports with a template" This reverts commit 0a80e30f4685e5534042589b4fc990549d8d797a. --- .github/ISSUE_TEMPLATE/bug_report.md | 46 ---------------------------- 1 file changed, 46 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 00bcd47e..00000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,46 +0,0 @@ -**Describe the bug** - -A clear and concise description of what the bug is. - -**To Reproduce** - -Steps to reproduce the behavior: - -1. Have a rack-attack config as follows: - -```ruby - # E.g. - - Rack::Attack.cache.store = " ... " - - Rack::Attack.throttle(... - - # And so on - -``` - -2. Start the app server -3. Visit the page with path '....' -4. See error - -**Expected behavior** - -A clear and concise description of what you expected to happen. - -**Screenshots** - -If applicable, add screenshots to help explain your problem. - -**Environment information (please complete the following information):** - - - rack-attack version: - - ruby version: - - rack version: - - rails version (if using rails): - - rails environment (if using rails): [e.g. development, production, all] - - redis gem version (if reporting redis-related issue): - - redis server version (if reporting redis-related issue): - -**Additional context** - -Add any other context about the problem you think can help here. From 8ededbc73882cd1c9498b19d6876dbe24959300b Mon Sep 17 00:00:00 2001 From: Gonzalo Rodriguez Date: Sat, 25 Apr 2020 15:30:42 -0300 Subject: [PATCH 025/113] ci: update rubies --- .travis.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 53d497ec..1b876135 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,10 +4,10 @@ cache: bundler rvm: - ruby-head - - 2.7.0 - - 2.6.5 - - 2.5.7 - - 2.4.9 + - 2.7.1 + - 2.6.6 + - 2.5.8 + - 2.4.10 before_install: - yes | gem update --system @@ -34,11 +34,11 @@ matrix: - rvm: ruby-head exclude: - gemfile: gemfiles/rack_1.gemfile - rvm: 2.7.0 + rvm: 2.7.1 - gemfile: gemfiles/rails_4_2.gemfile - rvm: 2.7.0 + rvm: 2.7.1 - gemfile: gemfiles/rails_6_0.gemfile - rvm: 2.4.9 + rvm: 2.4.10 fast_finish: true services: From 53c1d93fd1619425553e4436c428959c3049b1e2 Mon Sep 17 00:00:00 2001 From: Gonzalo Rodriguez Date: Sat, 25 Apr 2020 15:42:30 -0300 Subject: [PATCH 026/113] style: avoid multiline ternary operator --- .rubocop.yml | 6 ++++++ lib/rack/attack.rb | 14 ++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index b12c0b4b..2aebea2e 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -68,6 +68,12 @@ Style/FrozenStringLiteralComment: Style/HashSyntax: Enabled: true +Style/MultilineTernaryOperator: + Enabled: true + +Style/NestedTernaryOperator: + Enabled: true + Style/OptionalArguments: Enabled: true diff --git a/lib/rack/attack.rb b/lib/rack/attack.rb index d29bf687..47ba82f0 100644 --- a/lib/rack/attack.rb +++ b/lib/rack/attack.rb @@ -110,12 +110,18 @@ def call(env) @app.call(env) elsif configuration.blocklisted?(request) # Deprecated: Keeping blocklisted_response for backwards compatibility - configuration.blocklisted_response ? configuration.blocklisted_response.call(env) : - configuration.blocklisted_callback.call(request) + if configuration.blocklisted_response + configuration.blocklisted_response.call(env) + else + configuration.blocklisted_callback.call(request) + end elsif configuration.throttled?(request) # Deprecated: Keeping throttled_response for backwards compatibility - configuration.throttled_response ? configuration.throttled_response.call(env) : - configuration.throttled_callback.call(request) + if configuration.throttled_response + configuration.throttled_response.call(env) + else + configuration.throttled_callback.call(request) + end else configuration.tracked?(request) @app.call(env) From 9923012fe81fe4a31f0f1e0f6100d268911a1c87 Mon Sep 17 00:00:00 2001 From: fatkodima Date: Thu, 10 Oct 2019 13:32:04 +0300 Subject: [PATCH 027/113] Allow to reset state between tests --- lib/rack/attack.rb | 5 +++++ lib/rack/attack/cache.rb | 11 +++++++++++ lib/rack/attack/store_proxy/redis_proxy.rb | 13 +++++++++++++ spec/rack_attack_spec.rb | 22 ++++++++++++++++++++++ 4 files changed, 51 insertions(+) diff --git a/lib/rack/attack.rb b/lib/rack/attack.rb index de5f7456..f209d590 100644 --- a/lib/rack/attack.rb +++ b/lib/rack/attack.rb @@ -14,6 +14,7 @@ class Attack class Error < StandardError; end class MisconfiguredStoreError < Error; end class MissingStoreError < Error; end + class IncompatibleStoreError < Error; end autoload :Check, 'rack/attack/check' autoload :Throttle, 'rack/attack/throttle' @@ -53,6 +54,10 @@ def clear! @configuration.clear_configuration end + def reset! + cache.reset! + end + extend Forwardable def_delegators( :@configuration, diff --git a/lib/rack/attack/cache.rb b/lib/rack/attack/cache.rb index cfa2efa4..0defa646 100644 --- a/lib/rack/attack/cache.rb +++ b/lib/rack/attack/cache.rb @@ -41,6 +41,17 @@ def delete(unprefixed_key) store.delete("#{prefix}:#{unprefixed_key}") end + def reset! + if store.respond_to?(:delete_matched) + store.delete_matched("#{prefix}*") + else + raise( + Rack::Attack::IncompatibleStoreError, + "Configured store #{store.class.name} doesn't respond to #delete_matched method" + ) + end + end + private def key_and_expiry(unprefixed_key, period) diff --git a/lib/rack/attack/store_proxy/redis_proxy.rb b/lib/rack/attack/store_proxy/redis_proxy.rb index d4e6f3a1..e51b3e92 100644 --- a/lib/rack/attack/store_proxy/redis_proxy.rb +++ b/lib/rack/attack/store_proxy/redis_proxy.rb @@ -43,6 +43,19 @@ def delete(key, _options = {}) rescuing { del(key) } end + def delete_matched(matcher, _options = nil) + cursor = "0" + + rescuing do + # Fetch keys in batches using SCAN to avoid blocking the Redis server. + loop do + cursor, keys = scan(cursor, match: matcher, count: 1000) + del(*keys) unless keys.empty? + break if cursor == "0" + end + end + end + private def rescuing diff --git a/spec/rack_attack_spec.rb b/spec/rack_attack_spec.rb index 647a08d7..d9bab4d7 100644 --- a/spec/rack_attack_spec.rb +++ b/spec/rack_attack_spec.rb @@ -99,4 +99,26 @@ end end end + + describe 'reset!' do + it 'raises an error when is not supported by cache store' do + Rack::Attack.cache.store = Class.new + assert_raises(Rack::Attack::IncompatibleStoreError) do + Rack::Attack.reset! + end + end + + if defined?(Redis) + it 'should delete rack attack keys' do + redis = Redis.new + redis.set('key', 'value') + redis.set("#{Rack::Attack.cache.prefix}::key", 'value') + Rack::Attack.cache.store = redis + Rack::Attack.reset! + + _(redis.get('key')).must_equal 'value' + _(redis.get("#{Rack::Attack.cache.prefix}::key")).must_be_nil + end + end + end end From 8bbd0ab702bced82d15b8d5f52b28242ef66a05d Mon Sep 17 00:00:00 2001 From: Gonzalo Date: Wed, 18 Dec 2019 13:35:08 -0300 Subject: [PATCH 028/113] ci: test against ruby 2.7.0 - don't test ruby 2.7.0 with incompatible rails versions --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index 05069073..c310aac6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ cache: bundler rvm: - ruby-head + - 2.7.0-preview3 - 2.6.5 - 2.5.7 - 2.4.9 @@ -33,6 +34,10 @@ matrix: allow_failures: - rvm: ruby-head exclude: + - gemfile: gemfiles/rack_1_6.gemfile + rvm: 2.7.0-preview3 + - gemfile: gemfiles/rails_4_2.gemfile + rvm: 2.7.0-preview3 - gemfile: gemfiles/rails_6_0.gemfile rvm: 2.4.9 - gemfile: gemfiles/rails_6_0.gemfile From fadb98f25c45d25ea840c3bce2ec0f5149ac7ce8 Mon Sep 17 00:00:00 2001 From: Gonzalo Rodriguez Date: Tue, 22 Oct 2019 14:28:34 -0300 Subject: [PATCH 029/113] ci: update Travis dist to bionic --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c310aac6..129cf5b2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -dist: xenial +dist: bionic language: ruby cache: bundler From aeac2d488707438ebbca2ecbb63295a59d3d2800 Mon Sep 17 00:00:00 2001 From: Gonzalo Date: Mon, 6 Jan 2020 13:09:04 -0300 Subject: [PATCH 030/113] ci: update to final ruby 2.7 --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 129cf5b2..4033113c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ cache: bundler rvm: - ruby-head - - 2.7.0-preview3 + - 2.7.0 - 2.6.5 - 2.5.7 - 2.4.9 @@ -35,9 +35,9 @@ matrix: - rvm: ruby-head exclude: - gemfile: gemfiles/rack_1_6.gemfile - rvm: 2.7.0-preview3 + rvm: 2.7.0 - gemfile: gemfiles/rails_4_2.gemfile - rvm: 2.7.0-preview3 + rvm: 2.7.0 - gemfile: gemfiles/rails_6_0.gemfile rvm: 2.4.9 - gemfile: gemfiles/rails_6_0.gemfile From 8787f7db5a7fb359923d6fd0f8a7802d18aec306 Mon Sep 17 00:00:00 2001 From: Gonzalo Date: Tue, 4 Feb 2020 14:15:07 -0300 Subject: [PATCH 031/113] ci: test against latest rack minor versions --- .travis.yml | 6 +++--- Appraisals | 8 ++++---- gemfiles/{rack_1_6.gemfile => rack_1.gemfile} | 2 +- gemfiles/{rack_2_0.gemfile => rack_2.gemfile} | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) rename gemfiles/{rack_1_6.gemfile => rack_1.gemfile} (88%) rename gemfiles/{rack_2_0.gemfile => rack_2.gemfile} (80%) diff --git a/.travis.yml b/.travis.yml index 4033113c..ece47e21 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,8 +15,8 @@ before_install: - gem install bundler -v "~> 2.0" gemfile: - - gemfiles/rack_2_0.gemfile - - gemfiles/rack_1_6.gemfile + - gemfiles/rack_2.gemfile + - gemfiles/rack_1.gemfile - gemfiles/rails_6_0.gemfile - gemfiles/rails_5_2.gemfile - gemfiles/rails_5_1.gemfile @@ -34,7 +34,7 @@ matrix: allow_failures: - rvm: ruby-head exclude: - - gemfile: gemfiles/rack_1_6.gemfile + - gemfile: gemfiles/rack_1.gemfile rvm: 2.7.0 - gemfile: gemfiles/rails_4_2.gemfile rvm: 2.7.0 diff --git a/Appraisals b/Appraisals index 834d2018..ef44d14e 100644 --- a/Appraisals +++ b/Appraisals @@ -1,16 +1,16 @@ # frozen_string_literal: true -appraise "rack_2_0" do - gem "rack", "~> 2.0.4" +appraise "rack_2" do + gem "rack", "~> 2.0" end -appraise "rack_1_6" do +appraise "rack_1" do # Override activesupport and actionpack version constraints by making # it more loose so it's compatible with rack 1.6.x gem "actionpack", ">= 4.2" gem "activesupport", ">= 4.2" - gem "rack", "~> 1.6.9" + gem "rack", "~> 1.6" # Override rack-test version constraint by making it more loose # so it's compatible with actionpack 4.2.x diff --git a/gemfiles/rack_1_6.gemfile b/gemfiles/rack_1.gemfile similarity index 88% rename from gemfiles/rack_1_6.gemfile rename to gemfiles/rack_1.gemfile index 0cc00506..2390b859 100644 --- a/gemfiles/rack_1_6.gemfile +++ b/gemfiles/rack_1.gemfile @@ -4,7 +4,7 @@ source "https://rubygems.org" gem "actionpack", ">= 4.2" gem "activesupport", ">= 4.2" -gem "rack", "~> 1.6.9" +gem "rack", "~> 1.6" gem "rack-test", ">= 0.6" gemspec path: "../" diff --git a/gemfiles/rack_2_0.gemfile b/gemfiles/rack_2.gemfile similarity index 80% rename from gemfiles/rack_2_0.gemfile rename to gemfiles/rack_2.gemfile index 9915a10d..964e087d 100644 --- a/gemfiles/rack_2_0.gemfile +++ b/gemfiles/rack_2.gemfile @@ -2,6 +2,6 @@ source "https://rubygems.org" -gem "rack", "~> 2.0.4" +gem "rack", "~> 2.0" gemspec path: "../" From 4c33737ed337c0a14c1b362ae7a5697cdad49441 Mon Sep 17 00:00:00 2001 From: Gonzalo Rodriguez Date: Wed, 25 Dec 2019 18:10:42 -0300 Subject: [PATCH 032/113] build: update rubocop to v0.78 --- .rubocop.yml | 12 ++++++------ rack-attack.gemspec | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 28f8502a..7fe2ca65 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -21,6 +21,9 @@ Gemspec: Layout: Enabled: true +Layout/LineLength: + Max: 120 + Lint: Enabled: true @@ -29,9 +32,6 @@ Naming: Exclude: - "lib/rack/attack/path_normalizer.rb" -Metrics/LineLength: - Max: 120 - Performance: Enabled: true @@ -83,6 +83,9 @@ Style/RedundantBegin: Style/RedundantFreeze: Enabled: true +Style/RedundantPercentQ: + Enabled: true + Style/RedundantSelf: Enabled: true @@ -94,6 +97,3 @@ Style/SingleLineMethods: Style/SpecialGlobalVars: Enabled: true - -Style/UnneededPercentQ: - Enabled: true diff --git a/rack-attack.gemspec b/rack-attack.gemspec index 0bebfa36..99deeb72 100644 --- a/rack-attack.gemspec +++ b/rack-attack.gemspec @@ -37,7 +37,7 @@ Gem::Specification.new do |s| s.add_development_dependency "minitest-stub-const", "~> 0.6" s.add_development_dependency 'rack-test', "~> 1.0" s.add_development_dependency 'rake', "~> 13.0" - s.add_development_dependency "rubocop", "0.75.0" + s.add_development_dependency "rubocop", "0.78.0" s.add_development_dependency "rubocop-performance", "~> 1.5.0" s.add_development_dependency "timecop", "~> 0.9.1" From 76bbada48fcd9ed2d0b02bc7e8f6f36ce35bab9e Mon Sep 17 00:00:00 2001 From: Gonzalo Rodriguez Date: Sat, 25 Apr 2020 15:30:42 -0300 Subject: [PATCH 033/113] ci: update rubies --- .travis.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index ece47e21..98432ae6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,10 +4,10 @@ cache: bundler rvm: - ruby-head - - 2.7.0 - - 2.6.5 - - 2.5.7 - - 2.4.9 + - 2.7.1 + - 2.6.6 + - 2.5.8 + - 2.4.10 - 2.3.8 before_install: @@ -35,11 +35,11 @@ matrix: - rvm: ruby-head exclude: - gemfile: gemfiles/rack_1.gemfile - rvm: 2.7.0 + rvm: 2.7.1 - gemfile: gemfiles/rails_4_2.gemfile - rvm: 2.7.0 + rvm: 2.7.1 - gemfile: gemfiles/rails_6_0.gemfile - rvm: 2.4.9 + rvm: 2.4.10 - gemfile: gemfiles/rails_6_0.gemfile rvm: 2.3.8 fast_finish: true From aa071aa5df4289dd3d7029b187320afa62974685 Mon Sep 17 00:00:00 2001 From: Gonzalo Rodriguez Date: Sat, 25 Apr 2020 16:41:28 -0300 Subject: [PATCH 034/113] Bump gem version to v6.3.0 --- CHANGELOG.md | 18 ++++++++++++++++++ lib/rack/attack/version.rb | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7f382d4..fb5506a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,21 @@ All notable changes to this project will be documented in this file. +## [6.3.0] - 2020-04-26 + +### Added + +- `Rack::Attack.reset!` to reset state (#436) (@fatkodima) +- `Rack::Attack.throttled_response_retry_after_header=` setting that enables a `Retry-After` response header when client is throttled (#440) (@fatkodima) + +### Changed + +- No longer swallow Redis non-connection errors if Redis is configured as cache store (#450) (@fatkodima) + +### Fixed + +- `Rack::Attack.clear_configuration` also clears `blocklisted_response` and `throttled_response` back to defaults + ## [6.2.2] - 2019-12-18 ### Fixed @@ -201,6 +216,7 @@ so your custom code is less prone to race conditions ([#282](https://github.com/ - Remove unused variable - Extract mandatory options to constants +[6.3.0]: https://github.com/kickstarter/rack-attack/compare/v6.2.2...v6.3.0/ [6.2.2]: https://github.com/kickstarter/rack-attack/compare/v6.2.1...v6.2.2/ [6.2.1]: https://github.com/kickstarter/rack-attack/compare/v6.2.0...v6.2.1/ [6.2.0]: https://github.com/kickstarter/rack-attack/compare/v6.1.0...v6.2.0/ @@ -229,3 +245,5 @@ so your custom code is less prone to race conditions ([#282](https://github.com/ [2.3.0]: https://github.com/kickstarter/rack-attack/compare/v2.2.1...v2.3.0/ [2.2.1]: https://github.com/kickstarter/rack-attack/compare/v2.2.0...v2.2.1/ [2.2.0]: https://github.com/kickstarter/rack-attack/compare/v2.1.1...v2.2.0/ + +[@fatkodima]: https://github.com/fatkodima diff --git a/lib/rack/attack/version.rb b/lib/rack/attack/version.rb index a4b448c6..67b60a55 100644 --- a/lib/rack/attack/version.rb +++ b/lib/rack/attack/version.rb @@ -2,6 +2,6 @@ module Rack class Attack - VERSION = '6.2.2' + VERSION = '6.3.0' end end From 35e49834006975ad13e28bce90b2e56f5b2eb796 Mon Sep 17 00:00:00 2001 From: Gonzalo Rodriguez Date: Sun, 26 Apr 2020 13:09:13 -0300 Subject: [PATCH 035/113] doc: add Testing section to the README --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 866cf354..96d0db8a 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ See the [Backing & Hacking blog post](https://www.kickstarter.com/backing-and-ha - [Customizing responses](#customizing-responses) - [RateLimit headers for well-behaved clients](#ratelimit-headers-for-well-behaved-clients) - [Logging & Instrumentation](#logging--instrumentation) +- [Testing](#testing) - [How it works](#how-it-works) - [About Tracks](#about-tracks) - [Testing](#testing) @@ -398,6 +399,16 @@ ActiveSupport::Notifications.subscribe(/rack_attack/) do |name, start, finish, r end ``` +## Testing + +### Disabling + +`Rack::Attack.enabled = false` can be used to either completely disable Rack::Attack in your tests, or to disable/enable for specific test cases only. + +### Test case isolation + +`Rack::Attack.reset!` can be used in your test suite to clear any Rack::Attack state between different test cases. + ## How it works The Rack::Attack middleware compares each request against *safelists*, *blocklists*, *throttles*, and *tracks* that you define. There are none by default. From d5a240d9d2427d97153a458139b63b7ca4312b94 Mon Sep 17 00:00:00 2001 From: Ryan Laughlin Date: Mon, 18 May 2020 17:14:12 -0400 Subject: [PATCH 036/113] Fix deprecation warning in Rails 5.2.4.3 --- lib/rack/attack/store_proxy/redis_cache_store_proxy.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rack/attack/store_proxy/redis_cache_store_proxy.rb b/lib/rack/attack/store_proxy/redis_cache_store_proxy.rb index 380215f7..f57c6d5e 100644 --- a/lib/rack/attack/store_proxy/redis_cache_store_proxy.rb +++ b/lib/rack/attack/store_proxy/redis_cache_store_proxy.rb @@ -15,7 +15,7 @@ def increment(name, amount = 1, options = {}) # # So in order to workaround this we use RedisCacheStore#write (which sets expiration) to initialize # the counter. After that we continue using the original RedisCacheStore#increment. - if options[:expires_in] && !read(name) + if options[:expires_in] && !read(name, raw: true) write(name, amount, options) amount From 029b5481fe4e5a729d4ac568eee8a7143b4e0ed1 Mon Sep 17 00:00:00 2001 From: Ryan Laughlin Date: Mon, 18 May 2020 17:41:58 -0400 Subject: [PATCH 037/113] Consolidate #testing section of README --- README.md | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 60b8d2f9..86d1fad2 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,6 @@ See the [Backing & Hacking blog post](https://www.kickstarter.com/backing-and-ha - [Testing](#testing) - [How it works](#how-it-works) - [About Tracks](#about-tracks) -- [Testing](#testing) - [Performance](#performance) - [Motivation](#motivation) - [Contributing](#contributing) @@ -396,6 +395,10 @@ end ## Testing +A note on developing and testing apps using Rack::Attack - if you are using throttling in particular, you will +need to enable the cache in your development environment. See [Caching with Rails](http://guides.rubyonrails.org/caching_with_rails.html) +for more on how to do this. + ### Disabling `Rack::Attack.enabled = false` can be used to either completely disable Rack::Attack in your tests, or to disable/enable for specific test cases only. @@ -441,13 +444,6 @@ can cleanly monkey patch helper methods onto the `Rack::Attack.track` doesn't affect request processing. Tracks are an easy way to log and measure requests matching arbitrary attributes. - -## Testing - -A note on developing and testing apps using Rack::Attack - if you are using throttling in particular, you will -need to enable the cache in your development environment. See [Caching with Rails](http://guides.rubyonrails.org/caching_with_rails.html) -for more on how to do this. - ## Performance The overhead of running Rack::Attack is typically negligible (a few milliseconds per request), From 31dd7a8d1794ab2651325ae0413c16b709b2cbd1 Mon Sep 17 00:00:00 2001 From: Ryan Laughlin Date: Wed, 20 May 2020 10:34:58 -0400 Subject: [PATCH 038/113] Override RedisCacheStoreProxy#read to always use `raw: true` --- lib/rack/attack/store_proxy/redis_cache_store_proxy.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/rack/attack/store_proxy/redis_cache_store_proxy.rb b/lib/rack/attack/store_proxy/redis_cache_store_proxy.rb index f57c6d5e..80412593 100644 --- a/lib/rack/attack/store_proxy/redis_cache_store_proxy.rb +++ b/lib/rack/attack/store_proxy/redis_cache_store_proxy.rb @@ -15,7 +15,7 @@ def increment(name, amount = 1, options = {}) # # So in order to workaround this we use RedisCacheStore#write (which sets expiration) to initialize # the counter. After that we continue using the original RedisCacheStore#increment. - if options[:expires_in] && !read(name, raw: true) + if options[:expires_in] && !read(name) write(name, amount, options) amount @@ -24,6 +24,10 @@ def increment(name, amount = 1, options = {}) end end + def read(name, options = {}) + super(name, options.merge!(raw: true)) + end + def write(name, value, options = {}) super(name, value, options.merge!(raw: true)) end From 02f56979f1774dddb8027c4f62cd897847fecfa6 Mon Sep 17 00:00:00 2001 From: Gonzalo Rodriguez Date: Wed, 20 May 2020 21:33:13 -0300 Subject: [PATCH 039/113] Merge pull request #482 from splitwise/rofreg/avoid-rails-5.2.4.3-deprecation-warning Fix deprecation warning in Rails 5.2.4.3 --- lib/rack/attack/store_proxy/redis_cache_store_proxy.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/rack/attack/store_proxy/redis_cache_store_proxy.rb b/lib/rack/attack/store_proxy/redis_cache_store_proxy.rb index 78807f86..1de361cf 100644 --- a/lib/rack/attack/store_proxy/redis_cache_store_proxy.rb +++ b/lib/rack/attack/store_proxy/redis_cache_store_proxy.rb @@ -24,6 +24,10 @@ def increment(name, amount = 1, options = {}) end end + def read(name, options = {}) + super(name, options.merge!(raw: true)) + end + def write(name, value, options = {}) super(name, value, options.merge!(raw: true)) end From 3a5d10c8b371ece53c46ff95a393b8c7346397f6 Mon Sep 17 00:00:00 2001 From: Gonzalo Rodriguez Date: Wed, 20 May 2020 23:19:26 -0300 Subject: [PATCH 040/113] Bump gem version to v6.3.1 --- CHANGELOG.md | 8 ++++++++ lib/rack/attack/version.rb | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb5506a9..d44d1e0d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to this project will be documented in this file. +## [6.3.1] - 2020-05-21 + +### Fixed + +- Warning when using `ActiveSupport::Cache::RedisCacheStore` as a cache store with rails 5.2.4.3 (#482) (@rofreg) + ## [6.3.0] - 2020-04-26 ### Added @@ -216,6 +222,7 @@ so your custom code is less prone to race conditions ([#282](https://github.com/ - Remove unused variable - Extract mandatory options to constants +[6.3.1]: https://github.com/kickstarter/rack-attack/compare/v6.3.0...v6.3.1/ [6.3.0]: https://github.com/kickstarter/rack-attack/compare/v6.2.2...v6.3.0/ [6.2.2]: https://github.com/kickstarter/rack-attack/compare/v6.2.1...v6.2.2/ [6.2.1]: https://github.com/kickstarter/rack-attack/compare/v6.2.0...v6.2.1/ @@ -247,3 +254,4 @@ so your custom code is less prone to race conditions ([#282](https://github.com/ [2.2.0]: https://github.com/kickstarter/rack-attack/compare/v2.1.1...v2.2.0/ [@fatkodima]: https://github.com/fatkodima +[@rofreg]: https://github.com/rofreg diff --git a/lib/rack/attack/version.rb b/lib/rack/attack/version.rb index 67b60a55..7b5859ca 100644 --- a/lib/rack/attack/version.rb +++ b/lib/rack/attack/version.rb @@ -2,6 +2,6 @@ module Rack class Attack - VERSION = '6.3.0' + VERSION = '6.3.1' end end From c07fcdde434b6864c556baf0c5adf1e0edab854c Mon Sep 17 00:00:00 2001 From: Olle Jonsson Date: Sun, 24 May 2020 17:50:56 +0200 Subject: [PATCH 041/113] Use RuboCop 0.84.0 - this enables each of the new Cops and marks each with the version they appeared in --- .rubocop.yml | 18 +++++++++++++++--- lib/rack/attack/cache.rb | 1 + lib/rack/attack/check.rb | 1 + lib/rack/attack/throttle.rb | 1 + rack-attack.gemspec | 2 +- 5 files changed, 19 insertions(+), 4 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 2aebea2e..45bd079c 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -21,12 +21,27 @@ Gemspec: Layout: Enabled: true +Layout/EmptyLinesAroundAttributeAccessor: # (0.83) + Enabled: true + +Layout/SpaceAroundMethodCallOperator: # (0.82) + Enabled: true + Layout/LineLength: Max: 120 Lint: Enabled: true +Lint/DeprecatedOpenSSLConstant: # (0.84) + Enabled: true + +Lint/RaiseException: # (0.81) + Enabled: true + +Lint/StructNewOverride: # (0.81) + Enabled: true + Naming: Enabled: true Exclude: @@ -42,9 +57,6 @@ Style/BlockDelimiters: Enabled: true IgnoredMethods: [] # Workaround rubocop bug: https://github.com/rubocop-hq/rubocop/issues/6179 -Style/BracesAroundHashParameters: - Enabled: true - Style/ClassAndModuleChildren: Enabled: true Exclude: diff --git a/lib/rack/attack/cache.rb b/lib/rack/attack/cache.rb index f27259e1..0e1f606e 100644 --- a/lib/rack/attack/cache.rb +++ b/lib/rack/attack/cache.rb @@ -12,6 +12,7 @@ def initialize end attr_reader :store + def store=(store) @store = if (proxy = BaseProxy.lookup(store)) diff --git a/lib/rack/attack/check.rb b/lib/rack/attack/check.rb index 4c985ebe..c9f3ff7d 100644 --- a/lib/rack/attack/check.rb +++ b/lib/rack/attack/check.rb @@ -4,6 +4,7 @@ module Rack class Attack class Check attr_reader :name, :block, :type + def initialize(name, options = {}, &block) @name = name @block = block diff --git a/lib/rack/attack/throttle.rb b/lib/rack/attack/throttle.rb index a1e21a7c..1cc50f40 100644 --- a/lib/rack/attack/throttle.rb +++ b/lib/rack/attack/throttle.rb @@ -6,6 +6,7 @@ class Throttle MANDATORY_OPTIONS = [:limit, :period].freeze attr_reader :name, :limit, :period, :block, :type + def initialize(name, options, &block) @name = name @block = block diff --git a/rack-attack.gemspec b/rack-attack.gemspec index 28dd3f8d..69042f1e 100644 --- a/rack-attack.gemspec +++ b/rack-attack.gemspec @@ -37,7 +37,7 @@ Gem::Specification.new do |s| s.add_development_dependency "minitest-stub-const", "~> 0.6" s.add_development_dependency 'rack-test', "~> 1.0" s.add_development_dependency 'rake', "~> 13.0" - s.add_development_dependency "rubocop", "0.78.0" + s.add_development_dependency "rubocop", "0.84.0" s.add_development_dependency "rubocop-performance", "~> 1.5.0" s.add_development_dependency "timecop", "~> 0.9.1" From f92889b3600c37ae0a45649fc75c7eafc8d370f5 Mon Sep 17 00:00:00 2001 From: Tristan Toye Date: Sun, 7 Jun 2020 12:11:30 -0400 Subject: [PATCH 042/113] Clarify Calling HTTP_APIKey header in example (#488) * Clarify Calling HTTP_ header in example In trying to track down a bug here turns out I was trying to reference the wrong header shown in the readme. Printing our `request.env` it becomes clear this is just the full request object: ``` {"rack.version"=>[1, 3], "rack.errors"=>#>, "rack.multithread"=>true, "rack.multiprocess"=>false, "rack.run_once"=>false, "SCRIPT_NAME"=>"", "QUERY_STRING"=>"", "SERVER_PROTOCOL"=>"HTTP/1.1", "SERVER_SOFTWARE"=>"puma 4.3.5 Mysterious Traveller", "GATEWAY_INTERFACE"=>"CGI/1.2", "REQUEST_METHOD"=>"POST", "REQUEST_PATH"=>"/api/v1/....", "REQUEST_URI"=>"/api/v1/...", "HTTP_VERSION"=>"HTTP/1.1", "HTTP_HOST"=>"example.com", "HTTP_APIKEY"=>"secret_key", "CONTENT_TYPE"=>"application/json", "HTTP_USER_AGENT"=>"PostmanRuntime/7.25.0", "HTTP_ACCEPT"=>"*/*", "HTTP_CACHE_CONTROL"=>"no-cache", ... ``` * Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 86d1fad2..f08c44cf 100644 --- a/README.md +++ b/README.md @@ -135,7 +135,7 @@ E.g. # Provided that trusted users use an HTTP request header named APIKey Rack::Attack.safelist("mark any authenticated access safe") do |request| # Requests are allowed if the return value is truthy - request.env["APIKey"] == "secret-string" + request.env["HTTP_APIKEY"] == "secret-string" end # Always allow requests from localhost From 03926e0b753e21a5a08ec4e4d1e7106284e047e9 Mon Sep 17 00:00:00 2001 From: Eliot Sykes Date: Tue, 28 Jul 2020 11:17:00 +0100 Subject: [PATCH 043/113] Mitigate login throttle bypasses in docs This commit mitigates rate limit bypasses in the configuration docs by normalizing the email throttle key. (The normalization process used is the same as used by the Clearance gem.) --- Often an authentication process normalizes email addresses and usernames before look up, say by downcasing and removing any whitespace. Throttles that do not perform the same normalization are vulnerable to rate limit bypasses. For example, an attacker can bypass a vulnerable throttle by using unlimited case and whitespace variants for the same email address: - Variant 1: `victim@example.org` - Variant 2: `victim@example. org` (one whitespace) - Variant 3: `victim@example. org` (two whitespaces) - Variant 4: `ViCtIm@eXaMpLe.org` - etc, etc. All of these variants resolve to the same email address, but allow an attacker to bypass a vulnerable throttle. To mitigate, the email throttle key should be normalized using the same logic the authentication process uses for normalizing emails. --- README.md | 6 ++++-- docs/example_configuration.md | 7 ++++--- examples/rack_attack.rb | 4 +++- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index f08c44cf..b33c64a3 100644 --- a/README.md +++ b/README.md @@ -258,10 +258,12 @@ Rack::Attack.throttle("requests by ip", limit: 5, period: 2) do |request| end # Throttle login attempts for a given email parameter to 6 reqs/minute -# Return the email as a discriminator on POST /login requests +# Return the *normalized* email as a discriminator on POST /login requests Rack::Attack.throttle('limit logins per email', limit: 6, period: 60) do |req| if req.path == '/login' && req.post? - req.params['email'] + # Normalize the email, using the same logic as your authentication process, to + # protect against rate limit bypasses. + req.params['email'].to_s.downcase.gsub(/\s+/, "") end end diff --git a/docs/example_configuration.md b/docs/example_configuration.md index 069f04e9..e1aaa385 100644 --- a/docs/example_configuration.md +++ b/docs/example_configuration.md @@ -53,7 +53,7 @@ class Rack::Attack # Throttle POST requests to /login by email param # - # Key: "rack::attack:#{Time.now.to_i/:period}:logins/email:#{req.email}" + # Key: "rack::attack:#{Time.now.to_i/:period}:logins/email:#{normalized_email}" # # Note: This creates a problem where a malicious user could intentionally # throttle logins for another user and force their login requests to be @@ -61,8 +61,9 @@ class Rack::Attack # on wood!) throttle("logins/email", limit: 5, period: 20.seconds) do |req| if req.path == '/login' && req.post? - # return the email if present, nil otherwise - req.params['email'].presence + # Normalize the email, using the same logic as your authentication process, to + # protect against rate limit bypasses. Return the normalized email if present, nil otherwise. + req.params['email'].to_s.downcase.gsub(/\s+/, "").presence end end diff --git a/examples/rack_attack.rb b/examples/rack_attack.rb index 43f1348a..7423f39a 100644 --- a/examples/rack_attack.rb +++ b/examples/rack_attack.rb @@ -13,8 +13,10 @@ end # Throttle login attempts per email, 10/minute/email +# Normalize the email, using the same logic as your authentication process, to +# protect against rate limit bypasses. Rack::Attack.throttle "logins/email", limit: 2, period: 60 do |req| - req.post? && req.path == "/login" && req.params['email'] + req.post? && req.path == "/login" && req.params['email'].to_s.downcase.gsub(/\s+/, "") end # blocklist bad IPs from accessing admin pages From 9020201ff5be02046c50203959b51b4a3305a374 Mon Sep 17 00:00:00 2001 From: fukayatsu Date: Tue, 15 Dec 2020 23:20:58 +0900 Subject: [PATCH 044/113] Fix Ruby 2.7 kwargs warning in RedisCacheStoreProxy --- lib/rack/attack/store_proxy/redis_cache_store_proxy.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rack/attack/store_proxy/redis_cache_store_proxy.rb b/lib/rack/attack/store_proxy/redis_cache_store_proxy.rb index 80412593..00670f06 100644 --- a/lib/rack/attack/store_proxy/redis_cache_store_proxy.rb +++ b/lib/rack/attack/store_proxy/redis_cache_store_proxy.rb @@ -10,7 +10,7 @@ def self.handle?(store) store.class.name == "ActiveSupport::Cache::RedisCacheStore" end - def increment(name, amount = 1, options = {}) + def increment(name, amount = 1, **options) # RedisCacheStore#increment ignores options[:expires_in]. # # So in order to workaround this we use RedisCacheStore#write (which sets expiration) to initialize From 7b3376021b47e2a9bfe20a1e792c9b6e28a6dbcf Mon Sep 17 00:00:00 2001 From: Joe Van Dyk Date: Thu, 24 Dec 2020 12:58:47 -0800 Subject: [PATCH 045/113] Fix speling in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b33c64a3..4c977dd7 100644 --- a/README.md +++ b/README.md @@ -374,7 +374,7 @@ Rack::Attack uses the [ActiveSupport::Notifications](http://api.rubyonrails.org/ You can subscribe to `rack_attack` events and log it, graph it, etc. -To get notified about specific type of events, subscribe to the event name followed by the `rack_attack` namesapce. +To get notified about specific type of events, subscribe to the event name followed by the `rack_attack` namespace. E.g. for throttles use: ```ruby From cbae022df107b58997ac11f15a442a1631811b39 Mon Sep 17 00:00:00 2001 From: Gonzalo Date: Fri, 25 Dec 2020 23:47:14 -0300 Subject: [PATCH 046/113] build: update rubocop to earlier version fixing the LineLength crash --- .rubocop.yml | 1 + rack-attack.gemspec | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.rubocop.yml b/.rubocop.yml index 45bd079c..745bb907 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -8,6 +8,7 @@ inherit_mode: AllCops: TargetRubyVersion: 2.4 DisabledByDefault: true + NewCops: disable Exclude: - "examples/instrumentation.rb" - "gemfiles/**/*" diff --git a/rack-attack.gemspec b/rack-attack.gemspec index 69042f1e..e1c12a08 100644 --- a/rack-attack.gemspec +++ b/rack-attack.gemspec @@ -37,7 +37,7 @@ Gem::Specification.new do |s| s.add_development_dependency "minitest-stub-const", "~> 0.6" s.add_development_dependency 'rack-test', "~> 1.0" s.add_development_dependency 'rake', "~> 13.0" - s.add_development_dependency "rubocop", "0.84.0" + s.add_development_dependency "rubocop", "0.89.1" s.add_development_dependency "rubocop-performance", "~> 1.5.0" s.add_development_dependency "timecop", "~> 0.9.1" From 26476670f40239ff51bb3ab1843e7b71a4255fd4 Mon Sep 17 00:00:00 2001 From: Gonzalo Date: Fri, 25 Dec 2020 23:51:59 -0300 Subject: [PATCH 047/113] docs: update repo references after move to rack org --- CHANGELOG.md | 92 +++++++++++++++++----------------- CONTRIBUTING.md | 8 +-- README.md | 4 +- docs/advanced_configuration.md | 12 ++--- rack-attack.gemspec | 8 +-- 5 files changed, 62 insertions(+), 62 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d44d1e0d..a7940ac9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -96,9 +96,9 @@ All notable changes to this project will be documented in this file. ### Added -- Support "plain" `Redis` as a cache store backend ([#280](https://github.com/kickstarter/rack-attack/pull/280)). Thanks @bfad and @ryandv. +- Support "plain" `Redis` as a cache store backend ([#280](https://github.com/rack/rack-attack/pull/280)). Thanks @bfad and @ryandv. - When overwriting `Rack::Attack.throttled_response` you can now access the exact epoch integer that was used for caching -so your custom code is less prone to race conditions ([#282](https://github.com/kickstarter/rack-attack/pull/282)). Thanks @doliveirakn. +so your custom code is less prone to race conditions ([#282](https://github.com/rack/rack-attack/pull/282)). Thanks @doliveirakn. ### Dependency changes @@ -120,43 +120,43 @@ so your custom code is less prone to race conditions ([#282](https://github.com/ ### Added -- Add support for [`ActiveSupport::Cache::RedisCacheStore`](http://api.rubyonrails.org/classes/ActiveSupport/Cache/RedisCacheStore.html) as a store backend ([#340](https://github.com/kickstarter/rack-attack/pull/340) and [#350](https://github.com/kickstarter/rack-attack/pull/350)) +- Add support for [`ActiveSupport::Cache::RedisCacheStore`](http://api.rubyonrails.org/classes/ActiveSupport/Cache/RedisCacheStore.html) as a store backend ([#340](https://github.com/rack/rack-attack/pull/340) and [#350](https://github.com/rack/rack-attack/pull/350)) ## [5.2.0] - 2018-03-29 ### Added -- Shorthand for blocking an IP address `Rack::Attack.blocklist_ip("1.2.3.4")` ([#320](https://github.com/kickstarter/rack-attack/pull/320)) -- Shorthand for blocking an IP subnet `Rack::Attack.blocklist_ip("1.2.0.0/16")` ([#320](https://github.com/kickstarter/rack-attack/pull/320)) -- Shorthand for safelisting an IP address `Rack::Attack.safelist_ip("5.6.7.8")` ([#320](https://github.com/kickstarter/rack-attack/pull/320)) -- Shorthand for safelisting an IP subnet `Rack::Attack.safelist_ip("5.6.0.0/16")` ([#320](https://github.com/kickstarter/rack-attack/pull/320)) -- Throw helpful error message when using `allow2ban` but cache store is misconfigured ([#315](https://github.com/kickstarter/rack-attack/issues/315)) -- Throw helpful error message when using `fail2ban` but cache store is misconfigured ([#315](https://github.com/kickstarter/rack-attack/issues/315)) +- Shorthand for blocking an IP address `Rack::Attack.blocklist_ip("1.2.3.4")` ([#320](https://github.com/rack/rack-attack/pull/320)) +- Shorthand for blocking an IP subnet `Rack::Attack.blocklist_ip("1.2.0.0/16")` ([#320](https://github.com/rack/rack-attack/pull/320)) +- Shorthand for safelisting an IP address `Rack::Attack.safelist_ip("5.6.7.8")` ([#320](https://github.com/rack/rack-attack/pull/320)) +- Shorthand for safelisting an IP subnet `Rack::Attack.safelist_ip("5.6.0.0/16")` ([#320](https://github.com/rack/rack-attack/pull/320)) +- Throw helpful error message when using `allow2ban` but cache store is misconfigured ([#315](https://github.com/rack/rack-attack/issues/315)) +- Throw helpful error message when using `fail2ban` but cache store is misconfigured ([#315](https://github.com/rack/rack-attack/issues/315)) ## [5.1.0] - 2018-03-10 - - Fixes edge case bug when using ruby 2.5.0 and redis [#253](https://github.com/kickstarter/rack-attack/issues/253) ([#271](https://github.com/kickstarter/rack-attack/issues/271)) - - Throws errors with better semantics when missing or misconfigured store caches to aid in developers debugging their configs ([#274](https://github.com/kickstarter/rack-attack/issues/274)) - - Removed legacy code that was originally intended for Rails 3 apps ([#264](https://github.com/kickstarter/rack-attack/issues/264)) + - Fixes edge case bug when using ruby 2.5.0 and redis [#253](https://github.com/rack/rack-attack/issues/253) ([#271](https://github.com/rack/rack-attack/issues/271)) + - Throws errors with better semantics when missing or misconfigured store caches to aid in developers debugging their configs ([#274](https://github.com/rack/rack-attack/issues/274)) + - Removed legacy code that was originally intended for Rails 3 apps ([#264](https://github.com/rack/rack-attack/issues/264)) ## [5.0.1] - 2016-08-11 - - Fixes arguments passed to deprecated internal methods. ([#198](https://github.com/kickstarter/rack-attack/issues/198)) + - Fixes arguments passed to deprecated internal methods. ([#198](https://github.com/rack/rack-attack/issues/198)) ## [5.0.0] - 2016-08-09 - - Deprecate `whitelist`/`blacklist` in favor of `safelist`/`blocklist`. ([#181](https://github.com/kickstarter/rack-attack/issues/181), + - Deprecate `whitelist`/`blacklist` in favor of `safelist`/`blocklist`. ([#181](https://github.com/rack/rack-attack/issues/181), thanks @renee-travisci). To upgrade and fix deprecations, find and replace instances of `whitelist` and `blacklist` with `safelist` and `blocklist`. If you reference `rack.attack.match_type`, note that it will have values like `:safelist`/`:blocklist`. - Remove test coverage for unsupported ruby dependencies: ruby 2.0, activesupport 3.2/4.0, and dalli 1. ## [4.4.1] - 2016-02-17 - Fix a bug affecting apps using Redis::Store and ActiveSupport that could generate an error - saying dalli was a required dependency. I learned all about ActiveSupport autoloading. ([#165](https://github.com/kickstarter/rack-attack/issues/165)) + saying dalli was a required dependency. I learned all about ActiveSupport autoloading. ([#165](https://github.com/rack/rack-attack/issues/165)) ## [4.4.0] - 2016-02-10 - - New: support for MemCacheStore ([#153](https://github.com/kickstarter/rack-attack/issues/153)). Thanks @elhu. + - New: support for MemCacheStore ([#153](https://github.com/rack/rack-attack/issues/153)). Thanks @elhu. - Some documentation and test harness improvements. ## [4.3.1] - 2015-12-18 @@ -222,36 +222,36 @@ so your custom code is less prone to race conditions ([#282](https://github.com/ - Remove unused variable - Extract mandatory options to constants -[6.3.1]: https://github.com/kickstarter/rack-attack/compare/v6.3.0...v6.3.1/ -[6.3.0]: https://github.com/kickstarter/rack-attack/compare/v6.2.2...v6.3.0/ -[6.2.2]: https://github.com/kickstarter/rack-attack/compare/v6.2.1...v6.2.2/ -[6.2.1]: https://github.com/kickstarter/rack-attack/compare/v6.2.0...v6.2.1/ -[6.2.0]: https://github.com/kickstarter/rack-attack/compare/v6.1.0...v6.2.0/ -[6.1.0]: https://github.com/kickstarter/rack-attack/compare/v6.0.0...v6.1.0/ -[6.0.0]: https://github.com/kickstarter/rack-attack/compare/v5.4.2...v6.0.0/ -[5.4.2]: https://github.com/kickstarter/rack-attack/compare/v5.4.1...v5.4.2/ -[5.4.1]: https://github.com/kickstarter/rack-attack/compare/v5.4.0...v5.4.1/ -[5.4.0]: https://github.com/kickstarter/rack-attack/compare/v5.3.2...v5.4.0/ -[5.3.2]: https://github.com/kickstarter/rack-attack/compare/v5.3.1...v5.3.2/ -[5.3.1]: https://github.com/kickstarter/rack-attack/compare/v5.3.0...v5.3.1/ -[5.3.0]: https://github.com/kickstarter/rack-attack/compare/v5.2.0...v5.3.0/ -[5.2.0]: https://github.com/kickstarter/rack-attack/compare/v5.1.0...v5.2.0/ -[5.1.0]: https://github.com/kickstarter/rack-attack/compare/v5.0.1...v5.1.0/ -[5.0.1]: https://github.com/kickstarter/rack-attack/compare/v5.0.0...v5.0.1/ -[5.0.0]: https://github.com/kickstarter/rack-attack/compare/v4.4.1...v5.0.0/ -[4.4.1]: https://github.com/kickstarter/rack-attack/compare/v4.4.0...v4.4.1/ -[4.4.0]: https://github.com/kickstarter/rack-attack/compare/v4.3.1...v4.4.0/ -[4.3.1]: https://github.com/kickstarter/rack-attack/compare/v4.3.0...v4.3.1/ -[4.3.0]: https://github.com/kickstarter/rack-attack/compare/v4.2.0...v4.3.0/ -[4.2.0]: https://github.com/kickstarter/rack-attack/compare/v4.1.1...v4.2.0/ -[4.1.1]: https://github.com/kickstarter/rack-attack/compare/v4.1.0...v4.1.1/ -[4.1.0]: https://github.com/kickstarter/rack-attack/compare/v4.0.1...v4.1.0/ -[4.0.1]: https://github.com/kickstarter/rack-attack/compare/v4.0.0...v4.0.1/ -[4.0.0]: https://github.com/kickstarter/rack-attack/compare/v3.0.0...v4.0.0/ -[3.0.0]: https://github.com/kickstarter/rack-attack/compare/v2.3.0...v3.0.0/ -[2.3.0]: https://github.com/kickstarter/rack-attack/compare/v2.2.1...v2.3.0/ -[2.2.1]: https://github.com/kickstarter/rack-attack/compare/v2.2.0...v2.2.1/ -[2.2.0]: https://github.com/kickstarter/rack-attack/compare/v2.1.1...v2.2.0/ +[6.3.1]: https://github.com/rack/rack-attack/compare/v6.3.0...v6.3.1/ +[6.3.0]: https://github.com/rack/rack-attack/compare/v6.2.2...v6.3.0/ +[6.2.2]: https://github.com/rack/rack-attack/compare/v6.2.1...v6.2.2/ +[6.2.1]: https://github.com/rack/rack-attack/compare/v6.2.0...v6.2.1/ +[6.2.0]: https://github.com/rack/rack-attack/compare/v6.1.0...v6.2.0/ +[6.1.0]: https://github.com/rack/rack-attack/compare/v6.0.0...v6.1.0/ +[6.0.0]: https://github.com/rack/rack-attack/compare/v5.4.2...v6.0.0/ +[5.4.2]: https://github.com/rack/rack-attack/compare/v5.4.1...v5.4.2/ +[5.4.1]: https://github.com/rack/rack-attack/compare/v5.4.0...v5.4.1/ +[5.4.0]: https://github.com/rack/rack-attack/compare/v5.3.2...v5.4.0/ +[5.3.2]: https://github.com/rack/rack-attack/compare/v5.3.1...v5.3.2/ +[5.3.1]: https://github.com/rack/rack-attack/compare/v5.3.0...v5.3.1/ +[5.3.0]: https://github.com/rack/rack-attack/compare/v5.2.0...v5.3.0/ +[5.2.0]: https://github.com/rack/rack-attack/compare/v5.1.0...v5.2.0/ +[5.1.0]: https://github.com/rack/rack-attack/compare/v5.0.1...v5.1.0/ +[5.0.1]: https://github.com/rack/rack-attack/compare/v5.0.0...v5.0.1/ +[5.0.0]: https://github.com/rack/rack-attack/compare/v4.4.1...v5.0.0/ +[4.4.1]: https://github.com/rack/rack-attack/compare/v4.4.0...v4.4.1/ +[4.4.0]: https://github.com/rack/rack-attack/compare/v4.3.1...v4.4.0/ +[4.3.1]: https://github.com/rack/rack-attack/compare/v4.3.0...v4.3.1/ +[4.3.0]: https://github.com/rack/rack-attack/compare/v4.2.0...v4.3.0/ +[4.2.0]: https://github.com/rack/rack-attack/compare/v4.1.1...v4.2.0/ +[4.1.1]: https://github.com/rack/rack-attack/compare/v4.1.0...v4.1.1/ +[4.1.0]: https://github.com/rack/rack-attack/compare/v4.0.1...v4.1.0/ +[4.0.1]: https://github.com/rack/rack-attack/compare/v4.0.0...v4.0.1/ +[4.0.0]: https://github.com/rack/rack-attack/compare/v3.0.0...v4.0.0/ +[3.0.0]: https://github.com/rack/rack-attack/compare/v2.3.0...v3.0.0/ +[2.3.0]: https://github.com/rack/rack-attack/compare/v2.2.1...v2.3.0/ +[2.2.1]: https://github.com/rack/rack-attack/compare/v2.2.0...v2.2.1/ +[2.2.0]: https://github.com/rack/rack-attack/compare/v2.1.1...v2.2.0/ [@fatkodima]: https://github.com/fatkodima [@rofreg]: https://github.com/rofreg diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d6645611..63817fc7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,12 +8,12 @@ This project is intended to be a safe, welcoming space for collaboration, and co Any of the following is greatly appreciated: -* Helping users by answering to their [questions](https://github.com/kickstarter/rack-attack/issues?q=is%3Aopen+is%3Aissue+label%3A%22type%3A+question%22) -* Helping users troubleshoot their [error reports](https://github.com/kickstarter/rack-attack/issues?q=is%3Aissue+is%3Aopen+label%3A%22type%3A+error+report%22) to figure out if the error is caused by an actual bug or some misconfiguration -* Giving feedback by commenting in other users [feature requests](https://github.com/kickstarter/rack-attack/issues?q=is%3Aissue+is%3Aopen+label%3A%22type%3A+feature+request%22) +* Helping users by answering to their [questions](https://github.com/rack/rack-attack/issues?q=is%3Aopen+is%3Aissue+label%3A%22type%3A+question%22) +* Helping users troubleshoot their [error reports](https://github.com/rack/rack-attack/issues?q=is%3Aissue+is%3Aopen+label%3A%22type%3A+error+report%22) to figure out if the error is caused by an actual bug or some misconfiguration +* Giving feedback by commenting in other users [feature requests](https://github.com/rack/rack-attack/issues?q=is%3Aissue+is%3Aopen+label%3A%22type%3A+feature+request%22) * Reporting an error you are experiencing * Suggesting a new feature you think it would be useful for many users -* If you want to work on fixing an actual issue and you don't know where to start, those labeled [good first issue](https://github.com/kickstarter/rack-attack/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) may be a good choice +* If you want to work on fixing an actual issue and you don't know where to start, those labeled [good first issue](https://github.com/rack/rack-attack/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) may be a good choice ## Style Guide diff --git a/README.md b/README.md index b33c64a3..43539e45 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ __Note__: You are viewing the development version README. -For the README consistent with the latest released version see https://github.com/kickstarter/rack-attack/blob/6-stable/README.md. +For the README consistent with the latest released version see https://github.com/rack/rack-attack/blob/6-stable/README.md. # Rack::Attack @@ -10,7 +10,7 @@ Protect your Rails and Rack apps from bad clients. Rack::Attack lets you easily See the [Backing & Hacking blog post](https://www.kickstarter.com/backing-and-hacking/rack-attack-protection-from-abusive-clients) introducing Rack::Attack. [![Gem Version](https://badge.fury.io/rb/rack-attack.svg)](https://badge.fury.io/rb/rack-attack) -[![Build Status](https://travis-ci.org/kickstarter/rack-attack.svg?branch=master)](https://travis-ci.org/kickstarter/rack-attack) +[![Build Status](https://travis-ci.org/rack/rack-attack.svg?branch=master)](https://travis-ci.org/rack/rack-attack) [![Code Climate](https://codeclimate.com/github/kickstarter/rack-attack.svg)](https://codeclimate.com/github/kickstarter/rack-attack) [![Join the chat at https://gitter.im/rack-attack/rack-attack](https://badges.gitter.im/rack-attack/rack-attack.svg)](https://gitter.im/rack-attack/rack-attack) diff --git a/docs/advanced_configuration.md b/docs/advanced_configuration.md index 0fd1fe4c..6d8737ea 100644 --- a/docs/advanced_configuration.md +++ b/docs/advanced_configuration.md @@ -6,7 +6,7 @@ If you're feeling ambitious or you have a very particular use-case for Rack::Att ### Exponential Backoff -By layering throttles with linearly increasing limits and exponentially increasing periods, you can mimic an exponential backoff throttle. See [#106](https://github.com/kickstarter/rack-attack/issues/106) for more discussion. +By layering throttles with linearly increasing limits and exponentially increasing periods, you can mimic an exponential backoff throttle. See [#106](https://github.com/rack/rack-attack/issues/106) for more discussion. ```ruby # Allows 20 requests in 8 seconds @@ -24,7 +24,7 @@ end ### Rack::Attack::Request Helpers -You can define helpers on requests like `localhost?` or `subdomain` by monkey-patching `Rack::Attack::Request`. See [#73](https://github.com/kickstarter/rack-attack/issues/73) for more discussion. +You can define helpers on requests like `localhost?` or `subdomain` by monkey-patching `Rack::Attack::Request`. See [#73](https://github.com/rack/rack-attack/issues/73) for more discussion. ```ruby class Rack::Attack::Request < ::Rack::Request @@ -38,7 +38,7 @@ Rack::Attack.safelist("localhost") { |req| req.localhost? } ### Blocklisting From ENV Variables -You can have `Rack::Attack` configure its blocklists from ENV variables to simplify maintenance. See [#110](https://github.com/kickstarter/rack-attack/issues/110) for more discussion. +You can have `Rack::Attack` configure its blocklists from ENV variables to simplify maintenance. See [#110](https://github.com/rack/rack-attack/issues/110) for more discussion. ```ruby class Rack::Attack @@ -57,7 +57,7 @@ end ### Reset Specific Throttles -By doing a bunch of monkey-patching, you can add a helper for resetting specific throttles. The implementation is kind of long, so see [#113](https://github.com/kickstarter/rack-attack/issues/113) for more discussion. +By doing a bunch of monkey-patching, you can add a helper for resetting specific throttles. The implementation is kind of long, so see [#113](https://github.com/rack/rack-attack/issues/113) for more discussion. ```ruby Rack::Attack.reset_throttle "logins/email", "user@example.com" @@ -65,7 +65,7 @@ Rack::Attack.reset_throttle "logins/email", "user@example.com" ### Blocklisting From Rails.cache -You can configure blocklists to check values stored in `Rails.cache` to allow setting blocklists from inside your application. See [#111](https://github.com/kickstarter/rack-attack/issues/111) for more discussion. +You can configure blocklists to check values stored in `Rails.cache` to allow setting blocklists from inside your application. See [#111](https://github.com/rack/rack-attack/issues/111) for more discussion. ```ruby # Block attacks from IPs in cache @@ -78,7 +78,7 @@ end ### Throttle Basic Auth Crackers -An example implementation for blocking hackers who spam basic auth attempts. See [#47](https://github.com/kickstarter/rack-attack/issues/47) for more discussion. +An example implementation for blocking hackers who spam basic auth attempts. See [#47](https://github.com/rack/rack-attack/issues/47) for more discussion. ```ruby # After 5 requests with incorrect auth in 1 minute, diff --git a/rack-attack.gemspec b/rack-attack.gemspec index e1c12a08..34689a36 100644 --- a/rack-attack.gemspec +++ b/rack-attack.gemspec @@ -15,16 +15,16 @@ Gem::Specification.new do |s| s.email = "aaron@ktheory.com" s.files = Dir.glob("{bin,lib}/**/*") + %w(Rakefile README.md) - s.homepage = 'https://github.com/kickstarter/rack-attack' + s.homepage = 'https://github.com/rack/rack-attack' s.rdoc_options = ["--charset=UTF-8"] s.require_paths = ["lib"] s.summary = 'Block & throttle abusive requests' s.test_files = Dir.glob("spec/**/*") s.metadata = { - "bug_tracker_uri" => "https://github.com/kickstarter/rack-attack/issues", - "changelog_uri" => "https://github.com/kickstarter/rack-attack/blob/master/CHANGELOG.md", - "source_code_uri" => "https://github.com/kickstarter/rack-attack" + "bug_tracker_uri" => "https://github.com/rack/rack-attack/issues", + "changelog_uri" => "https://github.com/rack/rack-attack/blob/master/CHANGELOG.md", + "source_code_uri" => "https://github.com/rack/rack-attack" } s.required_ruby_version = '>= 2.4' From 55d5e370fd048a558c554dbaa521de99daa730ed Mon Sep 17 00:00:00 2001 From: Gonzalo Date: Fri, 25 Dec 2020 23:55:58 -0300 Subject: [PATCH 048/113] test: update ruby and rails versions --- .travis.yml | 26 ++++++++++++------- Appraisals | 12 ++++----- .../active_support_redis_cache_store.gemfile | 2 +- ...e_support_redis_cache_store_pooled.gemfile | 2 +- .../{rails_5_1.gemfile => rails_6_1.gemfile} | 2 +- rack-attack.gemspec | 2 +- ...e_support_redis_cache_store_pooled_spec.rb | 2 +- .../active_support_redis_cache_store_spec.rb | 2 +- 8 files changed, 28 insertions(+), 22 deletions(-) rename gemfiles/{rails_5_1.gemfile => rails_6_1.gemfile} (77%) diff --git a/.travis.yml b/.travis.yml index 1b876135..03ee277f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,11 +3,11 @@ language: ruby cache: bundler rvm: - - ruby-head - - 2.7.1 + - 3.0.0 + - 2.7.2 - 2.6.6 - 2.5.8 - - 2.4.10 + before_install: - yes | gem update --system @@ -16,9 +16,9 @@ before_install: gemfile: - gemfiles/rack_2.gemfile - gemfiles/rack_1.gemfile + - gemfiles/rails_6_1.gemfile - gemfiles/rails_6_0.gemfile - gemfiles/rails_5_2.gemfile - - gemfiles/rails_5_1.gemfile - gemfiles/rails_4_2.gemfile - gemfiles/dalli2.gemfile - gemfiles/redis_4.gemfile @@ -30,15 +30,21 @@ gemfile: - gemfiles/active_support_redis_store.gemfile matrix: - allow_failures: - - rvm: ruby-head exclude: - gemfile: gemfiles/rack_1.gemfile - rvm: 2.7.1 + rvm: 3.0.0 + - gemfile: gemfiles/rails_5_2.gemfile + rvm: 3.0.0 + - gemfile: gemfiles/rails_4_2.gemfile + rvm: 3.0.0 + - gemfile: gemfiles/dalli2.gemfile + rvm: 3.0.0 + - gemfile: gemfiles/connection_pool_dalli.gemfile + rvm: 3.0.0 + - gemfile: gemfiles/rack_1.gemfile + rvm: 2.7.2 - gemfile: gemfiles/rails_4_2.gemfile - rvm: 2.7.1 - - gemfile: gemfiles/rails_6_0.gemfile - rvm: 2.4.10 + rvm: 2.7.2 fast_finish: true services: diff --git a/Appraisals b/Appraisals index ef44d14e..03c6a706 100644 --- a/Appraisals +++ b/Appraisals @@ -17,6 +17,10 @@ appraise "rack_1" do gem "rack-test", ">= 0.6" end +appraise 'rails_6-1' do + gem 'railties', '~> 6.1.0' +end + appraise 'rails_6-0' do gem 'railties', '~> 6.0.0' end @@ -25,10 +29,6 @@ appraise 'rails_5-2' do gem 'railties', '~> 5.2.0' end -appraise 'rails_5-1' do - gem 'railties', '~> 5.1.0' -end - appraise 'rails_4-2' do gem 'railties', '~> 4.2.0' @@ -55,12 +55,12 @@ appraise "connection_pool_dalli" do end appraise "active_support_redis_cache_store" do - gem "activesupport", "~> 5.2.0" + gem "activesupport", "~> 6.1.0" gem "redis", "~> 4.0" end appraise "active_support_redis_cache_store_pooled" do - gem "activesupport", "~> 5.2.0" + gem "activesupport", "~> 6.1.0" gem "connection_pool", "~> 2.2" gem "redis", "~> 4.0" end diff --git a/gemfiles/active_support_redis_cache_store.gemfile b/gemfiles/active_support_redis_cache_store.gemfile index 30e1e38b..8acbbe13 100644 --- a/gemfiles/active_support_redis_cache_store.gemfile +++ b/gemfiles/active_support_redis_cache_store.gemfile @@ -2,7 +2,7 @@ source "https://rubygems.org" -gem "activesupport", "~> 5.2.0" +gem "activesupport", "~> 6.1.0" gem "redis", "~> 4.0" gemspec path: "../" diff --git a/gemfiles/active_support_redis_cache_store_pooled.gemfile b/gemfiles/active_support_redis_cache_store_pooled.gemfile index 9232a9b5..cf1e92e2 100644 --- a/gemfiles/active_support_redis_cache_store_pooled.gemfile +++ b/gemfiles/active_support_redis_cache_store_pooled.gemfile @@ -2,7 +2,7 @@ source "https://rubygems.org" -gem "activesupport", "~> 5.2.0" +gem "activesupport", "~> 6.1.0" gem "connection_pool", "~> 2.2" gem "redis", "~> 4.0" diff --git a/gemfiles/rails_5_1.gemfile b/gemfiles/rails_6_1.gemfile similarity index 77% rename from gemfiles/rails_5_1.gemfile rename to gemfiles/rails_6_1.gemfile index 66a5a0b9..4c467fec 100644 --- a/gemfiles/rails_5_1.gemfile +++ b/gemfiles/rails_6_1.gemfile @@ -2,6 +2,6 @@ source "https://rubygems.org" -gem "railties", "~> 5.1.0" +gem "railties", "~> 6.1.0" gemspec path: "../" diff --git a/rack-attack.gemspec b/rack-attack.gemspec index 34689a36..75ca531e 100644 --- a/rack-attack.gemspec +++ b/rack-attack.gemspec @@ -46,5 +46,5 @@ Gem::Specification.new do |s| s.add_development_dependency 'byebug', '~> 11.0' end - s.add_development_dependency 'railties', '>= 4.2', '< 6.1' + s.add_development_dependency 'railties', '>= 4.2', '< 6.2' end diff --git a/spec/acceptance/stores/active_support_redis_cache_store_pooled_spec.rb b/spec/acceptance/stores/active_support_redis_cache_store_pooled_spec.rb index 9c26e8d6..fe951074 100644 --- a/spec/acceptance/stores/active_support_redis_cache_store_pooled_spec.rb +++ b/spec/acceptance/stores/active_support_redis_cache_store_pooled_spec.rb @@ -21,6 +21,6 @@ Rack::Attack.cache.store.clear end - it_works_for_cache_backed_features(fetch_from_store: ->(key) { Rack::Attack.cache.store.fetch(key) }) + it_works_for_cache_backed_features(fetch_from_store: ->(key) { Rack::Attack.cache.store.read(key) }) end end diff --git a/spec/acceptance/stores/active_support_redis_cache_store_spec.rb b/spec/acceptance/stores/active_support_redis_cache_store_spec.rb index f595ec2a..a824edea 100644 --- a/spec/acceptance/stores/active_support_redis_cache_store_spec.rb +++ b/spec/acceptance/stores/active_support_redis_cache_store_spec.rb @@ -20,6 +20,6 @@ Rack::Attack.cache.store.clear end - it_works_for_cache_backed_features(fetch_from_store: ->(key) { Rack::Attack.cache.store.fetch(key) }) + it_works_for_cache_backed_features(fetch_from_store: ->(key) { Rack::Attack.cache.store.read(key) }) end end From 5945fbcdf537b87f92a4d942d44a6ce140551e8e Mon Sep 17 00:00:00 2001 From: brchristian Date: Sat, 16 Jan 2021 10:19:33 -0800 Subject: [PATCH 049/113] Use single quotes in example configuration --- docs/example_configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/example_configuration.md b/docs/example_configuration.md index e1aaa385..b30a4ab0 100644 --- a/docs/example_configuration.md +++ b/docs/example_configuration.md @@ -59,7 +59,7 @@ class Rack::Attack # throttle logins for another user and force their login requests to be # denied, but that's not very common and shouldn't happen to you. (Knock # on wood!) - throttle("logins/email", limit: 5, period: 20.seconds) do |req| + throttle('logins/email', limit: 5, period: 20.seconds) do |req| if req.path == '/login' && req.post? # Normalize the email, using the same logic as your authentication process, to # protect against rate limit bypasses. Return the normalized email if present, nil otherwise. From 1d2c646ae17bc06f55678e9d2f182fa953069107 Mon Sep 17 00:00:00 2001 From: fatkodima Date: Fri, 18 Oct 2019 01:23:51 +0300 Subject: [PATCH 050/113] Remove support for ruby 2.3 (cherry picked from commit 56361ab56b96c909d6796f05e05bf224dd05d050) --- .rubocop.yml | 2 +- .travis.yml | 3 --- rack-attack.gemspec | 2 +- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 7fe2ca65..b12c0b4b 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -6,7 +6,7 @@ inherit_mode: - Exclude AllCops: - TargetRubyVersion: 2.3 + TargetRubyVersion: 2.4 DisabledByDefault: true Exclude: - "examples/instrumentation.rb" diff --git a/.travis.yml b/.travis.yml index 98432ae6..1b876135 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,6 @@ rvm: - 2.6.6 - 2.5.8 - 2.4.10 - - 2.3.8 before_install: - yes | gem update --system @@ -40,8 +39,6 @@ matrix: rvm: 2.7.1 - gemfile: gemfiles/rails_6_0.gemfile rvm: 2.4.10 - - gemfile: gemfiles/rails_6_0.gemfile - rvm: 2.3.8 fast_finish: true services: diff --git a/rack-attack.gemspec b/rack-attack.gemspec index 99deeb72..28dd3f8d 100644 --- a/rack-attack.gemspec +++ b/rack-attack.gemspec @@ -27,7 +27,7 @@ Gem::Specification.new do |s| "source_code_uri" => "https://github.com/kickstarter/rack-attack" } - s.required_ruby_version = '>= 2.3' + s.required_ruby_version = '>= 2.4' s.add_runtime_dependency 'rack', ">= 1.0", "< 3" From 614e10aa9c01fcb191e8d7c86d94335c9859c13e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Spie=C3=9F?= Date: Thu, 12 Dec 2019 18:13:13 +0100 Subject: [PATCH 051/113] Use gender-neutral pronoun in Readme (cherry picked from commit 58b4042e3576bd704ea46b6808f4a4822db71464) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 96d0db8a..69c92ad6 100644 --- a/README.md +++ b/README.md @@ -343,7 +343,7 @@ end While Rack::Attack's primary focus is minimizing harm from abusive clients, it can also be used to return rate limit data that's helpful for well-behaved clients. -If you want to return to user how many seconds to wait until he can start sending requests again, this can be done through enabling `Retry-After` header: +If you want to return to user how many seconds to wait until they can start sending requests again, this can be done through enabling `Retry-After` header: ```ruby Rack::Attack.throttled_response_retry_after_header = true ``` From 9da0bb77125b75364125160695f690ea20157e5b Mon Sep 17 00:00:00 2001 From: Ryan Laughlin Date: Mon, 18 May 2020 17:41:58 -0400 Subject: [PATCH 052/113] Consolidate #testing section of README (cherry picked from commit 029b5481fe4e5a729d4ac568eee8a7143b4e0ed1) --- README.md | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 69c92ad6..6a5dea5e 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,6 @@ See the [Backing & Hacking blog post](https://www.kickstarter.com/backing-and-ha - [Testing](#testing) - [How it works](#how-it-works) - [About Tracks](#about-tracks) -- [Testing](#testing) - [Performance](#performance) - [Motivation](#motivation) - [Contributing](#contributing) @@ -401,6 +400,10 @@ end ## Testing +A note on developing and testing apps using Rack::Attack - if you are using throttling in particular, you will +need to enable the cache in your development environment. See [Caching with Rails](http://guides.rubyonrails.org/caching_with_rails.html) +for more on how to do this. + ### Disabling `Rack::Attack.enabled = false` can be used to either completely disable Rack::Attack in your tests, or to disable/enable for specific test cases only. @@ -445,13 +448,6 @@ can cleanly monkey patch helper methods onto the `Rack::Attack.track` doesn't affect request processing. Tracks are an easy way to log and measure requests matching arbitrary attributes. - -## Testing - -A note on developing and testing apps using Rack::Attack - if you are using throttling in particular, you will -need to enable the cache in your development environment. See [Caching with Rails](http://guides.rubyonrails.org/caching_with_rails.html) -for more on how to do this. - ## Performance The overhead of running Rack::Attack is typically negligible (a few milliseconds per request), From 44b6a7353a342b416fae61c63ab33f324143d5c1 Mon Sep 17 00:00:00 2001 From: Olle Jonsson Date: Sun, 24 May 2020 17:50:56 +0200 Subject: [PATCH 053/113] Use RuboCop 0.84.0 - this enables each of the new Cops and marks each with the version they appeared in (cherry picked from commit c07fcdde434b6864c556baf0c5adf1e0edab854c) --- .rubocop.yml | 18 +++++++++++++++--- lib/rack/attack/cache.rb | 1 + lib/rack/attack/check.rb | 1 + lib/rack/attack/throttle.rb | 1 + rack-attack.gemspec | 2 +- 5 files changed, 19 insertions(+), 4 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index b12c0b4b..8426f7fa 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -21,12 +21,27 @@ Gemspec: Layout: Enabled: true +Layout/EmptyLinesAroundAttributeAccessor: # (0.83) + Enabled: true + +Layout/SpaceAroundMethodCallOperator: # (0.82) + Enabled: true + Layout/LineLength: Max: 120 Lint: Enabled: true +Lint/DeprecatedOpenSSLConstant: # (0.84) + Enabled: true + +Lint/RaiseException: # (0.81) + Enabled: true + +Lint/StructNewOverride: # (0.81) + Enabled: true + Naming: Enabled: true Exclude: @@ -42,9 +57,6 @@ Style/BlockDelimiters: Enabled: true IgnoredMethods: [] # Workaround rubocop bug: https://github.com/rubocop-hq/rubocop/issues/6179 -Style/BracesAroundHashParameters: - Enabled: true - Style/ClassAndModuleChildren: Enabled: true Exclude: diff --git a/lib/rack/attack/cache.rb b/lib/rack/attack/cache.rb index 0defa646..c74bc2ad 100644 --- a/lib/rack/attack/cache.rb +++ b/lib/rack/attack/cache.rb @@ -12,6 +12,7 @@ def initialize end attr_reader :store + def store=(store) @store = StoreProxy.build(store) end diff --git a/lib/rack/attack/check.rb b/lib/rack/attack/check.rb index 4c985ebe..c9f3ff7d 100644 --- a/lib/rack/attack/check.rb +++ b/lib/rack/attack/check.rb @@ -4,6 +4,7 @@ module Rack class Attack class Check attr_reader :name, :block, :type + def initialize(name, options = {}, &block) @name = name @block = block diff --git a/lib/rack/attack/throttle.rb b/lib/rack/attack/throttle.rb index 3b80d9e7..96c0f3be 100644 --- a/lib/rack/attack/throttle.rb +++ b/lib/rack/attack/throttle.rb @@ -6,6 +6,7 @@ class Throttle MANDATORY_OPTIONS = [:limit, :period].freeze attr_reader :name, :limit, :period, :block, :type + def initialize(name, options, &block) @name = name @block = block diff --git a/rack-attack.gemspec b/rack-attack.gemspec index 28dd3f8d..69042f1e 100644 --- a/rack-attack.gemspec +++ b/rack-attack.gemspec @@ -37,7 +37,7 @@ Gem::Specification.new do |s| s.add_development_dependency "minitest-stub-const", "~> 0.6" s.add_development_dependency 'rack-test', "~> 1.0" s.add_development_dependency 'rake', "~> 13.0" - s.add_development_dependency "rubocop", "0.78.0" + s.add_development_dependency "rubocop", "0.84.0" s.add_development_dependency "rubocop-performance", "~> 1.5.0" s.add_development_dependency "timecop", "~> 0.9.1" From 48531f546c53230050f31e848f8746565b7c4150 Mon Sep 17 00:00:00 2001 From: Tristan Toye Date: Sun, 7 Jun 2020 12:11:30 -0400 Subject: [PATCH 054/113] Clarify Calling HTTP_APIKey header in example (#488) * Clarify Calling HTTP_ header in example In trying to track down a bug here turns out I was trying to reference the wrong header shown in the readme. Printing our `request.env` it becomes clear this is just the full request object: ``` {"rack.version"=>[1, 3], "rack.errors"=>#>, "rack.multithread"=>true, "rack.multiprocess"=>false, "rack.run_once"=>false, "SCRIPT_NAME"=>"", "QUERY_STRING"=>"", "SERVER_PROTOCOL"=>"HTTP/1.1", "SERVER_SOFTWARE"=>"puma 4.3.5 Mysterious Traveller", "GATEWAY_INTERFACE"=>"CGI/1.2", "REQUEST_METHOD"=>"POST", "REQUEST_PATH"=>"/api/v1/....", "REQUEST_URI"=>"/api/v1/...", "HTTP_VERSION"=>"HTTP/1.1", "HTTP_HOST"=>"example.com", "HTTP_APIKEY"=>"secret_key", "CONTENT_TYPE"=>"application/json", "HTTP_USER_AGENT"=>"PostmanRuntime/7.25.0", "HTTP_ACCEPT"=>"*/*", "HTTP_CACHE_CONTROL"=>"no-cache", ... ``` * Update README.md (cherry picked from commit f92889b3600c37ae0a45649fc75c7eafc8d370f5) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6a5dea5e..d43032d0 100644 --- a/README.md +++ b/README.md @@ -140,7 +140,7 @@ E.g. # Provided that trusted users use an HTTP request header named APIKey Rack::Attack.safelist("mark any authenticated access safe") do |request| # Requests are allowed if the return value is truthy - request.env["APIKey"] == "secret-string" + request.env["HTTP_APIKEY"] == "secret-string" end # Always allow requests from localhost From 1cd4a1cd79eaa31ada98300984f543d954ccae24 Mon Sep 17 00:00:00 2001 From: Eliot Sykes Date: Tue, 28 Jul 2020 11:17:00 +0100 Subject: [PATCH 055/113] Mitigate login throttle bypasses in docs This commit mitigates rate limit bypasses in the configuration docs by normalizing the email throttle key. (The normalization process used is the same as used by the Clearance gem.) --- Often an authentication process normalizes email addresses and usernames before look up, say by downcasing and removing any whitespace. Throttles that do not perform the same normalization are vulnerable to rate limit bypasses. For example, an attacker can bypass a vulnerable throttle by using unlimited case and whitespace variants for the same email address: - Variant 1: `victim@example.org` - Variant 2: `victim@example. org` (one whitespace) - Variant 3: `victim@example. org` (two whitespaces) - Variant 4: `ViCtIm@eXaMpLe.org` - etc, etc. All of these variants resolve to the same email address, but allow an attacker to bypass a vulnerable throttle. To mitigate, the email throttle key should be normalized using the same logic the authentication process uses for normalizing emails. (cherry picked from commit 03926e0b753e21a5a08ec4e4d1e7106284e047e9) --- README.md | 6 ++++-- docs/example_configuration.md | 7 ++++--- examples/rack_attack.rb | 4 +++- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index d43032d0..fe43e41f 100644 --- a/README.md +++ b/README.md @@ -263,10 +263,12 @@ Rack::Attack.throttle("requests by ip", limit: 5, period: 2) do |request| end # Throttle login attempts for a given email parameter to 6 reqs/minute -# Return the email as a discriminator on POST /login requests +# Return the *normalized* email as a discriminator on POST /login requests Rack::Attack.throttle('limit logins per email', limit: 6, period: 60) do |req| if req.path == '/login' && req.post? - req.params['email'] + # Normalize the email, using the same logic as your authentication process, to + # protect against rate limit bypasses. + req.params['email'].to_s.downcase.gsub(/\s+/, "") end end diff --git a/docs/example_configuration.md b/docs/example_configuration.md index 069f04e9..e1aaa385 100644 --- a/docs/example_configuration.md +++ b/docs/example_configuration.md @@ -53,7 +53,7 @@ class Rack::Attack # Throttle POST requests to /login by email param # - # Key: "rack::attack:#{Time.now.to_i/:period}:logins/email:#{req.email}" + # Key: "rack::attack:#{Time.now.to_i/:period}:logins/email:#{normalized_email}" # # Note: This creates a problem where a malicious user could intentionally # throttle logins for another user and force their login requests to be @@ -61,8 +61,9 @@ class Rack::Attack # on wood!) throttle("logins/email", limit: 5, period: 20.seconds) do |req| if req.path == '/login' && req.post? - # return the email if present, nil otherwise - req.params['email'].presence + # Normalize the email, using the same logic as your authentication process, to + # protect against rate limit bypasses. Return the normalized email if present, nil otherwise. + req.params['email'].to_s.downcase.gsub(/\s+/, "").presence end end diff --git a/examples/rack_attack.rb b/examples/rack_attack.rb index 43f1348a..7423f39a 100644 --- a/examples/rack_attack.rb +++ b/examples/rack_attack.rb @@ -13,8 +13,10 @@ end # Throttle login attempts per email, 10/minute/email +# Normalize the email, using the same logic as your authentication process, to +# protect against rate limit bypasses. Rack::Attack.throttle "logins/email", limit: 2, period: 60 do |req| - req.post? && req.path == "/login" && req.params['email'] + req.post? && req.path == "/login" && req.params['email'].to_s.downcase.gsub(/\s+/, "") end # blocklist bad IPs from accessing admin pages From 476144ee4002bde9e6396d7bbc16927d60ac4fbf Mon Sep 17 00:00:00 2001 From: Gonzalo Date: Fri, 25 Dec 2020 23:47:14 -0300 Subject: [PATCH 056/113] build: update rubocop to earlier version fixing the LineLength crash (cherry picked from commit cbae022df107b58997ac11f15a442a1631811b39) --- .rubocop.yml | 1 + rack-attack.gemspec | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.rubocop.yml b/.rubocop.yml index 8426f7fa..e8b5e04b 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -8,6 +8,7 @@ inherit_mode: AllCops: TargetRubyVersion: 2.4 DisabledByDefault: true + NewCops: disable Exclude: - "examples/instrumentation.rb" - "gemfiles/**/*" diff --git a/rack-attack.gemspec b/rack-attack.gemspec index 69042f1e..e1c12a08 100644 --- a/rack-attack.gemspec +++ b/rack-attack.gemspec @@ -37,7 +37,7 @@ Gem::Specification.new do |s| s.add_development_dependency "minitest-stub-const", "~> 0.6" s.add_development_dependency 'rack-test', "~> 1.0" s.add_development_dependency 'rake', "~> 13.0" - s.add_development_dependency "rubocop", "0.84.0" + s.add_development_dependency "rubocop", "0.89.1" s.add_development_dependency "rubocop-performance", "~> 1.5.0" s.add_development_dependency "timecop", "~> 0.9.1" From 63160696387c6bef31c57f4ebfacebb9a51b1a9f Mon Sep 17 00:00:00 2001 From: Gonzalo Date: Fri, 25 Dec 2020 23:51:59 -0300 Subject: [PATCH 057/113] docs: update repo references after move to rack org (cherry picked from commit 26476670f40239ff51bb3ab1843e7b71a4255fd4) --- CHANGELOG.md | 92 +++++++++++++++++----------------- CONTRIBUTING.md | 8 +-- README.md | 4 +- docs/advanced_configuration.md | 12 ++--- rack-attack.gemspec | 8 +-- 5 files changed, 62 insertions(+), 62 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d44d1e0d..a7940ac9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -96,9 +96,9 @@ All notable changes to this project will be documented in this file. ### Added -- Support "plain" `Redis` as a cache store backend ([#280](https://github.com/kickstarter/rack-attack/pull/280)). Thanks @bfad and @ryandv. +- Support "plain" `Redis` as a cache store backend ([#280](https://github.com/rack/rack-attack/pull/280)). Thanks @bfad and @ryandv. - When overwriting `Rack::Attack.throttled_response` you can now access the exact epoch integer that was used for caching -so your custom code is less prone to race conditions ([#282](https://github.com/kickstarter/rack-attack/pull/282)). Thanks @doliveirakn. +so your custom code is less prone to race conditions ([#282](https://github.com/rack/rack-attack/pull/282)). Thanks @doliveirakn. ### Dependency changes @@ -120,43 +120,43 @@ so your custom code is less prone to race conditions ([#282](https://github.com/ ### Added -- Add support for [`ActiveSupport::Cache::RedisCacheStore`](http://api.rubyonrails.org/classes/ActiveSupport/Cache/RedisCacheStore.html) as a store backend ([#340](https://github.com/kickstarter/rack-attack/pull/340) and [#350](https://github.com/kickstarter/rack-attack/pull/350)) +- Add support for [`ActiveSupport::Cache::RedisCacheStore`](http://api.rubyonrails.org/classes/ActiveSupport/Cache/RedisCacheStore.html) as a store backend ([#340](https://github.com/rack/rack-attack/pull/340) and [#350](https://github.com/rack/rack-attack/pull/350)) ## [5.2.0] - 2018-03-29 ### Added -- Shorthand for blocking an IP address `Rack::Attack.blocklist_ip("1.2.3.4")` ([#320](https://github.com/kickstarter/rack-attack/pull/320)) -- Shorthand for blocking an IP subnet `Rack::Attack.blocklist_ip("1.2.0.0/16")` ([#320](https://github.com/kickstarter/rack-attack/pull/320)) -- Shorthand for safelisting an IP address `Rack::Attack.safelist_ip("5.6.7.8")` ([#320](https://github.com/kickstarter/rack-attack/pull/320)) -- Shorthand for safelisting an IP subnet `Rack::Attack.safelist_ip("5.6.0.0/16")` ([#320](https://github.com/kickstarter/rack-attack/pull/320)) -- Throw helpful error message when using `allow2ban` but cache store is misconfigured ([#315](https://github.com/kickstarter/rack-attack/issues/315)) -- Throw helpful error message when using `fail2ban` but cache store is misconfigured ([#315](https://github.com/kickstarter/rack-attack/issues/315)) +- Shorthand for blocking an IP address `Rack::Attack.blocklist_ip("1.2.3.4")` ([#320](https://github.com/rack/rack-attack/pull/320)) +- Shorthand for blocking an IP subnet `Rack::Attack.blocklist_ip("1.2.0.0/16")` ([#320](https://github.com/rack/rack-attack/pull/320)) +- Shorthand for safelisting an IP address `Rack::Attack.safelist_ip("5.6.7.8")` ([#320](https://github.com/rack/rack-attack/pull/320)) +- Shorthand for safelisting an IP subnet `Rack::Attack.safelist_ip("5.6.0.0/16")` ([#320](https://github.com/rack/rack-attack/pull/320)) +- Throw helpful error message when using `allow2ban` but cache store is misconfigured ([#315](https://github.com/rack/rack-attack/issues/315)) +- Throw helpful error message when using `fail2ban` but cache store is misconfigured ([#315](https://github.com/rack/rack-attack/issues/315)) ## [5.1.0] - 2018-03-10 - - Fixes edge case bug when using ruby 2.5.0 and redis [#253](https://github.com/kickstarter/rack-attack/issues/253) ([#271](https://github.com/kickstarter/rack-attack/issues/271)) - - Throws errors with better semantics when missing or misconfigured store caches to aid in developers debugging their configs ([#274](https://github.com/kickstarter/rack-attack/issues/274)) - - Removed legacy code that was originally intended for Rails 3 apps ([#264](https://github.com/kickstarter/rack-attack/issues/264)) + - Fixes edge case bug when using ruby 2.5.0 and redis [#253](https://github.com/rack/rack-attack/issues/253) ([#271](https://github.com/rack/rack-attack/issues/271)) + - Throws errors with better semantics when missing or misconfigured store caches to aid in developers debugging their configs ([#274](https://github.com/rack/rack-attack/issues/274)) + - Removed legacy code that was originally intended for Rails 3 apps ([#264](https://github.com/rack/rack-attack/issues/264)) ## [5.0.1] - 2016-08-11 - - Fixes arguments passed to deprecated internal methods. ([#198](https://github.com/kickstarter/rack-attack/issues/198)) + - Fixes arguments passed to deprecated internal methods. ([#198](https://github.com/rack/rack-attack/issues/198)) ## [5.0.0] - 2016-08-09 - - Deprecate `whitelist`/`blacklist` in favor of `safelist`/`blocklist`. ([#181](https://github.com/kickstarter/rack-attack/issues/181), + - Deprecate `whitelist`/`blacklist` in favor of `safelist`/`blocklist`. ([#181](https://github.com/rack/rack-attack/issues/181), thanks @renee-travisci). To upgrade and fix deprecations, find and replace instances of `whitelist` and `blacklist` with `safelist` and `blocklist`. If you reference `rack.attack.match_type`, note that it will have values like `:safelist`/`:blocklist`. - Remove test coverage for unsupported ruby dependencies: ruby 2.0, activesupport 3.2/4.0, and dalli 1. ## [4.4.1] - 2016-02-17 - Fix a bug affecting apps using Redis::Store and ActiveSupport that could generate an error - saying dalli was a required dependency. I learned all about ActiveSupport autoloading. ([#165](https://github.com/kickstarter/rack-attack/issues/165)) + saying dalli was a required dependency. I learned all about ActiveSupport autoloading. ([#165](https://github.com/rack/rack-attack/issues/165)) ## [4.4.0] - 2016-02-10 - - New: support for MemCacheStore ([#153](https://github.com/kickstarter/rack-attack/issues/153)). Thanks @elhu. + - New: support for MemCacheStore ([#153](https://github.com/rack/rack-attack/issues/153)). Thanks @elhu. - Some documentation and test harness improvements. ## [4.3.1] - 2015-12-18 @@ -222,36 +222,36 @@ so your custom code is less prone to race conditions ([#282](https://github.com/ - Remove unused variable - Extract mandatory options to constants -[6.3.1]: https://github.com/kickstarter/rack-attack/compare/v6.3.0...v6.3.1/ -[6.3.0]: https://github.com/kickstarter/rack-attack/compare/v6.2.2...v6.3.0/ -[6.2.2]: https://github.com/kickstarter/rack-attack/compare/v6.2.1...v6.2.2/ -[6.2.1]: https://github.com/kickstarter/rack-attack/compare/v6.2.0...v6.2.1/ -[6.2.0]: https://github.com/kickstarter/rack-attack/compare/v6.1.0...v6.2.0/ -[6.1.0]: https://github.com/kickstarter/rack-attack/compare/v6.0.0...v6.1.0/ -[6.0.0]: https://github.com/kickstarter/rack-attack/compare/v5.4.2...v6.0.0/ -[5.4.2]: https://github.com/kickstarter/rack-attack/compare/v5.4.1...v5.4.2/ -[5.4.1]: https://github.com/kickstarter/rack-attack/compare/v5.4.0...v5.4.1/ -[5.4.0]: https://github.com/kickstarter/rack-attack/compare/v5.3.2...v5.4.0/ -[5.3.2]: https://github.com/kickstarter/rack-attack/compare/v5.3.1...v5.3.2/ -[5.3.1]: https://github.com/kickstarter/rack-attack/compare/v5.3.0...v5.3.1/ -[5.3.0]: https://github.com/kickstarter/rack-attack/compare/v5.2.0...v5.3.0/ -[5.2.0]: https://github.com/kickstarter/rack-attack/compare/v5.1.0...v5.2.0/ -[5.1.0]: https://github.com/kickstarter/rack-attack/compare/v5.0.1...v5.1.0/ -[5.0.1]: https://github.com/kickstarter/rack-attack/compare/v5.0.0...v5.0.1/ -[5.0.0]: https://github.com/kickstarter/rack-attack/compare/v4.4.1...v5.0.0/ -[4.4.1]: https://github.com/kickstarter/rack-attack/compare/v4.4.0...v4.4.1/ -[4.4.0]: https://github.com/kickstarter/rack-attack/compare/v4.3.1...v4.4.0/ -[4.3.1]: https://github.com/kickstarter/rack-attack/compare/v4.3.0...v4.3.1/ -[4.3.0]: https://github.com/kickstarter/rack-attack/compare/v4.2.0...v4.3.0/ -[4.2.0]: https://github.com/kickstarter/rack-attack/compare/v4.1.1...v4.2.0/ -[4.1.1]: https://github.com/kickstarter/rack-attack/compare/v4.1.0...v4.1.1/ -[4.1.0]: https://github.com/kickstarter/rack-attack/compare/v4.0.1...v4.1.0/ -[4.0.1]: https://github.com/kickstarter/rack-attack/compare/v4.0.0...v4.0.1/ -[4.0.0]: https://github.com/kickstarter/rack-attack/compare/v3.0.0...v4.0.0/ -[3.0.0]: https://github.com/kickstarter/rack-attack/compare/v2.3.0...v3.0.0/ -[2.3.0]: https://github.com/kickstarter/rack-attack/compare/v2.2.1...v2.3.0/ -[2.2.1]: https://github.com/kickstarter/rack-attack/compare/v2.2.0...v2.2.1/ -[2.2.0]: https://github.com/kickstarter/rack-attack/compare/v2.1.1...v2.2.0/ +[6.3.1]: https://github.com/rack/rack-attack/compare/v6.3.0...v6.3.1/ +[6.3.0]: https://github.com/rack/rack-attack/compare/v6.2.2...v6.3.0/ +[6.2.2]: https://github.com/rack/rack-attack/compare/v6.2.1...v6.2.2/ +[6.2.1]: https://github.com/rack/rack-attack/compare/v6.2.0...v6.2.1/ +[6.2.0]: https://github.com/rack/rack-attack/compare/v6.1.0...v6.2.0/ +[6.1.0]: https://github.com/rack/rack-attack/compare/v6.0.0...v6.1.0/ +[6.0.0]: https://github.com/rack/rack-attack/compare/v5.4.2...v6.0.0/ +[5.4.2]: https://github.com/rack/rack-attack/compare/v5.4.1...v5.4.2/ +[5.4.1]: https://github.com/rack/rack-attack/compare/v5.4.0...v5.4.1/ +[5.4.0]: https://github.com/rack/rack-attack/compare/v5.3.2...v5.4.0/ +[5.3.2]: https://github.com/rack/rack-attack/compare/v5.3.1...v5.3.2/ +[5.3.1]: https://github.com/rack/rack-attack/compare/v5.3.0...v5.3.1/ +[5.3.0]: https://github.com/rack/rack-attack/compare/v5.2.0...v5.3.0/ +[5.2.0]: https://github.com/rack/rack-attack/compare/v5.1.0...v5.2.0/ +[5.1.0]: https://github.com/rack/rack-attack/compare/v5.0.1...v5.1.0/ +[5.0.1]: https://github.com/rack/rack-attack/compare/v5.0.0...v5.0.1/ +[5.0.0]: https://github.com/rack/rack-attack/compare/v4.4.1...v5.0.0/ +[4.4.1]: https://github.com/rack/rack-attack/compare/v4.4.0...v4.4.1/ +[4.4.0]: https://github.com/rack/rack-attack/compare/v4.3.1...v4.4.0/ +[4.3.1]: https://github.com/rack/rack-attack/compare/v4.3.0...v4.3.1/ +[4.3.0]: https://github.com/rack/rack-attack/compare/v4.2.0...v4.3.0/ +[4.2.0]: https://github.com/rack/rack-attack/compare/v4.1.1...v4.2.0/ +[4.1.1]: https://github.com/rack/rack-attack/compare/v4.1.0...v4.1.1/ +[4.1.0]: https://github.com/rack/rack-attack/compare/v4.0.1...v4.1.0/ +[4.0.1]: https://github.com/rack/rack-attack/compare/v4.0.0...v4.0.1/ +[4.0.0]: https://github.com/rack/rack-attack/compare/v3.0.0...v4.0.0/ +[3.0.0]: https://github.com/rack/rack-attack/compare/v2.3.0...v3.0.0/ +[2.3.0]: https://github.com/rack/rack-attack/compare/v2.2.1...v2.3.0/ +[2.2.1]: https://github.com/rack/rack-attack/compare/v2.2.0...v2.2.1/ +[2.2.0]: https://github.com/rack/rack-attack/compare/v2.1.1...v2.2.0/ [@fatkodima]: https://github.com/fatkodima [@rofreg]: https://github.com/rofreg diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d6645611..63817fc7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,12 +8,12 @@ This project is intended to be a safe, welcoming space for collaboration, and co Any of the following is greatly appreciated: -* Helping users by answering to their [questions](https://github.com/kickstarter/rack-attack/issues?q=is%3Aopen+is%3Aissue+label%3A%22type%3A+question%22) -* Helping users troubleshoot their [error reports](https://github.com/kickstarter/rack-attack/issues?q=is%3Aissue+is%3Aopen+label%3A%22type%3A+error+report%22) to figure out if the error is caused by an actual bug or some misconfiguration -* Giving feedback by commenting in other users [feature requests](https://github.com/kickstarter/rack-attack/issues?q=is%3Aissue+is%3Aopen+label%3A%22type%3A+feature+request%22) +* Helping users by answering to their [questions](https://github.com/rack/rack-attack/issues?q=is%3Aopen+is%3Aissue+label%3A%22type%3A+question%22) +* Helping users troubleshoot their [error reports](https://github.com/rack/rack-attack/issues?q=is%3Aissue+is%3Aopen+label%3A%22type%3A+error+report%22) to figure out if the error is caused by an actual bug or some misconfiguration +* Giving feedback by commenting in other users [feature requests](https://github.com/rack/rack-attack/issues?q=is%3Aissue+is%3Aopen+label%3A%22type%3A+feature+request%22) * Reporting an error you are experiencing * Suggesting a new feature you think it would be useful for many users -* If you want to work on fixing an actual issue and you don't know where to start, those labeled [good first issue](https://github.com/kickstarter/rack-attack/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) may be a good choice +* If you want to work on fixing an actual issue and you don't know where to start, those labeled [good first issue](https://github.com/rack/rack-attack/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) may be a good choice ## Style Guide diff --git a/README.md b/README.md index fe43e41f..da709db4 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ __Note__: You are viewing the development version README. -For the README consistent with the latest released version see https://github.com/kickstarter/rack-attack/blob/6-stable/README.md. +For the README consistent with the latest released version see https://github.com/rack/rack-attack/blob/6-stable/README.md. # Rack::Attack @@ -10,7 +10,7 @@ Protect your Rails and Rack apps from bad clients. Rack::Attack lets you easily See the [Backing & Hacking blog post](https://www.kickstarter.com/backing-and-hacking/rack-attack-protection-from-abusive-clients) introducing Rack::Attack. [![Gem Version](https://badge.fury.io/rb/rack-attack.svg)](https://badge.fury.io/rb/rack-attack) -[![Build Status](https://travis-ci.org/kickstarter/rack-attack.svg?branch=master)](https://travis-ci.org/kickstarter/rack-attack) +[![Build Status](https://travis-ci.org/rack/rack-attack.svg?branch=master)](https://travis-ci.org/rack/rack-attack) [![Code Climate](https://codeclimate.com/github/kickstarter/rack-attack.svg)](https://codeclimate.com/github/kickstarter/rack-attack) [![Join the chat at https://gitter.im/rack-attack/rack-attack](https://badges.gitter.im/rack-attack/rack-attack.svg)](https://gitter.im/rack-attack/rack-attack) diff --git a/docs/advanced_configuration.md b/docs/advanced_configuration.md index 0fd1fe4c..6d8737ea 100644 --- a/docs/advanced_configuration.md +++ b/docs/advanced_configuration.md @@ -6,7 +6,7 @@ If you're feeling ambitious or you have a very particular use-case for Rack::Att ### Exponential Backoff -By layering throttles with linearly increasing limits and exponentially increasing periods, you can mimic an exponential backoff throttle. See [#106](https://github.com/kickstarter/rack-attack/issues/106) for more discussion. +By layering throttles with linearly increasing limits and exponentially increasing periods, you can mimic an exponential backoff throttle. See [#106](https://github.com/rack/rack-attack/issues/106) for more discussion. ```ruby # Allows 20 requests in 8 seconds @@ -24,7 +24,7 @@ end ### Rack::Attack::Request Helpers -You can define helpers on requests like `localhost?` or `subdomain` by monkey-patching `Rack::Attack::Request`. See [#73](https://github.com/kickstarter/rack-attack/issues/73) for more discussion. +You can define helpers on requests like `localhost?` or `subdomain` by monkey-patching `Rack::Attack::Request`. See [#73](https://github.com/rack/rack-attack/issues/73) for more discussion. ```ruby class Rack::Attack::Request < ::Rack::Request @@ -38,7 +38,7 @@ Rack::Attack.safelist("localhost") { |req| req.localhost? } ### Blocklisting From ENV Variables -You can have `Rack::Attack` configure its blocklists from ENV variables to simplify maintenance. See [#110](https://github.com/kickstarter/rack-attack/issues/110) for more discussion. +You can have `Rack::Attack` configure its blocklists from ENV variables to simplify maintenance. See [#110](https://github.com/rack/rack-attack/issues/110) for more discussion. ```ruby class Rack::Attack @@ -57,7 +57,7 @@ end ### Reset Specific Throttles -By doing a bunch of monkey-patching, you can add a helper for resetting specific throttles. The implementation is kind of long, so see [#113](https://github.com/kickstarter/rack-attack/issues/113) for more discussion. +By doing a bunch of monkey-patching, you can add a helper for resetting specific throttles. The implementation is kind of long, so see [#113](https://github.com/rack/rack-attack/issues/113) for more discussion. ```ruby Rack::Attack.reset_throttle "logins/email", "user@example.com" @@ -65,7 +65,7 @@ Rack::Attack.reset_throttle "logins/email", "user@example.com" ### Blocklisting From Rails.cache -You can configure blocklists to check values stored in `Rails.cache` to allow setting blocklists from inside your application. See [#111](https://github.com/kickstarter/rack-attack/issues/111) for more discussion. +You can configure blocklists to check values stored in `Rails.cache` to allow setting blocklists from inside your application. See [#111](https://github.com/rack/rack-attack/issues/111) for more discussion. ```ruby # Block attacks from IPs in cache @@ -78,7 +78,7 @@ end ### Throttle Basic Auth Crackers -An example implementation for blocking hackers who spam basic auth attempts. See [#47](https://github.com/kickstarter/rack-attack/issues/47) for more discussion. +An example implementation for blocking hackers who spam basic auth attempts. See [#47](https://github.com/rack/rack-attack/issues/47) for more discussion. ```ruby # After 5 requests with incorrect auth in 1 minute, diff --git a/rack-attack.gemspec b/rack-attack.gemspec index e1c12a08..34689a36 100644 --- a/rack-attack.gemspec +++ b/rack-attack.gemspec @@ -15,16 +15,16 @@ Gem::Specification.new do |s| s.email = "aaron@ktheory.com" s.files = Dir.glob("{bin,lib}/**/*") + %w(Rakefile README.md) - s.homepage = 'https://github.com/kickstarter/rack-attack' + s.homepage = 'https://github.com/rack/rack-attack' s.rdoc_options = ["--charset=UTF-8"] s.require_paths = ["lib"] s.summary = 'Block & throttle abusive requests' s.test_files = Dir.glob("spec/**/*") s.metadata = { - "bug_tracker_uri" => "https://github.com/kickstarter/rack-attack/issues", - "changelog_uri" => "https://github.com/kickstarter/rack-attack/blob/master/CHANGELOG.md", - "source_code_uri" => "https://github.com/kickstarter/rack-attack" + "bug_tracker_uri" => "https://github.com/rack/rack-attack/issues", + "changelog_uri" => "https://github.com/rack/rack-attack/blob/master/CHANGELOG.md", + "source_code_uri" => "https://github.com/rack/rack-attack" } s.required_ruby_version = '>= 2.4' From 38c89afcf3f4e606f053381833582eb83a5c308c Mon Sep 17 00:00:00 2001 From: Joe Van Dyk Date: Thu, 24 Dec 2020 12:58:47 -0800 Subject: [PATCH 058/113] Fix speling in README (cherry picked from commit 7b3376021b47e2a9bfe20a1e792c9b6e28a6dbcf) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index da709db4..86b92c44 100644 --- a/README.md +++ b/README.md @@ -379,7 +379,7 @@ Rack::Attack uses the [ActiveSupport::Notifications](http://api.rubyonrails.org/ You can subscribe to `rack_attack` events and log it, graph it, etc. -To get notified about specific type of events, subscribe to the event name followed by the `rack_attack` namesapce. +To get notified about specific type of events, subscribe to the event name followed by the `rack_attack` namespace. E.g. for throttles use: ```ruby From dda248993602924536aec97f8c29582f348a3b4d Mon Sep 17 00:00:00 2001 From: fukayatsu Date: Tue, 15 Dec 2020 23:20:58 +0900 Subject: [PATCH 059/113] Fix Ruby 2.7 kwargs warning in RedisCacheStoreProxy (cherry picked from commit 9020201ff5be02046c50203959b51b4a3305a374) --- lib/rack/attack/store_proxy/redis_cache_store_proxy.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rack/attack/store_proxy/redis_cache_store_proxy.rb b/lib/rack/attack/store_proxy/redis_cache_store_proxy.rb index 1de361cf..9d1be742 100644 --- a/lib/rack/attack/store_proxy/redis_cache_store_proxy.rb +++ b/lib/rack/attack/store_proxy/redis_cache_store_proxy.rb @@ -10,7 +10,7 @@ def self.handle?(store) store.class.name == "ActiveSupport::Cache::RedisCacheStore" end - def increment(name, amount = 1, options = {}) + def increment(name, amount = 1, **options) # RedisCacheStore#increment ignores options[:expires_in]. # # So in order to workaround this we use RedisCacheStore#write (which sets expiration) to initialize From 33d1bac4a56098a02a1afeb2c7392cf9e0943474 Mon Sep 17 00:00:00 2001 From: Gonzalo Date: Sat, 23 Jan 2021 13:58:14 -0300 Subject: [PATCH 060/113] test: update ruby and rails versions --- .travis.yml | 25 ++++++++++++++----- Appraisals | 8 ++++-- .../active_support_redis_cache_store.gemfile | 2 +- ...e_support_redis_cache_store_pooled.gemfile | 2 +- gemfiles/rails_6_1.gemfile | 7 ++++++ rack-attack.gemspec | 2 +- ...e_support_redis_cache_store_pooled_spec.rb | 2 +- .../active_support_redis_cache_store_spec.rb | 2 +- 8 files changed, 37 insertions(+), 13 deletions(-) create mode 100644 gemfiles/rails_6_1.gemfile diff --git a/.travis.yml b/.travis.yml index 1b876135..b2c79be4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,8 +3,8 @@ language: ruby cache: bundler rvm: - - ruby-head - - 2.7.1 + - 3.0.0 + - 2.7.2 - 2.6.6 - 2.5.8 - 2.4.10 @@ -16,6 +16,7 @@ before_install: gemfile: - gemfiles/rack_2.gemfile - gemfiles/rack_1.gemfile + - gemfiles/rails_6_1.gemfile - gemfiles/rails_6_0.gemfile - gemfiles/rails_5_2.gemfile - gemfiles/rails_5_1.gemfile @@ -30,13 +31,25 @@ gemfile: - gemfiles/active_support_redis_store.gemfile matrix: - allow_failures: - - rvm: ruby-head exclude: - gemfile: gemfiles/rack_1.gemfile - rvm: 2.7.1 + rvm: 3.0.0 + - gemfile: gemfiles/rails_5_2.gemfile + rvm: 3.0.0 + - gemfile: gemfiles/rails_5_1.gemfile + rvm: 3.0.0 - gemfile: gemfiles/rails_4_2.gemfile - rvm: 2.7.1 + rvm: 3.0.0 + - gemfile: gemfiles/dalli2.gemfile + rvm: 3.0.0 + - gemfile: gemfiles/connection_pool_dalli.gemfile + rvm: 3.0.0 + - gemfile: gemfiles/rack_1.gemfile + rvm: 2.7.2 + - gemfile: gemfiles/rails_4_2.gemfile + rvm: 2.7.2 + - gemfile: gemfiles/rails_6_1.gemfile + rvm: 2.4.10 - gemfile: gemfiles/rails_6_0.gemfile rvm: 2.4.10 fast_finish: true diff --git a/Appraisals b/Appraisals index ef44d14e..d4ee0a3f 100644 --- a/Appraisals +++ b/Appraisals @@ -17,6 +17,10 @@ appraise "rack_1" do gem "rack-test", ">= 0.6" end +appraise 'rails_6-1' do + gem 'railties', '~> 6.1.0' +end + appraise 'rails_6-0' do gem 'railties', '~> 6.0.0' end @@ -55,12 +59,12 @@ appraise "connection_pool_dalli" do end appraise "active_support_redis_cache_store" do - gem "activesupport", "~> 5.2.0" + gem "activesupport", ">= 5.2", "< 6.2" gem "redis", "~> 4.0" end appraise "active_support_redis_cache_store_pooled" do - gem "activesupport", "~> 5.2.0" + gem "activesupport", ">= 5.2", "< 6.2" gem "connection_pool", "~> 2.2" gem "redis", "~> 4.0" end diff --git a/gemfiles/active_support_redis_cache_store.gemfile b/gemfiles/active_support_redis_cache_store.gemfile index 30e1e38b..ac227aa0 100644 --- a/gemfiles/active_support_redis_cache_store.gemfile +++ b/gemfiles/active_support_redis_cache_store.gemfile @@ -2,7 +2,7 @@ source "https://rubygems.org" -gem "activesupport", "~> 5.2.0" +gem "activesupport", ">= 5.2", "< 6.2" gem "redis", "~> 4.0" gemspec path: "../" diff --git a/gemfiles/active_support_redis_cache_store_pooled.gemfile b/gemfiles/active_support_redis_cache_store_pooled.gemfile index 9232a9b5..4e3af697 100644 --- a/gemfiles/active_support_redis_cache_store_pooled.gemfile +++ b/gemfiles/active_support_redis_cache_store_pooled.gemfile @@ -2,7 +2,7 @@ source "https://rubygems.org" -gem "activesupport", "~> 5.2.0" +gem "activesupport", ">= 5.2", "< 6.2" gem "connection_pool", "~> 2.2" gem "redis", "~> 4.0" diff --git a/gemfiles/rails_6_1.gemfile b/gemfiles/rails_6_1.gemfile new file mode 100644 index 00000000..4c467fec --- /dev/null +++ b/gemfiles/rails_6_1.gemfile @@ -0,0 +1,7 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "railties", "~> 6.1.0" + +gemspec path: "../" diff --git a/rack-attack.gemspec b/rack-attack.gemspec index 34689a36..75ca531e 100644 --- a/rack-attack.gemspec +++ b/rack-attack.gemspec @@ -46,5 +46,5 @@ Gem::Specification.new do |s| s.add_development_dependency 'byebug', '~> 11.0' end - s.add_development_dependency 'railties', '>= 4.2', '< 6.1' + s.add_development_dependency 'railties', '>= 4.2', '< 6.2' end diff --git a/spec/acceptance/stores/active_support_redis_cache_store_pooled_spec.rb b/spec/acceptance/stores/active_support_redis_cache_store_pooled_spec.rb index 9c26e8d6..fe951074 100644 --- a/spec/acceptance/stores/active_support_redis_cache_store_pooled_spec.rb +++ b/spec/acceptance/stores/active_support_redis_cache_store_pooled_spec.rb @@ -21,6 +21,6 @@ Rack::Attack.cache.store.clear end - it_works_for_cache_backed_features(fetch_from_store: ->(key) { Rack::Attack.cache.store.fetch(key) }) + it_works_for_cache_backed_features(fetch_from_store: ->(key) { Rack::Attack.cache.store.read(key) }) end end diff --git a/spec/acceptance/stores/active_support_redis_cache_store_spec.rb b/spec/acceptance/stores/active_support_redis_cache_store_spec.rb index f595ec2a..a824edea 100644 --- a/spec/acceptance/stores/active_support_redis_cache_store_spec.rb +++ b/spec/acceptance/stores/active_support_redis_cache_store_spec.rb @@ -20,6 +20,6 @@ Rack::Attack.cache.store.clear end - it_works_for_cache_backed_features(fetch_from_store: ->(key) { Rack::Attack.cache.store.fetch(key) }) + it_works_for_cache_backed_features(fetch_from_store: ->(key) { Rack::Attack.cache.store.read(key) }) end end From 6328ddcb1932c64459b93b7a9bdcbb661e73d3da Mon Sep 17 00:00:00 2001 From: Gonzalo Date: Sat, 23 Jan 2021 14:14:01 -0300 Subject: [PATCH 061/113] Bump gem version to v6.4 --- CHANGELOG.md | 11 +++++++++++ lib/rack/attack/version.rb | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a7940ac9..71008c9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ All notable changes to this project will be documented in this file. +## [6.4.0] - 2021-01-23 + +### Added + +- Added support for ruby v3.0 + +### Removed + +- Dropped support for ruby v2.3 + ## [6.3.1] - 2020-05-21 ### Fixed @@ -222,6 +232,7 @@ so your custom code is less prone to race conditions ([#282](https://github.com/ - Remove unused variable - Extract mandatory options to constants +[6.4.0]: https://github.com/rack/rack-attack/compare/v6.3.1...v6.4.0/ [6.3.1]: https://github.com/rack/rack-attack/compare/v6.3.0...v6.3.1/ [6.3.0]: https://github.com/rack/rack-attack/compare/v6.2.2...v6.3.0/ [6.2.2]: https://github.com/rack/rack-attack/compare/v6.2.1...v6.2.2/ diff --git a/lib/rack/attack/version.rb b/lib/rack/attack/version.rb index 7b5859ca..fb119229 100644 --- a/lib/rack/attack/version.rb +++ b/lib/rack/attack/version.rb @@ -2,6 +2,6 @@ module Rack class Attack - VERSION = '6.3.1' + VERSION = '6.4.0' end end From df354cd1418c0cabbe63674c6e9aefc83d4781ac Mon Sep 17 00:00:00 2001 From: fatkodima Date: Fri, 11 Oct 2019 16:17:07 +0300 Subject: [PATCH 062/113] Make discriminators case-insensitive by default --- lib/rack/attack.rb | 5 +++- lib/rack/attack/throttle.rb | 11 ++++++-- spec/rack_attack_throttle_spec.rb | 44 +++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 3 deletions(-) diff --git a/lib/rack/attack.rb b/lib/rack/attack.rb index f209d590..b02dd39b 100644 --- a/lib/rack/attack.rb +++ b/lib/rack/attack.rb @@ -32,7 +32,7 @@ class IncompatibleStoreError < Error; end autoload :Allow2Ban, 'rack/attack/allow2ban' class << self - attr_accessor :enabled, :notifier + attr_accessor :enabled, :notifier, :discriminator_normalizer attr_reader :configuration def instrument(request) @@ -84,6 +84,9 @@ def reset! # Set defaults @enabled = true @notifier = ActiveSupport::Notifications if defined?(ActiveSupport::Notifications) + @discriminator_normalizer = lambda do |discriminator| + discriminator.to_s.strip.downcase + end @configuration = Configuration.new attr_reader :configuration diff --git a/lib/rack/attack/throttle.rb b/lib/rack/attack/throttle.rb index 96c0f3be..1cc50f40 100644 --- a/lib/rack/attack/throttle.rb +++ b/lib/rack/attack/throttle.rb @@ -23,8 +23,7 @@ def cache end def matched_by?(request) - discriminator = block.call(request) - + discriminator = discriminator_for(request) return false unless discriminator current_period = period_for(request) @@ -50,6 +49,14 @@ def matched_by?(request) private + def discriminator_for(request) + discriminator = block.call(request) + if discriminator && Rack::Attack.discriminator_normalizer + discriminator = Rack::Attack.discriminator_normalizer.call(discriminator) + end + discriminator + end + def period_for(request) period.respond_to?(:call) ? period.call(request) : period end diff --git a/spec/rack_attack_throttle_spec.rb b/spec/rack_attack_throttle_spec.rb index dcb1e414..feb599cb 100644 --- a/spec/rack_attack_throttle_spec.rb +++ b/spec/rack_attack_throttle_spec.rb @@ -144,3 +144,47 @@ end end end + +describe 'Rack::Attack.throttle with discriminator_normalizer' do + before do + @period = 60 + @emails = [ + "person@example.com", + "PERSON@example.com ", + " person@example.com\r\n ", + ] + Rack::Attack.cache.store = ActiveSupport::Cache::MemoryStore.new + Rack::Attack.throttle('logins/email', limit: 4, period: @period) do |req| + if req.path == '/login' && req.post? + req.params['email'] + end + end + end + + it 'should not differentiate requests when discriminator_normalizer is enabled' do + post_logins + key = "rack::attack:#{Time.now.to_i / @period}:logins/email:person@example.com" + _(Rack::Attack.cache.store.read(key)).must_equal 3 + end + + it 'should differentiate requests when discriminator_normalizer is disabled' do + begin + prev = Rack::Attack.discriminator_normalizer + Rack::Attack.discriminator_normalizer = nil + + post_logins + @emails.each do |email| + key = "rack::attack:#{Time.now.to_i / @period}:logins/email:#{email}" + _(Rack::Attack.cache.store.read(key)).must_equal 1 + end + ensure + Rack::Attack.discriminator_normalizer = prev + end + end + + def post_logins + @emails.each do |email| + post '/login', email: email + end + end +end From e131750a6bf49549c9c07596490b177cd2f56e56 Mon Sep 17 00:00:00 2001 From: fatkodima Date: Thu, 24 Oct 2019 02:48:32 +0300 Subject: [PATCH 063/113] Make store proxies lookup dynamic --- lib/rack/attack.rb | 13 +++++---- lib/rack/attack/base_proxy.rb | 27 +++++++++++++++++++ lib/rack/attack/cache.rb | 7 ++++- lib/rack/attack/store_proxy.rb | 21 --------------- .../active_support_redis_store_proxy.rb | 4 +-- lib/rack/attack/store_proxy/dalli_proxy.rb | 4 +-- .../store_proxy/mem_cache_store_proxy.rb | 4 +-- .../store_proxy/redis_cache_store_proxy.rb | 4 +-- lib/rack/attack/store_proxy/redis_proxy.rb | 6 ++--- .../attack/store_proxy/redis_store_proxy.rb | 2 +- 10 files changed, 51 insertions(+), 41 deletions(-) create mode 100644 lib/rack/attack/base_proxy.rb delete mode 100644 lib/rack/attack/store_proxy.rb diff --git a/lib/rack/attack.rb b/lib/rack/attack.rb index b02dd39b..afb5b088 100644 --- a/lib/rack/attack.rb +++ b/lib/rack/attack.rb @@ -6,6 +6,12 @@ require 'rack/attack/configuration' require 'rack/attack/path_normalizer' require 'rack/attack/request' +require 'rack/attack/store_proxy/dalli_proxy' +require 'rack/attack/store_proxy/mem_cache_store_proxy' +require 'rack/attack/store_proxy/redis_proxy' +require 'rack/attack/store_proxy/redis_store_proxy' +require 'rack/attack/store_proxy/redis_cache_store_proxy' +require 'rack/attack/store_proxy/active_support_redis_store_proxy' require 'rack/attack/railtie' if defined?(::Rails) @@ -21,13 +27,6 @@ class IncompatibleStoreError < Error; end autoload :Safelist, 'rack/attack/safelist' autoload :Blocklist, 'rack/attack/blocklist' autoload :Track, 'rack/attack/track' - autoload :StoreProxy, 'rack/attack/store_proxy' - autoload :DalliProxy, 'rack/attack/store_proxy/dalli_proxy' - autoload :MemCacheStoreProxy, 'rack/attack/store_proxy/mem_cache_store_proxy' - autoload :RedisProxy, 'rack/attack/store_proxy/redis_proxy' - autoload :RedisStoreProxy, 'rack/attack/store_proxy/redis_store_proxy' - autoload :RedisCacheStoreProxy, 'rack/attack/store_proxy/redis_cache_store_proxy' - autoload :ActiveSupportRedisStoreProxy, 'rack/attack/store_proxy/active_support_redis_store_proxy' autoload :Fail2Ban, 'rack/attack/fail2ban' autoload :Allow2Ban, 'rack/attack/allow2ban' diff --git a/lib/rack/attack/base_proxy.rb b/lib/rack/attack/base_proxy.rb new file mode 100644 index 00000000..3e3c28af --- /dev/null +++ b/lib/rack/attack/base_proxy.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'delegate' + +module Rack + class Attack + class BaseProxy < SimpleDelegator + class << self + def proxies + @@proxies ||= [] + end + + def inherited(klass) + proxies << klass + end + + def lookup(store) + proxies.find { |proxy| proxy.handle?(store) } + end + + def handle?(_store) + raise NotImplementedError + end + end + end + end +end diff --git a/lib/rack/attack/cache.rb b/lib/rack/attack/cache.rb index c74bc2ad..0e1f606e 100644 --- a/lib/rack/attack/cache.rb +++ b/lib/rack/attack/cache.rb @@ -14,7 +14,12 @@ def initialize attr_reader :store def store=(store) - @store = StoreProxy.build(store) + @store = + if (proxy = BaseProxy.lookup(store)) + proxy.new(store) + else + store + end end def count(unprefixed_key, period) diff --git a/lib/rack/attack/store_proxy.rb b/lib/rack/attack/store_proxy.rb deleted file mode 100644 index 55c63e21..00000000 --- a/lib/rack/attack/store_proxy.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -module Rack - class Attack - module StoreProxy - PROXIES = [ - DalliProxy, - MemCacheStoreProxy, - RedisStoreProxy, - RedisProxy, - RedisCacheStoreProxy, - ActiveSupportRedisStoreProxy - ].freeze - - def self.build(store) - klass = PROXIES.find { |proxy| proxy.handle?(store) } - klass ? klass.new(store) : store - end - end - end -end diff --git a/lib/rack/attack/store_proxy/active_support_redis_store_proxy.rb b/lib/rack/attack/store_proxy/active_support_redis_store_proxy.rb index 68f0326f..d2c0e3b2 100644 --- a/lib/rack/attack/store_proxy/active_support_redis_store_proxy.rb +++ b/lib/rack/attack/store_proxy/active_support_redis_store_proxy.rb @@ -1,11 +1,11 @@ # frozen_string_literal: true -require 'delegate' +require 'rack/attack/base_proxy' module Rack class Attack module StoreProxy - class ActiveSupportRedisStoreProxy < SimpleDelegator + class ActiveSupportRedisStoreProxy < BaseProxy def self.handle?(store) defined?(::Redis) && defined?(::ActiveSupport::Cache::RedisStore) && diff --git a/lib/rack/attack/store_proxy/dalli_proxy.rb b/lib/rack/attack/store_proxy/dalli_proxy.rb index 360e2198..48198bb2 100644 --- a/lib/rack/attack/store_proxy/dalli_proxy.rb +++ b/lib/rack/attack/store_proxy/dalli_proxy.rb @@ -1,11 +1,11 @@ # frozen_string_literal: true -require 'delegate' +require 'rack/attack/base_proxy' module Rack class Attack module StoreProxy - class DalliProxy < SimpleDelegator + class DalliProxy < BaseProxy def self.handle?(store) return false unless defined?(::Dalli) diff --git a/lib/rack/attack/store_proxy/mem_cache_store_proxy.rb b/lib/rack/attack/store_proxy/mem_cache_store_proxy.rb index f8c036c9..122037f0 100644 --- a/lib/rack/attack/store_proxy/mem_cache_store_proxy.rb +++ b/lib/rack/attack/store_proxy/mem_cache_store_proxy.rb @@ -1,11 +1,11 @@ # frozen_string_literal: true -require 'delegate' +require 'rack/attack/base_proxy' module Rack class Attack module StoreProxy - class MemCacheStoreProxy < SimpleDelegator + class MemCacheStoreProxy < BaseProxy def self.handle?(store) defined?(::Dalli) && defined?(::ActiveSupport::Cache::MemCacheStore) && diff --git a/lib/rack/attack/store_proxy/redis_cache_store_proxy.rb b/lib/rack/attack/store_proxy/redis_cache_store_proxy.rb index 9d1be742..00670f06 100644 --- a/lib/rack/attack/store_proxy/redis_cache_store_proxy.rb +++ b/lib/rack/attack/store_proxy/redis_cache_store_proxy.rb @@ -1,11 +1,11 @@ # frozen_string_literal: true -require 'delegate' +require 'rack/attack/base_proxy' module Rack class Attack module StoreProxy - class RedisCacheStoreProxy < SimpleDelegator + class RedisCacheStoreProxy < BaseProxy def self.handle?(store) store.class.name == "ActiveSupport::Cache::RedisCacheStore" end diff --git a/lib/rack/attack/store_proxy/redis_proxy.rb b/lib/rack/attack/store_proxy/redis_proxy.rb index e51b3e92..3127de63 100644 --- a/lib/rack/attack/store_proxy/redis_proxy.rb +++ b/lib/rack/attack/store_proxy/redis_proxy.rb @@ -1,11 +1,11 @@ # frozen_string_literal: true -require 'delegate' +require 'rack/attack/base_proxy' module Rack class Attack module StoreProxy - class RedisProxy < SimpleDelegator + class RedisProxy < BaseProxy def initialize(*args) if Gem::Version.new(Redis::VERSION) < Gem::Version.new("3") warn 'RackAttack requires Redis gem >= 3.0.0.' @@ -15,7 +15,7 @@ def initialize(*args) end def self.handle?(store) - defined?(::Redis) && store.is_a?(::Redis) + defined?(::Redis) && store.class == ::Redis end def read(key) diff --git a/lib/rack/attack/store_proxy/redis_store_proxy.rb b/lib/rack/attack/store_proxy/redis_store_proxy.rb index 6be54128..28557bcb 100644 --- a/lib/rack/attack/store_proxy/redis_store_proxy.rb +++ b/lib/rack/attack/store_proxy/redis_store_proxy.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'delegate' +require 'rack/attack/store_proxy/redis_proxy' module Rack class Attack From 1e5fb868f685dca552d2fefe84863b273728bbae Mon Sep 17 00:00:00 2001 From: fatkodima Date: Thu, 31 Oct 2019 14:35:29 +0200 Subject: [PATCH 064/113] Auto include middleware for older railses --- README.md | 7 +------ lib/rack/attack/railtie.rb | 4 +--- spec/acceptance/rails_middleware_spec.rb | 21 +++------------------ 3 files changed, 5 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 86b92c44..738cb16d 100644 --- a/README.md +++ b/README.md @@ -71,12 +71,7 @@ Or install it yourself as: Then tell your ruby web application to use rack-attack as a middleware. -a) For __rails__ applications with versions >= 5.1 it is used by default. For older rails versions you should enable it explicitly: -```ruby -# In config/application.rb - -config.middleware.use Rack::Attack -``` +a) For __rails__ applications it is used by default. You can disable it permanently (like for specific environment) or temporarily (can be useful for specific test cases) by writing: diff --git a/lib/rack/attack/railtie.rb b/lib/rack/attack/railtie.rb index 398ac6c2..234e1267 100644 --- a/lib/rack/attack/railtie.rb +++ b/lib/rack/attack/railtie.rb @@ -4,9 +4,7 @@ module Rack class Attack class Railtie < ::Rails::Railtie initializer "rack-attack.middleware" do |app| - if Gem::Version.new(::Rails::VERSION::STRING) >= Gem::Version.new("5.1") - app.middleware.use(Rack::Attack) - end + app.middleware.use(Rack::Attack) end end end diff --git a/spec/acceptance/rails_middleware_spec.rb b/spec/acceptance/rails_middleware_spec.rb index 8e7b014a..0e14e895 100644 --- a/spec/acceptance/rails_middleware_spec.rb +++ b/spec/acceptance/rails_middleware_spec.rb @@ -12,24 +12,9 @@ end end - if Gem::Version.new(Rails::VERSION::STRING) >= Gem::Version.new("5.1") - it "is used by default" do - @app.initialize! - assert_equal 1, @app.middleware.count(Rack::Attack) - end - - it "is not added when it was explicitly deleted" do - @app.config.middleware.delete(Rack::Attack) - @app.initialize! - refute @app.middleware.include?(Rack::Attack) - end - end - - if Gem::Version.new(Rails::VERSION::STRING) < Gem::Version.new("5.1") - it "is not used by default" do - @app.initialize! - assert_equal 0, @app.middleware.count(Rack::Attack) - end + it "is used by default" do + @app.initialize! + assert @app.middleware.include?(Rack::Attack) end end end From d1b01f0b4ae669bbea862aa778c42a62d31265e6 Mon Sep 17 00:00:00 2001 From: Gonzalo Date: Fri, 25 Dec 2020 23:55:58 -0300 Subject: [PATCH 065/113] test: update ruby and rails versions --- .travis.yml | 8 -------- Appraisals | 8 ++------ gemfiles/active_support_redis_cache_store.gemfile | 2 +- gemfiles/active_support_redis_cache_store_pooled.gemfile | 2 +- gemfiles/rails_5_1.gemfile | 7 ------- 5 files changed, 4 insertions(+), 23 deletions(-) delete mode 100644 gemfiles/rails_5_1.gemfile diff --git a/.travis.yml b/.travis.yml index b2c79be4..22750297 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,6 @@ rvm: - 2.7.2 - 2.6.6 - 2.5.8 - - 2.4.10 before_install: - yes | gem update --system @@ -19,7 +18,6 @@ gemfile: - gemfiles/rails_6_1.gemfile - gemfiles/rails_6_0.gemfile - gemfiles/rails_5_2.gemfile - - gemfiles/rails_5_1.gemfile - gemfiles/rails_4_2.gemfile - gemfiles/dalli2.gemfile - gemfiles/redis_4.gemfile @@ -36,8 +34,6 @@ matrix: rvm: 3.0.0 - gemfile: gemfiles/rails_5_2.gemfile rvm: 3.0.0 - - gemfile: gemfiles/rails_5_1.gemfile - rvm: 3.0.0 - gemfile: gemfiles/rails_4_2.gemfile rvm: 3.0.0 - gemfile: gemfiles/dalli2.gemfile @@ -48,10 +44,6 @@ matrix: rvm: 2.7.2 - gemfile: gemfiles/rails_4_2.gemfile rvm: 2.7.2 - - gemfile: gemfiles/rails_6_1.gemfile - rvm: 2.4.10 - - gemfile: gemfiles/rails_6_0.gemfile - rvm: 2.4.10 fast_finish: true services: diff --git a/Appraisals b/Appraisals index d4ee0a3f..03c6a706 100644 --- a/Appraisals +++ b/Appraisals @@ -29,10 +29,6 @@ appraise 'rails_5-2' do gem 'railties', '~> 5.2.0' end -appraise 'rails_5-1' do - gem 'railties', '~> 5.1.0' -end - appraise 'rails_4-2' do gem 'railties', '~> 4.2.0' @@ -59,12 +55,12 @@ appraise "connection_pool_dalli" do end appraise "active_support_redis_cache_store" do - gem "activesupport", ">= 5.2", "< 6.2" + gem "activesupport", "~> 6.1.0" gem "redis", "~> 4.0" end appraise "active_support_redis_cache_store_pooled" do - gem "activesupport", ">= 5.2", "< 6.2" + gem "activesupport", "~> 6.1.0" gem "connection_pool", "~> 2.2" gem "redis", "~> 4.0" end diff --git a/gemfiles/active_support_redis_cache_store.gemfile b/gemfiles/active_support_redis_cache_store.gemfile index ac227aa0..8acbbe13 100644 --- a/gemfiles/active_support_redis_cache_store.gemfile +++ b/gemfiles/active_support_redis_cache_store.gemfile @@ -2,7 +2,7 @@ source "https://rubygems.org" -gem "activesupport", ">= 5.2", "< 6.2" +gem "activesupport", "~> 6.1.0" gem "redis", "~> 4.0" gemspec path: "../" diff --git a/gemfiles/active_support_redis_cache_store_pooled.gemfile b/gemfiles/active_support_redis_cache_store_pooled.gemfile index 4e3af697..cf1e92e2 100644 --- a/gemfiles/active_support_redis_cache_store_pooled.gemfile +++ b/gemfiles/active_support_redis_cache_store_pooled.gemfile @@ -2,7 +2,7 @@ source "https://rubygems.org" -gem "activesupport", ">= 5.2", "< 6.2" +gem "activesupport", "~> 6.1.0" gem "connection_pool", "~> 2.2" gem "redis", "~> 4.0" diff --git a/gemfiles/rails_5_1.gemfile b/gemfiles/rails_5_1.gemfile deleted file mode 100644 index 66a5a0b9..00000000 --- a/gemfiles/rails_5_1.gemfile +++ /dev/null @@ -1,7 +0,0 @@ -# This file was generated by Appraisal - -source "https://rubygems.org" - -gem "railties", "~> 5.1.0" - -gemspec path: "../" From 0f1a72a4d4f1056f89d60dd1d8e6f345be600b57 Mon Sep 17 00:00:00 2001 From: brchristian Date: Sat, 16 Jan 2021 10:19:33 -0800 Subject: [PATCH 066/113] Use single quotes in example configuration --- docs/example_configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/example_configuration.md b/docs/example_configuration.md index e1aaa385..b30a4ab0 100644 --- a/docs/example_configuration.md +++ b/docs/example_configuration.md @@ -59,7 +59,7 @@ class Rack::Attack # throttle logins for another user and force their login requests to be # denied, but that's not very common and shouldn't happen to you. (Knock # on wood!) - throttle("logins/email", limit: 5, period: 20.seconds) do |req| + throttle('logins/email', limit: 5, period: 20.seconds) do |req| if req.path == '/login' && req.post? # Normalize the email, using the same logic as your authentication process, to # protect against rate limit bypasses. Return the normalized email if present, nil otherwise. From f3f0df3fc0c3438ac5be7e94583a6b8cb905f861 Mon Sep 17 00:00:00 2001 From: Gonzalo Date: Sun, 7 Feb 2021 13:34:26 -0300 Subject: [PATCH 067/113] refactor: attempt to avoid user confusion by clarifying method is used by throttle --- lib/rack/attack.rb | 4 ++-- lib/rack/attack/throttle.rb | 4 ++-- spec/rack_attack_throttle_spec.rb | 12 ++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/rack/attack.rb b/lib/rack/attack.rb index afb5b088..7aa29c73 100644 --- a/lib/rack/attack.rb +++ b/lib/rack/attack.rb @@ -31,7 +31,7 @@ class IncompatibleStoreError < Error; end autoload :Allow2Ban, 'rack/attack/allow2ban' class << self - attr_accessor :enabled, :notifier, :discriminator_normalizer + attr_accessor :enabled, :notifier, :throttle_discriminator_normalizer attr_reader :configuration def instrument(request) @@ -83,7 +83,7 @@ def reset! # Set defaults @enabled = true @notifier = ActiveSupport::Notifications if defined?(ActiveSupport::Notifications) - @discriminator_normalizer = lambda do |discriminator| + @throttle_discriminator_normalizer = lambda do |discriminator| discriminator.to_s.strip.downcase end @configuration = Configuration.new diff --git a/lib/rack/attack/throttle.rb b/lib/rack/attack/throttle.rb index 1cc50f40..69923393 100644 --- a/lib/rack/attack/throttle.rb +++ b/lib/rack/attack/throttle.rb @@ -51,8 +51,8 @@ def matched_by?(request) def discriminator_for(request) discriminator = block.call(request) - if discriminator && Rack::Attack.discriminator_normalizer - discriminator = Rack::Attack.discriminator_normalizer.call(discriminator) + if discriminator && Rack::Attack.throttle_discriminator_normalizer + discriminator = Rack::Attack.throttle_discriminator_normalizer.call(discriminator) end discriminator end diff --git a/spec/rack_attack_throttle_spec.rb b/spec/rack_attack_throttle_spec.rb index feb599cb..0b0d68ac 100644 --- a/spec/rack_attack_throttle_spec.rb +++ b/spec/rack_attack_throttle_spec.rb @@ -145,7 +145,7 @@ end end -describe 'Rack::Attack.throttle with discriminator_normalizer' do +describe 'Rack::Attack.throttle with throttle_discriminator_normalizer' do before do @period = 60 @emails = [ @@ -161,16 +161,16 @@ end end - it 'should not differentiate requests when discriminator_normalizer is enabled' do + it 'should not differentiate requests when throttle_discriminator_normalizer is enabled' do post_logins key = "rack::attack:#{Time.now.to_i / @period}:logins/email:person@example.com" _(Rack::Attack.cache.store.read(key)).must_equal 3 end - it 'should differentiate requests when discriminator_normalizer is disabled' do + it 'should differentiate requests when throttle_discriminator_normalizer is disabled' do begin - prev = Rack::Attack.discriminator_normalizer - Rack::Attack.discriminator_normalizer = nil + prev = Rack::Attack.throttle_discriminator_normalizer + Rack::Attack.throttle_discriminator_normalizer = nil post_logins @emails.each do |email| @@ -178,7 +178,7 @@ _(Rack::Attack.cache.store.read(key)).must_equal 1 end ensure - Rack::Attack.discriminator_normalizer = prev + Rack::Attack.throttle_discriminator_normalizer = prev end end From 12a8390d2dc0160f2966f0793a12ac2ce76bfe35 Mon Sep 17 00:00:00 2001 From: Gonzalo Date: Sun, 7 Feb 2021 13:34:46 -0300 Subject: [PATCH 068/113] Bump gem version to v6.5 --- CHANGELOG.md | 20 ++++++++++++++++++++ lib/rack/attack/version.rb | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 71008c9b..e7fbfbf3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,25 @@ All notable changes to this project will be documented in this file. +## [6.5.0] - 2021-02-07 + +### Added + +- Added ability to normalize throttle discriminator by setting `Rack::Attack.throttle_discriminator_normalizer` (@fatkodima) + + Example: + + Rack::Attack.throttle_discriminator_normalizer = ->(discriminator) { ... } + + or disable default normalization with: + + Rack::Attack.throttle_discriminator_normalizer = nil + +### Removed + +- Dropped support for ruby v2.4 +- Dropped support for rails v5.1 + ## [6.4.0] - 2021-01-23 ### Added @@ -232,6 +251,7 @@ so your custom code is less prone to race conditions ([#282](https://github.com/ - Remove unused variable - Extract mandatory options to constants +[6.5.0]: https://github.com/rack/rack-attack/compare/v6.4.0...v6.5.0/ [6.4.0]: https://github.com/rack/rack-attack/compare/v6.3.1...v6.4.0/ [6.3.1]: https://github.com/rack/rack-attack/compare/v6.3.0...v6.3.1/ [6.3.0]: https://github.com/rack/rack-attack/compare/v6.2.2...v6.3.0/ diff --git a/lib/rack/attack/version.rb b/lib/rack/attack/version.rb index fb119229..0c28e155 100644 --- a/lib/rack/attack/version.rb +++ b/lib/rack/attack/version.rb @@ -2,6 +2,6 @@ module Rack class Attack - VERSION = '6.4.0' + VERSION = '6.5.0' end end From 2257f008763b405f6a1515b460f80ed5d0f06c2c Mon Sep 17 00:00:00 2001 From: Gonzalo Date: Sun, 21 Mar 2021 18:28:59 -0300 Subject: [PATCH 069/113] docs: update 'How can I help?' section --- CONTRIBUTING.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 63817fc7..042a12b3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,11 +8,12 @@ This project is intended to be a safe, welcoming space for collaboration, and co Any of the following is greatly appreciated: -* Helping users by answering to their [questions](https://github.com/rack/rack-attack/issues?q=is%3Aopen+is%3Aissue+label%3A%22type%3A+question%22) +* Helping users by trying to answer their [questions](https://github.com/rack/rack-attack/discussions/categories/questions-q-a) * Helping users troubleshoot their [error reports](https://github.com/rack/rack-attack/issues?q=is%3Aissue+is%3Aopen+label%3A%22type%3A+error+report%22) to figure out if the error is caused by an actual bug or some misconfiguration -* Giving feedback by commenting in other users [feature requests](https://github.com/rack/rack-attack/issues?q=is%3Aissue+is%3Aopen+label%3A%22type%3A+feature+request%22) -* Reporting an error you are experiencing -* Suggesting a new feature you think it would be useful for many users +* Giving feedback by commenting in other users [ideas](https://github.com/rack/rack-attack/discussions/categories/ideas-proposals) or [general discussions](https://github.com/rack/rack-attack/discussions/categories/general) +* Open a [new issue](https://github.com/rack/rack-attack/issues/new) if you are experiencing an error and know the 'Steps to reproduce' +* Start a [new discussion](https://github.com/rack/rack-attack/discussions/new) if you have an idea you think it would be useful for many users +* Start a [new discussion](https://github.com/rack/rack-attack/discussions/new) if you have a question * If you want to work on fixing an actual issue and you don't know where to start, those labeled [good first issue](https://github.com/rack/rack-attack/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) may be a good choice ## Style Guide From 17b7368a95a4a7ab4f23760b02ccdc5b7e115ce7 Mon Sep 17 00:00:00 2001 From: Gonzalo Date: Tue, 15 Jun 2021 12:29:14 -0300 Subject: [PATCH 070/113] docs: attempt to avoid README version confusion --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0beda016..26d29d79 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -__Note__: You are viewing the development version README. +:warning: You are viewing the development's branch version of README which might contain documentation for unreleased features. For the README consistent with the latest released version see https://github.com/rack/rack-attack/blob/6-stable/README.md. # Rack::Attack From 50b9f371561b35efb5d99eee38cbdf1391044f59 Mon Sep 17 00:00:00 2001 From: Gonzalo Date: Mon, 19 Jul 2021 20:54:19 -0300 Subject: [PATCH 071/113] ci: move from TravisCI to GitHub Actions --- .github/workflows/build.yml | 63 +++++++++++++++++++++++++++++++++++++ .travis.yml | 51 ------------------------------ 2 files changed, 63 insertions(+), 51 deletions(-) create mode 100644 .github/workflows/build.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..ad9e48c8 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,63 @@ +name: build + +on: push + +jobs: + test: + runs-on: ubuntu-18.04 + services: + redis: + image: redis + ports: + - 6379:6379 + memcached: + image: memcached + ports: + - 11211:11211 + strategy: + matrix: + ruby: + - 3.0.0 + - 2.7.2 + - 2.6.6 + - 2.5.8 + gemfile: + - rack_2 + - rack_1 + - rails_6_1 + - rails_6_0 + - rails_5_2 + - rails_4_2 + - dalli2 + - redis_4 + - redis_3 + - connection_pool_dalli + - active_support_redis_cache_store + - active_support_redis_cache_store_pooled + - redis_store + - active_support_redis_store + exclude: + - gemfile: rack_1 + ruby: 3.0.0 + - gemfile: rails_5_2 + ruby: 3.0.0 + - gemfile: rails_4_2 + ruby: 3.0.0 + - gemfile: dalli2 + ruby: 3.0.0 + - gemfile: connection_pool_dalli + ruby: 3.0.0 + - gemfile: rack_1 + ruby: 2.7.2 + - gemfile: rails_4_2 + ruby: 2.7.2 + env: + BUNDLE_GEMFILE: gemfiles/${{ matrix.gemfile }}.gemfile + steps: + - uses: actions/checkout@v2 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true + - run: bundle exec rake + diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 22750297..00000000 --- a/.travis.yml +++ /dev/null @@ -1,51 +0,0 @@ -dist: bionic -language: ruby -cache: bundler - -rvm: - - 3.0.0 - - 2.7.2 - - 2.6.6 - - 2.5.8 - -before_install: - - yes | gem update --system - - gem install bundler -v "~> 2.0" - -gemfile: - - gemfiles/rack_2.gemfile - - gemfiles/rack_1.gemfile - - gemfiles/rails_6_1.gemfile - - gemfiles/rails_6_0.gemfile - - gemfiles/rails_5_2.gemfile - - gemfiles/rails_4_2.gemfile - - gemfiles/dalli2.gemfile - - gemfiles/redis_4.gemfile - - gemfiles/redis_3.gemfile - - gemfiles/connection_pool_dalli.gemfile - - gemfiles/active_support_redis_cache_store.gemfile - - gemfiles/active_support_redis_cache_store_pooled.gemfile - - gemfiles/redis_store.gemfile - - gemfiles/active_support_redis_store.gemfile - -matrix: - exclude: - - gemfile: gemfiles/rack_1.gemfile - rvm: 3.0.0 - - gemfile: gemfiles/rails_5_2.gemfile - rvm: 3.0.0 - - gemfile: gemfiles/rails_4_2.gemfile - rvm: 3.0.0 - - gemfile: gemfiles/dalli2.gemfile - rvm: 3.0.0 - - gemfile: gemfiles/connection_pool_dalli.gemfile - rvm: 3.0.0 - - gemfile: gemfiles/rack_1.gemfile - rvm: 2.7.2 - - gemfile: gemfiles/rails_4_2.gemfile - rvm: 2.7.2 - fast_finish: true - -services: - - redis - - memcached From 7f2ccca650009e9009424d0fab136facb9ac2b33 Mon Sep 17 00:00:00 2001 From: Gonzalo Date: Wed, 21 Jul 2021 18:52:11 -0300 Subject: [PATCH 072/113] ci: run GitHub Actions CI for pull requests --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ad9e48c8..5d360d8f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,6 +1,6 @@ name: build -on: push +on: [push, pull_request] jobs: test: From 511efd15c99814cd93df641e214f6de027ec6649 Mon Sep 17 00:00:00 2001 From: Gonzalo Date: Wed, 21 Jul 2021 18:58:13 -0300 Subject: [PATCH 073/113] ci: update rubies --- .github/workflows/build.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5d360d8f..dbb536f8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,9 +17,9 @@ jobs: strategy: matrix: ruby: - - 3.0.0 - - 2.7.2 - - 2.6.6 + - 3.0.2 + - 2.7.4 + - 2.6.8 - 2.5.8 gemfile: - rack_2 @@ -38,19 +38,19 @@ jobs: - active_support_redis_store exclude: - gemfile: rack_1 - ruby: 3.0.0 + ruby: 3.0.2 - gemfile: rails_5_2 - ruby: 3.0.0 + ruby: 3.0.2 - gemfile: rails_4_2 - ruby: 3.0.0 + ruby: 3.0.2 - gemfile: dalli2 - ruby: 3.0.0 + ruby: 3.0.2 - gemfile: connection_pool_dalli - ruby: 3.0.0 + ruby: 3.0.2 - gemfile: rack_1 - ruby: 2.7.2 + ruby: 2.7.4 - gemfile: rails_4_2 - ruby: 2.7.2 + ruby: 2.7.4 env: BUNDLE_GEMFILE: gemfiles/${{ matrix.gemfile }}.gemfile steps: From a20d58e022d1902f4a771133d054a41e398a5c01 Mon Sep 17 00:00:00 2001 From: Gonzalo Date: Wed, 21 Jul 2021 18:59:09 -0300 Subject: [PATCH 074/113] ci: update machine os --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dbb536f8..d7ebc137 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,7 +4,7 @@ on: [push, pull_request] jobs: test: - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 services: redis: image: redis From 1a872aa24cb59f9053a69a1eee5e9008e20121c9 Mon Sep 17 00:00:00 2001 From: Gonzalo Date: Wed, 21 Jul 2021 19:16:32 -0300 Subject: [PATCH 075/113] docs: update CI badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 26d29d79..a2658d28 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Protect your Rails and Rack apps from bad clients. Rack::Attack lets you easily See the [Backing & Hacking blog post](https://www.kickstarter.com/backing-and-hacking/rack-attack-protection-from-abusive-clients) introducing Rack::Attack. [![Gem Version](https://badge.fury.io/rb/rack-attack.svg)](https://badge.fury.io/rb/rack-attack) -[![Build Status](https://travis-ci.org/rack/rack-attack.svg?branch=master)](https://travis-ci.org/rack/rack-attack) +[![build](https://github.com/rack/rack-attack/actions/workflows/build.yml/badge.svg)](https://github.com/rack/rack-attack/actions/workflows/build.yml) [![Code Climate](https://codeclimate.com/github/kickstarter/rack-attack.svg)](https://codeclimate.com/github/kickstarter/rack-attack) [![Join the chat at https://gitter.im/rack-attack/rack-attack](https://badges.gitter.im/rack-attack/rack-attack.svg)](https://gitter.im/rack-attack/rack-attack) From 0fbfda0146836cc9fc0f8e191230aff49cbbedf7 Mon Sep 17 00:00:00 2001 From: thomas morgan Date: Wed, 20 Oct 2021 10:18:33 -0600 Subject: [PATCH 076/113] test against dalli 3.0 --- .github/workflows/build.yml | 3 +-- Appraisals | 6 +++++- gemfiles/connection_pool_dalli.gemfile | 2 +- gemfiles/dalli3.gemfile | 7 +++++++ spec/acceptance/stores/active_support_dalli_store_spec.rb | 6 +++++- 5 files changed, 19 insertions(+), 5 deletions(-) create mode 100644 gemfiles/dalli3.gemfile diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d7ebc137..2c33c805 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,6 +28,7 @@ jobs: - rails_6_0 - rails_5_2 - rails_4_2 + - dalli3 - dalli2 - redis_4 - redis_3 @@ -45,8 +46,6 @@ jobs: ruby: 3.0.2 - gemfile: dalli2 ruby: 3.0.2 - - gemfile: connection_pool_dalli - ruby: 3.0.2 - gemfile: rack_1 ruby: 2.7.4 - gemfile: rails_4_2 diff --git a/Appraisals b/Appraisals index 03c6a706..3b546fb6 100644 --- a/Appraisals +++ b/Appraisals @@ -41,6 +41,10 @@ appraise 'dalli2' do gem 'dalli', '~> 2.0' end +appraise 'dalli3' do + gem 'dalli', '~> 3.0' +end + appraise 'redis_4' do gem 'redis', '~> 4.0' end @@ -51,7 +55,7 @@ end appraise "connection_pool_dalli" do gem "connection_pool", "~> 2.2" - gem "dalli", "~> 2.7" + gem "dalli", "~> 3.0" end appraise "active_support_redis_cache_store" do diff --git a/gemfiles/connection_pool_dalli.gemfile b/gemfiles/connection_pool_dalli.gemfile index 69dc8870..67c69a59 100644 --- a/gemfiles/connection_pool_dalli.gemfile +++ b/gemfiles/connection_pool_dalli.gemfile @@ -3,6 +3,6 @@ source "https://rubygems.org" gem "connection_pool", "~> 2.2" -gem "dalli", "~> 2.7" +gem "dalli", "~> 3.0" gemspec path: "../" diff --git a/gemfiles/dalli3.gemfile b/gemfiles/dalli3.gemfile new file mode 100644 index 00000000..5f4d073b --- /dev/null +++ b/gemfiles/dalli3.gemfile @@ -0,0 +1,7 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "dalli", "~> 3.0" + +gemspec path: "../" diff --git a/spec/acceptance/stores/active_support_dalli_store_spec.rb b/spec/acceptance/stores/active_support_dalli_store_spec.rb index 58964434..70355161 100644 --- a/spec/acceptance/stores/active_support_dalli_store_spec.rb +++ b/spec/acceptance/stores/active_support_dalli_store_spec.rb @@ -2,7 +2,11 @@ require_relative "../../spec_helper" -if defined?(::Dalli) +should_run = + defined?(::Dalli) && + Gem::Version.new(::Dalli::VERSION) < Gem::Version.new("3") + +if should_run require_relative "../../support/cache_store_helper" require "active_support/cache/dalli_store" require "timecop" From 2fc8c7b65fdc36b38cbf767f2f57b725189f4b75 Mon Sep 17 00:00:00 2001 From: thomas morgan Date: Wed, 20 Oct 2021 10:19:45 -0600 Subject: [PATCH 077/113] support rails 7.0 --- .github/workflows/build.yml | 5 +++++ Appraisals | 5 +++++ gemfiles/rails_7_0.gemfile | 8 ++++++++ lib/rack/attack/store_proxy/mem_cache_store_proxy.rb | 4 ++++ rack-attack.gemspec | 2 +- 5 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 gemfiles/rails_7_0.gemfile diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2c33c805..f4d8d4bd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,6 +24,7 @@ jobs: gemfile: - rack_2 - rack_1 + - rails_7_0 - rails_6_1 - rails_6_0 - rails_5_2 @@ -50,6 +51,10 @@ jobs: ruby: 2.7.4 - gemfile: rails_4_2 ruby: 2.7.4 + - gemfile: rails_7_0 + ruby: 2.6.8 + - gemfile: rails_7_0 + ruby: 2.5.8 env: BUNDLE_GEMFILE: gemfiles/${{ matrix.gemfile }}.gemfile steps: diff --git a/Appraisals b/Appraisals index 3b546fb6..60a9d176 100644 --- a/Appraisals +++ b/Appraisals @@ -17,6 +17,11 @@ appraise "rack_1" do gem "rack-test", ">= 0.6" end +appraise 'rails_7-0' do + gem 'railties', '~> 7.0.0.a' + gem 'dalli', '~> 3.0' +end + appraise 'rails_6-1' do gem 'railties', '~> 6.1.0' end diff --git a/gemfiles/rails_7_0.gemfile b/gemfiles/rails_7_0.gemfile new file mode 100644 index 00000000..dafcdacc --- /dev/null +++ b/gemfiles/rails_7_0.gemfile @@ -0,0 +1,8 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "railties", "~> 7.0.0.a" +gem "dalli", "~> 3.0" + +gemspec path: "../" diff --git a/lib/rack/attack/store_proxy/mem_cache_store_proxy.rb b/lib/rack/attack/store_proxy/mem_cache_store_proxy.rb index 122037f0..f7b66c92 100644 --- a/lib/rack/attack/store_proxy/mem_cache_store_proxy.rb +++ b/lib/rack/attack/store_proxy/mem_cache_store_proxy.rb @@ -12,6 +12,10 @@ def self.handle?(store) store.is_a?(::ActiveSupport::Cache::MemCacheStore) end + def read(name, options = {}) + super(name, options.merge!(raw: true)) + end + def write(name, value, options = {}) super(name, value, options.merge!(raw: true)) end diff --git a/rack-attack.gemspec b/rack-attack.gemspec index 75ca531e..11e72f98 100644 --- a/rack-attack.gemspec +++ b/rack-attack.gemspec @@ -46,5 +46,5 @@ Gem::Specification.new do |s| s.add_development_dependency 'byebug', '~> 11.0' end - s.add_development_dependency 'railties', '>= 4.2', '< 6.2' + s.add_development_dependency 'railties', '>= 4.2', '< 7.1' end From 78bc155ac9ed37553e44ea865859d0ba02bfe008 Mon Sep 17 00:00:00 2001 From: Andrew Baber Date: Fri, 19 Nov 2021 12:30:18 -0500 Subject: [PATCH 078/113] docs: update link to rack spec in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a2658d28..6ed8d573 100644 --- a/README.md +++ b/README.md @@ -312,7 +312,7 @@ Note that `Rack::Attack.cache` is only used for throttling, allow2ban and fail2b ## 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 Rack::Attack.blocklisted_callback = lambda do |request| From 7bcd3b1529ce7e828f9bf1ab13536125825dcee3 Mon Sep 17 00:00:00 2001 From: Gonzalo Date: Wed, 15 Dec 2021 11:59:10 -0300 Subject: [PATCH 079/113] ci: update rubies --- .github/workflows/build.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2c33c805..00b806ab 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,9 +17,9 @@ jobs: strategy: matrix: ruby: - - 3.0.2 - - 2.7.4 - - 2.6.8 + - 3.0.3 + - 2.7.5 + - 2.6.9 - 2.5.8 gemfile: - rack_2 @@ -39,17 +39,17 @@ jobs: - active_support_redis_store exclude: - gemfile: rack_1 - ruby: 3.0.2 + ruby: 3.0.3 - gemfile: rails_5_2 - ruby: 3.0.2 + ruby: 3.0.3 - gemfile: rails_4_2 - ruby: 3.0.2 + ruby: 3.0.3 - gemfile: dalli2 - ruby: 3.0.2 + ruby: 3.0.3 - gemfile: rack_1 - ruby: 2.7.4 + ruby: 2.7.5 - gemfile: rails_4_2 - ruby: 2.7.4 + ruby: 2.7.5 env: BUNDLE_GEMFILE: gemfiles/${{ matrix.gemfile }}.gemfile steps: From 4e90859a37c11f414e2e51cdc72bb8903e8dc27e Mon Sep 17 00:00:00 2001 From: Johnathan Lyman Date: Mon, 17 Jan 2022 12:25:43 -0800 Subject: [PATCH 080/113] Update README to mention .clear_configuration Adds a line to the Test case isolation section about `.clear_configuration`. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6ed8d573..5a0fa25a 100644 --- a/README.md +++ b/README.md @@ -407,7 +407,7 @@ for more on how to do this. ### 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 From c95f9624aaa42b2d164867393297c88f73f58b93 Mon Sep 17 00:00:00 2001 From: Orhan Toy Date: Fri, 28 Jan 2022 19:04:52 +0100 Subject: [PATCH 081/113] Include LICENSE in gem build --- rack-attack.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rack-attack.gemspec b/rack-attack.gemspec index 75ca531e..fd7cac27 100644 --- a/rack-attack.gemspec +++ b/rack-attack.gemspec @@ -14,7 +14,7 @@ Gem::Specification.new do |s| s.description = "A rack middleware for throttling and blocking abusive requests" 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.rdoc_options = ["--charset=UTF-8"] s.require_paths = ["lib"] From 8bf9d4efad0fb9e6409f32119a4de66c1baa8f81 Mon Sep 17 00:00:00 2001 From: Gonzalo Date: Sat, 29 Jan 2022 15:06:13 -0300 Subject: [PATCH 082/113] refactor: attempt to make method name more self explanatory and clear --- README.md | 9 ++++---- lib/rack/attack.rb | 12 +++++----- lib/rack/attack/configuration.rb | 22 +++++++++---------- .../customizing_blocked_response_spec.rb | 4 ++-- .../customizing_throttled_response_spec.rb | 4 ++-- spec/rack_attack_spec.rb | 8 +++---- 6 files changed, 29 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 5a0fa25a..d6ae610c 100644 --- a/README.md +++ b/README.md @@ -315,13 +315,13 @@ Note that `Rack::Attack.cache` is only used for throttling, allow2ban and fail2b 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 -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 # DOSed the site. Rack::Attack returns 403 for blocklists by default [ 503, {}, ['Blocked']] 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 # request.env['rack.attack.matched'], # request.env['rack.attack.match_type'], @@ -427,10 +427,9 @@ def call(env) if safelisted?(req) @app.call(env) elsif blocklisted?(req) - self.class.blocklisted_callback.call(req) + self.class.blocklisted_responder.call(req) elsif throttled?(req) - self.class.throttled_response.call(env) - self.class.throttled_callback.call(req) + self.class.throttled_responder.call(req) else tracked?(req) @app.call(env) diff --git a/lib/rack/attack.rb b/lib/rack/attack.rb index bc9dbed4..9b134165 100644 --- a/lib/rack/attack.rb +++ b/lib/rack/attack.rb @@ -66,10 +66,10 @@ def reset! :safelist_ip, :throttle, :track, - :throttled_callback, - :throttled_callback=, - :blocklisted_callback, - :blocklisted_callback=, + :throttled_responder, + :throttled_responder=, + :blocklisted_responder, + :blocklisted_responder=, :blocklisted_response, :blocklisted_response=, :throttled_response, @@ -113,14 +113,14 @@ def call(env) if configuration.blocklisted_response configuration.blocklisted_response.call(env) else - configuration.blocklisted_callback.call(request) + configuration.blocklisted_responder.call(request) end elsif configuration.throttled?(request) # Deprecated: Keeping throttled_response for backwards compatibility if configuration.throttled_response configuration.throttled_response.call(env) else - configuration.throttled_callback.call(request) + configuration.throttled_responder.call(request) end else configuration.tracked?(request) diff --git a/lib/rack/attack/configuration.rb b/lib/rack/attack/configuration.rb index c6093106..67bcc611 100644 --- a/lib/rack/attack/configuration.rb +++ b/lib/rack/attack/configuration.rb @@ -5,9 +5,9 @@ module Rack class Attack 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 match_data = req.env['rack.attack.match_data'] now = match_data[:epoch_time] @@ -20,22 +20,22 @@ class Configuration end 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 - def blocklisted_response=(callback) + def blocklisted_response=(responder) # TODO: uncomment in 7.0 # warn "[DEPRECATION] Rack::Attack.blocklisted_response is deprecated. "\ - # "Please use Rack::Attack.blocklisted_callback instead." - @blocklisted_response = callback + # "Please use Rack::Attack.blocklisted_responder instead." + @blocklisted_response = responder end - def throttled_response=(callback) + def throttled_response=(responder) # TODO: uncomment in 7.0 # warn "[DEPRECATION] Rack::Attack.throttled_response is deprecated. "\ - # "Please use Rack::Attack.throttled_callback instead" - @throttled_response = callback + # "Please use Rack::Attack.throttled_responder instead" + @throttled_response = responder end def initialize @@ -115,8 +115,8 @@ def set_defaults @anonymous_safelists = [] @throttled_response_retry_after_header = false - @blocklisted_callback = DEFAULT_BLOCKLISTED_CALLBACK - @throttled_callback = DEFAULT_THROTTLED_CALLBACK + @blocklisted_responder = DEFAULT_BLOCKLISTED_RESPONDER + @throttled_responder = DEFAULT_THROTTLED_RESPONDER # Deprecated: Keeping these for backwards compatibility @blocklisted_response = nil diff --git a/spec/acceptance/customizing_blocked_response_spec.rb b/spec/acceptance/customizing_blocked_response_spec.rb index 9a538620..86fcd02a 100644 --- a/spec/acceptance/customizing_blocked_response_spec.rb +++ b/spec/acceptance/customizing_blocked_response_spec.rb @@ -14,7 +14,7 @@ assert_equal 403, last_response.status - Rack::Attack.blocklisted_callback = lambda do |_req| + Rack::Attack.blocklisted_responder = lambda do |_req| [503, {}, ["Blocked"]] end @@ -28,7 +28,7 @@ matched = nil match_type = nil - Rack::Attack.blocklisted_callback = lambda do |req| + Rack::Attack.blocklisted_responder = lambda do |req| matched = req.env['rack.attack.matched'] match_type = req.env['rack.attack.match_type'] diff --git a/spec/acceptance/customizing_throttled_response_spec.rb b/spec/acceptance/customizing_throttled_response_spec.rb index df875ecc..39a39502 100644 --- a/spec/acceptance/customizing_throttled_response_spec.rb +++ b/spec/acceptance/customizing_throttled_response_spec.rb @@ -20,7 +20,7 @@ assert_equal 429, last_response.status - Rack::Attack.throttled_callback = lambda do |_req| + Rack::Attack.throttled_responder = lambda do |_req| [503, {}, ["Throttled"]] end @@ -36,7 +36,7 @@ match_data = nil match_discriminator = nil - Rack::Attack.throttled_callback = lambda do |req| + Rack::Attack.throttled_responder = lambda do |req| matched = req.env['rack.attack.matched'] match_type = req.env['rack.attack.match_type'] match_data = req.env['rack.attack.match_data'] diff --git a/spec/rack_attack_spec.rb b/spec/rack_attack_spec.rb index c55fa206..c8358352 100644 --- a/spec/rack_attack_spec.rb +++ b/spec/rack_attack_spec.rb @@ -64,15 +64,15 @@ end end - describe '#blocklisted_callback' do + describe '#blocklisted_responder' do it 'should exist' do - _(Rack::Attack.blocklisted_callback).must_respond_to :call + _(Rack::Attack.blocklisted_responder).must_respond_to :call end end - describe '#throttled_callback' do + describe '#throttled_responder' do it 'should exist' do - _(Rack::Attack.throttled_callback).must_respond_to :call + _(Rack::Attack.throttled_responder).must_respond_to :call end end end From aaeff6d0ae7d06a97c3f438321dea3edbca55acd Mon Sep 17 00:00:00 2001 From: Gonzalo Date: Sat, 29 Jan 2022 15:09:54 -0300 Subject: [PATCH 083/113] feat: deprecate throttled_response and blocklisted_response --- lib/rack/attack/configuration.rb | 10 ++++------ spec/acceptance/customizing_blocked_response_spec.rb | 6 ++++-- spec/acceptance/customizing_throttled_response_spec.rb | 6 ++++-- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/lib/rack/attack/configuration.rb b/lib/rack/attack/configuration.rb index 67bcc611..ba353ab4 100644 --- a/lib/rack/attack/configuration.rb +++ b/lib/rack/attack/configuration.rb @@ -25,16 +25,14 @@ class Configuration attr_reader :blocklisted_response, :throttled_response # Keeping these for backwards compatibility def blocklisted_response=(responder) - # TODO: uncomment in 7.0 - # warn "[DEPRECATION] Rack::Attack.blocklisted_response is deprecated. "\ - # "Please use Rack::Attack.blocklisted_responder instead." + warn "[DEPRECATION] Rack::Attack.blocklisted_response is deprecated. "\ + "Please use Rack::Attack.blocklisted_responder instead." @blocklisted_response = responder end def throttled_response=(responder) - # TODO: uncomment in 7.0 - # warn "[DEPRECATION] Rack::Attack.throttled_response is deprecated. "\ - # "Please use Rack::Attack.throttled_responder instead" + warn "[DEPRECATION] Rack::Attack.throttled_response is deprecated. "\ + "Please use Rack::Attack.throttled_responder instead" @throttled_response = responder end diff --git a/spec/acceptance/customizing_blocked_response_spec.rb b/spec/acceptance/customizing_blocked_response_spec.rb index 86fcd02a..1ca127cc 100644 --- a/spec/acceptance/customizing_blocked_response_spec.rb +++ b/spec/acceptance/customizing_blocked_response_spec.rb @@ -46,8 +46,10 @@ assert_equal 403, last_response.status - Rack::Attack.blocklisted_response = lambda do |_env| - [503, {}, ["Blocked"]] + silence_warnings do + Rack::Attack.blocklisted_response = lambda do |_env| + [503, {}, ["Blocked"]] + end end get "/", {}, "REMOTE_ADDR" => "1.2.3.4" diff --git a/spec/acceptance/customizing_throttled_response_spec.rb b/spec/acceptance/customizing_throttled_response_spec.rb index 39a39502..0990975e 100644 --- a/spec/acceptance/customizing_throttled_response_spec.rb +++ b/spec/acceptance/customizing_throttled_response_spec.rb @@ -68,8 +68,10 @@ assert_equal 429, last_response.status - Rack::Attack.throttled_response = lambda do |_req| - [503, {}, ["Throttled"]] + silence_warnings do + Rack::Attack.throttled_response = lambda do |_req| + [503, {}, ["Throttled"]] + end end get "/", {}, "REMOTE_ADDR" => "1.2.3.4" From 501ab01573e0aadfc0b2e66fcdfdabc0cea8ff17 Mon Sep 17 00:00:00 2001 From: Gonzalo Date: Sat, 29 Jan 2022 15:36:51 -0300 Subject: [PATCH 084/113] ci: run tests against ruby 3.1 --- .github/workflows/build.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 00b806ab..22a09c4f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,6 +17,7 @@ jobs: strategy: matrix: ruby: + - 3.1.0 - 3.0.3 - 2.7.5 - 2.6.9 @@ -38,6 +39,14 @@ jobs: - redis_store - active_support_redis_store exclude: + - gemfile: rack_1 + ruby: 3.1.0 + - gemfile: rails_5_2 + ruby: 3.1.0 + - gemfile: rails_4_2 + ruby: 3.1.0 + - gemfile: dalli2 + ruby: 3.1.0 - gemfile: rack_1 ruby: 3.0.3 - gemfile: rails_5_2 From 97abc93889e3e5f447317236d84beb3e8163de31 Mon Sep 17 00:00:00 2001 From: Gonzalo Date: Sat, 29 Jan 2022 15:52:40 -0300 Subject: [PATCH 085/113] test: update rails 7 appraisal after final release --- Appraisals | 3 +-- gemfiles/rails_7_0.gemfile | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Appraisals b/Appraisals index 60a9d176..9035597e 100644 --- a/Appraisals +++ b/Appraisals @@ -18,8 +18,7 @@ appraise "rack_1" do end appraise 'rails_7-0' do - gem 'railties', '~> 7.0.0.a' - gem 'dalli', '~> 3.0' + gem 'railties', '~> 7.0.0' end appraise 'rails_6-1' do diff --git a/gemfiles/rails_7_0.gemfile b/gemfiles/rails_7_0.gemfile index dafcdacc..495c72d0 100644 --- a/gemfiles/rails_7_0.gemfile +++ b/gemfiles/rails_7_0.gemfile @@ -2,7 +2,6 @@ source "https://rubygems.org" -gem "railties", "~> 7.0.0.a" -gem "dalli", "~> 3.0" +gem "railties", "~> 7.0.0" gemspec path: "../" From d0ec4de69bf4a8b421ffda2e42181669b30ce01e Mon Sep 17 00:00:00 2001 From: Gonzalo Date: Sat, 29 Jan 2022 16:15:15 -0300 Subject: [PATCH 086/113] Bump gem version to v6.6.0 --- CHANGELOG.md | 16 ++++++++++++++++ lib/rack/attack/version.rb | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7fbfbf3..552aaac1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,19 @@ All notable changes to this project will be documented in this file. +## [6.6.0] - 2022-01-29 + +### Added + +- Ability to have access to the `request` object instead of only `env` (still can access env with `request.env`) when +customizing throttle and blocklist responses with new methods `Rack::Attack.blocklisted_responder=` and +`Rack::Attack.throttled_responder=` which yield the request to your lambda. ([@NikolayRys]) + +### Deprecated + +- `Rack::Attack.blocklisted_response=` +- `Rack::Attack.throttled_response=` + ## [6.5.0] - 2021-02-07 ### Added @@ -251,6 +264,8 @@ so your custom code is less prone to race conditions ([#282](https://github.com/ - Remove unused variable - Extract mandatory options to constants + +[6.6.0]: https://github.com/rack/rack-attack/compare/v6.5.0...v6.6.0/ [6.5.0]: https://github.com/rack/rack-attack/compare/v6.4.0...v6.5.0/ [6.4.0]: https://github.com/rack/rack-attack/compare/v6.3.1...v6.4.0/ [6.3.1]: https://github.com/rack/rack-attack/compare/v6.3.0...v6.3.1/ @@ -286,3 +301,4 @@ so your custom code is less prone to race conditions ([#282](https://github.com/ [@fatkodima]: https://github.com/fatkodima [@rofreg]: https://github.com/rofreg +[@NikolayRys]: https://github.com/NikolayRys diff --git a/lib/rack/attack/version.rb b/lib/rack/attack/version.rb index 0c28e155..19defa79 100644 --- a/lib/rack/attack/version.rb +++ b/lib/rack/attack/version.rb @@ -2,6 +2,6 @@ module Rack class Attack - VERSION = '6.5.0' + VERSION = '6.6.0' end end From 82181325bc5e2eedf04100fe5cf465a445bb5856 Mon Sep 17 00:00:00 2001 From: Gonzalo Date: Tue, 1 Feb 2022 09:48:23 -0300 Subject: [PATCH 087/113] docs: update docs to point to main branch --- rack-attack.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rack-attack.gemspec b/rack-attack.gemspec index 25669f11..cf7db71f 100644 --- a/rack-attack.gemspec +++ b/rack-attack.gemspec @@ -23,7 +23,7 @@ Gem::Specification.new do |s| s.metadata = { "bug_tracker_uri" => "https://github.com/rack/rack-attack/issues", - "changelog_uri" => "https://github.com/rack/rack-attack/blob/master/CHANGELOG.md", + "changelog_uri" => "https://github.com/rack/rack-attack/blob/main/CHANGELOG.md", "source_code_uri" => "https://github.com/rack/rack-attack" } From c01208afe69b52e34e164dbb7fc2988384b827d0 Mon Sep 17 00:00:00 2001 From: Alexey Zapparov Date: Fri, 4 Feb 2022 21:14:00 +0100 Subject: [PATCH 088/113] fix: Fix redis-rb 4.6.0 deprecation warnings Redis 4.6.0 deprecated calling commands on `Redis` inside `#pipelined`: redis.pipelined do redis.get("key") end The above should be: redis.pipelined do |pipeline| pipeline.get("key") end See: https://github.com/redis/redis-rb/pull/1059 --- lib/rack/attack/store_proxy/redis_proxy.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/rack/attack/store_proxy/redis_proxy.rb b/lib/rack/attack/store_proxy/redis_proxy.rb index 3127de63..830d39de 100644 --- a/lib/rack/attack/store_proxy/redis_proxy.rb +++ b/lib/rack/attack/store_proxy/redis_proxy.rb @@ -32,9 +32,9 @@ def write(key, value, options = {}) def increment(key, amount, options = {}) rescuing do - pipelined do - incrby(key, amount) - expire(key, options[:expires_in]) if options[:expires_in] + pipelined do |redis| + redis.incrby(key, amount) + redis.expire(key, options[:expires_in]) if options[:expires_in] end.first end end From 933c0576b8ab1b398d627f87aef24585fe263135 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Sat, 19 Feb 2022 13:56:02 +1300 Subject: [PATCH 089/113] Lower case headers. (#573) --- lib/rack/attack/configuration.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/rack/attack/configuration.rb b/lib/rack/attack/configuration.rb index ba353ab4..c90fdee6 100644 --- a/lib/rack/attack/configuration.rb +++ b/lib/rack/attack/configuration.rb @@ -5,7 +5,7 @@ module Rack class Attack class Configuration - DEFAULT_BLOCKLISTED_RESPONDER = lambda { |_req| [403, { 'Content-Type' => 'text/plain' }, ["Forbidden\n"]] } + 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 @@ -13,9 +13,9 @@ class Configuration 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"]] + [429, { 'content-type' => 'text/plain', 'retry-after' => retry_after.to_s }, ["Retry later\n"]] else - [429, { 'Content-Type' => 'text/plain' }, ["Retry later\n"]] + [429, { 'content-type' => 'text/plain' }, ["Retry later\n"]] end end From 8313e38df329f9c78eb9578a7e94d8518506397a Mon Sep 17 00:00:00 2001 From: Adrian CB Date: Tue, 8 Mar 2022 16:58:58 +1100 Subject: [PATCH 090/113] fix(documentation): Updating README with non-deprecated configuration. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d6ae610c..fb8aa214 100644 --- a/README.md +++ b/README.md @@ -347,8 +347,8 @@ Rack::Attack.throttled_response_retry_after_header = true Here's an example response that includes conventional `RateLimit-*` headers: ```ruby -Rack::Attack.throttled_response = lambda do |env| - match_data = env['rack.attack.match_data'] +Rack::Attack.throttled_responder = lambda do |request| + match_data = request.env['rack.attack.match_data'] now = match_data[:epoch_time] headers = { From 07822ada9219047cff0ad6ce4a50fd3f94a976f4 Mon Sep 17 00:00:00 2001 From: Gonzalo Date: Wed, 13 Apr 2022 22:47:06 -0300 Subject: [PATCH 091/113] ci: update rubies --- .github/workflows/build.yml | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3ae67c43..7552edf5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,10 +17,10 @@ jobs: strategy: matrix: ruby: - - 3.1.0 - - 3.0.3 - - 2.7.5 - - 2.6.9 + - 3.1.2 + - 3.0.4 + - 2.7.6 + - 2.6.10 - 2.5.8 gemfile: - rack_2 @@ -41,27 +41,27 @@ jobs: - active_support_redis_store exclude: - gemfile: rack_1 - ruby: 3.1.0 + ruby: 3.1.2 - gemfile: rails_5_2 - ruby: 3.1.0 + ruby: 3.1.2 - gemfile: rails_4_2 - ruby: 3.1.0 + ruby: 3.1.2 - gemfile: dalli2 - ruby: 3.1.0 + ruby: 3.1.2 - gemfile: rack_1 - ruby: 3.0.3 + ruby: 3.0.4 - gemfile: rails_5_2 - ruby: 3.0.3 + ruby: 3.0.4 - gemfile: rails_4_2 - ruby: 3.0.3 + ruby: 3.0.4 - gemfile: dalli2 - ruby: 3.0.3 + ruby: 3.0.4 - gemfile: rack_1 - ruby: 2.7.5 + ruby: 2.7.6 - gemfile: rails_4_2 - ruby: 2.7.5 + ruby: 2.7.6 - gemfile: rails_7_0 - ruby: 2.6.9 + ruby: 2.6.10 - gemfile: rails_7_0 ruby: 2.5.8 env: From f5f92f44594f9c1ae33f9bfeeaa329348941350c Mon Sep 17 00:00:00 2001 From: Gonzalo Date: Wed, 13 Apr 2022 22:50:01 -0300 Subject: [PATCH 092/113] Bump gem version to v6.6.1 --- CHANGELOG.md | 8 ++++++++ lib/rack/attack/version.rb | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 552aaac1..3983c4d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to this project will be documented in this file. +## [6.6.1] - 2022-04-14 + +### Fixed + +- Fixes deprecation warning in redis 4.6+ ([@ixti]) + ## [6.6.0] - 2022-01-29 ### Added @@ -265,6 +271,7 @@ so your custom code is less prone to race conditions ([#282](https://github.com/ - Extract mandatory options to constants +[6.6.1]: https://github.com/rack/rack-attack/compare/v6.6.0...v6.6.1/ [6.6.0]: https://github.com/rack/rack-attack/compare/v6.5.0...v6.6.0/ [6.5.0]: https://github.com/rack/rack-attack/compare/v6.4.0...v6.5.0/ [6.4.0]: https://github.com/rack/rack-attack/compare/v6.3.1...v6.4.0/ @@ -302,3 +309,4 @@ so your custom code is less prone to race conditions ([#282](https://github.com/ [@fatkodima]: https://github.com/fatkodima [@rofreg]: https://github.com/rofreg [@NikolayRys]: https://github.com/NikolayRys +[@ixti]: https://github.com/ixti diff --git a/lib/rack/attack/version.rb b/lib/rack/attack/version.rb index 19defa79..fe7ae8db 100644 --- a/lib/rack/attack/version.rb +++ b/lib/rack/attack/version.rb @@ -2,6 +2,6 @@ module Rack class Attack - VERSION = '6.6.0' + VERSION = '6.6.1' end end From d3be2f31a5ccd0d5a96b916e9e4a3e431ab70f92 Mon Sep 17 00:00:00 2001 From: Katsuhiko YOSHIDA Date: Thu, 28 Apr 2022 19:10:46 +0900 Subject: [PATCH 093/113] Replace git.io URL --- lib/rack/attack/cache.rb | 2 +- lib/rack/attack/path_normalizer.rb | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/rack/attack/cache.rb b/lib/rack/attack/cache.rb index 0e1f606e..e3fb6386 100644 --- a/lib/rack/attack/cache.rb +++ b/lib/rack/attack/cache.rb @@ -62,7 +62,7 @@ def reset! def key_and_expiry(unprefixed_key, period) @last_epoch_time = Time.now.to_i - # Add 1 to expires_in to avoid timing error: https://git.io/i1PHXA + # Add 1 to expires_in to avoid timing error: https://github.com/rack/rack-attack/pull/85 expires_in = (period - (@last_epoch_time % period) + 1).to_i ["#{prefix}:#{(@last_epoch_time / period).to_i}:#{unprefixed_key}", expires_in] end diff --git a/lib/rack/attack/path_normalizer.rb b/lib/rack/attack/path_normalizer.rb index 110ff6fa..deafa888 100644 --- a/lib/rack/attack/path_normalizer.rb +++ b/lib/rack/attack/path_normalizer.rb @@ -4,7 +4,9 @@ module Rack class Attack # When using Rack::Attack with a Rails app, developers expect the request path # to be normalized. In particular, trailing slashes are stripped. - # (See https://git.io/v0rrR for implementation.) + # (See + # https://github.com/rails/rails/blob/f8edd20/actionpack/lib/action_dispatch/journey/router/utils.rb#L5-L22 + # for implementation.) # # Look for an ActionDispatch utility class that Rails folks would expect # to normalize request paths. If unavailable, use a fallback class that From e9b753b4e7123ef0398d35c908b490eb9113c465 Mon Sep 17 00:00:00 2001 From: Gonzalo Date: Sun, 7 Aug 2022 15:47:25 -0300 Subject: [PATCH 094/113] test: update rack-test to v2 from v1 --- rack-attack.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rack-attack.gemspec b/rack-attack.gemspec index cf7db71f..4cbcd51c 100644 --- a/rack-attack.gemspec +++ b/rack-attack.gemspec @@ -35,7 +35,7 @@ Gem::Specification.new do |s| s.add_development_dependency "bundler", ">= 1.17", "< 3.0" s.add_development_dependency 'minitest', "~> 5.11" s.add_development_dependency "minitest-stub-const", "~> 0.6" - s.add_development_dependency 'rack-test', "~> 1.0" + s.add_development_dependency 'rack-test', "~> 2.0" s.add_development_dependency 'rake', "~> 13.0" s.add_development_dependency "rubocop", "0.89.1" s.add_development_dependency "rubocop-performance", "~> 1.5.0" From 4d201f7e425f99a0c1f0956fbcc935614d695308 Mon Sep 17 00:00:00 2001 From: Maksim Abramchuk Date: Sat, 3 Sep 2022 22:37:52 +0100 Subject: [PATCH 095/113] Update example description to not suggest using a deprecated method. (#589) --- docs/example_configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/example_configuration.md b/docs/example_configuration.md index b30a4ab0..cfe77581 100644 --- a/docs/example_configuration.md +++ b/docs/example_configuration.md @@ -75,7 +75,7 @@ class Rack::Attack # If you want to return 503 so that the attacker might be fooled into # believing that they've successfully broken your app (or you just want to # customize the response), then uncomment these lines. - # self.throttled_response = lambda do |env| + # self.throttled_responder = lambda do |env| # [ 503, # status # {}, # headers # ['']] # body From a6bbbdc779f875b035c8a40464488df8256f2d06 Mon Sep 17 00:00:00 2001 From: Nate Berkopec Date: Fri, 16 Dec 2022 15:22:34 +0900 Subject: [PATCH 096/113] Add note about cache stores and in-memory caches. This was part of the "lessons learned" from Bannerbear's DDoS attack: https://www.bannerbear.com/blog/how-to-survive-a-ddos-attack/ --- README.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fb8aa214..545003cc 100644 --- a/README.md +++ b/README.md @@ -305,10 +305,15 @@ end Throttle, allow2ban and fail2ban state is stored in a configurable cache (which defaults to `Rails.cache` if present), presumably backed by memcached or redis ([at least gem v3.0.0](https://rubygems.org/gems/redis)). ```ruby -Rack::Attack.cache.store = ActiveSupport::Cache::MemoryStore.new # defaults to Rails.cache +# This is the default +Rack::Attack.cache.store = Rails.cache +# It is recommended to use a separate database for throttling/allow2ban/fail2ban. +Rack::Attack.cache.store = ActiveSupport::Cache::RedisCacheStore.new(url: "...") ``` -Note that `Rack::Attack.cache` is only used for throttling, allow2ban and fail2ban filtering; not blocklisting and safelisting. Your cache store must implement `increment` and `write` like [ActiveSupport::Cache::Store](http://api.rubyonrails.org/classes/ActiveSupport/Cache/Store.html). +Most applications should use a new, separate database used only for `rack-attack`. During an actual attack or periods of heavy load, this database will come under heavy load. Keeping it on a separate database instance will give you additional resilience and make sure that other functions (like caching for your application) don't go down. + +Note that `Rack::Attack.cache` is only used for throttling, allow2ban and fail2ban filtering; not blocklisting and safelisting. Your cache store must implement `increment` and `write` like [ActiveSupport::Cache::Store](http://api.rubyonrails.org/classes/ActiveSupport/Cache/Store.html). This means that other cache stores which inherit from ActiveSupport::Cache::Store are also compatible. In-memory stores which are not backed by an external database, such as `ActiveSupport::Cache::MemoryStore.new`, will be mostly ineffective because each Ruby process in your deployment will have it's own state, effectively multiplying the number of requests each client can make by the number of Ruby processes you have deployed. ## Customizing responses From e039ecde2aceeee45ab3fde41a9ef8e97a7ab717 Mon Sep 17 00:00:00 2001 From: Gonzalo <456459+grzuy@users.noreply.github.com> Date: Mon, 24 Jul 2023 22:13:39 -0300 Subject: [PATCH 097/113] ci: tests against redis gem v5 (#612) * ci: tests against redis gem v5 * ci: fixes incompatibility between redis-store 1.9.2 and redis 5 --- .github/workflows/build.yml | 2 +- Appraisals | 13 +++++++------ gemfiles/active_support_redis_cache_store.gemfile | 2 +- .../active_support_redis_cache_store_pooled.gemfile | 2 +- gemfiles/active_support_redis_store.gemfile | 1 + gemfiles/{redis_3.gemfile => redis_5.gemfile} | 2 +- 6 files changed, 12 insertions(+), 10 deletions(-) rename gemfiles/{redis_3.gemfile => redis_5.gemfile} (80%) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7552edf5..b4a41ae4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -32,8 +32,8 @@ jobs: - rails_4_2 - dalli3 - dalli2 + - redis_5 - redis_4 - - redis_3 - connection_pool_dalli - active_support_redis_cache_store - active_support_redis_cache_store_pooled diff --git a/Appraisals b/Appraisals index 9035597e..14727233 100644 --- a/Appraisals +++ b/Appraisals @@ -49,12 +49,12 @@ appraise 'dalli3' do gem 'dalli', '~> 3.0' end -appraise 'redis_4' do - gem 'redis', '~> 4.0' +appraise 'redis_5' do + gem 'redis', '~> 5.0' end -appraise 'redis_3' do - gem 'redis', '~> 3.3' +appraise 'redis_4' do + gem 'redis', '~> 4.0' end appraise "connection_pool_dalli" do @@ -64,13 +64,13 @@ end appraise "active_support_redis_cache_store" do gem "activesupport", "~> 6.1.0" - gem "redis", "~> 4.0" + gem "redis", "~> 5.0" end appraise "active_support_redis_cache_store_pooled" do gem "activesupport", "~> 6.1.0" gem "connection_pool", "~> 2.2" - gem "redis", "~> 4.0" + gem "redis", "~> 5.0" end appraise "redis_store" do @@ -79,4 +79,5 @@ end appraise "active_support_redis_store" do gem "redis-activesupport", "~> 5.0" + gem 'redis-store', '1.9.1' # remove after https://github.com/redis-store/redis-store/issues/358 is resolved end diff --git a/gemfiles/active_support_redis_cache_store.gemfile b/gemfiles/active_support_redis_cache_store.gemfile index 8acbbe13..21a16e7d 100644 --- a/gemfiles/active_support_redis_cache_store.gemfile +++ b/gemfiles/active_support_redis_cache_store.gemfile @@ -3,6 +3,6 @@ source "https://rubygems.org" gem "activesupport", "~> 6.1.0" -gem "redis", "~> 4.0" +gem "redis", "~> 5.0" gemspec path: "../" diff --git a/gemfiles/active_support_redis_cache_store_pooled.gemfile b/gemfiles/active_support_redis_cache_store_pooled.gemfile index cf1e92e2..f74738f2 100644 --- a/gemfiles/active_support_redis_cache_store_pooled.gemfile +++ b/gemfiles/active_support_redis_cache_store_pooled.gemfile @@ -4,6 +4,6 @@ source "https://rubygems.org" gem "activesupport", "~> 6.1.0" gem "connection_pool", "~> 2.2" -gem "redis", "~> 4.0" +gem "redis", "~> 5.0" gemspec path: "../" diff --git a/gemfiles/active_support_redis_store.gemfile b/gemfiles/active_support_redis_store.gemfile index 517c70f9..b3567fa3 100644 --- a/gemfiles/active_support_redis_store.gemfile +++ b/gemfiles/active_support_redis_store.gemfile @@ -3,5 +3,6 @@ source "https://rubygems.org" gem "redis-activesupport", "~> 5.0" +gem "redis-store", "1.9.1" gemspec path: "../" diff --git a/gemfiles/redis_3.gemfile b/gemfiles/redis_5.gemfile similarity index 80% rename from gemfiles/redis_3.gemfile rename to gemfiles/redis_5.gemfile index 403482c1..0aac1021 100644 --- a/gemfiles/redis_3.gemfile +++ b/gemfiles/redis_5.gemfile @@ -2,6 +2,6 @@ source "https://rubygems.org" -gem "redis", "~> 3.3" +gem "redis", "~> 5.0" gemspec path: "../" From b708de32f3667a65bd2a549983cd05671e9e690a Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Tue, 25 Jul 2023 15:16:43 +1200 Subject: [PATCH 098/113] Support Rack 3. (#586) --- .github/workflows/build.yml | 1 + Appraisals | 4 ++++ gemfiles/rack_2.gemfile | 1 + gemfiles/rack_3.gemfile | 7 +++++++ lib/rack/attack/cache.rb | 10 ++++++++-- lib/rack/attack/railtie.rb | 6 ++++++ rack-attack.gemspec | 4 ++-- spec/acceptance/rails_middleware_spec.rb | 2 +- spec/rack_attack_instrumentation_spec.rb | 1 + spec/rack_attack_spec.rb | 4 ++++ spec/spec_helper.rb | 7 ++++--- 11 files changed, 39 insertions(+), 8 deletions(-) create mode 100644 gemfiles/rack_3.gemfile diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b4a41ae4..18e768ae 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,6 +23,7 @@ jobs: - 2.6.10 - 2.5.8 gemfile: + - rack_3 - rack_2 - rack_1 - rails_7_0 diff --git a/Appraisals b/Appraisals index 14727233..2d270527 100644 --- a/Appraisals +++ b/Appraisals @@ -1,5 +1,9 @@ # frozen_string_literal: true +appraise "rack_3" do + gem "rack", "~> 3.0" +end + appraise "rack_2" do gem "rack", "~> 2.0" end diff --git a/gemfiles/rack_2.gemfile b/gemfiles/rack_2.gemfile index 964e087d..41b64ab4 100644 --- a/gemfiles/rack_2.gemfile +++ b/gemfiles/rack_2.gemfile @@ -3,5 +3,6 @@ source "https://rubygems.org" gem "rack", "~> 2.0" +gem "railties" gemspec path: "../" diff --git a/gemfiles/rack_3.gemfile b/gemfiles/rack_3.gemfile new file mode 100644 index 00000000..24e3a766 --- /dev/null +++ b/gemfiles/rack_3.gemfile @@ -0,0 +1,7 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "rack", "~> 3.0" + +gemspec path: "../" diff --git a/lib/rack/attack/cache.rb b/lib/rack/attack/cache.rb index e3fb6386..ecbd3368 100644 --- a/lib/rack/attack/cache.rb +++ b/lib/rack/attack/cache.rb @@ -6,8 +6,14 @@ class Cache attr_accessor :prefix attr_reader :last_epoch_time - def initialize - self.store = ::Rails.cache if defined?(::Rails.cache) + def self.default_store + if Object.const_defined?(:Rails) && Rails.respond_to?(:cache) + ::Rails.cache + end + end + + def initialize(store: self.class.default_store) + self.store = store @prefix = 'rack::attack' end diff --git a/lib/rack/attack/railtie.rb b/lib/rack/attack/railtie.rb index 234e1267..9521493b 100644 --- a/lib/rack/attack/railtie.rb +++ b/lib/rack/attack/railtie.rb @@ -1,5 +1,11 @@ # frozen_string_literal: true +begin + require 'rails/railtie' +rescue LoadError + return +end + module Rack class Attack class Railtie < ::Rails::Railtie diff --git a/rack-attack.gemspec b/rack-attack.gemspec index 4cbcd51c..25849538 100644 --- a/rack-attack.gemspec +++ b/rack-attack.gemspec @@ -29,7 +29,7 @@ Gem::Specification.new do |s| s.required_ruby_version = '>= 2.4' - s.add_runtime_dependency 'rack', ">= 1.0", "< 3" + s.add_runtime_dependency 'rack', ">= 1.0", "< 4" s.add_development_dependency 'appraisal', '~> 2.2' s.add_development_dependency "bundler", ">= 1.17", "< 3.0" @@ -46,5 +46,5 @@ Gem::Specification.new do |s| s.add_development_dependency 'byebug', '~> 11.0' end - s.add_development_dependency 'railties', '>= 4.2', '< 7.1' + s.add_development_dependency "activesupport" end diff --git a/spec/acceptance/rails_middleware_spec.rb b/spec/acceptance/rails_middleware_spec.rb index 0e14e895..31dc6209 100644 --- a/spec/acceptance/rails_middleware_spec.rb +++ b/spec/acceptance/rails_middleware_spec.rb @@ -2,7 +2,7 @@ require_relative "../spec_helper" -if defined?(Rails) +if defined?(Rails::Application) describe "Middleware for Rails" do before do @app = Class.new(Rails::Application) do diff --git a/spec/rack_attack_instrumentation_spec.rb b/spec/rack_attack_instrumentation_spec.rb index a8b4527f..111ad148 100644 --- a/spec/rack_attack_instrumentation_spec.rb +++ b/spec/rack_attack_instrumentation_spec.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require_relative "spec_helper" +require 'active_support' # ActiveSupport::Subscribers added in ~> 4.0.2.0 if ActiveSupport::VERSION::MAJOR > 3 diff --git a/spec/rack_attack_spec.rb b/spec/rack_attack_spec.rb index c8358352..40936017 100644 --- a/spec/rack_attack_spec.rb +++ b/spec/rack_attack_spec.rb @@ -11,6 +11,10 @@ end it 'blocks requests with trailing slash' do + if Rack::Attack::PathNormalizer == Rack::Attack::FallbackPathNormalizer + skip "Normalization is only present on Rails" + end + get '/foo/' _(last_response.status).must_equal 403 end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 10f856bf..ef715339 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -5,8 +5,7 @@ require "minitest/autorun" require "minitest/pride" require "rack/test" -require "rails" - +require "active_support" require "rack/attack" if RUBY_ENGINE == "ruby" @@ -29,7 +28,9 @@ class MiniTest::Spec include Rack::Test::Methods before do - Rails.cache = nil + if Object.const_defined?(:Rails) && Rails.respond_to?(:cache) + Rails.cache.clear + end end after do From 02ef216788cf4fd534ff789c4b7fe7f42548d10a Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Wed, 26 Jul 2023 16:37:35 +1200 Subject: [PATCH 099/113] Gem release management. (#614) * Use `bake-gem` for release management. * Prefer `require_relative` for loading `version.rb` in gemspec. --- Gemfile | 5 +++++ rack-attack.gemspec | 5 +---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index 7f4f5e95..69753c94 100644 --- a/Gemfile +++ b/Gemfile @@ -3,3 +3,8 @@ source 'https://rubygems.org' gemspec + +group :maintenance, optional: true do + gem "bake" + gem "bake-gem" +end diff --git a/rack-attack.gemspec b/rack-attack.gemspec index 25849538..1cfe2b9f 100644 --- a/rack-attack.gemspec +++ b/rack-attack.gemspec @@ -1,9 +1,6 @@ # frozen_string_literal: true -lib = File.expand_path('lib', __dir__) -$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) - -require 'rack/attack/version' +require_relative 'lib/rack/attack/version' Gem::Specification.new do |s| s.name = 'rack-attack' From d82c9fd7e3efc6fa9859d917d7ab2d90e5a258cf Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Wed, 26 Jul 2023 16:39:28 +1200 Subject: [PATCH 100/113] Bump minor version. --- lib/rack/attack/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rack/attack/version.rb b/lib/rack/attack/version.rb index fe7ae8db..754fe57b 100644 --- a/lib/rack/attack/version.rb +++ b/lib/rack/attack/version.rb @@ -2,6 +2,6 @@ module Rack class Attack - VERSION = '6.6.1' + VERSION = '6.7.0' end end From 31adbd0ad8c82c54a5f002f584f37456bdbe3c1a Mon Sep 17 00:00:00 2001 From: James Mead Date: Wed, 2 Aug 2023 08:01:56 +0100 Subject: [PATCH 101/113] Rename MiniTest -> Minitest (#618) The latest version of minitest dropped the "ancient MiniTest compatibility layer" [1], so we need to use the newer module name, `Minitest`. [1]: https://github.com/minitest/minitest/blob/master/History.rdoc#label-5.19.0+-2F+2023-07-26 --- spec/spec_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index ef715339..5cd8d888 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -24,7 +24,7 @@ def safe_require(name) safe_require "redis-activesupport" safe_require "redis-store" -class MiniTest::Spec +class Minitest::Spec include Rack::Test::Methods before do From 2c52bef1b1d68f7a27659ea971d0daf8474f2e42 Mon Sep 17 00:00:00 2001 From: James Mead Date: Wed, 2 Aug 2023 08:11:17 +0100 Subject: [PATCH 102/113] Update changelog for v6.7.0 (#617) --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3983c4d1..b6d54343 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ All notable changes to this project will be documented in this file. +## [6.7.0] - 2023-07-26 + +- Replace git.io URL by @kyoshidajp in #579 +- test: update rack-test to v2 from v1 by @grzuy in #587 +- Update example description to not suggest using a deprecated method by @MaksimAbramchuk in #589 +- Add note about cache stores and in-memory caches. by @nateberkopec in #604 +- ci: tests against redis gem v5 by @grzuy in #612 +- Support rack 3 by @ioquatix in #586 +- Gem release management. by @ioquatix in #614 + ## [6.6.1] - 2022-04-14 ### Fixed From a7d7be1eead76a1b3e30dbe402cc41caf799ea28 Mon Sep 17 00:00:00 2001 From: James Mead Date: Wed, 2 Aug 2023 10:05:20 +0100 Subject: [PATCH 103/113] Changelog changes (#619) * Add missing reference to CHANGELOG.md I missed this in 2c52bef1b1d68f7a27659ea971d0daf8474f2e42. * Explain that CHANGELOG.md will no longer be updated --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b6d54343..35125fb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -All notable changes to this project will be documented in this file. +This file will no longer be updated - all changes after v6.7.0 will only be documented in the relevant release note. ## [6.7.0] - 2023-07-26 @@ -281,6 +281,7 @@ so your custom code is less prone to race conditions ([#282](https://github.com/ - Extract mandatory options to constants +[6.7.0]: https://github.com/rack/rack-attack/compare/v6.6.1...v6.7.0/ [6.6.1]: https://github.com/rack/rack-attack/compare/v6.6.0...v6.6.1/ [6.6.0]: https://github.com/rack/rack-attack/compare/v6.5.0...v6.6.0/ [6.5.0]: https://github.com/rack/rack-attack/compare/v6.4.0...v6.5.0/ From c9bd363a69faedaf0590be6ba191db46db387a25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20K=C3=A4ppler?= Date: Thu, 24 Aug 2023 13:46:14 +0200 Subject: [PATCH 104/113] Add Ruby 3.2.2 to CI build matrix, update older Rubies (#622) * Add Ruby 3.2.2 to CI build matrix * CI: Update older Rubies to latest versions --- .github/workflows/build.yml | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 18e768ae..4047aeab 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,9 +17,10 @@ jobs: strategy: matrix: ruby: - - 3.1.2 - - 3.0.4 - - 2.7.6 + - 3.2.2 + - 3.1.4 + - 3.0.6 + - 2.7.8 - 2.6.10 - 2.5.8 gemfile: @@ -42,25 +43,33 @@ jobs: - active_support_redis_store exclude: - gemfile: rack_1 - ruby: 3.1.2 + ruby: 3.2.2 - gemfile: rails_5_2 - ruby: 3.1.2 + ruby: 3.2.2 - gemfile: rails_4_2 - ruby: 3.1.2 + ruby: 3.2.2 - gemfile: dalli2 - ruby: 3.1.2 + ruby: 3.2.2 - gemfile: rack_1 - ruby: 3.0.4 + ruby: 3.1.4 - gemfile: rails_5_2 - ruby: 3.0.4 + ruby: 3.1.4 - gemfile: rails_4_2 - ruby: 3.0.4 + ruby: 3.1.4 - gemfile: dalli2 - ruby: 3.0.4 + ruby: 3.1.4 - gemfile: rack_1 - ruby: 2.7.6 + ruby: 3.0.6 + - gemfile: rails_5_2 + ruby: 3.0.6 + - gemfile: rails_4_2 + ruby: 3.0.6 + - gemfile: dalli2 + ruby: 3.0.6 + - gemfile: rack_1 + ruby: 2.7.8 - gemfile: rails_4_2 - ruby: 2.7.6 + ruby: 2.7.8 - gemfile: rails_7_0 ruby: 2.6.10 - gemfile: rails_7_0 From 9a7815c3322670fcc850bb7661e107eaa573f49a Mon Sep 17 00:00:00 2001 From: Mitchell Henke Date: Sat, 14 Oct 2023 09:29:19 -0500 Subject: [PATCH 105/113] Remove support for unmaintained ActiveSupport::Cache::RedisStore (#625) * remove support for unmaintained ActiveSupport::Cache::RedisStore * ci: removes active_support_redis_store matrix job * test: removes unecessary redis-activesupport require * test: removes dead code * fix typo --------- Co-authored-by: Gonzalo <456459+grzuy@users.noreply.github.com> --- .github/workflows/build.yml | 1 - Appraisals | 5 --- gemfiles/active_support_redis_store.gemfile | 8 ---- lib/rack/attack.rb | 1 - .../active_support_redis_store_proxy.rb | 39 ------------------- .../stores/active_support_redis_store_spec.rb | 20 ---------- spec/acceptance/stores/redis_store_spec.rb | 2 +- spec/integration/offline_spec.rb | 12 ------ spec/spec_helper.rb | 1 - 9 files changed, 1 insertion(+), 88 deletions(-) delete mode 100644 gemfiles/active_support_redis_store.gemfile delete mode 100644 lib/rack/attack/store_proxy/active_support_redis_store_proxy.rb delete mode 100644 spec/acceptance/stores/active_support_redis_store_spec.rb diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4047aeab..7afda9ae 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -40,7 +40,6 @@ jobs: - active_support_redis_cache_store - active_support_redis_cache_store_pooled - redis_store - - active_support_redis_store exclude: - gemfile: rack_1 ruby: 3.2.2 diff --git a/Appraisals b/Appraisals index 2d270527..4f222a0f 100644 --- a/Appraisals +++ b/Appraisals @@ -80,8 +80,3 @@ end appraise "redis_store" do gem "redis-store", "~> 1.5" end - -appraise "active_support_redis_store" do - gem "redis-activesupport", "~> 5.0" - gem 'redis-store', '1.9.1' # remove after https://github.com/redis-store/redis-store/issues/358 is resolved -end diff --git a/gemfiles/active_support_redis_store.gemfile b/gemfiles/active_support_redis_store.gemfile deleted file mode 100644 index b3567fa3..00000000 --- a/gemfiles/active_support_redis_store.gemfile +++ /dev/null @@ -1,8 +0,0 @@ -# This file was generated by Appraisal - -source "https://rubygems.org" - -gem "redis-activesupport", "~> 5.0" -gem "redis-store", "1.9.1" - -gemspec path: "../" diff --git a/lib/rack/attack.rb b/lib/rack/attack.rb index 9b134165..b3488ba2 100644 --- a/lib/rack/attack.rb +++ b/lib/rack/attack.rb @@ -11,7 +11,6 @@ require 'rack/attack/store_proxy/redis_proxy' require 'rack/attack/store_proxy/redis_store_proxy' require 'rack/attack/store_proxy/redis_cache_store_proxy' -require 'rack/attack/store_proxy/active_support_redis_store_proxy' require 'rack/attack/railtie' if defined?(::Rails) diff --git a/lib/rack/attack/store_proxy/active_support_redis_store_proxy.rb b/lib/rack/attack/store_proxy/active_support_redis_store_proxy.rb deleted file mode 100644 index d2c0e3b2..00000000 --- a/lib/rack/attack/store_proxy/active_support_redis_store_proxy.rb +++ /dev/null @@ -1,39 +0,0 @@ -# frozen_string_literal: true - -require 'rack/attack/base_proxy' - -module Rack - class Attack - module StoreProxy - class ActiveSupportRedisStoreProxy < BaseProxy - def self.handle?(store) - defined?(::Redis) && - defined?(::ActiveSupport::Cache::RedisStore) && - store.is_a?(::ActiveSupport::Cache::RedisStore) - end - - def increment(name, amount = 1, options = {}) - # #increment ignores options[:expires_in]. - # - # So in order to workaround this we use #write (which sets expiration) to initialize - # the counter. After that we continue using the original #increment. - if options[:expires_in] && !read(name) - write(name, amount, options) - - amount - else - super - end - end - - def read(name, options = {}) - super(name, options.merge!(raw: true)) - end - - def write(name, value, options = {}) - super(name, value, options.merge!(raw: true)) - end - end - end - end -end diff --git a/spec/acceptance/stores/active_support_redis_store_spec.rb b/spec/acceptance/stores/active_support_redis_store_spec.rb deleted file mode 100644 index 75e4d68d..00000000 --- a/spec/acceptance/stores/active_support_redis_store_spec.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true - -require_relative "../../spec_helper" - -if defined?(::ActiveSupport::Cache::RedisStore) - require_relative "../../support/cache_store_helper" - require "timecop" - - describe "ActiveSupport::Cache::RedisStore as a cache backend" do - before do - Rack::Attack.cache.store = ActiveSupport::Cache::RedisStore.new - end - - after do - Rack::Attack.cache.store.clear - end - - it_works_for_cache_backed_features(fetch_from_store: ->(key) { Rack::Attack.cache.store.read(key) }) - end -end diff --git a/spec/acceptance/stores/redis_store_spec.rb b/spec/acceptance/stores/redis_store_spec.rb index d7e8e115..dee35bcf 100644 --- a/spec/acceptance/stores/redis_store_spec.rb +++ b/spec/acceptance/stores/redis_store_spec.rb @@ -6,7 +6,7 @@ if defined?(::Redis::Store) require "timecop" - describe "ActiveSupport::Cache::RedisStore as a cache backend" do + describe "Redis::Store as a cache backend" do before do Rack::Attack.cache.store = ::Redis::Store.new end diff --git a/spec/integration/offline_spec.rb b/spec/integration/offline_spec.rb index d4960633..85429a42 100644 --- a/spec/integration/offline_spec.rb +++ b/spec/integration/offline_spec.rb @@ -21,18 +21,6 @@ end end -if defined?(::ActiveSupport::Cache::RedisStore) - describe 'when Redis is offline' do - include OfflineExamples - - before do - @cache = Rack::Attack::Cache.new - # Use presumably unused port for Redis client - @cache.store = ActiveSupport::Cache::RedisStore.new(host: '127.0.0.1', port: 3333) - end - end -end - if defined?(Redis) && defined?(ActiveSupport::Cache::RedisCacheStore) && Redis::VERSION >= '4' describe 'when Redis is offline' do include OfflineExamples diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 5cd8d888..f529e6a1 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -21,7 +21,6 @@ def safe_require(name) safe_require "connection_pool" safe_require "dalli" safe_require "redis" -safe_require "redis-activesupport" safe_require "redis-store" class Minitest::Spec From e9f472786afbfc3acb1b8646cf157a45863e4d9b Mon Sep 17 00:00:00 2001 From: Santiago Bartesaghi Date: Mon, 16 Oct 2023 20:04:35 -0300 Subject: [PATCH 106/113] Update rubocop (#629) * Upgrade rubocop gem * Fix obsolete parameter * Fix Lint/MissingSuper * Fix Lint/ConstantDefinitionInBlock * Fix Layout/EmptyLineBetweenDefs * Add rubocop-minitest * Add rubocop-rake * Upgrade rubocop-performance --- .rubocop.yml | 3 ++- lib/rack/attack.rb | 3 +++ lib/rack/attack/base_proxy.rb | 1 + rack-attack.gemspec | 6 +++-- .../cache_store_config_for_fail2ban_spec.rb | 4 +-- .../extending_request_object_spec.rb | 10 +++---- spec/rack_attack_request_spec.rb | 6 ++--- spec/rack_attack_track_spec.rb | 26 ++++++++++--------- 8 files changed, 31 insertions(+), 28 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 745bb907..865fea87 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,5 +1,7 @@ require: + - rubocop-minitest - rubocop-performance + - rubocop-rake inherit_mode: merge: @@ -56,7 +58,6 @@ Security: Style/BlockDelimiters: Enabled: true - IgnoredMethods: [] # Workaround rubocop bug: https://github.com/rubocop-hq/rubocop/issues/6179 Style/ClassAndModuleChildren: Enabled: true diff --git a/lib/rack/attack.rb b/lib/rack/attack.rb index b3488ba2..c9094b21 100644 --- a/lib/rack/attack.rb +++ b/lib/rack/attack.rb @@ -17,8 +17,11 @@ module Rack class Attack class Error < StandardError; end + class MisconfiguredStoreError < Error; end + class MissingStoreError < Error; end + class IncompatibleStoreError < Error; end autoload :Check, 'rack/attack/check' diff --git a/lib/rack/attack/base_proxy.rb b/lib/rack/attack/base_proxy.rb index 3e3c28af..f10af3d4 100644 --- a/lib/rack/attack/base_proxy.rb +++ b/lib/rack/attack/base_proxy.rb @@ -11,6 +11,7 @@ def proxies end def inherited(klass) + super proxies << klass end diff --git a/rack-attack.gemspec b/rack-attack.gemspec index 1cfe2b9f..41cc7a8f 100644 --- a/rack-attack.gemspec +++ b/rack-attack.gemspec @@ -34,8 +34,10 @@ Gem::Specification.new do |s| s.add_development_dependency "minitest-stub-const", "~> 0.6" s.add_development_dependency 'rack-test', "~> 2.0" s.add_development_dependency 'rake', "~> 13.0" - s.add_development_dependency "rubocop", "0.89.1" - s.add_development_dependency "rubocop-performance", "~> 1.5.0" + s.add_development_dependency "rubocop", "1.12.1" + s.add_development_dependency "rubocop-minitest", "~> 0.11.1" + s.add_development_dependency "rubocop-performance", "~> 1.10.2" + s.add_development_dependency "rubocop-rake", "~> 0.5.1" s.add_development_dependency "timecop", "~> 0.9.1" # byebug only works with MRI diff --git a/spec/acceptance/cache_store_config_for_fail2ban_spec.rb b/spec/acceptance/cache_store_config_for_fail2ban_spec.rb index 6f330eee..6fd79807 100644 --- a/spec/acceptance/cache_store_config_for_fail2ban_spec.rb +++ b/spec/acceptance/cache_store_config_for_fail2ban_spec.rb @@ -79,7 +79,7 @@ def write(key, value); end end it "works with any object that responds to #read, #write and #increment" do - FakeStore = Class.new do + fake_store_class = Class.new do attr_accessor :backend def initialize @@ -100,7 +100,7 @@ def increment(key, _count, _options = {}) end end - Rack::Attack.cache.store = FakeStore.new + Rack::Attack.cache.store = fake_store_class.new get "/" assert_equal 200, last_response.status diff --git a/spec/acceptance/extending_request_object_spec.rb b/spec/acceptance/extending_request_object_spec.rb index a4ea1a62..5449b90c 100644 --- a/spec/acceptance/extending_request_object_spec.rb +++ b/spec/acceptance/extending_request_object_spec.rb @@ -4,10 +4,8 @@ describe "Extending the request object" do before do - class Rack::Attack::Request - def authorized? - env["APIKey"] == "private-secret" - end + Rack::Attack::Request.define_method :authorized? do + env["APIKey"] == "private-secret" end Rack::Attack.blocklist("unauthorized requests") do |request| @@ -17,9 +15,7 @@ def authorized? # We don't want the extension to leak to other test cases after do - class Rack::Attack::Request - remove_method :authorized? - end + Rack::Attack::Request.undef_method :authorized? end it "forbids request if blocklist condition is true" do diff --git a/spec/rack_attack_request_spec.rb b/spec/rack_attack_request_spec.rb index 8d4d27fc..8f27301a 100644 --- a/spec/rack_attack_request_spec.rb +++ b/spec/rack_attack_request_spec.rb @@ -5,10 +5,8 @@ describe 'Rack::Attack' do describe 'helpers' do before do - class Rack::Attack::Request - def remote_ip - ip - end + Rack::Attack::Request.define_method :remote_ip do + ip end Rack::Attack.safelist('valid IP') do |req| diff --git a/spec/rack_attack_track_spec.rb b/spec/rack_attack_track_spec.rb index 0db66e47..d8c53b8a 100644 --- a/spec/rack_attack_track_spec.rb +++ b/spec/rack_attack_track_spec.rb @@ -3,17 +3,19 @@ require_relative 'spec_helper' describe 'Rack::Attack.track' do - class Counter - def self.incr - @counter += 1 - end + let(:counter_class) do + Class.new do + def self.incr + @counter += 1 + end - def self.reset - @counter = 0 - end + def self.reset + @counter = 0 + end - def self.check - @counter + def self.check + @counter + end end end @@ -32,19 +34,19 @@ def self.check describe "with a notification subscriber and two tracks" do before do - Counter.reset + counter_class.reset # A second track Rack::Attack.track("homepage") { |req| req.path == "/" } ActiveSupport::Notifications.subscribe("track.rack_attack") do |*_args| - Counter.incr + counter_class.incr end get "/" end it "should notify twice" do - _(Counter.check).must_equal 2 + _(counter_class.check).must_equal 2 end end From 3543f61b640555f161b6d7f76fdbd622ead32dd3 Mon Sep 17 00:00:00 2001 From: Santiago Bartesaghi Date: Tue, 17 Oct 2023 21:19:29 -0300 Subject: [PATCH 107/113] Fix #588 don't fail if request.ip is missing (#630) * Fix #588 don't fail if request.ip is missing * Fix Rails 4 suite * Improve tests --------- Co-authored-by: Gonzalo <456459+grzuy@users.noreply.github.com> --- lib/rack/attack/configuration.rb | 8 ++++++-- spec/acceptance/blocking_ip_spec.rb | 6 ++++++ spec/acceptance/safelisting_ip_spec.rb | 6 ++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/lib/rack/attack/configuration.rb b/lib/rack/attack/configuration.rb index c90fdee6..a4bdc987 100644 --- a/lib/rack/attack/configuration.rb +++ b/lib/rack/attack/configuration.rb @@ -61,11 +61,15 @@ def blocklist(name = nil, &block) end def blocklist_ip(ip_address) - @anonymous_blocklists << Blocklist.new { |request| IPAddr.new(ip_address).include?(IPAddr.new(request.ip)) } + @anonymous_blocklists << Blocklist.new do |request| + request.ip && !request.ip.empty? && IPAddr.new(ip_address).include?(IPAddr.new(request.ip)) + end end def safelist_ip(ip_address) - @anonymous_safelists << Safelist.new { |request| IPAddr.new(ip_address).include?(IPAddr.new(request.ip)) } + @anonymous_safelists << Safelist.new do |request| + request.ip && !request.ip.empty? && IPAddr.new(ip_address).include?(IPAddr.new(request.ip)) + end end def throttle(name, options, &block) diff --git a/spec/acceptance/blocking_ip_spec.rb b/spec/acceptance/blocking_ip_spec.rb index 102a8fce..4d08042f 100644 --- a/spec/acceptance/blocking_ip_spec.rb +++ b/spec/acceptance/blocking_ip_spec.rb @@ -19,6 +19,12 @@ assert_equal 200, last_response.status end + it "succeeds if IP is missing" do + get "/", {}, "REMOTE_ADDR" => "" + + assert_equal 200, last_response.status + end + it "notifies when the request is blocked" do notified = false notification_type = nil diff --git a/spec/acceptance/safelisting_ip_spec.rb b/spec/acceptance/safelisting_ip_spec.rb index c0b8faf5..fd80dc1b 100644 --- a/spec/acceptance/safelisting_ip_spec.rb +++ b/spec/acceptance/safelisting_ip_spec.rb @@ -17,6 +17,12 @@ assert_equal 403, last_response.status end + it "forbids request if blocklist condition is true and safelist is false (missing IP)" do + get "/admin", {}, "REMOTE_ADDR" => "" + + assert_equal 403, last_response.status + end + it "succeeds if blocklist condition is false and safelist is false" do get "/", {}, "REMOTE_ADDR" => "1.2.3.4" From 8d77a5b0f1fac27951e7977c899092b1a84af891 Mon Sep 17 00:00:00 2001 From: Santiago Bartesaghi Date: Wed, 18 Oct 2023 21:50:23 -0300 Subject: [PATCH 108/113] Add Rails 7.1 to CI (#631) --- .github/workflows/build.yml | 5 +++++ Appraisals | 4 ++++ gemfiles/rails_7_1.gemfile | 7 +++++++ 3 files changed, 16 insertions(+) create mode 100644 gemfiles/rails_7_1.gemfile diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7afda9ae..46d67464 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,6 +27,7 @@ jobs: - rack_3 - rack_2 - rack_1 + - rails_7_1 - rails_7_0 - rails_6_1 - rails_6_0 @@ -73,6 +74,10 @@ jobs: ruby: 2.6.10 - gemfile: rails_7_0 ruby: 2.5.8 + - gemfile: rails_7_1 + ruby: 2.6.10 + - gemfile: rails_7_1 + ruby: 2.5.8 env: BUNDLE_GEMFILE: gemfiles/${{ matrix.gemfile }}.gemfile steps: diff --git a/Appraisals b/Appraisals index 4f222a0f..fe499170 100644 --- a/Appraisals +++ b/Appraisals @@ -21,6 +21,10 @@ appraise "rack_1" do gem "rack-test", ">= 0.6" end +appraise 'rails_7-1' do + gem 'railties', '~> 7.1.0' +end + appraise 'rails_7-0' do gem 'railties', '~> 7.0.0' end diff --git a/gemfiles/rails_7_1.gemfile b/gemfiles/rails_7_1.gemfile new file mode 100644 index 00000000..036d9836 --- /dev/null +++ b/gemfiles/rails_7_1.gemfile @@ -0,0 +1,7 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "railties", "~> 7.1.0" + +gemspec path: "../" From 070e467ef220a4566abed7dc4c64c278d66b812f Mon Sep 17 00:00:00 2001 From: Santiago Bartesaghi Date: Tue, 24 Oct 2023 19:14:27 -0300 Subject: [PATCH 109/113] ci: test against ActiveSupport::RedisCacheStore v5 (#632) --- .github/workflows/build.yml | 14 ++++++++++++++ Appraisals | 11 +++++++++++ .../active_support_5_redis_cache_store.gemfile | 13 +++++++++++++ ...tive_support_5_redis_cache_store_pooled.gemfile | 14 ++++++++++++++ gemfiles/active_support_redis_cache_store.gemfile | 5 +++++ ...active_support_redis_cache_store_pooled.gemfile | 5 +++++ gemfiles/connection_pool_dalli.gemfile | 5 +++++ gemfiles/dalli2.gemfile | 5 +++++ gemfiles/dalli3.gemfile | 5 +++++ gemfiles/rack_1.gemfile | 5 +++++ gemfiles/rack_2.gemfile | 6 +++++- gemfiles/rack_3.gemfile | 5 +++++ gemfiles/rails_4_2.gemfile | 5 +++++ gemfiles/rails_5_2.gemfile | 5 +++++ gemfiles/rails_6_0.gemfile | 5 +++++ gemfiles/rails_6_1.gemfile | 5 +++++ gemfiles/rails_7_0.gemfile | 5 +++++ gemfiles/rails_7_1.gemfile | 5 +++++ gemfiles/redis_4.gemfile | 5 +++++ gemfiles/redis_5.gemfile | 5 +++++ gemfiles/redis_store.gemfile | 5 +++++ 21 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 gemfiles/active_support_5_redis_cache_store.gemfile create mode 100644 gemfiles/active_support_5_redis_cache_store_pooled.gemfile diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 46d67464..6ca20631 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -40,12 +40,18 @@ jobs: - connection_pool_dalli - active_support_redis_cache_store - active_support_redis_cache_store_pooled + - active_support_5_redis_cache_store + - active_support_5_redis_cache_store_pooled - redis_store exclude: - gemfile: rack_1 ruby: 3.2.2 - gemfile: rails_5_2 ruby: 3.2.2 + - gemfile: active_support_5_redis_cache_store + ruby: 3.2.2 + - gemfile: active_support_5_redis_cache_store_pooled + ruby: 3.2.2 - gemfile: rails_4_2 ruby: 3.2.2 - gemfile: dalli2 @@ -54,6 +60,10 @@ jobs: ruby: 3.1.4 - gemfile: rails_5_2 ruby: 3.1.4 + - gemfile: active_support_5_redis_cache_store + ruby: 3.1.4 + - gemfile: active_support_5_redis_cache_store_pooled + ruby: 3.1.4 - gemfile: rails_4_2 ruby: 3.1.4 - gemfile: dalli2 @@ -62,6 +72,10 @@ jobs: ruby: 3.0.6 - gemfile: rails_5_2 ruby: 3.0.6 + - gemfile: active_support_5_redis_cache_store + ruby: 3.0.6 + - gemfile: active_support_5_redis_cache_store_pooled + ruby: 3.0.6 - gemfile: rails_4_2 ruby: 3.0.6 - gemfile: dalli2 diff --git a/Appraisals b/Appraisals index fe499170..89062b53 100644 --- a/Appraisals +++ b/Appraisals @@ -81,6 +81,17 @@ appraise "active_support_redis_cache_store_pooled" do gem "redis", "~> 5.0" end +appraise "active_support_5_redis_cache_store" do + gem "activesupport", "~> 5.2.0" + gem "redis", "~> 5.0" +end + +appraise "active_support_5_redis_cache_store_pooled" do + gem "activesupport", "~> 5.2.0" + gem "connection_pool", "~> 2.2" + gem "redis", "~> 5.0" +end + appraise "redis_store" do gem "redis-store", "~> 1.5" end diff --git a/gemfiles/active_support_5_redis_cache_store.gemfile b/gemfiles/active_support_5_redis_cache_store.gemfile new file mode 100644 index 00000000..0b800a0f --- /dev/null +++ b/gemfiles/active_support_5_redis_cache_store.gemfile @@ -0,0 +1,13 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "activesupport", "~> 5.2.0" +gem "redis", "~> 5.0" + +group :maintenance, optional: true do + gem "bake" + gem "bake-gem" +end + +gemspec path: "../" diff --git a/gemfiles/active_support_5_redis_cache_store_pooled.gemfile b/gemfiles/active_support_5_redis_cache_store_pooled.gemfile new file mode 100644 index 00000000..9127da50 --- /dev/null +++ b/gemfiles/active_support_5_redis_cache_store_pooled.gemfile @@ -0,0 +1,14 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "activesupport", "~> 5.2.0" +gem "connection_pool", "~> 2.2" +gem "redis", "~> 5.0" + +group :maintenance, optional: true do + gem "bake" + gem "bake-gem" +end + +gemspec path: "../" diff --git a/gemfiles/active_support_redis_cache_store.gemfile b/gemfiles/active_support_redis_cache_store.gemfile index 21a16e7d..72fb5b1d 100644 --- a/gemfiles/active_support_redis_cache_store.gemfile +++ b/gemfiles/active_support_redis_cache_store.gemfile @@ -5,4 +5,9 @@ source "https://rubygems.org" gem "activesupport", "~> 6.1.0" gem "redis", "~> 5.0" +group :maintenance, optional: true do + gem "bake" + gem "bake-gem" +end + gemspec path: "../" diff --git a/gemfiles/active_support_redis_cache_store_pooled.gemfile b/gemfiles/active_support_redis_cache_store_pooled.gemfile index f74738f2..36a40f57 100644 --- a/gemfiles/active_support_redis_cache_store_pooled.gemfile +++ b/gemfiles/active_support_redis_cache_store_pooled.gemfile @@ -6,4 +6,9 @@ gem "activesupport", "~> 6.1.0" gem "connection_pool", "~> 2.2" gem "redis", "~> 5.0" +group :maintenance, optional: true do + gem "bake" + gem "bake-gem" +end + gemspec path: "../" diff --git a/gemfiles/connection_pool_dalli.gemfile b/gemfiles/connection_pool_dalli.gemfile index 67c69a59..f84eb52e 100644 --- a/gemfiles/connection_pool_dalli.gemfile +++ b/gemfiles/connection_pool_dalli.gemfile @@ -5,4 +5,9 @@ source "https://rubygems.org" gem "connection_pool", "~> 2.2" gem "dalli", "~> 3.0" +group :maintenance, optional: true do + gem "bake" + gem "bake-gem" +end + gemspec path: "../" diff --git a/gemfiles/dalli2.gemfile b/gemfiles/dalli2.gemfile index c47d5afa..eb7e4acb 100644 --- a/gemfiles/dalli2.gemfile +++ b/gemfiles/dalli2.gemfile @@ -4,4 +4,9 @@ source "https://rubygems.org" gem "dalli", "~> 2.0" +group :maintenance, optional: true do + gem "bake" + gem "bake-gem" +end + gemspec path: "../" diff --git a/gemfiles/dalli3.gemfile b/gemfiles/dalli3.gemfile index 5f4d073b..3873dedf 100644 --- a/gemfiles/dalli3.gemfile +++ b/gemfiles/dalli3.gemfile @@ -4,4 +4,9 @@ source "https://rubygems.org" gem "dalli", "~> 3.0" +group :maintenance, optional: true do + gem "bake" + gem "bake-gem" +end + gemspec path: "../" diff --git a/gemfiles/rack_1.gemfile b/gemfiles/rack_1.gemfile index 2390b859..36b2f91b 100644 --- a/gemfiles/rack_1.gemfile +++ b/gemfiles/rack_1.gemfile @@ -7,4 +7,9 @@ gem "activesupport", ">= 4.2" gem "rack", "~> 1.6" gem "rack-test", ">= 0.6" +group :maintenance, optional: true do + gem "bake" + gem "bake-gem" +end + gemspec path: "../" diff --git a/gemfiles/rack_2.gemfile b/gemfiles/rack_2.gemfile index 41b64ab4..246c981a 100644 --- a/gemfiles/rack_2.gemfile +++ b/gemfiles/rack_2.gemfile @@ -3,6 +3,10 @@ source "https://rubygems.org" gem "rack", "~> 2.0" -gem "railties" + +group :maintenance, optional: true do + gem "bake" + gem "bake-gem" +end gemspec path: "../" diff --git a/gemfiles/rack_3.gemfile b/gemfiles/rack_3.gemfile index 24e3a766..f0735014 100644 --- a/gemfiles/rack_3.gemfile +++ b/gemfiles/rack_3.gemfile @@ -4,4 +4,9 @@ source "https://rubygems.org" gem "rack", "~> 3.0" +group :maintenance, optional: true do + gem "bake" + gem "bake-gem" +end + gemspec path: "../" diff --git a/gemfiles/rails_4_2.gemfile b/gemfiles/rails_4_2.gemfile index 055cf9f6..4209e6dc 100644 --- a/gemfiles/rails_4_2.gemfile +++ b/gemfiles/rails_4_2.gemfile @@ -5,4 +5,9 @@ source "https://rubygems.org" gem "railties", "~> 4.2.0" gem "rack-test", ">= 0.6" +group :maintenance, optional: true do + gem "bake" + gem "bake-gem" +end + gemspec path: "../" diff --git a/gemfiles/rails_5_2.gemfile b/gemfiles/rails_5_2.gemfile index 8b2627fc..161bb698 100644 --- a/gemfiles/rails_5_2.gemfile +++ b/gemfiles/rails_5_2.gemfile @@ -4,4 +4,9 @@ source "https://rubygems.org" gem "railties", "~> 5.2.0" +group :maintenance, optional: true do + gem "bake" + gem "bake-gem" +end + gemspec path: "../" diff --git a/gemfiles/rails_6_0.gemfile b/gemfiles/rails_6_0.gemfile index 4cd55a81..679fea78 100644 --- a/gemfiles/rails_6_0.gemfile +++ b/gemfiles/rails_6_0.gemfile @@ -4,4 +4,9 @@ source "https://rubygems.org" gem "railties", "~> 6.0.0" +group :maintenance, optional: true do + gem "bake" + gem "bake-gem" +end + gemspec path: "../" diff --git a/gemfiles/rails_6_1.gemfile b/gemfiles/rails_6_1.gemfile index 4c467fec..b1e5c039 100644 --- a/gemfiles/rails_6_1.gemfile +++ b/gemfiles/rails_6_1.gemfile @@ -4,4 +4,9 @@ source "https://rubygems.org" gem "railties", "~> 6.1.0" +group :maintenance, optional: true do + gem "bake" + gem "bake-gem" +end + gemspec path: "../" diff --git a/gemfiles/rails_7_0.gemfile b/gemfiles/rails_7_0.gemfile index 495c72d0..6f490fff 100644 --- a/gemfiles/rails_7_0.gemfile +++ b/gemfiles/rails_7_0.gemfile @@ -4,4 +4,9 @@ source "https://rubygems.org" gem "railties", "~> 7.0.0" +group :maintenance, optional: true do + gem "bake" + gem "bake-gem" +end + gemspec path: "../" diff --git a/gemfiles/rails_7_1.gemfile b/gemfiles/rails_7_1.gemfile index 036d9836..fdfb546f 100644 --- a/gemfiles/rails_7_1.gemfile +++ b/gemfiles/rails_7_1.gemfile @@ -4,4 +4,9 @@ source "https://rubygems.org" gem "railties", "~> 7.1.0" +group :maintenance, optional: true do + gem "bake" + gem "bake-gem" +end + gemspec path: "../" diff --git a/gemfiles/redis_4.gemfile b/gemfiles/redis_4.gemfile index 701e936c..e8b82f16 100644 --- a/gemfiles/redis_4.gemfile +++ b/gemfiles/redis_4.gemfile @@ -4,4 +4,9 @@ source "https://rubygems.org" gem "redis", "~> 4.0" +group :maintenance, optional: true do + gem "bake" + gem "bake-gem" +end + gemspec path: "../" diff --git a/gemfiles/redis_5.gemfile b/gemfiles/redis_5.gemfile index 0aac1021..fc9b4655 100644 --- a/gemfiles/redis_5.gemfile +++ b/gemfiles/redis_5.gemfile @@ -4,4 +4,9 @@ source "https://rubygems.org" gem "redis", "~> 5.0" +group :maintenance, optional: true do + gem "bake" + gem "bake-gem" +end + gemspec path: "../" diff --git a/gemfiles/redis_store.gemfile b/gemfiles/redis_store.gemfile index 8aafc6d1..e32d1e9e 100644 --- a/gemfiles/redis_store.gemfile +++ b/gemfiles/redis_store.gemfile @@ -4,4 +4,9 @@ source "https://rubygems.org" gem "redis-store", "~> 1.5" +group :maintenance, optional: true do + gem "bake" + gem "bake-gem" +end + gemspec path: "../" From c7098d610b7ba5e2f868eac4d9425889095a5611 Mon Sep 17 00:00:00 2001 From: Santiago Bartesaghi Date: Wed, 25 Oct 2023 21:15:53 -0300 Subject: [PATCH 110/113] ci: improve ActiveSupport testing (#633) --- .github/workflows/build.yml | 24 +++++++++++++++-- Appraisals | 26 +++++++++++++++++-- ...ctive_support_6_redis_cache_store.gemfile} | 0 ...upport_6_redis_cache_store_pooled.gemfile} | 0 ...tive_support_7_0_redis_cache_store.gemfile | 13 ++++++++++ ...pport_7_0_redis_cache_store_pooled.gemfile | 14 ++++++++++ ...tive_support_7_1_redis_cache_store.gemfile | 13 ++++++++++ ...pport_7_1_redis_cache_store_pooled.gemfile | 14 ++++++++++ 8 files changed, 100 insertions(+), 4 deletions(-) rename gemfiles/{active_support_redis_cache_store.gemfile => active_support_6_redis_cache_store.gemfile} (100%) rename gemfiles/{active_support_redis_cache_store_pooled.gemfile => active_support_6_redis_cache_store_pooled.gemfile} (100%) create mode 100644 gemfiles/active_support_7_0_redis_cache_store.gemfile create mode 100644 gemfiles/active_support_7_0_redis_cache_store_pooled.gemfile create mode 100644 gemfiles/active_support_7_1_redis_cache_store.gemfile create mode 100644 gemfiles/active_support_7_1_redis_cache_store_pooled.gemfile diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6ca20631..cc3200cc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -38,8 +38,12 @@ jobs: - redis_5 - redis_4 - connection_pool_dalli - - active_support_redis_cache_store - - active_support_redis_cache_store_pooled + - active_support_7_1_redis_cache_store + - active_support_7_1_redis_cache_store_pooled + - active_support_7_0_redis_cache_store + - active_support_7_0_redis_cache_store_pooled + - active_support_6_redis_cache_store + - active_support_6_redis_cache_store_pooled - active_support_5_redis_cache_store - active_support_5_redis_cache_store_pooled - redis_store @@ -88,10 +92,26 @@ jobs: ruby: 2.6.10 - gemfile: rails_7_0 ruby: 2.5.8 + - gemfile: active_support_7_0_redis_cache_store + ruby: 2.5.8 + - gemfile: active_support_7_0_redis_cache_store + ruby: 2.6.10 + - gemfile: active_support_7_0_redis_cache_store_pooled + ruby: 2.5.8 + - gemfile: active_support_7_0_redis_cache_store_pooled + ruby: 2.6.10 - gemfile: rails_7_1 ruby: 2.6.10 - gemfile: rails_7_1 ruby: 2.5.8 + - gemfile: active_support_7_1_redis_cache_store + ruby: 2.5.8 + - gemfile: active_support_7_1_redis_cache_store + ruby: 2.6.10 + - gemfile: active_support_7_1_redis_cache_store_pooled + ruby: 2.5.8 + - gemfile: active_support_7_1_redis_cache_store_pooled + ruby: 2.6.10 env: BUNDLE_GEMFILE: gemfiles/${{ matrix.gemfile }}.gemfile steps: diff --git a/Appraisals b/Appraisals index 89062b53..185740dc 100644 --- a/Appraisals +++ b/Appraisals @@ -70,12 +70,34 @@ appraise "connection_pool_dalli" do gem "dalli", "~> 3.0" end -appraise "active_support_redis_cache_store" do +appraise "active_support_7-1_redis_cache_store" do + gem "activesupport", "~> 7.1.0" + gem "redis", "~> 5.0" +end + +appraise "active_support_7-1_redis_cache_store_pooled" do + gem "activesupport", "~> 7.1.0" + gem "connection_pool", "~> 2.2" + gem "redis", "~> 5.0" +end + +appraise "active_support_7-0_redis_cache_store" do + gem "activesupport", "~> 7.0.0" + gem "redis", "~> 5.0" +end + +appraise "active_support_7-0_redis_cache_store_pooled" do + gem "activesupport", "~> 7.0.0" + gem "connection_pool", "~> 2.2" + gem "redis", "~> 5.0" +end + +appraise "active_support_6_redis_cache_store" do gem "activesupport", "~> 6.1.0" gem "redis", "~> 5.0" end -appraise "active_support_redis_cache_store_pooled" do +appraise "active_support_6_redis_cache_store_pooled" do gem "activesupport", "~> 6.1.0" gem "connection_pool", "~> 2.2" gem "redis", "~> 5.0" diff --git a/gemfiles/active_support_redis_cache_store.gemfile b/gemfiles/active_support_6_redis_cache_store.gemfile similarity index 100% rename from gemfiles/active_support_redis_cache_store.gemfile rename to gemfiles/active_support_6_redis_cache_store.gemfile diff --git a/gemfiles/active_support_redis_cache_store_pooled.gemfile b/gemfiles/active_support_6_redis_cache_store_pooled.gemfile similarity index 100% rename from gemfiles/active_support_redis_cache_store_pooled.gemfile rename to gemfiles/active_support_6_redis_cache_store_pooled.gemfile diff --git a/gemfiles/active_support_7_0_redis_cache_store.gemfile b/gemfiles/active_support_7_0_redis_cache_store.gemfile new file mode 100644 index 00000000..a94cfe88 --- /dev/null +++ b/gemfiles/active_support_7_0_redis_cache_store.gemfile @@ -0,0 +1,13 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "activesupport", "~> 7.0.0" +gem "redis", "~> 5.0" + +group :maintenance, optional: true do + gem "bake" + gem "bake-gem" +end + +gemspec path: "../" diff --git a/gemfiles/active_support_7_0_redis_cache_store_pooled.gemfile b/gemfiles/active_support_7_0_redis_cache_store_pooled.gemfile new file mode 100644 index 00000000..bd2a6e71 --- /dev/null +++ b/gemfiles/active_support_7_0_redis_cache_store_pooled.gemfile @@ -0,0 +1,14 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "activesupport", "~> 7.0.0" +gem "connection_pool", "~> 2.2" +gem "redis", "~> 5.0" + +group :maintenance, optional: true do + gem "bake" + gem "bake-gem" +end + +gemspec path: "../" diff --git a/gemfiles/active_support_7_1_redis_cache_store.gemfile b/gemfiles/active_support_7_1_redis_cache_store.gemfile new file mode 100644 index 00000000..a0602ba5 --- /dev/null +++ b/gemfiles/active_support_7_1_redis_cache_store.gemfile @@ -0,0 +1,13 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "activesupport", "~> 7.1.0" +gem "redis", "~> 5.0" + +group :maintenance, optional: true do + gem "bake" + gem "bake-gem" +end + +gemspec path: "../" diff --git a/gemfiles/active_support_7_1_redis_cache_store_pooled.gemfile b/gemfiles/active_support_7_1_redis_cache_store_pooled.gemfile new file mode 100644 index 00000000..ae2d6d96 --- /dev/null +++ b/gemfiles/active_support_7_1_redis_cache_store_pooled.gemfile @@ -0,0 +1,14 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "activesupport", "~> 7.1.0" +gem "connection_pool", "~> 2.2" +gem "redis", "~> 5.0" + +group :maintenance, optional: true do + gem "bake" + gem "bake-gem" +end + +gemspec path: "../" From 36b830ab3ca8622f6e1acafcca1245ec0f75f1fd Mon Sep 17 00:00:00 2001 From: Gonzalo <456459+grzuy@users.noreply.github.com> Date: Wed, 25 Oct 2023 23:37:16 -0300 Subject: [PATCH 111/113] ci: update tested rails versions (#626) --- .github/workflows/build.yml | 9 ---- Appraisals | 8 ---- gemfiles/rails_4_2.gemfile | 13 ------ spec/rack_attack_instrumentation_spec.rb | 52 +++++++++++------------- 4 files changed, 24 insertions(+), 58 deletions(-) delete mode 100644 gemfiles/rails_4_2.gemfile diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cc3200cc..3237bd4d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -32,7 +32,6 @@ jobs: - rails_6_1 - rails_6_0 - rails_5_2 - - rails_4_2 - dalli3 - dalli2 - redis_5 @@ -56,8 +55,6 @@ jobs: ruby: 3.2.2 - gemfile: active_support_5_redis_cache_store_pooled ruby: 3.2.2 - - gemfile: rails_4_2 - ruby: 3.2.2 - gemfile: dalli2 ruby: 3.2.2 - gemfile: rack_1 @@ -68,8 +65,6 @@ jobs: ruby: 3.1.4 - gemfile: active_support_5_redis_cache_store_pooled ruby: 3.1.4 - - gemfile: rails_4_2 - ruby: 3.1.4 - gemfile: dalli2 ruby: 3.1.4 - gemfile: rack_1 @@ -80,14 +75,10 @@ jobs: ruby: 3.0.6 - gemfile: active_support_5_redis_cache_store_pooled ruby: 3.0.6 - - gemfile: rails_4_2 - ruby: 3.0.6 - gemfile: dalli2 ruby: 3.0.6 - gemfile: rack_1 ruby: 2.7.8 - - gemfile: rails_4_2 - ruby: 2.7.8 - gemfile: rails_7_0 ruby: 2.6.10 - gemfile: rails_7_0 diff --git a/Appraisals b/Appraisals index 185740dc..f68e8b7f 100644 --- a/Appraisals +++ b/Appraisals @@ -41,14 +41,6 @@ appraise 'rails_5-2' do gem 'railties', '~> 5.2.0' end -appraise 'rails_4-2' do - gem 'railties', '~> 4.2.0' - - # Override rack-test version constraint by making it more loose - # so it's compatible with actionpack 4.2.x - gem "rack-test", ">= 0.6" -end - appraise 'dalli2' do gem 'dalli', '~> 2.0' end diff --git a/gemfiles/rails_4_2.gemfile b/gemfiles/rails_4_2.gemfile deleted file mode 100644 index 4209e6dc..00000000 --- a/gemfiles/rails_4_2.gemfile +++ /dev/null @@ -1,13 +0,0 @@ -# This file was generated by Appraisal - -source "https://rubygems.org" - -gem "railties", "~> 4.2.0" -gem "rack-test", ">= 0.6" - -group :maintenance, optional: true do - gem "bake" - gem "bake-gem" -end - -gemspec path: "../" diff --git a/spec/rack_attack_instrumentation_spec.rb b/spec/rack_attack_instrumentation_spec.rb index 111ad148..d2291f77 100644 --- a/spec/rack_attack_instrumentation_spec.rb +++ b/spec/rack_attack_instrumentation_spec.rb @@ -2,42 +2,38 @@ require_relative "spec_helper" require 'active_support' +require 'active_support/subscriber' -# ActiveSupport::Subscribers added in ~> 4.0.2.0 -if ActiveSupport::VERSION::MAJOR > 3 - require_relative 'spec_helper' - require 'active_support/subscriber' - class CustomSubscriber < ActiveSupport::Subscriber - @notification_count = 0 +class CustomSubscriber < ActiveSupport::Subscriber + @notification_count = 0 - class << self - attr_accessor :notification_count - end + class << self + attr_accessor :notification_count + end - def throttle(_event) - self.class.notification_count += 1 - end + def throttle(_event) + self.class.notification_count += 1 end +end - describe 'Rack::Attack.instrument' do - before do - @period = 60 # Use a long period; failures due to cache key rotation less likely - Rack::Attack.cache.store = ActiveSupport::Cache::MemoryStore.new - Rack::Attack.throttle('ip/sec', limit: 1, period: @period) { |req| req.ip } - end +describe 'Rack::Attack.instrument' do + before do + @period = 60 # Use a long period; failures due to cache key rotation less likely + Rack::Attack.cache.store = ActiveSupport::Cache::MemoryStore.new + Rack::Attack.throttle('ip/sec', limit: 1, period: @period) { |req| req.ip } + end - describe "with throttling" do - before do - ActiveSupport::Notifications.stub(:notifier, ActiveSupport::Notifications::Fanout.new) do - CustomSubscriber.attach_to("rack_attack") - 2.times { get '/', {}, 'REMOTE_ADDR' => '1.2.3.4' } - end + describe "with throttling" do + before do + ActiveSupport::Notifications.stub(:notifier, ActiveSupport::Notifications::Fanout.new) do + CustomSubscriber.attach_to("rack_attack") + 2.times { get '/', {}, 'REMOTE_ADDR' => '1.2.3.4' } end + end - it 'should instrument without error' do - _(last_response.status).must_equal 429 - assert_equal 1, CustomSubscriber.notification_count - end + it 'should instrument without error' do + _(last_response.status).must_equal 429 + assert_equal 1, CustomSubscriber.notification_count end end end From 4a68debb7a767ccf4e54b122d32f7741f608831d Mon Sep 17 00:00:00 2001 From: Santiago Bartesaghi Date: Thu, 26 Oct 2023 19:59:16 -0300 Subject: [PATCH 112/113] ci: automatically run with latest ruby patch version (#634) --- .github/workflows/build.yml | 68 ++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3237bd4d..c7950f0f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,12 +17,12 @@ jobs: strategy: matrix: ruby: - - 3.2.2 - - 3.1.4 - - 3.0.6 - - 2.7.8 - - 2.6.10 - - 2.5.8 + - '3.2' + - '3.1' + - '3.0' + - '2.7' + - '2.6' + - '2.5' gemfile: - rack_3 - rack_2 @@ -48,61 +48,61 @@ jobs: - redis_store exclude: - gemfile: rack_1 - ruby: 3.2.2 + ruby: '3.2' - gemfile: rails_5_2 - ruby: 3.2.2 + ruby: '3.2' - gemfile: active_support_5_redis_cache_store - ruby: 3.2.2 + ruby: '3.2' - gemfile: active_support_5_redis_cache_store_pooled - ruby: 3.2.2 + ruby: '3.2' - gemfile: dalli2 - ruby: 3.2.2 + ruby: '3.2' - gemfile: rack_1 - ruby: 3.1.4 + ruby: '3.1' - gemfile: rails_5_2 - ruby: 3.1.4 + ruby: '3.1' - gemfile: active_support_5_redis_cache_store - ruby: 3.1.4 + ruby: '3.1' - gemfile: active_support_5_redis_cache_store_pooled - ruby: 3.1.4 + ruby: '3.1' - gemfile: dalli2 - ruby: 3.1.4 + ruby: '3.1' - gemfile: rack_1 - ruby: 3.0.6 + ruby: '3.0' - gemfile: rails_5_2 - ruby: 3.0.6 + ruby: '3.0' - gemfile: active_support_5_redis_cache_store - ruby: 3.0.6 + ruby: '3.0' - gemfile: active_support_5_redis_cache_store_pooled - ruby: 3.0.6 + ruby: '3.0' - gemfile: dalli2 - ruby: 3.0.6 + ruby: '3.0' - gemfile: rack_1 - ruby: 2.7.8 + ruby: '2.7' - gemfile: rails_7_0 - ruby: 2.6.10 + ruby: '2.6' - gemfile: rails_7_0 - ruby: 2.5.8 + ruby: '2.5' - gemfile: active_support_7_0_redis_cache_store - ruby: 2.5.8 + ruby: '2.6' - gemfile: active_support_7_0_redis_cache_store - ruby: 2.6.10 + ruby: '2.5' - gemfile: active_support_7_0_redis_cache_store_pooled - ruby: 2.5.8 + ruby: '2.6' - gemfile: active_support_7_0_redis_cache_store_pooled - ruby: 2.6.10 + ruby: '2.5' - gemfile: rails_7_1 - ruby: 2.6.10 + ruby: '2.6' - gemfile: rails_7_1 - ruby: 2.5.8 + ruby: '2.5' - gemfile: active_support_7_1_redis_cache_store - ruby: 2.5.8 + ruby: '2.6' - gemfile: active_support_7_1_redis_cache_store - ruby: 2.6.10 + ruby: '2.5' - gemfile: active_support_7_1_redis_cache_store_pooled - ruby: 2.5.8 + ruby: '2.6' - gemfile: active_support_7_1_redis_cache_store_pooled - ruby: 2.6.10 + ruby: '2.5' env: BUNDLE_GEMFILE: gemfiles/${{ matrix.gemfile }}.gemfile steps: From ffc210250f4fc7e494671068396d6b1b1db3db6c Mon Sep 17 00:00:00 2001 From: Santiago Bartesaghi Date: Fri, 27 Oct 2023 21:46:07 -0300 Subject: [PATCH 113/113] refactor: minor refactoring (#635) --- lib/rack/attack/throttle.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/rack/attack/throttle.rb b/lib/rack/attack/throttle.rb index 69923393..0ec5f7aa 100644 --- a/lib/rack/attack/throttle.rb +++ b/lib/rack/attack/throttle.rb @@ -38,8 +38,9 @@ def matched_by?(request) epoch_time: cache.last_epoch_time } + annotate_request_with_throttle_data(request, data) + (count > current_limit).tap do |throttled| - annotate_request_with_throttle_data(request, data) if throttled annotate_request_with_matched_data(request, data) Rack::Attack.instrument(request)