Compare commits

...

2 commits

Author SHA1 Message Date
Artemii Peretiachenko
1fb33e1bd8 Hide contact honeypot with inline styles so it stays invisible without CSS.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-26 19:17:25 +02:00
Artemii Peretiachenko
1d5596db5f Fix contact honeypot so browser autofill does not swallow submissions.
Rename the trap field away from "website" and keep legacy detection so real messages still reach Telegram.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-26 19:16:36 +02:00
11 changed files with 57 additions and 38 deletions

View file

@ -104,6 +104,6 @@ Backup is written even if Telegram fails (`telegram_ok: false`), so you still ha
## Behaviour notes
- Honeypot field `website`: if filled, returns `200` without notifying Telegram or writing a backup.
- Honeypot field `hp_company` (legacy `website` still checked): if filled, returns `200` without notifying Telegram or writing a backup.
- In-memory rate limit: 5 POSTs per IP per 60 seconds → `429`.
- Field limits: name/contact ≤ 200 chars, message ≤ 4000.

View file

@ -114,8 +114,10 @@ def validate_fields(data: dict) -> tuple[str, str, str] | str:
return "invalid"
# Honeypot — treat as success upstream; caller checks separately.
website = data.get("website", "")
if isinstance(website, str) and website.strip():
# Accept legacy "website" (autofill magnets) and current "hp_company".
for pot in ("hp_company", "website"):
raw_pot = data.get(pot, "")
if isinstance(raw_pot, str) and raw_pot.strip():
return "honeypot"
def field(key: str, max_len: int) -> str | None:

View file

@ -428,10 +428,10 @@
<h3>Contact form</h3>
<form id="contactForm" data-status-loading="..." data-status-success="Sent ✓" data-status-error="Could not send. Please try again.">
<label class="hp-field" aria-hidden="true">
<span>Website</span>
<input type="text" name="website" tabindex="-1" autocomplete="off">
</label>
<div class="hp-field" aria-hidden="true" style="position:absolute;left:-10000px;top:auto;width:1px;height:1px;margin:0;padding:0;overflow:hidden;clip:rect(0,0,0,0);border:0;opacity:0;pointer-events:none">
<label for="hp_company">Company fax</label>
<input id="hp_company" type="text" name="hp_company" value="" tabindex="-1" autocomplete="off">
</div>
<label>
<span>Name</span>

View file

@ -277,10 +277,10 @@
<h3>Contact form</h3>
<form id="contactForm" data-status-loading="..." data-status-success="Sent ✓" data-status-error="Could not send. Please try again.">
<label class="hp-field" aria-hidden="true">
<span>Website</span>
<input type="text" name="website" tabindex="-1" autocomplete="off">
</label>
<div class="hp-field" aria-hidden="true" style="position:absolute;left:-10000px;top:auto;width:1px;height:1px;margin:0;padding:0;overflow:hidden;clip:rect(0,0,0,0);border:0;opacity:0;pointer-events:none">
<label for="hp_company">Company fax</label>
<input id="hp_company" type="text" name="hp_company" value="" tabindex="-1" autocomplete="off">
</div>
<label>
<span>Name</span>

View file

@ -428,10 +428,10 @@
<h3>Форма для связи</h3>
<form id="contactForm" data-status-loading="..." data-status-success="Отправлено ✓" data-status-error="Не удалось отправить. Попробуйте ещё раз.">
<label class="hp-field" aria-hidden="true">
<span>Website</span>
<input type="text" name="website" tabindex="-1" autocomplete="off">
</label>
<div class="hp-field" aria-hidden="true" style="position:absolute;left:-10000px;top:auto;width:1px;height:1px;margin:0;padding:0;overflow:hidden;clip:rect(0,0,0,0);border:0;opacity:0;pointer-events:none">
<label for="hp_company">Company fax</label>
<input id="hp_company" type="text" name="hp_company" value="" tabindex="-1" autocomplete="off">
</div>
<label>
<span>Имя</span>

View file

@ -333,10 +333,10 @@
<h3 data-i18n="form.title"></h3>
<form id="contactForm" data-status-loading="{{FORM_STATUS_LOADING}}" data-status-success="{{FORM_STATUS_SUCCESS}}" data-status-error="{{FORM_STATUS_ERROR}}">
<label class="hp-field" aria-hidden="true">
<span>Website</span>
<input type="text" name="website" tabindex="-1" autocomplete="off">
</label>
<div class="hp-field" aria-hidden="true" style="position:absolute;left:-10000px;top:auto;width:1px;height:1px;margin:0;padding:0;overflow:hidden;clip:rect(0,0,0,0);border:0;opacity:0;pointer-events:none">
<label for="hp_company">Company fax</label>
<input id="hp_company" type="text" name="hp_company" value="" tabindex="-1" autocomplete="off">
</div>
<label>
<span data-i18n="form.nameLabel"></span>

