bot-away 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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