Skip to content

Stop keying the rate limiter on spoofable forwarding headers - #6

Merged
tinkthemaker merged 1 commit into
mainfrom
devin/1787109481-harden-rate-limit-key
Aug 23, 2026
Merged

Stop keying the rate limiter on spoofable forwarding headers#6
tinkthemaker merged 1 commit into
mainfrom
devin/1787109481-harden-rate-limit-key

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes the security finding that the token-bucket limiter (the only abuse control on the unauthenticated cert-viewer / cors-tester / misconfig-mapper endpoints) was keyed on client-supplied headers. An attacker could rotate X-Vercel-Forwarded-For per request to get a fresh bucket every time — unlimited outbound SSRF-scanner use plus unbounded growth of the in-memory buckets map.

Changes in lib/security/rate-limit.ts:

  • clientKeyFromHeaders now only honors forwarding headers when the deployment declares a trusted proxy (VERCEL=1, where the edge overwrites these headers, or explicit TRUST_PROXY_HEADERS=1), and only when the value parses as an IP (node:net isIP). Otherwise it returns a shared "direct" key, so an untrusted deployment can't be tricked into per-header buckets at all.
  • rateLimit now also enforces a global secondary cap independent of the client key:
    rateLimit(key):
      global bucket (120/min)  -> 429 if exceeded, regardless of key
      per-key bucket (12/min)  -> 429 if exceeded
    
    so even a valid-IP-rotating attacker behind a trusted proxy is bounded.
  • Bucket map is hard-capped at 1000 entries: creating a new bucket at the cap evicts the soonest-expiring entry (never the global bucket), closing the memory-growth vector.

Tests updated/added: header trust gating, non-IP header rejection, per-key trip at 12, and global trip at 120 with rotating keys. The old test asserting unconditional header trust codified the vulnerable behavior and was replaced.

No endpoint code changes needed — all three routes already call rateLimit(clientKeyFromHeaders(...)).

Link to Devin session: https://app.devin.ai/sessions/895ec46b1c4244678bfb15206edf6990
Requested by: @tinkthemaker

Summary by CodeRabbit

  • New Features

    • Added global and per-client request limits to improve service stability.
    • Added automatic cleanup of expired rate-limit entries.
    • Improved client identification using validated network information when trusted proxy settings are enabled.
  • Bug Fixes

    • Prevented requests from bypassing global limits by rotating client keys.
    • Added a reliable fallback for requests without valid client network information.

@tinkthemaker tinkthemaker self-assigned this Aug 19, 2026
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@vercel

vercel Bot commented Aug 19, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cyber-toolbox Building Building Preview Aug 19, 2026 3:18am

@coderabbitai

coderabbitai Bot commented Aug 19, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3db74b27-76cd-409b-a248-5ce23fbf6e12

📥 Commits

Reviewing files that changed from the base of the PR and between bf38289 and de97298.

📒 Files selected for processing (2)
  • lib/security/rate-limit.ts
  • tests/security/rate-limit.test.ts

Included review availability: Your plan provides up to 3 included reviews per hour; 2 remain after this review.


📝 Walkthrough

Walkthrough

The rate limiter now enforces global and per-key request limits, manages bucket capacity, and cleans expired buckets. Client-key extraction validates IP addresses and uses forwarding headers only when trusted proxy configuration is enabled.

Changes

Rate-limit security controls

Layer / File(s) Summary
Global and per-key bucket enforcement
lib/security/rate-limit.ts, tests/security/rate-limit.test.ts
The limiter adds validated token buckets, a global request cap, bounded eviction, periodic cleanup, and global-before-per-key enforcement. Tests verify blocking at request 120.
Trusted proxy client-key resolution
lib/security/rate-limit.ts, tests/security/rate-limit.test.ts
Forwarding headers are used only when trusted proxy settings are enabled. Invalid or unavailable addresses resolve to "direct". Tests cover precedence and validation.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Merge Risk: 🟡 Moderate · up to de972

The change prevents header rotation from bypassing rate limits and caps in-memory growth, but one unauthenticated client can still consume the shared process capacity needed by others, and trusted proxy behavior is not explicitly defined. Merge should wait for explicit owner acceptance of these bounded availability and deployment-contract risks or for a follow-up fix.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main security change: preventing rate-limit keys from using spoofable forwarding headers.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch devin/1787109481-harden-rate-limit-key

Comment @coderabbitai help to get the list of available commands.

@tinkthemaker
tinkthemaker merged commit d13920b into main Aug 23, 2026
4 checks passed
@tinkthemaker
tinkthemaker deleted the devin/1787109481-harden-rate-limit-key branch August 23, 2026 13:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant