Skip to content

Commit abd2d68

Browse files
committed
Add honeypot and refine short name checks to prevent demo spam
Implement a hidden organization field as a honeypot to catch bots, and update short name detection to require at least 3 characters with 2 unique letters, silently rejecting spam without creating admins. This enhances registration security against automated abuse.
1 parent 91edb31 commit abd2d68

4 files changed

Lines changed: 90 additions & 8 deletions

File tree

app/controllers/demo/registrations_controller.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,6 @@ def ensure_demo_tenant!
2727
end
2828

2929
def registration_params
30-
params.require(:demo_registration).permit(:name, :email, :note)
30+
params.require(:demo_registration).permit(:name, :email, :note, :organization)
3131
end
3232
end

app/models/demo/registration.rb

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ class Demo::Registration
77
attribute :name, :string
88
attribute :email, :string
99
attribute :note, :string
10+
attribute :organization, :string
1011

1112
attr_accessor :request
1213

@@ -25,15 +26,27 @@ def save
2526
private
2627

2728
def spam?
28-
detector = SpamDetector.new(nil)
29-
spam = detector.gibberish?(name) || detector.gibberish?(note)
29+
spam = honeypot_filled? || short_name? || gibberish?
3030
if spam
3131
Rails.error.unexpected("Demo registration spam detected",
32-
context: { name: name, email: email, note: note })
32+
context: { name: name, email: email, note: note, reason: spam })
3333
end
3434
spam
3535
end
3636

37+
def honeypot_filled?
38+
:honeypot if organization.present?
39+
end
40+
41+
def short_name?
42+
:short_name if name.present? && (name.strip.length < 3 || name.strip.scan(/\p{L}/).uniq.size < 2)
43+
end
44+
45+
def gibberish?
46+
detector = SpamDetector.new(nil)
47+
:gibberish if detector.gibberish?(name) || detector.gibberish?(note)
48+
end
49+
3750
def admin_must_be_valid
3851
return if build_admin.valid?
3952

app/views/demo/registrations/new.html.erb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@
1212
<ol class="list-none p-2">
1313
<%= f.input :name, label: t("demo.registrations.new.name_input"), required: true %>
1414
<%= f.input :email, label: t("demo.registrations.new.email_input"), required: true %>
15+
16+
<div class="hidden" aria-hidden="true">
17+
<%= f.input :organization, label: "Organization", required: false, input_html: { tabindex: "-1", autocomplete: "off" } %>
18+
</div>
19+
1520
<%= f.input :note, as: :text, label: t("demo.registrations.new.note_input"), required: false, input_html: { rows: 3, placeholder: t("demo.registrations.new.note_placeholder") } %>
1621
<%= f.action :submit, label: t("demo.registrations.new.submit"), button_html: { value: t("demo.registrations.new.submit") }, wrapper_html: { class: "actions mt-2" } %>
1722
</ol>

test/models/demo/registration_test.rb

Lines changed: 68 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -144,17 +144,81 @@ class Demo::RegistrationTest < ActiveSupport::TestCase
144144
end
145145
end
146146

147-
test "short name skips gibberish check and creates admin" do
147+
test "short name silently succeeds without creating admin" do
148148
in_demo_tenant do
149149
registration = Demo::Registration.new(
150-
name: "Jo",
151-
email: "jo@example.com")
150+
name: "as",
151+
email: "test@example.com")
152+
153+
assert_no_enqueued_emails do
154+
assert_raises(ActiveSupport::ErrorReporter::UnexpectedError) do
155+
assert registration.save
156+
end
157+
end
158+
159+
assert_not Admin.exists?(email: "test@example.com")
160+
end
161+
end
162+
163+
test "short name with single unique letter silently succeeds without creating admin" do
164+
in_demo_tenant do
165+
registration = Demo::Registration.new(
166+
name: "aaa",
167+
email: "test@example.com")
168+
169+
assert_no_enqueued_emails do
170+
assert_raises(ActiveSupport::ErrorReporter::UnexpectedError) do
171+
assert registration.save
172+
end
173+
end
174+
175+
assert_not Admin.exists?(email: "test@example.com")
176+
end
177+
end
178+
179+
test "three-letter name with distinct letters creates admin" do
180+
in_demo_tenant do
181+
registration = Demo::Registration.new(
182+
name: "Joe",
183+
email: "joe@example.com")
184+
185+
assert_enqueued_emails 2 do
186+
assert registration.save
187+
end
188+
189+
assert Admin.exists?(email: "joe@example.com")
190+
end
191+
end
192+
193+
test "honeypot filled silently succeeds without creating admin" do
194+
in_demo_tenant do
195+
registration = Demo::Registration.new(
196+
name: "Spam Bot",
197+
email: "bot@example.com",
198+
organization: "Acme Corp")
199+
200+
assert_no_enqueued_emails do
201+
assert_raises(ActiveSupport::ErrorReporter::UnexpectedError) do
202+
assert registration.save
203+
end
204+
end
205+
206+
assert_not Admin.exists?(email: "bot@example.com")
207+
end
208+
end
209+
210+
test "empty honeypot allows registration" do
211+
in_demo_tenant do
212+
registration = Demo::Registration.new(
213+
name: "Real Person",
214+
email: "real@example.com",
215+
organization: "")
152216

153217
assert_enqueued_emails 2 do
154218
assert registration.save
155219
end
156220

157-
assert Admin.exists?(email: "jo@example.com")
221+
assert Admin.exists?(email: "real@example.com")
158222
end
159223
end
160224

0 commit comments

Comments
 (0)