bot-away 1.2.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +14 -0
- data/History.txt +20 -0
- data/README.md +198 -0
- data/Rakefile +14 -94
- data/bot-away.gemspec +20 -87
- data/gemfiles/Gemfile.rails-3.0.x +8 -0
- data/gemfiles/Gemfile.rails-3.0.x.lock +121 -0
- data/gemfiles/Gemfile.rails-3.1.x +8 -0
- data/gemfiles/Gemfile.rails-3.1.x.lock +133 -0
- data/lib/bot-away.rb +15 -13
- data/lib/bot-away/action_dispatch/params_parser.rb +22 -0
- data/lib/bot-away/action_view/helpers/instance_tag.rb +36 -12
- data/lib/bot-away/param_parser.rb +2 -2
- data/lib/bot-away/railtie.rb +10 -0
- data/lib/bot-away/test_case.rb +58 -0
- data/lib/bot-away/test_case/controller_test_case.rb +11 -0
- data/lib/bot-away/test_case/instance_tag_test_case.rb +15 -0
- data/lib/bot-away/test_case/matchers.rb +20 -0
- data/lib/bot-away/test_case/matchers/honeypot_matcher.rb +30 -0
- data/lib/bot-away/test_case/matchers/obfuscation_matcher.rb +30 -0
- data/lib/bot-away/test_case/mock_object.rb +16 -0
- data/lib/bot-away/version.rb +12 -0
- data/lib/locale/honeypots.yml +6 -0
- data/spec/controllers/basic_form_view_spec.rb +112 -0
- data/spec/controllers/{test_controller_spec.rb → tests_controller_spec.rb} +29 -80
- data/spec/integration/params_post_spec.rb +42 -0
- data/spec/lib/action_view/helpers/instance_tag_spec.rb +94 -0
- data/spec/{views/lib → lib/action_view}/param_parser_spec.rb +10 -10
- data/spec/spec_helper.rb +37 -105
- data/spec/test_rails_app/app/controllers/tests_controller.rb +11 -0
- data/spec/test_rails_app/app/models/post.rb +13 -0
- data/spec/test_rails_app/app/views/tests/basic_form.html.erb +5 -0
- data/spec/test_rails_app/app/views/tests/model_form.html.erb +12 -0
- data/spec/test_rails_app/config/locales/bot-away-overrides.yml +6 -0
- data/spec/views/form_builder_spec.rb +118 -0
- metadata +94 -137
- data/Manifest.txt +0 -23
- data/README.rdoc +0 -179
- data/VERSION +0 -1
- data/lib/bot-away/action_dispatch/request.rb +0 -20
- data/spec/rspec_version.rb +0 -19
- data/spec/support/honeypot_matcher.rb +0 -30
- data/spec/support/obfuscation_helper.rb +0 -123
- data/spec/support/obfuscation_matcher.rb +0 -28
- data/spec/support/rails/mock_logger.rb +0 -21
- data/spec/support/test_controller.rb +0 -28
- data/spec/support/views/test/index.html.erb +0 -4
- data/spec/support/views/test/model_form.html.erb +0 -6
- data/spec/views/lib/action_view/helpers/instance_tag_spec.rb +0 -75
- data/spec/views/lib/disabled_for_spec.rb +0 -101
- data/spec/views/lib/form_builder_spec.rb +0 -56
data/VERSION
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
1.2.0
|
@@ -1,20 +0,0 @@
|
|
1
|
-
request = (defined?(Rails::VERSION) && Rails::VERSION::STRING >= "3.0") ?
|
2
|
-
ActionDispatch::Request : # Rails 3.0
|
3
|
-
ActionController::Request # Rails 2.3
|
4
|
-
|
5
|
-
request.module_eval do
|
6
|
-
def parameters_with_deobfuscation
|
7
|
-
# NFC what is happening behind the scenes but Rails 2.3 croaks when we memoize; Rails 3 croaks when we don't.
|
8
|
-
if defined?(Rails::VERSION) && Rails::VERSION::STRING >= "3.0"
|
9
|
-
@deobfuscated_parameters ||= begin
|
10
|
-
BotAway::ParamParser.new(ip, parameters_without_deobfuscation.dup).params
|
11
|
-
end
|
12
|
-
else
|
13
|
-
Rails.logger.info parameters_without_deobfuscation.inspect
|
14
|
-
BotAway::ParamParser.new(ip, parameters_without_deobfuscation.dup).params
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
alias_method_chain :parameters, :deobfuscation
|
19
|
-
alias_method :params, :parameters
|
20
|
-
end
|
data/spec/rspec_version.rb
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
unless defined?(RSPEC_VERSION)
|
2
|
-
begin
|
3
|
-
# RSpec 1.3.0
|
4
|
-
require 'spec/rake/spectask'
|
5
|
-
require 'spec/version'
|
6
|
-
|
7
|
-
RSPEC_VERSION = Spec::VERSION::STRING
|
8
|
-
rescue LoadError
|
9
|
-
# RSpec 2.0
|
10
|
-
begin
|
11
|
-
require 'rspec/core/rake_task'
|
12
|
-
require 'rspec/core/version'
|
13
|
-
|
14
|
-
RSPEC_VERSION = RSpec::Core::Version::STRING
|
15
|
-
rescue LoadError
|
16
|
-
raise "RSpec does not seem to be installed. You must install rspec to test this gem."
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
@@ -1,30 +0,0 @@
|
|
1
|
-
class HoneypotMatcher
|
2
|
-
def initialize(object_name, method_name)
|
3
|
-
@object_name, @method_name = object_name, method_name
|
4
|
-
end
|
5
|
-
|
6
|
-
def matches?(target)
|
7
|
-
target = target.call if target.kind_of?(Proc)
|
8
|
-
@target = target
|
9
|
-
if @method_name.nil?
|
10
|
-
@rx = /name="#{Regexp::escape @object_name}/m
|
11
|
-
else
|
12
|
-
@rx = /name="#{Regexp::escape @object_name}\[#{Regexp::escape @method_name}/m
|
13
|
-
end
|
14
|
-
@target[@rx]
|
15
|
-
end
|
16
|
-
|
17
|
-
def failure_message
|
18
|
-
"expected #{@target.inspect}\n to match #{@rx.to_s}"
|
19
|
-
end
|
20
|
-
|
21
|
-
def negative_failure_message
|
22
|
-
"expected #{@target.inspect}\n to not match #{@rx.to_s}"
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
def include_honeypot(object_name, method_name)
|
27
|
-
HoneypotMatcher.new(object_name, method_name)
|
28
|
-
end
|
29
|
-
|
30
|
-
alias contain_honeypot include_honeypot
|
@@ -1,123 +0,0 @@
|
|
1
|
-
module ObfuscationHelper
|
2
|
-
def controller_class
|
3
|
-
TestController
|
4
|
-
end
|
5
|
-
|
6
|
-
def dump
|
7
|
-
result = yield
|
8
|
-
puts result if ENV['DUMP']
|
9
|
-
result
|
10
|
-
end
|
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
|
-
|
30
|
-
def builder
|
31
|
-
return @builder if @builder
|
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 {})
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
# this is the obfuscated version of the string "object_name_method_name"
|
44
|
-
def obfuscated_id
|
45
|
-
self.class.obfuscated_id
|
46
|
-
end
|
47
|
-
|
48
|
-
# this is the obfuscated version of the string "object_name[method_name]"
|
49
|
-
def obfuscated_name
|
50
|
-
self.class.obfuscated_name
|
51
|
-
end
|
52
|
-
|
53
|
-
def object_name
|
54
|
-
self.class.object_name
|
55
|
-
end
|
56
|
-
|
57
|
-
def 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
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
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
|
123
|
-
end
|
@@ -1,28 +0,0 @@
|
|
1
|
-
class ObfuscationMatcher
|
2
|
-
def initialize(id, name)
|
3
|
-
@id, @name = id, name
|
4
|
-
end
|
5
|
-
|
6
|
-
def matches?(target)
|
7
|
-
target = target.call if target.kind_of?(Proc)
|
8
|
-
@target = target
|
9
|
-
match(:id) && match(:name)
|
10
|
-
end
|
11
|
-
|
12
|
-
def match(which)
|
13
|
-
@rx = /#{which}=['"]#{Regexp::escape instance_variable_get("@#{which}")}/
|
14
|
-
@target[@rx]
|
15
|
-
end
|
16
|
-
|
17
|
-
def failure_message
|
18
|
-
"expected #{@target.inspect}\n to match #{@rx.inspect}"
|
19
|
-
end
|
20
|
-
|
21
|
-
def negative_failure_message
|
22
|
-
"expected #{@target.inspect}\n to not match #{@rx.inspect}"
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
def be_obfuscated_as(id, name)
|
27
|
-
ObfuscationMatcher.new(id, name)
|
28
|
-
end
|
@@ -1,21 +0,0 @@
|
|
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
|
@@ -1,28 +0,0 @@
|
|
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,75 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
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
|
22
|
-
|
23
|
-
def default_instance_tag
|
24
|
-
ActionView::Helpers::InstanceTag.new("object_name", "method_name", template, mock_object)
|
25
|
-
end
|
26
|
-
|
27
|
-
subject { default_instance_tag }
|
28
|
-
|
29
|
-
context "with a valid text area tag" do
|
30
|
-
subject do
|
31
|
-
dump { default_instance_tag.to_text_area_tag }
|
32
|
-
end
|
33
|
-
|
34
|
-
it "should produce blank honeypot value" do
|
35
|
-
subject.should_not =~ /name="object_name\[method_name\]"[^>]+>method_value<\/textarea>/
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
context "with a valid input type=text tag" do
|
40
|
-
before(:each) { @tag_options = ["input", {:type => 'text', 'name' => 'object_name[method_name]', 'id' => 'object_name_method_name', 'value' => 'method_value'}] }
|
41
|
-
#subject { dump { default_instance_tag.tag(*@tag_options) } }
|
42
|
-
|
43
|
-
it "should turn off autocomplete for honeypots" do
|
44
|
-
subject.honeypot_tag(*@tag_options).should =~ /autocomplete="off"/
|
45
|
-
end
|
46
|
-
|
47
|
-
it "should obfuscate tag name" do
|
48
|
-
subject.obfuscated_tag(*@tag_options).should =~ /name="#{obfuscated_name}"/
|
49
|
-
end
|
50
|
-
|
51
|
-
it "should obfuscate tag id" do
|
52
|
-
subject.obfuscated_tag(*@tag_options).should =~ /id="#{obfuscated_id}"/
|
53
|
-
end
|
54
|
-
|
55
|
-
# it "should not obfuscate tag value" do
|
56
|
-
# subject.obfuscated_tag(*@tag_options).should =~ /value="@tag_options"/
|
57
|
-
# end
|
58
|
-
#
|
59
|
-
it "should include unobfuscated tag value" do
|
60
|
-
subject.obfuscated_tag(*@tag_options).should =~ /value="method_value"/
|
61
|
-
end
|
62
|
-
|
63
|
-
it "should create honeypot name" do
|
64
|
-
subject.honeypot_tag(*@tag_options).should =~ /name="object_name\[method_name\]"/
|
65
|
-
end
|
66
|
-
|
67
|
-
it "should create honeypot id" do
|
68
|
-
subject.honeypot_tag(*@tag_options).should =~ /id="object_name_method_name"/
|
69
|
-
end
|
70
|
-
|
71
|
-
it "should create empty honeypot tag value" do
|
72
|
-
subject.honeypot_tag(*@tag_options).should =~ /value=""/
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
@@ -1,101 +0,0 @@
|
|
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
|