bot-away 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,11 +1,112 @@
1
+ ENV['RAILS_ENV'] ||= 'test'
2
+
1
3
  require 'rubygems'
2
4
  require 'action_controller'
3
5
  require 'action_view'
4
6
 
5
- ActionController::Routing::Routes.load!
6
- ActionController::Base.session = { :key => "_myapp_session", :secret => "12345"*6 }
7
+ begin
8
+ # rails 2.x
9
+ ActionController::Routing::Routes.load!
10
+ ActionController::Base.session = { :key => "_myapp_session", :secret => "12345"*6 }
11
+ ActionController::Base.view_paths << File.expand_path(File.join(File.dirname(__FILE__), "support/views"))
12
+ RAILS_VERSION = "2.3"
13
+ rescue
14
+ # rails 3
15
+ #require 'active_record/railtie'
16
+ require 'action_controller/railtie'
17
+ require 'action_mailer/railtie'
18
+ require 'active_resource/railtie'
19
+ require 'rails/test_unit/railtie'
20
+
21
+ # stub out the csrf token so that it always produces a predictable (testable) result
22
+ module ActionView
23
+ # = Action View CSRF Helper
24
+ module Helpers
25
+ module CsrfHelper
26
+ # Returns a meta tag with the cross-site request forgery protection token
27
+ # for forms to use. Place this in your head.
28
+ def csrf_meta_tag
29
+ if protect_against_forgery?
30
+ %(<meta name="csrf-param" content="#{Rack::Utils.escape_html(request_forgery_protection_token)}"/>\n<meta name="csrf-token" content="1234"/>).html_safe
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ class BotAwayApp < Rails::Application
38
+ config.session_store :cookie_store, :key => "_myapp_session", :secret => "12345"*6
39
+ paths.app.views = File.expand_path(File.join(File.dirname(__FILE__), "support/views"))
40
+ config.active_support.deprecation = :stderr
41
+ end
42
+ BotAwayApp.initialize!
43
+ BotAwayApp.routes.draw do
44
+ match '/:controller/:action'
45
+ end
46
+
47
+ RAILS_VERSION = "3.0"
48
+ end
7
49
 
8
50
  require File.join(File.dirname(__FILE__), '../lib/bot-away')
51
+ require File.join(File.dirname(__FILE__), "rspec_version")
52
+
53
+ class MockObject
54
+ attr_accessor :method_name
55
+
56
+ def id
57
+ 1
58
+ end
59
+
60
+ # for testing grouped_collection_select
61
+ def object_name
62
+ [self]
63
+ end
64
+
65
+ def initialize
66
+ @method_name = 'method_value'
67
+ end
68
+ end
69
+
70
+
71
+ if RSPEC_VERSION < "2.0"
72
+ Spec::Runner.configure do |config|
73
+ config.before(:each) do
74
+ BotAway.reset!
75
+ end
76
+ end
77
+
78
+ if defined?(ActionController::TestProcess)
79
+ # note that this only matters for TEST requests; real ones use the params parser.
80
+ module ActionController::TestProcess
81
+ def process_with_deobfuscation(action, parameters = nil, session = nil, flash = nil, http_method = 'GET')
82
+ process_without_deobfuscation(action, parameters, session, flash, http_method)
83
+ @request.parameters.replace(BotAway::ParamParser.new(@request.ip, @request.params).params)
84
+ end
85
+
86
+ alias_method_chain :process, :deobfuscation
87
+ end
88
+ end
89
+ else
90
+ require 'rspec/rails'
91
+
92
+ # note that this only matters for TEST requests; real ones use the params parser.
93
+ module ActionController::TestCase::Behavior
94
+ def process_with_deobfuscation(action, parameters = nil, session = nil, flash = nil, http_method = 'GET')
95
+ process_without_deobfuscation(action, parameters, session, flash, http_method)
96
+ request.parameters.replace(BotAway::ParamParser.new(request.ip, request.params).params)
97
+ end
98
+
99
+ alias_method_chain :process, :deobfuscation
100
+ end
101
+
102
+ RSpec.configure do |config|
103
+ config.before(:each) do
104
+ BotAway.reset!
105
+ # BotAway doesn't work without forgery protection, and RSpec-Rails 2 disables it. Lost way too many hours on this.
106
+ ActionController::Base.allow_forgery_protection = true
107
+ end
108
+ end
109
+ end
9
110
 
