bot-away 1.2.0 → 2.0.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.
- 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
|