View file

@ -182,10 +182,10 @@
<h3 data-i18n="form.title"></h3>
<form id="contactForm" data-status-loading="{{FORM_STATUS_LOADING}}" data-status-success="{{FORM_STATUS_SUCCESS}}" data-status-error="{{FORM_STATUS_ERROR}}">
<label class="hp-field" aria-hidden="true">
<span>Website</span>
<input type="text" name="website" tabindex="-1" autocomplete="off">
</label>
<div class="hp-field" aria-hidden="true" style="position:absolute;left:-10000px;top:auto;width:1px;height:1px;margin:0;padding:0;overflow:hidden;clip:rect(0,0,0,0);border:0;opacity:0;pointer-events:none">
<label for="hp_company">Company fax</label>
<input id="hp_company" type="text" name="hp_company" value="" tabindex="-1" autocomplete="off">
</div>
<label>
<span data-i18n="form.nameLabel"></span>

View file

@ -441,8 +441,25 @@ a:hover{opacity:0.7;}
}
#contactForm label{display:block; margin-bottom:20px; font-size:13px; color:var(--text);}
#contactForm .hp-field{
position:absolute; left:-10000px; top:auto; width:1px; height:1px;
overflow:hidden; opacity:0; pointer-events:none;
position:absolute !important;
left:-10000px !important;
top:auto !important;
width:1px !important;
height:1px !important;
margin:0 !important;
padding:0 !important;
overflow:hidden !important;
clip:rect(0,0,0,0) !important;
border:0 !important;
opacity:0 !important;
pointer-events:none !important;
}
#contactForm .hp-field input{
width:1px !important;
height:1px !important;
margin:0 !important;
padding:0 !important;
border:0 !important;
}
#contactForm input, #contactForm textarea{
width:100%; margin-top:6px; padding:10px; border:1px solid var(--border);

View file

@ -277,10 +277,10 @@
<h3>Форма для связи</h3>
<form id="contactForm" data-status-loading="..." data-status-success="Отправлено ✓" data-status-error="Не удалось отправить. Попробуйте ещё раз.">
<label class="hp-field" aria-hidden="true">
<span>Website</span>
<input type="text" name="website" tabindex="-1" autocomplete="off">
</label>
<div class="hp-field" aria-hidden="true" style="position:absolute;left:-10000px;top:auto;width:1px;height:1px;margin:0;padding:0;overflow:hidden;clip:rect(0,0,0,0);border:0;opacity:0;pointer-events:none">
<label for="hp_company">Company fax</label>
<input id="hp_company" type="text" name="hp_company" value="" tabindex="-1" autocomplete="off">
</div>
<label>
<span>Имя</span>

View file

@ -428,10 +428,10 @@
<h3>Форма для зв'язку</h3>
<form id="contactForm" data-status-loading="..." data-status-success="Надіслано ✓" data-status-error="Не вдалося надіслати. Спробуйте ще раз.">
<label class="hp-field" aria-hidden="true">
<span>Website</span>
<input type="text" name="website" tabindex="-1" autocomplete="off">
</label>
<div class="hp-field" aria-hidden="true" style="position:absolute;left:-10000px;top:auto;width:1px;height:1px;margin:0;padding:0;overflow:hidden;clip:rect(0,0,0,0);border:0;opacity:0;pointer-events:none">
<label for="hp_company">Company fax</label>
<input id="hp_company" type="text" name="hp_company" value="" tabindex="-1" autocomplete="off">
</div>
<label>
<span>Ім'я</span>

View file

@ -277,10 +277,10 @@
<h3>Форма для зв'язку</h3>
<form id="contactForm" data-status-loading="..." data-status-success="Надіслано ✓" data-status-error="Не вдалося надіслати. Спробуйте ще раз.">
<label class="hp-field" aria-hidden="true">
<span>Website</span>
<input type="text" name="website" tabindex="-1" autocomplete="off">
</label>
<div class="hp-field" aria-hidden="true" style="position:absolute;left:-10000px;top:auto;width:1px;height:1px;margin:0;padding:0;overflow:hidden;clip:rect(0,0,0,0);border:0;opacity:0;pointer-events:none">
<label for="hp_company">Company fax</label>
<input id="hp_company" type="text" name="hp_company" value="" tabindex="-1" autocomplete="off">
</div>
<label>
<span>Ім'я</span>