10
111
  Dir[File.join(File.dirname(__FILE__), 'support/**/*.rb')].each do |fi|
11
112
  require fi
@@ -1,68 +1,123 @@
1
1
  module ObfuscationHelper
2
- def includes_honeypot(object_name, method_name)
3
- it "includes a honeypot called #{object_name}[#{method_name}]" do
4
- subject.should include_honeypot(object_name, method_name)
5
- end
6
- end
7
-
8
- def is_obfuscated_as(id, name)
9
- it "is obfuscated as #{id}, #{name}" do
10
- subject.should be_obfuscated_as(id, name)
11
- end
2
+ def controller_class
3
+ TestController
12
4
  end
13
5
 
14
6
  def dump
15
- returning(yield) { |x| puts x }
7
+ result = yield
8
+ puts result if ENV['DUMP']
9
+ result
16
10
  end
17
-
11
+
12
+ if RAILS_VERSION >= "3.0"
13
+ def self.included(base)
14
+ base.before(:each) do
15
+ session[:_csrf_token] = '1234'
16
+ controller.stub!(:protect_from_forgery?).and_return(true)
17
+ if respond_to?(:view)
18
+ controller.request.path_parameters["action"] ||= "index"
19
+ controller.action_name ||= "index"
20
+ view.stub!(:request_forgery_protection_token).and_return(:authenticity_token)
21
+ view.stub!(:form_authenticity_token).and_return('1234')
22
+ else
23
+ # response.stub!(:request_forgery_protection_token).and_return(:authenticity_token)
24
+ # response.stub!(:form_authenticity_token).and_return('1234')
25
+ end
26
+ end
27
+ end
28
+ end
29
+
18
30
  def builder
19
31
  return @builder if @builder
20
- response = TestController.call(Rack::MockRequest.env_for('/').merge({'REQUEST_URI' => '/',
21
- 'REMOTE_ADDR' => '127.0.0.1'}))
22
- response.template.controller.request_forgery_protection_token = :authenticity_token
23
- response.template.controller.session[:_csrf_token] = '1234'
24
- @builder = ActionView::Helpers::FormBuilder.new(:object_name, MockObject.new, response.template, {}, proc {})
25
- end
26
-
27
- def obfuscates(method, options = {}, unused = nil)
28
- if !options.kind_of?(Hash)
29
- options = { :obfuscated_id => options, :obfuscated_name => unused }
30
- end
31
-
32
- obfuscated_id = options[:obfuscated_id] || self.obfuscated_id
33
- obfuscated_name = options[:obfuscatd_name] || self.obfuscated_name
34
-
35
- value = yield
36
- context "##{method}" do
37
- subject { proc { dump { value } } }
38
-
39
- if options[:name]
40
- includes_honeypot(options[:name], nil)
41
- else
42
- includes_honeypot(options[:object_name] || object_name, options[:method_name] || method_name)
43
- end
44
- is_obfuscated_as(obfuscated_id, obfuscated_name)
32
+ if RAILS_VERSION < "3.0"
33
+ response = TestController.call(Rack::MockRequest.env_for('/').merge({'REQUEST_URI' => '/',
34
+ 'REMOTE_ADDR' => '127.0.0.1'}))
35
+ response.template.controller.request_forgery_protection_token = :authenticity_token
36
+ response.template.controller.session[:_csrf_token] = '1234'
37
+ @builder = ActionView::Helpers::FormBuilder.new(:object_name, MockObject.new, response.template, {}, proc {})
38
+ else
39
+ @builder = ActionView::Base.default_form_builder.new(:object_name, MockObject.new, view, {}, proc {})
45
40
  end
