Sign-up guard: honeypot is read-only until a trusted focus and ignores browser autofill (own email); masked value logging

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
martbost
2026-09-13 04:57:51 -05:00
parent 2bd612d9ba
commit 4caa0ef5a8
19 changed files with 33 additions and 19 deletions
+4 -1
View File
@@ -148,7 +148,10 @@ function codeGuard(req, b) {
// honeypot: bots fill it, humans never see it. Some form-filler extensions fill every field,
// hidden or not (seen 2026-09-11), so a filled honeypot is a CHALLENGE, never a silent drop:
// a person passes the icon check and gets the code, a bot cannot.
const hp = !!b.hp_field_x9;
const hpRaw = String(b.hp_field_x9 || '').trim();
const hpIsEmail = !!hpRaw && hpRaw.toLowerCase() === String(b.email || '').trim().toLowerCase();
const hp = !!hpRaw && !hpIsEmail; // 2026-09-13: phones were autofilling the hidden field; own email = autofill, not a bot
if (hpRaw) console.log('signup-guard honeypot-value', clientIp(req), hpIsEmail ? 'own-email' : (/^https?:/i.test(hpRaw) ? 'url' : /@/.test(hpRaw) ? 'other-email' : /^\+?[\d\s()-]{6,}$/.test(hpRaw) ? 'phone' : 'text'), 'len=' + hpRaw.length, hpIsEmail ? '' : hpRaw.slice(0, 2) + '***');
const fts = Number(b.fts) || 0;
if (!fts || now - fts < CODE_LIMITS.minFormMs) { guardLog(req, 'form-age', b); return { status: 400, body: { error: 'Give the page a second, then tap again.' } }; }
if (now - fts > 12 * 3600 * 1000) { guardLog(req, 'form-stale', b); return { status: 400, body: { error: 'This page has been open a long time. Refresh it, then tap again.' } }; }