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.
@@ -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
@@ -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 ActionController::Request.unfiltered_params
53
+ then you can simply add the name of the offending form element to the BotAway.unfiltered_params
54
54
  array like so:
55
- ActionController::Request.accepts_unfiltered_params 'role_ids'
56
- ActionController::Request.accepts_unfiltered_params 'user' # this can be called multiple times
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)
@@ -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.3'
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) && !ActionController::Request.unfiltered_params.include?(key)
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
@@ -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
- @rx = /name="#{Regexp::escape @object_name}\[#{Regexp::escape @method_name}/m
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, obfuscated_id = self.obfuscated_id, obfuscated_name = self.obfuscated_name)
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
- includes_honeypot(object_name, method_name)
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
- - 3
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-13 00:00:00 -04:00
17
+ date: 2010-06-21 00:00:00 -04:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency