feat: rate-limit unauthenticated public API routes#192
Conversation
|
Re: the trusted-IP bypass tests failing in CI — this is a false positive. The tests pass as written, and Laravel special-cases protected function formatServerHeaderKey($name)
{
if (! str_starts_with($name, 'HTTP_') && $name !== 'CONTENT_TYPE' && $name !== 'REMOTE_ADDR') {
return 'HTTP_'.$name;
}
return $name;
}So Confirmed empirically. Leaving the tests as-is. Separately, I pushed a fix for a real issue: the |
29daf5e to
a2a5741
Compare
a2a5741 to
b022d68
Compare
Adds rate limiting to the unauthenticated public API routes (register, register status, regions, and the health callback).
Greptile Summary
This PR adds rate limiting to the four unauthenticated public API routes (
/registerPOST,/register/{uuid}GET,/regionsGET, and the instance health callback) using Laravel named rate limiters defined inAppServiceProvider::boot(). Trusted internal IPs (configured viaPOLYDOCK_TRUSTED_IPS) and callers presenting a valid health token bypass the limits entirely.registerat 10/min,public-readat 60/min,instance-healthat 120/min) replace what would have been a shared anonymous throttle counter, giving each route its own per-IP bucket.trusted_ipsconfig key is parsed from a comma-separated env var; test suites addtearDowncache flushes to prevent counter bleed between tests.it_allows_trusted_ips_to_bypass_registration_status_throttle— passesREMOTE_ADDRas a header argument togetJsonrather than viawithServerVariables, so the trusted-IP guard never fires and the test fails at request 61 with a 429.Confidence Score: 4/5
The production rate-limiting logic is correct; a broken test will cause CI to fail on the trusted-IP bypass path for the registration-status route.
The core implementation — named rate limiters, trusted-IP bypass, token bypass for instance-health — is well-structured and the logic is sound. One test (it_allows_trusted_ips_to_bypass_registration_status_throttle) passes REMOTE_ADDR as a header to getJson instead of using withServerVariables, so $request->ip() returns 127.0.0.1, the trusted-IP check fails, and the test fails at request 61 with 429. The equivalent test for the POST register route was fixed correctly in this PR; this getJson variant was missed.
tests/Feature/Controllers/Api/RegisterControllerTest.php — the it_allows_trusted_ips_to_bypass_registration_status_throttle test uses the wrong approach for spoofing the client IP.
Important Files Changed
Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart TD A[Incoming Request] --> B{throttle middleware} B --> C{Named limiter closure} C --> D{IP in trusted_ips?} D -->|Yes| E[Limit::none — bypass] D -->|No| F{instance-health route?} F -->|Yes| G{Valid health token?} G -->|Yes| E G -->|No| H[Limit::perMinute 120 by IP] F -->|No| I{register route?} I -->|Yes| J[Limit::perMinute 10 by IP] I -->|No| K[Limit::perMinute 60 by IP] H --> L{Under limit?} J --> L K --> L L -->|Yes| M[Proceed to controller] L -->|No| N[429 Too Many Requests]%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%% flowchart TD A[Incoming Request] --> B{throttle middleware} B --> C{Named limiter closure} C --> D{IP in trusted_ips?} D -->|Yes| E[Limit::none — bypass] D -->|No| F{instance-health route?} F -->|Yes| G{Valid health token?} G -->|Yes| E G -->|No| H[Limit::perMinute 120 by IP] F -->|No| I{register route?} I -->|Yes| J[Limit::perMinute 10 by IP] I -->|No| K[Limit::perMinute 60 by IP] H --> L{Under limit?} J --> L K --> L L -->|Yes| M[Proceed to controller] L -->|No| N[429 Too Many Requests]Reviews (5): Last reviewed commit: "feat: rate-limit unauthenticated public ..." | Re-trigger Greptile