Repository navigation
feat: rate-limit guest ticket creation and replies per IP - #90
Merged
Merged
Conversation
Adds GuestRateLimiter (transient-backed fixed 60s window per scope and client IP, configurable through settings and a filter) and the tests that pin the endpoint behaviour. The endpoints are not wired yet, so the endpoint tests fail.
The widget ticket endpoint and the guest create/reply AJAX actions now answer 429 with Retry-After once a client IP passes 5 tickets or 10 replies a minute. The reply check runs before the guest token lookup so wrong-token guesses are counted.
Merged
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.
Port of escalated-dev/escalated-nestjs#130 (the reference implementation).
Behaviour
ticket:POST /wp-json/escalated/v1/widget/ticketsand theescalated_guest_createAJAX action (guest form shortcode). Default 5/min.reply: theescalated_guest_replyAJAX action. Default 10/min. The throttle runs before the guest token lookup, so wrong-token requests are counted.429with aRetry-Afterheader (seconds until the window resets). REST returns the usualWP_Errorbody with codeescalated_guest_rate_limited; AJAX returnswp_send_json_errorwithcode,message,retry_after.guest_rate_limit_enabled(default1),guest_rate_limit_tickets_per_minute(5),guest_rate_limit_replies_per_minute(10)escalated_guest_rate_limitfilter for hosts that configure in code.REMOTE_ADDR. Documented in the class docblock, README and CHANGELOG that sites behind a proxy/CDN must rewriteREMOTE_ADDRfrom the trusted proxy's forwarded header, or every guest shares one IP.Tests (written first, watched fail)
tests/Test_Guest_Rate_Limit.php(REST + limiter) andtests/Test_Guest_Ajax_Rate_Limit.php(AJAX), 17 tests:Retry-Afterin (0, 60], and no ticket row createdTicket not found.,Ticket not found., limitedBefore wiring the endpoints, 10 of the 17 failed (the limiter-only and disabled cases passed).
Results
Run locally in Docker (
wordpress:cli-php8.1, MySQL 8.0, WordPress 7.1.2 test suite, same layout asrun-tests.yml):vendor/bin/phpunit: OK, 563 tests, 1696 assertions, 4 skipped (main baseline: 546 tests, 1652 assertions, 4 skipped)vendor/bin/pint --test: PASS, 220 filesDifferences from the reference
permission_callback/callback andWP_Errorcannot carry aRetry-Afterheader. Malformed bodies (400 from arg validation) are therefore not counted; everything that reaches ticket creation is.escalated_guest_replyAJAX action, so that is what thereplyscope guards. A wrong token there answers 404 (Ticket not found.) rather than NestJS's 403.widget/chat/start) is not covered here, matching the reference's scope; it remains under the widget-wide 30/min limit.