46
41
  end
47
-
42
+
43
+ # this is the obfuscated version of the string "object_name_method_name"
48
44
  def obfuscated_id
49
- "e21372563297c728093bf74c3cb6b96c"
45
+ self.class.obfuscated_id
50
46
  end
51
-
47
+
48
+ # this is the obfuscated version of the string "object_name[method_name]"
52
49
  def obfuscated_name
53
- "a0844d45bf150668ff1d86a6eb491969"
50
+ self.class.obfuscated_name
54
51
  end
55
52
 
56
53
  def object_name
57
- "object_name"
54
+ self.class.object_name
58
55
  end
59
-
56
+
60
57
  def method_name
61
- "method_name"
58
+ self.class.method_name
59
+ end
60
+
61
+ module ClassMethods
62
+ def includes_honeypot(object_name, method_name)
63
+ it "includes a honeypot called #{object_name}[#{method_name}]" do
64
+ subject.should include_honeypot(object_name, method_name)
65
+ end
66
+ end
67
+
68
+ def is_obfuscated_as(id, name)
69
+ it "is obfuscated as #{id}, #{name}" do
70
+ subject.should be_obfuscated_as(id, name)
71
+ end
72
+ end
73
+
74
+ def obfuscates(method, options = {}, unused = nil, &block)
75
+ if !options.kind_of?(Hash)
76
+ options = { :obfuscated_id => options, :obfuscated_name => unused }
77
+ end
78
+
79
+ obfuscated_id = options[:obfuscated_id] || self.obfuscated_id
80
+ obfuscated_name = options[:obfuscatd_name] || self.obfuscated_name
81
+
82
+ context "##{method}" do
83
+ before(:each) { @obfuscates_value = instance_eval(&block) }
84
+ subject { proc { dump { @obfuscates_value } } }
85
+
86
+ if options[:name]
87
+ includes_honeypot(options[:name], nil)
88
+ else
89
+ includes_honeypot(options[:object_name] || object_name, options[:method_name] || method_name)
90
+ end
91
+ is_obfuscated_as(obfuscated_id, obfuscated_name)
92
+ end
93
+ end
94
+
95
+ def obfuscated_id
96
+ RAILS_VERSION >= "3.0" ? "f51a02a636f507f1bd64722451b71297" : "e21372563297c728093bf74c3cb6b96c"
97
+ end
98
+
99
+ def obfuscated_name
100
+ RAILS_VERSION >= "3.0" ? "cd538a9170613d6dedbcc54a0aa24881" : "a0844d45bf150668ff1d86a6eb491969"
101
+ end
102
+
103
+ def object_name
104
+ "object_name"
105
+ end
106
+
107
+ def method_name
108
+ "method_name"
109
+ end
62
110
  end
63
111
  end
64
112
 
65
- Spec::Runner.configure do |config|
66
- config.extend ObfuscationHelper
67
- config.include ObfuscationHelper
113
+ if RSPEC_VERSION < "2.0"
114
+ Spec::Runner.configure do |config|
115
+ config.extend ObfuscationHelper::ClassMethods
116
+ config.include ObfuscationHelper
117
+ end
118
+ else
119
+ RSpec.configure do |config|
120
+ config.extend ObfuscationHelper::ClassMethods
121
+ config.include ObfuscationHelper
122
+ end
68
123
  end
