bot-away 1.0.3 → 1.1.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/History.txt +9 -0
- data/README.rdoc +22 -3
- data/lib/bot-away.rb +23 -1
- data/lib/bot-away/action_view/helpers/instance_tag.rb +4 -2
- data/lib/bot-away/param_parser.rb +2 -1
- data/spec/lib/action_view/helpers/instance_tag_spec.rb +1 -1
- data/spec/lib/builder_spec.rb +16 -1
- data/spec/lib/param_parser_spec.rb +11 -0
- data/spec/support/honeypot_matcher.rb +5 -1
- data/spec/support/obfuscation_helper.rb +13 -2
- metadata +3 -3
data/History.txt
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
=== 1.1.0 2010-06-21
|
2
|
+
* Minor enhancements:
|
3
|
+
* ActionController::Request.accepts_unfiltered_params is now accessible as BotAway.accepts_unfiltered_params
|
4
|
+
* BotAway.show_honeypots = true will show honeypots as visible elements within the form, for debugging.
|
5
|
+
* BotAway.dump_params = true will dump the params hash as it was before BotAway modified it, for debugging.
|
6
|
+
* BotAway will no longer generate hashes for, or disguise, elements whose names have been added to
|
7
|
+
BotAway.unfiltered_params. This should resolve issues where JavaScript is involved (case in point:
|
8
|
+
CalendarDateSelect).
|
9
|
+
|
1
10
|
=== 1.0.3 2010-06-13
|
2
11
|
* Bugfixes:
|
3
12
|
* Resolved: On or before Rails 2.3.8, honeypots were generated as escaped HTML. This was because Rails started
|
data/README.rdoc
CHANGED
@@ -50,10 +50,10 @@ here's a brief run-down of what's going on:
|
|
50
50
|
|
51
51
|
* I feel this library has been fairly well-tested (99.5% test coverage as of this writing), but if you discover a bug
|
52
52
|
and can't be bothered to let me know about it (or you just don't have time to wait for a fix or fix it yourself),
|
53
|
-
then you can simply add the name of the offending form element to the
|
53
|
+
then you can simply add the name of the offending form element to the BotAway.unfiltered_params
|
54
54
|
array like so:
|
55
|
-
|
56
|
-
|
55
|
+
BotAway.accepts_unfiltered_params 'role_ids'
|
56
|
+
BotAway.accepts_unfiltered_params 'user' # this can be called multiple times
|
57
57
|
You should also take note that this is an array, not a hash. So if you have a user[role_ids] as well as a
|
58
58
|
group[role_ids], the +role_ids+ will not be matched on EITHER of these models.
|
59
59
|
|
@@ -77,6 +77,25 @@ In your Rails config/environment.rb:
|
|
77
77
|
|
78
78
|
That's it.
|
79
79
|
|
80
|
+
== OPTIONAL CONFIGURATION:
|
81
|
+
|
82
|
+
In general, there's not much to the configuration of Bot-Away. Most options are here for your debugging pleasure, in
|
83
|
+
case something isn't quite working as you'd expected and Bot-Away seems to be a potential cause. To set a configuration
|
84
|
+
option, you should do so from a Rails initializer -- just create a file with any name (ending with ".rb", of course)
|
85
|
+
in your config/initializers directory, and it'll get run during server start-up. Configuration options available to you
|
86
|
+
for Bot-Away are as follows:
|
87
|
+
|
88
|
+
BotAway.accepts_unfiltered_params "name_of_param", "name_of_another_param"
|
89
|
+
# Causes the specified parameter to NOT be filtered. If, for instance, "role_ids" is given, then parameters such as
|
90
|
+
# user[role_ids], group[role_ids], etc. will not be processed by Bot-Away.
|
91
|
+
BotAway.show_honeypots = true
|
92
|
+
# Causes Bot-Away to NOT disguise honeypots with CSS or any other technique. This will cause honeypot elements to
|
93
|
+
# be displayed on your page. Not ideal for production, but helpful for debugging browser auto-fill issues.
|
94
|
+
BotAway.dump_params = true
|
95
|
+
# Probably a major security risk in a production application, but again useful in development for gaining insight
|
96
|
+
# into what, exactly, is being passed by the browser into Rails before Bot-Away does its thing. If you're getting
|
97
|
+
# false positives, this may prove helpful in figuring out why.
|
98
|
+
|
80
99
|
== LICENSE:
|
81
100
|
|
82
101
|
(The MIT License)
|
data/lib/bot-away.rb
CHANGED
@@ -12,7 +12,29 @@ require 'bot-away/action_view/helpers/instance_tag'
|
|
12
12
|
require 'bot-away/spinner'
|
13
13
|
|
14
14
|
module BotAway
|
15
|
-
VERSION = '1.0
|
15
|
+
VERSION = '1.1.0'
|
16
|
+
|
17
|
+
class << self
|
18
|
+
attr_accessor :show_honeypots, :dump_params
|
19
|
+
|
20
|
+
def unfiltered_params(*args)
|
21
|
+
ActionController::Request.unfiltered_params(*args)
|
22
|
+
end
|
23
|
+
|
24
|
+
alias accepts_unfiltered_params unfiltered_params
|
25
|
+
|
26
|
+
def excluded?(object_name, method_name)
|
27
|
+
unfiltered_params.collect! { |u| u.to_s }
|
28
|
+
if (object_name &&
|
29
|
+
(unfiltered_params.include?(object_name.to_s) ||
|
30
|
+
unfiltered_params.include?("#{object_name}[#{method_name}]")) ||
|
31
|
+
unfiltered_params.include?(method_name.to_s))
|
32
|
+
true
|
33
|
+
else
|
34
|
+
false
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
16
38
|
end
|
17
39
|
|
18
40
|
# WHY do I have to do this???
|
@@ -3,7 +3,8 @@ class ActionView::Helpers::InstanceTag
|
|
3
3
|
|
4
4
|
def initialize_with_spinner(object_name, method_name, template_object, object = nil)
|
5
5
|
initialize_without_spinner(object_name, method_name, template_object, object)
|
6
|
-
if template_object.controller.send(:protect_against_forgery?)
|
6
|
+
if template_object.controller.send(:protect_against_forgery?) && !BotAway.excluded?(object_name, method_name)
|
7
|
+
puts "#{object_name.inspect} => #{method_name.inspect}"
|
7
8
|
@spinner = BotAway::Spinner.new(template_object.request.ip, object_name, template_object.form_authenticity_token)
|
8
9
|
end
|
9
10
|
end
|
@@ -29,7 +30,7 @@ class ActionView::Helpers::InstanceTag
|
|
29
30
|
yield if object
|
30
31
|
object
|
31
32
|
end
|
32
|
-
|
33
|
+
|
33
34
|
def honeypot_tag(name, options = nil, *args)
|
34
35
|
tag_without_honeypot(name, honeypot_options(options.dup? || {}), *args)
|
35
36
|
end
|
@@ -84,6 +85,7 @@ class ActionView::Helpers::InstanceTag
|
|
84
85
|
alias_method_chain :content_tag, :obfuscation
|
85
86
|
|
86
87
|
def disguise(element)
|
88
|
+
return element.replace("Honeypot(#{element})") if BotAway.show_honeypots
|
87
89
|
case rand(3)
|
88
90
|
when 0 # Hidden
|
89
91
|
element.replace "<div style='display:none;'>Leave this empty: #{element}</div>"
|
@@ -4,6 +4,7 @@ module BotAway
|
|
4
4
|
|
5
5
|
def initialize(ip, params, authenticity_token = params[:authenticity_token])
|
6
6
|
@ip, @params, @authenticity_token = ip, params, authenticity_token
|
7
|
+
Rails.logger.debug(params.inspect) if BotAway.dump_params
|
7
8
|
if authenticity_token
|
8
9
|
if catch(:bastard) { deobfuscate! } == :took_the_bait
|
9
10
|
params.clear
|
@@ -18,7 +19,7 @@ module BotAway
|
|
18
19
|
end
|
19
20
|
|
20
21
|
current.each do |key, value|
|
21
|
-
if object_name && !value.kind_of?(Hash) && !
|
22
|
+
if object_name && !value.kind_of?(Hash) && !BotAway.excluded?(object_name, key)
|
22
23
|
if value.blank? && params.keys.include?(spun_key = spinner.encode("#{object_name}[#{key}]"))
|
23
24
|
current[key] = params.delete(spun_key)
|
24
25
|
else
|
@@ -33,7 +33,7 @@ describe ActionView::Helpers::InstanceTag do
|
|
33
33
|
context "with a valid input type=text tag" do
|
34
34
|
before(:each) { @tag_options = ["input", {:type => 'text', 'name' => 'object_name[method_name]', 'id' => 'object_name_method_name', 'value' => 'method_value'}] }
|
35
35
|
#subject { dump { default_instance_tag.tag(*@tag_options) } }
|
36
|
-
|
36
|
+
|
37
37
|
it "should turn off autocomplete for honeypots" do
|
38
38
|
subject.honeypot_tag(*@tag_options).should =~ /autocomplete="off"/
|
39
39
|
end
|
data/spec/lib/builder_spec.rb
CHANGED
@@ -14,7 +14,22 @@ describe ActionView::Helpers::FormBuilder do
|
|
14
14
|
it "should not create honeypots with default values" do
|
15
15
|
builder.text_field(:method_name).should match(/name="object_name\[method_name\]"[^>]*?value=""/)
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
|
+
context "with BotAway.show_honeypots == true" do
|
19
|
+
before(:each) { BotAway.show_honeypots = true }
|
20
|
+
after(:each) { BotAway.show_honeypots = false }
|
21
|
+
|
22
|
+
it "should not disguise honeypots" do
|
23
|
+
builder.text_area(method_name).should_not match(/<\/div>/)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should not obfuscate names that have been explicitly ignored" do
|
28
|
+
BotAway.accepts_unfiltered_params 'method_name'
|
29
|
+
builder.text_field('method_name').should_not match(/name="a0844d45bf150668ff1d86a6eb491969/)
|
30
|
+
BotAway.unfiltered_params.delete 'method_name'
|
31
|
+
end
|
32
|
+
|
18
33
|
# select(method, choices, options = {}, html_options = {})
|
19
34
|
obfuscates(:select) { builder.select(:method_name, {1 => :a, 2 => :b }) }
|
20
35
|
|
@@ -14,6 +14,17 @@ describe BotAway::ParamParser do
|
|
14
14
|
end
|
15
15
|
|
16
16
|
subject { r = BotAway::ParamParser.new(@ip, @params); puts r.params.to_yaml; r }
|
17
|
+
|
18
|
+
context "with dump_params == true" do
|
19
|
+
before(:each) { BotAway.dump_params = true }
|
20
|
+
after(:each) { BotAway.dump_params = false }
|
21
|
+
|
22
|
+
it "should dump params as debug to Rails logger" do
|
23
|
+
@params = { 'test' => "hello", :posts => [1] }
|
24
|
+
Rails.logger.should_receive(:debug).with(@params.inspect)
|
25
|
+
subject
|
26
|
+
end
|
27
|
+
end
|
17
28
|
|
18
29
|
context "with blank honeypots" do
|
19
30
|
it "drops obfuscated params" do
|
@@ -6,7 +6,11 @@ class HoneypotMatcher
|
|
6
6
|
def matches?(target)
|
7
7
|
target = target.call if target.kind_of?(Proc)
|
8
8
|
@target = target
|
9
|
-
|
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
|
10
14
|
@target[@rx]
|
11
15
|
end
|
12
16
|
|
@@ -24,12 +24,23 @@ module ObfuscationHelper
|
|
24
24
|
@builder = ActionView::Helpers::FormBuilder.new(:object_name, MockObject.new, response.template, {}, proc {})
|
25
25
|
end
|
26
26
|
|
27
|
-
def obfuscates(method,
|
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
|
+
|
28
35
|
value = yield
|
29
36
|
context "##{method}" do
|
30
37
|
subject { proc { dump { value } } }
|
31
38
|
|
32
|
-
|
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
|
33
44
|
is_obfuscated_as(obfuscated_id, obfuscated_name)
|
34
45
|
end
|
35
46
|
end
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 1
|
7
|
+
- 1
|
7
8
|
- 0
|
8
|
-
|
9
|
-
version: 1.0.3
|
9
|
+
version: 1.1.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Colin MacKenzie IV
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-06-
|
17
|
+
date: 2010-06-21 00:00:00 -04:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|