bot-away 1.0.3 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|