Add honeypot_enabled config option#149
Conversation
e417863 to
bd3367b
Compare
Mirrors the existing `spinner_enabled` / `timestamp_enabled` switches. When `InvisibleCaptcha.honeypot_enabled = false`: - the view helper renders only the spinner field (no hidden text input for password managers to autofill into) - the controller-side `honeypot_spam?` check returns false without inspecting params Use case: in some apps (especially those whose users heavily lean on password managers like 1Password / Bitwarden / Dashlane), the manager autofills the honeypot text input with an unrelated saved value, the controller treats the submission as spam, and the gem responds with `head(200)` — a blank white page with no flash. Real users get blocked. Setting `honeypots = []` works as a workaround but still emits a stray nameless text input in the DOM (the gem's view helper always renders a honeypot field). This option lets the user disable the honeypot explicitly while keeping timestamp + spinner protection. Default is true (no behavior change for existing apps).
bd3367b to
db2b2d0
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #149 +/- ##
==========================================
- Coverage 98.44% 98.32% -0.12%
==========================================
Files 22 22
Lines 449 478 +29
==========================================
+ Hits 442 470 +28
- Misses 7 8 +1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
Adds a new global configuration flag honeypot_enabled to allow disabling honeypot rendering and honeypot-based spam detection (while preserving existing defaults/behavior when enabled). This addresses real-world false positives caused by password managers autofilling the hidden honeypot field.
Changes:
- Introduces
InvisibleCaptcha.honeypot_enabled(default:true) and wires it into initialization and documentation. - Updates view helper rendering to omit honeypot markup when disabled, and controller logic to short-circuit honeypot checks.
- Adds controller and view-helper specs covering the new configuration behavior.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
lib/invisible_captcha.rb |
Adds the honeypot_enabled config accessor and initializes it to true. |
lib/invisible_captcha/controller_ext.rb |
Skips honeypot spam detection entirely when honeypot_enabled is false. |
lib/invisible_captcha/view_helpers.rb |
Conditionally omits honeypot label/text input when disabled; still supports spinner output. |
spec/controllers_spec.rb |
Adds coverage ensuring requests pass when honeypot is disabled even if the honeypot param is present. |
spec/view_helpers_spec.rb |
Adds coverage for honeypot field presence/absence and combined behavior with spinner. |
README.md |
Documents the new honeypot_enabled option and initializer usage. |
CHANGELOG.md |
Notes the new config option under Unreleased. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| label = options.delete(:sentence_for_humans) || InvisibleCaptcha.sentence_for_humans | ||
| css_class = "#{honeypot}_#{Time.zone.now.to_i}" | ||
| end | ||
|
|
||
| styles = visibility_css(css_class, options) |
| it 'still renders the spinner if honeypot_enabled is false and spinner_enabled is true' do | ||
| InvisibleCaptcha.honeypot_enabled = false | ||
| InvisibleCaptcha.spinner_enabled = true | ||
| expect(invisible_captcha).to match(/name="spinner"/) | ||
| end |
|
hey @jjb I think we can work on the "drawback" in future PRs, I'm going to merge this one for now. Thanks! |
Summary
Adds
honeypot_enabledconfig, mirroringspinner_enabled/timestamp_enabled. Defaulttrue— no behavior change. Whenfalse, the view helper emits only the spinner field andhoneypot_spam?short-circuits tofalse.Why
Password managers can autofill the hidden honeypot text input with unrelated saved values. The controller treats the submission as spam and the gem responds with
head(200)— a blank white page with no flash. We saw real users blocked this way in production; 30-day audit on our app: 0 honeypot true positives, 7 false positives.honeypots = []works as a workaround (controller iterates nothing; view falls back to a nameless input browsers can't submit) but still emits the wrapper<div>/<style>/<label>/<input>into the DOM, and isn't documented as a way to disable the feature.drawback
when the spinner is on and the honepot is off, we end up with markup like this:
which works fine but has some invalid/irrelevant aspects to it
we could refactor things to generate nice output in all circumstances, it's a larger refator, but not very hard. i could do it if there is interest and you could give feedback and merge 1-2 precursor PRs as i make them.