@@ -0,0 +1,21 @@
1
+ require 'spec/mocks'
2
+
3
+ module Rails
4
+ class << self
5
+ def logger
6
+ return @logger if @logger
7
+ @logger = Object.new
8
+ klass = class << @logger; self; end
9
+ if RSPEC_VERSION < "2.0"
10
+ klass.send(:include, Spec::Mocks::Methods)
11
+ else
12
+ end
13
+
14
+ def @logger.debug(message); end
15
+ def @logger.info(message); end
16
+ def @logger.error(message); end
17
+ def @logger.warn(message); end
18
+ @logger
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,28 @@
1
+ class Post
2
+ attr_reader :subject, :body, :subscribers
3
+
4
+ if defined?(ActiveModel)
5
+ extend ActiveModel::Naming
6
+
7
+ def to_key
8
+ [1]
9
+ end
10
+ end
11
+ end
12
+
13
+ class ApplicationController < ActionController::Base
14
+
15
+ end
16
+
17
+ class TestController < ApplicationController
18
+ def index
19
+ end
20
+
21
+ def model_form
22
+ @post = Post.new
23
+ end
24
+
25
+ def proc_form
26
+ render :text => params.to_yaml
27
+ end
28
+ end
@@ -1,23 +1,29 @@
1
1
  require 'spec_helper'
2
2
 
3
- def template
4
- return @response if @response
5
- @response = TestController.call(Rack::MockRequest.env_for('/').merge({'REQUEST_URI' => '/',
6
- 'REMOTE_ADDR' => '127.0.0.1'}))
7
- @response.template.controller.request_forgery_protection_token = :authenticity_token
8
- @response.template.controller.session[:_csrf_token] = '1234'
9
- @response.template
10
- end
11
-
12
- def mock_object
13
- @mock_object ||= MockObject.new
14
- end
3
+ describe ActionView::Helpers::InstanceTag do
4
+ if method_defined?(:controller)
5
+ def template
6
+ view
7
+ end
8
+ else
9
+ def template
10
+ return @response if @response
11
+ @response = TestController.call(Rack::MockRequest.env_for('/').merge({'REQUEST_URI' => '/',
12
+ 'REMOTE_ADDR' => '127.0.0.1'}))
13
+ @response.template.controller.request_forgery_protection_token = :authenticity_token
14
+ @response.template.controller.session[:_csrf_token] = '1234'
15
+ @response.template
16
+ end
17
+ end
18
+
19
+ def mock_object
20
+ @mock_object ||= MockObject.new
21
+ end
15
22
 
16
- def default_instance_tag
17
- ActionView::Helpers::InstanceTag.new("object_name", "method_name", template, mock_object)
18
- end
23
+ def default_instance_tag
24
+ ActionView::Helpers::InstanceTag.new("object_name", "method_name", template, mock_object)
25
+ end
19
26
 
20
- describe ActionView::Helpers::InstanceTag do
21
27
  subject { default_instance_tag }
22
28
 
23
29
  context "with a valid text area tag" do
@@ -39,17 +45,17 @@ describe ActionView::Helpers::InstanceTag do
39
45
  end
40
46
 
41
47
  it "should obfuscate tag name" do
42
- subject.obfuscated_tag(*@tag_options).should =~ /name="a0844d45bf150668ff1d86a6eb491969"/
48
+ subject.obfuscated_tag(*@tag_options).should =~ /name="#{obfuscated_name}"/
43
49
  end
44
50
 
45
51
  it "should obfuscate tag id" do
46
- subject.obfuscated_tag(*@tag_options).should =~ /id="e21372563297c728093bf74c3cb6b96c"/
47
- end
48
-
49
- it "should not obfuscate tag value" do
50
- subject.obfuscated_tag(*@tag_options).should_not =~ /value="5a6a50d5fd0b5c8b1190d87eb0057e47"/
52
+ subject.obfuscated_tag(*@tag_options).should =~ /id="#{obfuscated_id}"/
51
53
  end
52
54
 
55
+ # it "should not obfuscate tag value" do
56
+ # subject.obfuscated_tag(*@tag_options).should =~ /value="@tag_options"/
57
+ # end
58
+ #
53
59
  it "should include unobfuscated tag value" do
54
60
  subject.obfuscated_tag(*@tag_options).should =~ /value="method_value"/
