Skip to content

feat(frontend): device test pings the plugin under a per-address limit - #2066

Merged
rpgmem merged 1 commit into
developfrom
claude/device-check-ping
Oct 24, 2026
Merged

rpgmem merged 1 commit into
developfrom
claude/device-check-ping

Conversation

@rpgmem

@rpgmem rpgmem commented Oct 24, 2026

Copy link
Copy Markdown
Owner

Summary

The device test sent two requests to the server. It sent a HEAD on the page itself for the clock row, and an empty POST to admin-ajax.php for the server row. Neither could be limited by the plugin: WordPress rejects the empty POST before any plugin code runs, and the page cache answers the HEAD. A page served from an old cache also went unnoticed. Closes #2064.

  • One endpoint of the plugin's own, ffc_device_check_ping:
    • public, nonce-free, sent with nocache_headers();
    • answers with the server's time and FFC_VERSION;
    • one request now feeds both the clock row and the "Server requests" row.
  • Old cached page: when the version the page was built with (ffcCoreConfig.version) differs from the server's, the server row warns that the page is an old copy and says how to fix it.
  • Per-address limit in fixed windows: default 60 runs per 600 s, 0 turns it off.
    • Edited on Settings → Rate Limit, in a new "Device Test" card beside the captcha cap, with autosave.
    • Clamped on save and on read by RateLimitSettingsReader, the typed reader of ffc_rate_limit_settings.
    • Putting the limits there avoids a new Settings → Frontend dependency, so the module-boundary baseline is unchanged.
    • Over the cap, the endpoint answers 429 with retryAfter. The row shows a warning with the wait, not a failure, because the device is not at fault.
  • Shared counter: the fixed-window counter from AltchaChallengeEndpoint::allow_mint() moves to Core\WindowThrottle, now used by both endpoints. The address is still hashed into the key and never stored raw. The ALTCHA tests pass unchanged.
  • Registration follows the endpoint-class idiom: DeviceCheck::init() registers its own hooks with self::AJAX_ACTION, and the Frontend constructor calls it.

Tests

  • DeviceCheckPingTest (separate process, RequestInput alias):
    • registration;
    • the answer and nocache_headers();
    • 429 with retryAfter past the cap, with a hashed key;
    • a cap of 0 counts nothing;
    • a request without an address goes through uncounted;
    • each address has its own count;
    • bounds on read.
  • TabRateLimitTest: defaults, bounds on save, 0 accepted as "no cap", a cleared field keeps the stored value.
  • DeviceCheckTest: the localized ping config.
  • Vitest: the clock fed by the ping, with exactly one request; a page with no ping configured; reached, old cached page, 429 warning, a firewall's 403 and 429 treated as refusals, blocked request; the verdict cases. Full Vitest suite: 2,568 passed.
  • Guards pass: AJAX wiring, module boundary, required numeric inputs, translation coverage and catalogue agreement, documentation coverage, settings paths, comment language.

Docs

  • The Rate Limit topic describes the new cap; the Certificates topic mentions the old-cache warning and where to set the limit.
  • Strings are in the three catalogues; the CHANGELOG has an Added entry.

Generated by Claude Code

The device test sent a HEAD for the clock and an empty POST to
admin-ajax for the server row. WordPress rejects the empty POST before
any plugin code runs and the page cache answers the HEAD, so neither
could be limited, and a page served from an old cache went unnoticed
(#2064).

- One public endpoint, ffc_device_check_ping, answers with the server's
  time and the plugin's version and feeds both rows. A version that
  differs from the page's names an old cached page.
- It is limited per address in fixed windows (default 60 per 600 s,
  0 off), edited on Settings > Rate Limit beside the captcha cap and
  bounded on save and on read by RateLimitSettingsReader. Over the cap
  the row warns with the wait instead of failing.
- The fixed-window counter the ALTCHA mint cap used moves to
  Core\WindowThrottle, now shared by both endpoints; the mint cap's
  behaviour is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018srx5hc431EYmG4MHUr2KA
@rpgmem rpgmem added enhancement New feature or request area:certificates labels Oct 24, 2026 — with Claude
@rpgmem
rpgmem marked this pull request as ready for review October 24, 2026 15:14
@rpgmem
rpgmem enabled auto-merge (squash) October 24, 2026 15:14
@rpgmem
rpgmem merged commit af9d1d8 into develop Oct 24, 2026
26 checks passed
@rpgmem
rpgmem deleted the claude/device-check-ping branch October 24, 2026 15:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:certificates enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants