fix(server): allow browser SDK request headers through CORS preflight - #1778
Conversation
The OpenAI and Anthropic browser SDKs send X-Stainless-* headers describing runtime and retry state. The preflight answered with a fixed allow-list that never mentioned them, so the browser blocked every request before it was sent and no browser SDK client could reach the proxy at all. Echo the requested headers for an origin that already passed the origin gate, keeping the static contract as the floor, and declare the dependency with Vary. The echo widens which headers an admitted caller may send, never which origins are admitted: a rejected origin is refused before any allow-list is built, and the preflight still carries no credential and no auth side effect. Closes #1773
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughThe PR adds roadmap closeout records and updates server CORS handling. Authorized origins can receive requested preflight headers with case-insensitive deduplication. Tests cover Stainless SDK headers across three API routes and reject unauthorized origins. ChangesRoadmap closeout and CORS handling
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
Suggested labels: Suggested reviewers: ✨ Finishing Touches📝 Generate docstrings
🧪 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 |
|
✅ Deterministic PR hygiene checks passed. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f46094a754
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| CRM 프로젝트 평가 요청 | ||
| 인사 나누기 | ||
| 김 | ||
| 김병준 |
There was a problem hiding this comment.
Remove personal account data from the tracked capture
The raw transcript includes the user's full name along with unrelated private ChatGPT history titles, so committing 000_source_capture_raw.txt publishes personal account data in the repository even though the automated privacy scan does not recognize names. Remove the raw UI capture or replace it with a narrowly redacted summary before this commit is pushed.
AGENTS.md reference: AGENTS.md:L281-L282
Useful? React with 👍 / 👎.
| #1774 — Responses 전용 encrypted annotation 누출 | ||
|
|
||
| V2 spawn_agent의 tool schema에 있는 encrypted: true가 generic openai-chat upstream으로 그대로 전달될 때, provider가 필수 message argument를 비워 반환하는 controlled A/B 재현이 있다. provider 이름별 예외가 아니라 protocol-boundary schema sanitizer로 처리해야 한다. |
There was a problem hiding this comment.
Move unfixed security triage out of devlog
This tracked _plan transcript records a controlled reproduction and patch strategy for unresolved defect #1774, with additional unresolved credential and routing boundary analysis later in the same file. Because devlog/ is public, pushing this commit publishes pre-disclosure working notes whose history is difficult to purge; keep the raw review in .tmp/ and commit only the fix and regression evidence once the affected defects are resolved.
AGENTS.md reference: AGENTS.md:L97-L104
Useful? React with 👍 / 👎.
Summary
The OpenAI and Anthropic browser SDKs send
X-Stainless-*request headers describing runtime and retry state. The CORS preflight answered with a fixedAccess-Control-Allow-Headerslist that never mentioned them, so the browser blocked the real request before it was ever sent — every browser SDK client was unable to reach the proxy.corsHeaders()now echoes the headers an already-allowed origin asked for, keeping the existing static list as the floor, and declares the dependency withVary: Origin, Access-Control-Request-Headers.The echo is gated on the origin check that already ran. It widens which headers an admitted caller may send, never which origins are admitted: a rejected origin is refused with 403 before any allow-list is built, and the preflight still carries no credential and produces no auth or account-pool side effect. Header names are normalized case-insensitively before de-duplication, so a caller re-requesting
content-typecannot duplicate the entry.Closes #1773
Verification
Run on the Linux validation host against this exact head:
bun x tsc --noEmitclean.New regression table-drives
/v1/messages,/v1/responses, and/v1/chat/completions: each must echo every requested Stainless header, preserve the static contract, avoid duplicatingcontent-type, and setVary. The rejected-origin case asserts 403 with no echoed header.Checklist
bun x tsc --noEmitcleanSummary by CodeRabbit
Bug Fixes
Tests