55
61
  end
@@ -0,0 +1,101 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Bot-Away" do
4
+ # Basically we're just switching BA on and off, so we only need 2 sets of tests: what to expect when it's on, and
5
+ # what to expect when it's off. The rest of this file just flips the switches.
6
+
7
+ def test_params
8
+ {"model"=>"User", "commit"=>"Sign in",
9
+ "authenticity_token"=>"XBQEDkXrm4E8U9slBX45TWNx7TPcx8ww2FSJRy/XXg4=",
10
+ "action"=>"index", "controller"=>"test", "user"=>{"username"=>"admin", "password"=>"Admin01"}
11
+ }
12
+ end
13
+
14
+ def self.it_should_be_disabled
15
+ it "should not obfuscate the field, because it should be disabled" do
16
+ builder.text_field('method_name').should_not match(/name="#{obfuscated_name}/)
17
+ end
18
+
19
+ it "should not drop invalid params, because it should be disabled" do
20
+ parms = BotAway::ParamParser.new('127.0.0.1', test_params).params
21
+ parms.should == test_params
22
+ end
23
+
24
+ it "should be disabled" do
25
+ BotAway.disabled_for?(:controller => 'test', :action => "index").should == true
26
+ end
27
+ end
28
+
29
+ def self.it_should_be_enabled
30
+ it "should obfuscate the field, because it should be enabled" do
31
+ builder.text_field('method_name').should be_obfuscated_as(obfuscated_id, obfuscated_name)
32
+ end
33
+
34
+ it "should drop invalid params, because it should be enabled" do
35
+ parms = BotAway::ParamParser.new('127.0.0.1', test_params).params
36
+ parms.should_not == test_params
37
+ end
38
+
39
+ it "should be disabled" do
40
+ BotAway.disabled_for?(:controller => 'test', :action => "index").should == false
41
+ end
42
+ end
43
+
44
+ # flip-switching begins
45
+
46
+ context "with matching controller name" do
47
+ context "and no action" do
48
+ before(:each) { BotAway.disabled_for :controller => 'test' }
49
+ it_should_be_disabled
50
+ end
51
+
52
+ context "and matching action" do
53
+ before(:each) { BotAway.disabled_for :controller => 'test', :action => 'index' }
54
+ it_should_be_disabled
55
+ end
56
+
57
+ context "and not matching action" do
58
+ before(:each) { BotAway.disabled_for :controller => 'test', :action => 'create' }
59
+ it_should_be_enabled
60
+ end
61
+ end
62
+
63
+ context "with not matching controller name" do
64
+ context "and no action" do
65
+ before(:each) { BotAway.disabled_for :controller => 'users' }
66
+ it_should_be_enabled
67
+ end
68
+
69
+ context "and matching action" do
70
+ before(:each) { BotAway.disabled_for :controller => 'users', :action => 'index' }
71
+ it_should_be_enabled
72
+ end
73
+
74
+ context "and not matching action" do
75
+ before(:each) { BotAway.disabled_for :controller => 'users', :action => 'create' }
76
+ it_should_be_enabled
77
+ end
78
+ end
79
+
80
+ context "with no controller name" do
81
+ context "and matching action" do
82
+ before(:each) { BotAway.disabled_for :action => 'index' }
83
+ it_should_be_disabled
84
+ end
85
+
86
+ context "and not matching action" do
87
+ before(:each) { BotAway.disabled_for :action => 'create' }
88
+ it_should_be_enabled
89
+ end
90
+ end
91
+
92
+ context "with matching mode" do
93
+ before(:each) { BotAway.disabled_for :mode => ENV['RAILS_ENV'] }
94
+ it_should_be_disabled
95
+ end
96
+
97
+ context "with not matching mode" do
98
+ before(:each) { BotAway.disabled_for :mode => "this_is_not_#{ENV['RAILS_ENV']}" }
99
+ it_should_be_enabled
100
+ end
101
+ end