fix(security): default TRUSTED_PROXY_DEPTH to fail closed - #231
Conversation
|
@Bayyan16 is attempting to deploy a commit to the Khubair Nasir's projects Team on Vercel. A member of the Team first needs to authorize it. |
📝 WalkthroughWalkthrough
ChangesTrusted proxy depth default
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
tests/middleware/proxy-depth-default.test.ts (1)
45-123: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick winAdd malformed-depth and WebSocket regression cases.
This suite proves the unset HTTP middleware path and the explicit
TRUSTED_PROXY_DEPTH=1opt-in, but the new security contract is broader: invalid values must also fail closed, andsrc/routes/ws.tshas its own parser. Without those cases, a future regression can reopen spoofed-IP influence over WS blocklist/auth-ban/connection-cap logic without tripping tests.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/middleware/proxy-depth-default.test.ts` around lines 45 - 123, The proxy-depth tests only cover the unset default and TRUSTED_PROXY_DEPTH=1 path, so add regression coverage for malformed TRUSTED_PROXY_DEPTH values that must fail closed and ignore spoofed X-Forwarded-For; use the existing middleware setup in proxy-depth-default.test.ts with the same mockSocketIp, readRateLimit, and ipBlocklist symbols. Also add WebSocket regression cases for src/routes/ws.ts, verifying its depth parsing follows the same secure default and does not let malformed or unset trusted-proxy settings affect blocklist/auth-ban/connection-cap decisions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@tests/middleware/proxy-depth-default.test.ts`:
- Around line 45-123: The proxy-depth tests only cover the unset default and
TRUSTED_PROXY_DEPTH=1 path, so add regression coverage for malformed
TRUSTED_PROXY_DEPTH values that must fail closed and ignore spoofed
X-Forwarded-For; use the existing middleware setup in
proxy-depth-default.test.ts with the same mockSocketIp, readRateLimit, and
ipBlocklist symbols. Also add WebSocket regression cases for src/routes/ws.ts,
verifying its depth parsing follows the same secure default and does not let
malformed or unset trusted-proxy settings affect
blocklist/auth-ban/connection-cap decisions.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 6e6cbf76-8f90-4083-b61b-c584094acfc3
📒 Files selected for processing (5)
src/middleware/ip-blocklist.tssrc/middleware/rate-limit.tssrc/routes/ws.tstests/middleware/proxy-depth-default.test.tstests/middleware/rate-limit.test.ts
|
A quick note on the failing CI check: the failure happens during The log shows: ENOENT: no such file or directory, scandir '/home/runner/work/percolator-sdk'This appears to be caused by the existing local SDK dependency resolution path ( Relevant middleware tests for this PR passed locally: ./node_modules/.bin/vitest run tests/middleware --reporter=verboseResult: Test Files 7 passed (7)
Tests 82 passed (82)Vercel also appears to require team authorization before deployment, which is separate from the code changes in this PR. |
Summary
Fixes #230.
This PR makes client-IP extraction fail closed by default by changing the default
TRUSTED_PROXY_DEPTHfrom1to0.Previously, when
TRUSTED_PROXY_DEPTHwas unset, the API assumed one trusted proxy and consumedX-Forwarded-Foras trusted input. SinceX-Forwarded-Foris client-controllable unless it is stripped or rewritten by a trusted reverse proxy, this could allow spoofed-IP behavior to influence HTTP rate limiting, IP blocklist decisions, and WebSocket abuse controls.With this change, forwarded headers are ignored unless operators explicitly opt in by setting
TRUSTED_PROXY_DEPTH=1.What Changed
This PR updates all client-IP extraction paths that previously defaulted to trusting one proxy:
src/middleware/rate-limit.tssrc/middleware/ip-blocklist.tssrc/routes/ws.tsThe default trusted proxy depth is changed from:
to:
The invalid-config fallback is also changed from fail-open to fail-closed:
This means that if
TRUSTED_PROXY_DEPTHis missing or invalid, the application no longer trusts client-supplied forwarded headers by default.Why This Fix Is Needed
X-Forwarded-Forshould only be trusted when the application is deployed behind a trusted proxy or load balancer that sanitizes the header.Before this change, the application defaulted to
TRUSTED_PROXY_DEPTH=1, which means requests with anX-Forwarded-Forheader could influence the client IP used by abuse-control logic even when the operator did not explicitly configure proxy trust.That client IP is used in multiple security-sensitive paths:
Defaulting to
0makes the behavior fail closed. Deployments that intentionally rely on trusted proxy headers can still opt in explicitly with:Test Changes
This PR adds regression coverage in:
The new tests verify that:
TRUSTED_PROXY_DEPTHis unset, spoofedX-Forwarded-Forvalues are ignored for HTTP rate limiting.TRUSTED_PROXY_DEPTHis unset, the IP blocklist decision uses the socket IP instead of spoofedX-Forwarded-For.TRUSTED_PROXY_DEPTH=1.This PR also updates the existing rate-limit middleware tests to explicitly exercise trusted-proxy behavior with
TRUSTED_PROXY_DEPTH=1.That keeps existing
X-Forwarded-Forrate-limit behavior covered, while the new regression test covers the secure default.Testing
Relevant middleware tests passed locally:
Result:
I also ran the broader Vitest suite locally. The full suite currently reaches unrelated failures in
tests/sdk-smoke.test.tsbecause local devnet v17PROGRAM_ID/MATCHER_PROGRAM_IDenvironment variables are not configured:Those SDK smoke failures are unrelated to this PR's middleware changes.
Notes
This PR does not remove trusted-proxy support.
It only changes the default behavior so that forwarded-header trust is explicit instead of enabled by default. Operators who deploy behind a trusted proxy can continue using forwarded headers by setting:
Summary by CodeRabbit
Bug Fixes
Tests