RateLimiter
in package
Counts requests per client IP in a fixed time window and blocks with HTTP 429 once the configured limit is exceeded. See the module-4 design spec §6.
Table of Contents
Constants
- CHALLENGE_ROUTE = '/creationell-captcha/v1/challenge'
- The plugin's own challenge route — the only route exempt from counting.
Methods
- run() : void
- Runs the rate limiter for the current request. Registered on `init` at priority 0. Terminates the request with 429 once the limit is exceeded.
- context() : array{ip: string, path: string, path_raw: string, method: string, script: string, action: string, is_ajax: bool}
- Builds the request context array.
- is_challenge_endpoint() : bool
- Whether the request is actually served by the plugin's own challenge endpoint — the one route that must stay reachable without being counted, because the widget fetches a challenge for every protected form.
- should_count() : bool
- Whether the current request counts toward the rate limit.
Constants
CHALLENGE_ROUTE
The plugin's own challenge route — the only route exempt from counting.
private
mixed
CHALLENGE_ROUTE
= '/creationell-captcha/v1/challenge'
Compared for EQUALITY against the route the request actually addresses
(CM-7); the previous str_contains() matched any path or rest_route
value that merely contained this string.
Methods
run()
Runs the rate limiter for the current request. Registered on `init` at priority 0. Terminates the request with 429 once the limit is exceeded.
public
run() : void
context()
Builds the request context array.
private
context(string $ip) : array{ip: string, path: string, path_raw: string, method: string, script: string, action: string, is_ajax: bool}
Parameters
- $ip : string
Return values
array{ip: string, path: string, path_raw: string, method: string, script: string, action: string, is_ajax: bool}is_challenge_endpoint()
Whether the request is actually served by the plugin's own challenge endpoint — the one route that must stay reachable without being counted, because the widget fetches a challenge for every protected form.
private
is_challenge_endpoint() : bool
BK-2 / CM-7: the previous implementation ran str_contains() over the
request path AND over the raw $_GET['rest_route'] value. Both are
client-controlled, and neither says anything about what WordPress will
actually serve: POST /wp-login.php?rest_route=creationell-captcha/v1/challenge
is an ordinary login POST — wp-login.php never calls parse_request(), so
rest_api_loaded() never runs — yet it turned the rate limiter off and
made the login brute-force limit unenforceable.
The route now comes from creationell_captcha_current_rest_route(), which only reports a route when core would really dispatch one, and it is compared for equality instead of by substring.
Return values
boolshould_count()
Whether the current request counts toward the rate limit.
private
should_count(array{ip: string, path: string, path_raw: string, method: string, script: string, action: string, is_ajax: bool} $context, string $scope) : bool
Parameters
- $context : array{ip: string, path: string, path_raw: string, method: string, script: string, action: string, is_ajax: bool}
-
Request context.
- $scope : string
-
The configured scope.