0.3.0: stronger PoW, replay protection, opt-in spam layers, email validation, tests#2
Merged
Merged
Conversation
Builds on the opt-in settings with several anti-bot improvements (0.3.0):
- Proof-of-work cost is now a "Protection strength" preset dropdown
(Low/Standard/High/Very high), settable site-wide and per form, defaulting to
a 20x stronger Standard (200k). Exact values still available via the
`genero/gravityforms_altcha/cost` filter.
- Replay protection: each solved challenge is single-use (deduped on its unique
signature for the rest of its lifetime), so a proof can't be replayed across
many submissions.
- Optional, independent spam layers (global toggles, applied via
gform_entry_is_spam so flagged entries are marked spam — recoverable — never
rejected):
- Rate limiting (default 2/min/IP). IP resolved independently of GF (which
sites often blank for GDPR) and kept only as a salted HMAC in a 60s
transient; never stores the raw IP. CDN client-IP header is configurable.
- Content heuristics: definite-spam keywords flag immediately; weaker signals
(link farms, injected markup, wrong-script text) must combine to flag.
Unit tests cover the fingerprint, cost clamp, IP resolver, and content scoring.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replaces the fixed per-minute allowance with a configurable max + window (genero/gravityforms_altcha/rate_limit_max + _window), defaulting to 3 per hour. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds an "Email validation" toggle that rejects undeliverable or disposable email addresses at the field with a corrective message, so visitors fix a bad address instead of silently never being reachable. Verifies via the Bouncer API (BOUNCER_API_KEY env var, or the bouncer_api_key filter). Fails open: risky/unknown verdicts, a missing key, or any API error never block. Definitive verdicts are cached for a day keyed by a hash of the email. Sends the email to a third-party service — document in your privacy policy. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…y/disposable) Replaces the hardcoded "undeliverable or disposable blocks" with admin checkboxes under the Email validation toggle, so each Bouncer verdict can be acted on independently. Defaults: undeliverable + disposable on, risky off (risky can reject some real catch-all/role addresses). Unchecked/unset verdicts never block. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Scan all visitor free text, including composite name/address sub-inputs (previously only top-level text/textarea — the Name field, where bots love to dump links, was invisible). - A URL in the name field is treated as near-certain spam. - Count scheme-less www. links and graduate link scoring (1–2 innocent, 3–4 a signal, 5+ damning) without double-counting http://www. - Match definite keywords on unicode word boundaries (no Scunthorpe false positives if a short keyword is configured). - Normalise zero-width / invisible characters before matching to defeat simple evasion. Single weak signals still never flag alone, so false positives stay near zero. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Splits the suite into three layers, mirroring the house pattern (gds-mcp): - unit: pure logic (existing), runs everywhere. - mocked: WP-glue tested with WordPress functions stubbed via Brain\Monkey — EmailValidator's Bouncer HTTP→verdict mapping/caching/fail-open, the rate-limit counter + unknown-IP guard, and replay dedup. Runs in CI (no DB, no GF) — `composer test` now covers unit + mocked. - integration: real WordPress via wp-phpunit (`composer test:integration`), exercising the GF settings, the gform_entry_is_spam spam layers, and cost resolution against an actual Gravity Forms install. GF is commercial, so these skip when it's absent (CI) and run via wp-env / DDEV. Pins phpunit to ^9 (wp-phpunit requirement) and adds a wp-env CI job that smoke-loads the plugin in real WordPress. .wp-env.json mounts a sibling gravityforms checkout for local integration runs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… the matrix The lock was generated on PHP 8.4 and pinned doctrine/instantiator 2.1.0 (needs ^8.4), breaking composer install on the 8.2/8.3 CI runners. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…s on DDEV/wp-env) The integration suite needs Gravity Forms, which is commercial and absent in CI, so the job only smoke-booted wp-env and skipped every test — not worth the Docker flakiness. Integration runs locally via 'composer test:integration' (wp-env/DDEV). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Summary
Builds on the opt-in settings (0.2.0) with a layered, first-party anti-bot/anti-spam upgrade — every layer is opt-in and tuned to never block real users (the fuzzy layers mark spam, recoverable, rather than rejecting).
Proof-of-work
costfilter.Spam layers (global toggles, apply to all forms; mark-as-spam, never reject)
Email validation (blocks, to help the visitor fix a bad address)
BOUNCER_API_KEY). Per-verdict checkboxes: undeliverable (default on), risky (off), disposable (on). Fails open —unknown/missing key/API error never block. Sends email to a third party (privacy note in README).Tests
^0.3(+ raise the e2e poll timeout for the higher PoW cost).🤖 Generated with Claude Code