Repository navigation
booking: give browser autofill nothing to classify in the honeypot - #51
distronode-com wants to merge 2 commits into
Conversation
Some real people could not book: POST /v1/bookings answered 400. The booking page's hidden honeypot was <label>Company<input name="company">, and @MinosChatzidakis worked out in #33 that Chrome's autofill fills it from the visitor's address profile. The server then treats a person as a bot. autocomplete="off" was already on the input and did not stop it. That label and name are exactly what Chrome looks for. Its classification patterns live in components/autofill/core/browser/form_parsing/resources/ legacy_regex_patterns.json (chromium/src 0520a5aa3f76), where COMPANY_NAME is company|business|organization|organisation, matched against both the label and the name attribute. The input now has no label and is name="hp", id="f-hp". The submit script reads it by that id and still posts it as "company", so the API, the embed widget and any third-party client are unchanged, and a filled value is still rejected. The name was chosen by running candidates through every pattern in that file, all languages, as a case-insensitive search the way ICU find() does: "hp" and "f-hp" match nothing. A label-free input is not label-free to Chrome, though. form_autofill_util.cc infers a label from nearby text, and for this markup its div walk climbs to #form-view and picks up the Back button and the chosen slot, e.g. "Back Fri, Sep 18 · 10:00 AM". That string was generated for every weekday, month and half hour in all eight shipped locales (32,264 strings) and run through the same patterns. One matches: German Wednesday, "Zurück Mi., ...", hits MIDDLE_INITIAL on "Mi". Chromium assigns MIDDLE_INITIAL only inside a first-name and last-name sequence (name_field_parser.cc), and this form has a single full-name field, so that cannot become a classification. The embed widget needs no change. Its honeypot has never had a label, name or id, and its inferred label comes from the same Back-plus-slot text. This is checked against Chromium's local heuristics only. Its server-side crowdsourced predictions, Safari, Firefox and password managers were not tested, and no real Chrome profile was autofilled. TestBookPage_honeypotGivesAutofillNothingToClassify renders the page and holds the honeypot to: one input, no label or text, no placeholder, aria-label, title or value, a name and id free of common autofill words, and a submit script that posts that input as "company". Putting the old markup back fails it on six assertions, and restoring only the "Company" label fails it on two. TestCreateBooking_honeypotRejected already covers the server side and still fails when the check is disabled. In a real browser against a built binary, a normal booking posted "company":"" and got 201 with no console errors, and a filled honeypot got 400 with "booking rejected: honeypot filled" in the log. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes The booking-page honeypot is stripped of Chrome Autofill classification signals so real bookers are no longer rejected as bots (#33), while the API field stays company.
- Neutral honeypot input —
book.htmldrops the "Company" label and usesname="hp"/id="f-hp"; the submit script still posts$('f-hp').valueas JSONcompany, so the server check, embed widget, and API clients stay unchanged. - Regression test —
TestBookPage_honeypotGivesAutofillNothingToClassifypins rendered markup (no label or autofill-shaped name/id) and the submit-script coupling; restoring the old field fails it. - Docs — ARCHITECTURE §9 and the unreleased changelog record why the input must give Autofill nothing to classify.
Grok | 𝕏
shockalotti
left a comment
There was a problem hiding this comment.
Approved. Minimal, honest scope: book.html only, with the embed exemption stated and justified in both CHANGELOG and ARCHITECTURE (its honeypot never had a label/name/id). The rendered-markup test is the right pin — it checks the property (nothing autofill can classify) rather than the specific strings, plus the API-compat half (still posts as company). Fixes #33 with no API change.
|
Approved and ready to merge, but this now conflicts with main (CHANGELOG Unreleased section — several PRs landed ahead of it). I cannot push to the fork (no write access), so please merge |
CHANGELOG conflict only: kept both [Unreleased] entries. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Merged Same note as on #50: 86cc414 on |

Fixes #33. Thanks to @MinosChatzidakis for the diagnosis: Chrome autofill was filling the honeypot, and renaming the field fixed it for them.
The problem
The booking page's hidden honeypot was
<label>Company<input name="company" autocomplete="off">. Chromium classifies fields usingcomponents/autofill/core/browser/form_parsing/resources/legacy_regex_patterns.json, whereCOMPANY_NAMEiscompany|business|organization|organisation, matched against both the label and thenameattribute. Chrome filled the field from the visitor's address profile despiteautocomplete="off", andPOST /v1/bookingsrejected a real person as a bot.The change
book.htmlonly. The input now has no label and isname="hp",id="f-hp". The submit script reads it by id and still postscompany, so the API, the embed widget and third-party clients are unchanged, and a filled value is still rejected.How the name was checked
I fetched that pattern file at chromium/src
0520a5aa3f76and ran candidates through every pattern, in all languages, as a case-insensitive search (ICUfind()semantics).hpandf-hpmatch nothing. As a sanity check,companyhitsCOMPANY_NAMEandextrahitsPHONE_EXTENSION.A label-free input still gets a label from Chrome:
form_autofill_util.ccinfers one from the surrounding DOM. For this markup that is the Back button text plus the chosen slot, for exampleBack Fri, Sep 18 · 10:00 AM. I generated that string for every weekday, month and half hour in all eight shipped locales (32,264 strings). One pattern matched: German WednesdayZurück Mi., …hitsMIDDLE_INITIAL.name_field_parser.ccassignsMIDDLE_INITIALonly inside a sequence that has both a first-name and a last-name field, and this form has one full-name field, so the match cannot become a classification.Not covered: Chrome's server-side predictions, Safari, Firefox and password managers. I did not reproduce the original fill with a real Chrome address profile.
Embed widget
No change needed. Its honeypot has never had a label, name or id, and its inferred label comes from the same Back-plus-slot text.
Tests
TestBookPage_honeypotGivesAutofillNothingToClassifyrenders the page and requires one input, no label or text, no placeholder, aria-label, title or value, a name and id free of common autofill words, and a submit script that posts that input ascompany. Restoring the old markup fails 6 assertions; restoring only the "Company" label fails 2.TestCreateBooking_honeypotRejectedstill covers the server side, and it fails when the check is disabled."company":""and got 201 with no console errors. A filled honeypot got 400, and the server loggedbooking rejected: honeypot filled.gofmt -l .is clean, andgo vet ./...,go build ./...andgo test ./...pass.docs/ARCHITECTURE.md§9 now explains why the honeypot input must give autofill nothing to classify.🤖 Generated with Claude Code