feat: Add optional human-verification challenge to form submissions - #2
Open
nfebe wants to merge 6 commits into
Open
feat: Add optional human-verification challenge to form submissions#2nfebe wants to merge 6 commits into
nfebe wants to merge 6 commits into
Conversation
Public form endpoints could only be defended with a honeypot and a minimum submit time, both of which cost an attacker nothing once they bother to look. Submissions can now additionally require a solved human-verification challenge, with Cloudflare Turnstile as the first provider and other providers pluggable behind a contract. The challenge is off unless configured, so existing installations keep their current behaviour and make no outbound call. A provider that cannot be reached answers 503 rather than 422: the submitter is probably human, and an outage at the provider should read as a retry rather than as an accusation. The verified token is discarded instead of being kept with the submission.
The origin allowlist was read by the submission controller but had no entry in the shipped config, so it could only ever be set per form. It can now be set once for every form via the environment.
A challenge set in config applied to every form and every client. Only a browser can render the widget and produce a token, so enabling one locked out mobile and server-to-server clients posting to the same API, with no way to exempt them. The challenge is now resolved per form, the same way destinations and the origin allowlist already are: the form's own setting wins and an unset one inherits the configured default. A form can opt out of challenges entirely, or require one the default does not. The check also moved to where the form is known, which is where the origin check already runs. A rejected or missing token still answers 422 and an unreachable provider still answers 503.
|
Coverage Report |
Code Review SummaryThis PR introduces a robust and flexible human-verification challenge system (Turnstile) to the form submission package. It supports global defaults, per-form overrides, and a clean opt-out mechanism for API clients. The error handling for external provider outages is particularly well-conceived. 🚀 Key Improvements
💡 Minor Suggestions
|
Matches how the delivery manager is already handed to the job that uses it.
… name A single driver name left no room for settings that vary by form, so any provider needing one would have forced another schema change. A form now stores the driver alongside an optional settings bag, which the driver alone interprets. The reserved "none" string is gone: an explicitly empty driver is what opts a form out. Turnstile reads a hostname setting, refusing a token minted for another site. The create migration ships the column in its final shape, so a fresh install gets it in one step. The follow-up migration now applies only where it is needed: it adds the column when an older table lacks it, widens and converts an earlier single-name column, and does nothing when the shape is already right. It keys that decision off narrow string types, because a JSON column reports as text on SQLite and cannot be recognised directly.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Wiring the contact form on whilesmart.com, the app was about to hand-roll Turnstile verification in its own middleware. It belongs here so the next project doesn't repeat it.
Submissions can optionally require a solved challenge. Off unless configured, Turnstile first, other providers behind a contract.
Only a browser can produce a token, so the challenge is set per form as well as by default, the way destinations and the origin allowlist already are. Worth knowing: forms are created on first use, so a key never submitted before inherits the default and gets challenged. An API-only form needs its row created ahead of the first call.
Bumped to 0.2.0 because
whilesmart/pagesneeds a release to depend on this.