fix: harden CORS proxy with timeout, size limit, and encoded path check#145
fix: harden CORS proxy with timeout, size limit, and encoded path check#145rsbh wants to merge 4 commits into
Conversation
- Decode path before traversal check to catch %2e%2e encoded variants - Add AbortSignal.timeout(30s) to upstream fetch calls - Reject request bodies larger than 1MB via Content-Length check - Return 504 for upstream timeouts instead of generic 502 Closes #118 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Review limit reached
More reviews will be available in 54 minutes. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThe API proxy handler in ChangesProxy request/response hardening
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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 |
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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.
Inline comments:
In `@packages/chronicle/src/server/api/apis-proxy.ts`:
- Around line 16-26: The isPathSafe function currently normalizes the path with
new URL(decoded, 'http://localhost').pathname which resolves `..` before the
safety check; change the logic to first inspect the raw decoded variable for any
`..` path segments (e.g., check decoded.split('/').includes('..') or a regex)
and reject if present, then proceed to validate scheme (the /^[a-z]+:\/\//i
test) and use normalized (new URL(...).pathname) only afterwards; ensure the
function returns false when the raw decoded contains `..` so concatenation later
with spec.server.url cannot be exploited.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 416145ef-a345-4ebd-ac5c-607291a2045a
📒 Files selected for processing (1)
packages/chronicle/src/server/api/apis-proxy.ts
| function isPathSafe(p: string): boolean { | ||
| let decoded: string; | ||
| try { | ||
| decoded = decodeURIComponent(p); | ||
| } catch { | ||
| return false; | ||
| } | ||
| if (/^[a-z]+:\/\//i.test(decoded)) return false; | ||
| const normalized = new URL(decoded, 'http://localhost').pathname; | ||
| return !normalized.split('/').includes('..'); | ||
| } |
There was a problem hiding this comment.
Path traversal check is bypassed due to URL normalization resolving .. segments.
The new URL(decoded, 'http://localhost').pathname normalizes the path by resolving .. segments before the check runs. For example, /../../../etc/passwd becomes /etc/passwd after normalization, so split('/').includes('..') returns false and the path is deemed safe. However, line 60 then concatenates the original path (containing ..) with spec.server.url, allowing the traversal to reach the upstream server.
Check the raw decoded path for .. segments before normalization:
🐛 Proposed fix
function isPathSafe(p: string): boolean {
let decoded: string;
try {
decoded = decodeURIComponent(p);
} catch {
return false;
}
if (/^[a-z]+:\/\//i.test(decoded)) return false;
- const normalized = new URL(decoded, 'http://localhost').pathname;
- return !normalized.split('/').includes('..');
+ // Check raw decoded path for traversal before any normalization
+ if (decoded.split('/').includes('..')) return false;
+ // Also reject protocol-relative URLs (//evil.com)
+ if (decoded.startsWith('//')) return false;
+ return true;
}🤖 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 `@packages/chronicle/src/server/api/apis-proxy.ts` around lines 16 - 26, The
isPathSafe function currently normalizes the path with new URL(decoded,
'http://localhost').pathname which resolves `..` before the safety check; change
the logic to first inspect the raw decoded variable for any `..` path segments
(e.g., check decoded.split('/').includes('..') or a regex) and reject if
present, then proceed to validate scheme (the /^[a-z]+:\/\//i test) and use
normalized (new URL(...).pathname) only afterwards; ensure the function returns
false when the raw decoded contains `..` so concatenation later with
spec.server.url cannot be exploited.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
%2e%2eencoded variantsAbortSignal.timeout(30s)to upstream fetch callsContent-LengthcheckCloses #118
Test plan
%2e%2ein path → should get 400🤖 Generated with Claude Code