Skip to content

Commit 2a9ea33

Browse files
authored
Add honeypot_enabled config option (#149)
1 parent 939f9ed commit 2a9ea33

7 files changed

Lines changed: 69 additions & 9 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
All notable changes to this project will be documented in this file.
44

5+
## [Unreleased]
6+
7+
- New `honeypot_enabled` config option (default true).
8+
59
## [2.3.0]
610

711
- Run honeypot + spinner checks and their callback also if timestamp triggers but passes through (#132)

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ You can customize:
118118
- `timestamp_error_message`: flash error message thrown when form submitted quicker than the `timestamp_threshold` value. It uses I18n by default.
119119
- `injectable_styles`: if enabled, you should call anywhere in your layout the following helper `<%= invisible_captcha_styles %>`. This allows you to inject styles, for example, in `<head>`. False by default, styles are injected inline with the honeypot.
120120
- `spinner_enabled`: option to disable the IP spinner validation. By default, true.
121+
- `honeypot_enabled`: option to disable the honeypot field at the application level. By default, true.
121122
- `secret`: customize the secret key to encode some internal values. By default, it reads the environment variable `ENV['INVISIBLE_CAPTCHA_SECRET']` and fallbacks to random value. Be careful, if you are running multiple Rails instances behind a load balancer, use always the same value via the environment variable.
122123

123124
To change these defaults, add the following to an initializer (recommended `config/initializers/invisible_captcha.rb`):
@@ -130,6 +131,7 @@ InvisibleCaptcha.setup do |config|
130131
# config.timestamp_enabled = true
131132
# config.injectable_styles = false
132133
# config.spinner_enabled = true
134+
# config.honeypot_enabled = true
133135

134136
# Leave these unset if you want to use I18n (see below)
135137
# config.sentence_for_humans = 'If you are a human, ignore this field'

lib/invisible_captcha.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class << self
1717
:visual_honeypots,
1818
:injectable_styles,
1919
:spinner_enabled,
20+
:honeypot_enabled,
2021
:secret
2122

2223
def init!
@@ -42,6 +43,9 @@ def init!
4243
# Spinner check enabled by default
4344
self.spinner_enabled = true
4445

46+
# Honeypot check enabled by default
47+
self.honeypot_enabled = true
48+
4549
# A secret key to encode some internal values
4650
self.secret = ENV['INVISIBLE_CAPTCHA_SECRET'] || SecureRandom.hex(64)
4751
end

lib/invisible_captcha/controller_ext.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ def spinner_spam?
8585
end
8686

8787
def honeypot_spam?(options = {})
88+
return false unless InvisibleCaptcha.honeypot_enabled
89+
8890
honeypot = options[:honeypot]
8991
scope = options[:scope] || controller_name.singularize
9092

lib/invisible_captcha/view_helpers.rb

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,17 @@ def invisible_captcha_styles
3333
private
3434

3535
def build_invisible_captcha(honeypot = nil, scope = nil, options = {})
36-
if honeypot.is_a?(Hash)
37-
options = honeypot
38-
honeypot = nil
39-
end
36+
return ''.html_safe unless InvisibleCaptcha.honeypot_enabled || InvisibleCaptcha.spinner_enabled
4037

41-
honeypot = honeypot ? honeypot.to_s : InvisibleCaptcha.get_honeypot
42-
label = options.delete(:sentence_for_humans) || InvisibleCaptcha.sentence_for_humans
43-
css_class = "#{honeypot}_#{Time.zone.now.to_i}"
38+
if InvisibleCaptcha.honeypot_enabled
39+
if honeypot.is_a?(Hash)
40+
options = honeypot
41+
honeypot = nil
42+
end
43+
honeypot = honeypot ? honeypot.to_s : InvisibleCaptcha.get_honeypot
44+
label = options.delete(:sentence_for_humans) || InvisibleCaptcha.sentence_for_humans
45+
css_class = "#{honeypot}_#{Time.zone.now.to_i}"
46+
end
4447

4548
styles = visibility_css(css_class, options)
4649

@@ -50,8 +53,10 @@ def build_invisible_captcha(honeypot = nil, scope = nil, options = {})
5053

5154
content_tag(:div, class: css_class) do
5255
concat styles unless InvisibleCaptcha.injectable_styles
53-
concat label_tag(build_label_name(honeypot, scope), label)
54-
concat text_field_tag(build_input_name(honeypot, scope), nil, default_honeypot_options.merge(options))
56+
if InvisibleCaptcha.honeypot_enabled
57+
concat label_tag(build_label_name(honeypot, scope), label)
58+
concat text_field_tag(build_input_name(honeypot, scope), nil, default_honeypot_options.merge(options))
59+
end
5560
if InvisibleCaptcha.spinner_enabled
5661
concat hidden_field_tag("spinner", session[:invisible_captcha_spinner], id: nil)
5762
end

spec/controllers_spec.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,24 @@ def custom_timestamp_callback
223223
end
224224
end
225225

226+
context 'with honeypot_enabled = false' do
227+
before(:each) do
228+
InvisibleCaptcha.honeypot_enabled = false
229+
session[:invisible_captcha_timestamp] = Time.zone.now.iso8601
230+
231+
# Wait for valid submission
232+
sleep InvisibleCaptcha.timestamp_threshold
233+
end
234+
235+
after(:each) { InvisibleCaptcha.honeypot_enabled = true }
236+
237+
it 'passes even when the honeypot field is filled' do
238+
post :create, params: { topic: { subtitle: 'spam-value', title: 'foo' } }
239+
240+
expect(response.body).to be_present
241+
end
242+
end
243+
226244
context 'spinner attribute' do
227245
before(:each) do
228246
InvisibleCaptcha.spinner_enabled = true

spec/view_helpers_spec.rb

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,31 @@
7575
end
7676
end
7777

78+
context "should have honeypot field" do
79+
it 'that exists by default, honeypot_enabled is true' do
80+
InvisibleCaptcha.honeypot_enabled = true
81+
InvisibleCaptcha.honeypots = [:foo_id]
82+
expect(invisible_captcha).to match(/name="foo_id"/)
83+
end
84+
85+
it 'that does not exist if honeypot_enabled is false' do
86+
InvisibleCaptcha.honeypot_enabled = false
87+
expect(invisible_captcha).not_to match(/type="text"/)
88+
end
89+
90+
it 'still renders the spinner if honeypot_enabled is false and spinner_enabled is true' do
91+
InvisibleCaptcha.honeypot_enabled = false
92+
InvisibleCaptcha.spinner_enabled = true
93+
expect(invisible_captcha).to match(/name="spinner"/)
94+
end
95+
96+
it 'renders nothing if both honeypot_enabled and spinner_enabled are false' do
97+
InvisibleCaptcha.honeypot_enabled = false
98+
InvisibleCaptcha.spinner_enabled = false
99+
expect(invisible_captcha).to eq('')
100+
end
101+
end
102+
78103
it 'should set spam timestamp' do
79104
invisible_captcha
80105
expect(session[:invisible_captcha_timestamp]).to eq(Time.zone.now.iso8601)

0 commit comments

Comments
 (0)