diff --git a/Appraisals b/Appraisals index 85c670b..bd04b28 100644 --- a/Appraisals +++ b/Appraisals @@ -1,13 +1,21 @@ appraise 'activesupport3.2' do gem 'activesupport', '~> 3.2.0' + gem 'actionpack', '~> 3.2.0' end appraise 'activesupport4.0' do gem 'activesupport', '~> 4.0.0' + gem 'actionpack', '~> 4.0.0' end appraise 'activesupport4.1' do gem 'activesupport', '~> 4.1.0' + gem 'actionpack', '~> 4.1.0' +end + +appraise 'activesupport4.2' do + gem 'activesupport', '~> 4.2.0' + gem 'actionpack', '~> 4.2.0' end appraise 'dalli1.1' do diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fb3adc..d4a5d65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,13 @@ # Changlog ## master (unreleased) + +## v4.3.1 - 18 Dec 2015 + - SECURITY FIX: Normalize request paths when using ActionDispatch. Thanks + Andres Riancho at @includesecurity for reporting it. - Remove support for ruby 1.9.x + - Add Code of Conduct + - Several documentation and testing improvements ## v4.3.0 - 22 May 2015 diff --git a/Gemfile b/Gemfile index 41e7aff..5d00c12 100644 --- a/Gemfile +++ b/Gemfile @@ -1,4 +1,9 @@ -source "https://rubygems.org" +source 'https://rubygems.org' + gemspec -#gem 'debugger', platform: 'ruby' +group :development do + gem 'pry' + gem 'guard' # NB: this is necessary in newer versions + gem 'guard-minitest' +end diff --git a/Guardfile b/Guardfile new file mode 100644 index 0000000..ebf900b --- /dev/null +++ b/Guardfile @@ -0,0 +1,10 @@ +# A sample Guardfile +# More info at https://github.com/guard/guard#readme + +guard :minitest do + # with Minitest::Spec + watch(%r{^spec/(.*)_spec\.rb$}) + watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" } + watch(%r{^spec/spec_helper\.rb$}) { 'spec' } +end + diff --git a/README.md b/README.md index 31a230b..b669ef9 100644 --- a/README.md +++ b/README.md @@ -147,6 +147,8 @@ Rack::Attack.blacklist('fail2ban pentesters') do |req| end ``` +Note that `Fail2Ban` filters are not automatically scoped to the blacklist, so when using multiple filters in an application the scoping must be added to the discriminator e.g. `"pentest:#{req.ip}"`. + #### Allow2Ban `Allow2Ban.filter` works the same way as the `Fail2Ban.filter` except that it *allows* requests from misbehaving clients until such time as they reach maxretry at which they are cut off as per normal. diff --git a/gemfiles/activesupport3.2.gemfile b/gemfiles/activesupport3.2.gemfile index 3068f49..aa24027 100644 --- a/gemfiles/activesupport3.2.gemfile +++ b/gemfiles/activesupport3.2.gemfile @@ -4,5 +4,12 @@ source "https://rubygems.org" gem "activesupport", "~> 3.2.0" gem "memcache-client" +gem "actionpack", "~> 3.2.0" + +group :development do + gem "pry" + gem "guard" + gem "guard-minitest" +end gemspec :path => "../" diff --git a/gemfiles/activesupport4.0.gemfile b/gemfiles/activesupport4.0.gemfile index 697ad67..de2923f 100644 --- a/gemfiles/activesupport4.0.gemfile +++ b/gemfiles/activesupport4.0.gemfile @@ -3,5 +3,12 @@ source "https://rubygems.org" gem "activesupport", "~> 4.0.0" +gem "actionpack", "~> 4.0.0" + +group :development do + gem "pry" + gem "guard" + gem "guard-minitest" +end gemspec :path => "../" diff --git a/gemfiles/activesupport4.1.gemfile b/gemfiles/activesupport4.1.gemfile index 670c4f8..891419a 100644 --- a/gemfiles/activesupport4.1.gemfile +++ b/gemfiles/activesupport4.1.gemfile @@ -3,5 +3,12 @@ source "https://rubygems.org" gem "activesupport", "~> 4.1.0" +gem "actionpack", "~> 4.1.0" + +group :development do + gem "pry" + gem "guard" + gem "guard-minitest" +end gemspec :path => "../" diff --git a/gemfiles/activesupport4.2.gemfile b/gemfiles/activesupport4.2.gemfile index 67154bc..1b122d1 100644 --- a/gemfiles/activesupport4.2.gemfile +++ b/gemfiles/activesupport4.2.gemfile @@ -2,6 +2,13 @@ source "https://rubygems.org" -gem "activesupport", "~> 4.2.1" +gem "activesupport", "~> 4.2.0" +gem "actionpack", "~> 4.2.0" + +group :development do + gem "pry" + gem "guard" + gem "guard-minitest" +end gemspec :path => "../" diff --git a/gemfiles/dalli1.1.gemfile b/gemfiles/dalli1.1.gemfile index 06ca591..eddf5c3 100644 --- a/gemfiles/dalli1.1.gemfile +++ b/gemfiles/dalli1.1.gemfile @@ -4,4 +4,10 @@ source "https://rubygems.org" gem "dalli", "1.1.5" +group :development do + gem "pry" + gem "guard" + gem "guard-minitest" +end + gemspec :path => "../" diff --git a/gemfiles/dalli2.gemfile b/gemfiles/dalli2.gemfile index e9fe487..93d097e 100644 --- a/gemfiles/dalli2.gemfile +++ b/gemfiles/dalli2.gemfile @@ -4,4 +4,10 @@ source "https://rubygems.org" gem "dalli", "~> 2.0" +group :development do + gem "pry" + gem "guard" + gem "guard-minitest" +end + gemspec :path => "../" diff --git a/lib/rack/attack.rb b/lib/rack/attack.rb index 14a4ed9..f91feb9 100644 --- a/lib/rack/attack.rb +++ b/lib/rack/attack.rb @@ -3,6 +3,7 @@ require 'forwardable' class Rack::Attack autoload :Cache, 'rack/attack/cache' + autoload :PathNormalizer, 'rack/attack/path_normalizer' autoload :Check, 'rack/attack/check' autoload :Throttle, 'rack/attack/throttle' autoload :Whitelist, 'rack/attack/whitelist' @@ -92,6 +93,7 @@ class Rack::Attack end def call(env) + env['PATH_INFO'] = PathNormalizer.normalize_path(env['PATH_INFO']) req = Rack::Attack::Request.new(env) if whitelisted?(req) diff --git a/lib/rack/attack/path_normalizer.rb b/lib/rack/attack/path_normalizer.rb new file mode 100644 index 0000000..afeb2f9 --- /dev/null +++ b/lib/rack/attack/path_normalizer.rb @@ -0,0 +1,27 @@ +class Rack::Attack + + # When using Rack::Attack with a Rails app, developers expect the request path + # to be normalized. In particular, trailing slashes are stripped. + # (See http://git.io/v0rrR for implementation.) + # + # Look for an ActionDispatch utility class that Rails folks would expect + # to normalize request paths. If unavailable, use a fallback class that + # doesn't normalize the path (as a non-Rails rack app developer expects). + + module FallbackPathNormalizer + def self.normalize_path(path) + path + end + end + + PathNormalizer = if defined?(::ActionDispatch::Journey::Router::Utils) + # For Rails 4+ apps + ::ActionDispatch::Journey::Router::Utils + elsif defined?(::Journey::Router::Utils) + # for Rails 3.2 + ::Journey::Router::Utils + else + FallbackPathNormalizer + end + +end diff --git a/lib/rack/attack/version.rb b/lib/rack/attack/version.rb index e7a3735..97612cc 100644 --- a/lib/rack/attack/version.rb +++ b/lib/rack/attack/version.rb @@ -1,5 +1,5 @@ module Rack class Attack - VERSION = '4.3.0' + VERSION = '4.3.1' end end diff --git a/rack-attack.gemspec b/rack-attack.gemspec index bef0f45..057aa6d 100644 --- a/rack-attack.gemspec +++ b/rack-attack.gemspec @@ -28,6 +28,7 @@ Gem::Specification.new do |s| s.add_development_dependency 'rake' s.add_development_dependency 'appraisal' s.add_development_dependency 'activesupport', '>= 3.0.0' + s.add_development_dependency 'actionpack', '>= 3.0.0' s.add_development_dependency 'redis-activesupport' s.add_development_dependency 'dalli' s.add_development_dependency 'connection_pool' diff --git a/spec/rack_attack_path_normalizer_spec.rb b/spec/rack_attack_path_normalizer_spec.rb new file mode 100644 index 0000000..1c5b66e --- /dev/null +++ b/spec/rack_attack_path_normalizer_spec.rb @@ -0,0 +1,17 @@ +require_relative 'spec_helper' + +describe Rack::Attack::PathNormalizer do + subject { Rack::Attack::PathNormalizer } + + it 'should have a normalize_path method' do + subject.normalize_path('/foo').must_equal '/foo' + end + + describe 'FallbackNormalizer' do + subject { Rack::Attack::FallbackPathNormalizer } + + it '#normalize_path does not change the path' do + subject.normalize_path('').must_equal '' + end + end +end diff --git a/spec/rack_attack_spec.rb b/spec/rack_attack_spec.rb index cd6b599..3084ffb 100644 --- a/spec/rack_attack_spec.rb +++ b/spec/rack_attack_spec.rb @@ -3,6 +3,17 @@ require_relative 'spec_helper' describe 'Rack::Attack' do allow_ok_requests + describe 'normalizing paths' do + before do + Rack::Attack.blacklist("banned_path") {|req| req.path == '/foo' } + end + + it 'blocks requests with trailing slash' do + get '/foo/' + last_response.status.must_equal 403 + end + end + describe 'blacklist' do before do @bad_ip = '1.2.3.4' diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index dec8563..118d1fe 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -5,12 +5,17 @@ require "minitest/autorun" require "minitest/pride" require "rack/test" require 'active_support' +require 'action_dispatch' + +# Load Journey for Rails 3.2 +require 'journey' if ActionPack::VERSION::MAJOR == 3 + require "rack/attack" begin - require 'debugger' + require 'pry' rescue LoadError - #nothing to do here + #nothing to do here end class MiniTest::Spec