diff --git a/rack-protection/README.md b/rack-protection/README.md index feaeff96..725572cc 100644 --- a/rack-protection/README.md +++ b/rack-protection/README.md @@ -80,3 +80,11 @@ Prevented by: gem install rack-protection +# Instrumentation + +Instrumentation is enabled by passing in an instrumenter as an option. +``` +use Rack::Protection, instrumenter: ActiveSupport::Notifications +``` + +The instrumenter is passed a namespace (String) and environment (Hash). The namespace is 'rack.protection' and the attack type can be obtained from the environment key 'rack.protection.attack'. diff --git a/rack-protection/lib/rack/protection/base.rb b/rack-protection/lib/rack/protection/base.rb index 76e35c58..50642524 100755 --- a/rack-protection/lib/rack/protection/base.rb +++ b/rack-protection/lib/rack/protection/base.rb @@ -44,6 +44,7 @@ module Rack def call(env) unless accepts? env warn env, "attack prevented by #{self.class}" + instrument env result = react env end result or app.call(env) @@ -60,6 +61,12 @@ module Rack l.warn(message) end + def instrument(env) + return unless i = options[:instrumenter] + env['rack.protection.attack'] = self.class.name.split('::').last.downcase + i.instrument('rack.protection', env) + end + def deny(env) [options[:status], {'Content-Type' => 'text/plain'}, [options[:message]]] end diff --git a/rack-protection/spec/protection_spec.rb b/rack-protection/spec/protection_spec.rb index 8ed6d3ec..69d87584 100755 --- a/rack-protection/spec/protection_spec.rb +++ b/rack-protection/spec/protection_spec.rb @@ -46,4 +46,25 @@ describe Rack::Protection do it { should be_false } end end + + describe "#instrument" do + let(:env) { { 'rack.protection.attack' => 'base' } } + let(:instrumenter) { double('Instrumenter') } + + after do + app.instrument(env) + end + + context 'with an instrumenter specified' do + let(:app) { Rack::Protection::Base.new(nil, :instrumenter => instrumenter) } + + it { instrumenter.should_receive(:instrument).with('rack.protection', env) } + end + + context 'with no instrumenter specified' do + let(:app) { Rack::Protection::Base.new(nil) } + + it { instrumenter.should_not_receive(:instrument) } + end + end end