Skip to content

fix(oauth): bound OrcaRouter key-exchange responses - #4159

Open
luvs01 wants to merge 2 commits into
lidge-jun:devfrom
luvs01:agent/orcarouter-bounded-response-20260910
Open

fix(oauth): bound OrcaRouter key-exchange responses#4159
luvs01 wants to merge 2 commits into
lidge-jun:devfrom
luvs01:agent/orcarouter-bounded-response-20260910

Conversation

@luvs01

@luvs01 luvs01 commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Summary

OrcaRouter's successful browser-login key exchange used response.json(), materializing the whole body before validating the key and accepting replacement decoding for malformed UTF-8. Read at most 64 KiB through the existing raw-byte primitive, then require valid UTF-8 JSON. Reuse the same existing 30-second signal for response headers and body consumption. Non-abort body failures retain a fixed error without reflecting upstream text or an error cause; cancellation preserves the original reason.

The first commit also closes the previously documented shared byte-reader follow-up needed by this consumer: when a signal is already aborted, readBoundedResponseBytes now attempts to cancel the original body before rejecting, matching the existing text-body reader. It does not attach a reader or wait for cancellation that rejects or never settles. The second commit adds the OrcaRouter limit, its regression matrix and eight localized documentation paragraphs. That public report establishes prior disclosure, not maintainer approval of this implementation.

Existing PKCE, origin/private-network consent, key/user identity, omitted-versus-invalid scope and terminal reauthentication behavior are retained. The new size limit applies to the successful login key-exchange body, not inference payloads. Tests use synthetic responses and local callback fixtures; this PR makes no claim of a live provider authentication test or a proven native-fetch connection leak.

Verification

  • Current head: 9e28465c26d4429cbfcb764152e904a68b1c807f, based on dev 386b6a0d9a8acef818b9c40ebd472e4974750199.
  • Both authored bounded-reader/OrcaRouter patches rebased unchanged. Earlier focused validation passed 78 tests / 510 assertions, with a 425-page documentation build. These synthetic fixtures are not a live provider login test.
  • Current-head author cross-platform CI run 34439096989: 26/26 jobs passed, bound to 9e28465c26d4429cbfcb764152e904a68b1c807f. The checklist CI attestation refers to this completed matrix; local focused results are listed separately.
  • Historical large local runs, where mentioned previously, remain incomplete diagnostic evidence and are not reported as green.

Security review

The maintainer-sponsored label is applied. Independent source review found no blocker. Maintainer merge approval remains separate.

Checklist

  • Scope stays focused and avoids unrelated cleanup.
  • Docs or release notes were updated when needed.
  • Security-sensitive changes were reviewed for secrets, auth, and unsafe defaults.

Review readiness checklist

  • All CI tests are green on my local testing.
  • I pushed my PR to the latest dev commit.
  • I resolved all correct Codex and CodeRabbit findings.
  • My PR is ready for review.

Readiness base check: 7 commits behind current dev; within the repository allowance of ten.

@coderabbitai

coderabbitai Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 918a6e19-97b3-4171-8848-c57b696f9ef0

📥 Commits

Reviewing files that changed from the base of the PR and between a7509fe and d9fbb1e.

📒 Files selected for processing (12)
  • docs-site/src/content/docs/fr/guides/providers.md
  • docs-site/src/content/docs/guides/providers.md
  • docs-site/src/content/docs/ja/guides/providers.md
  • docs-site/src/content/docs/ko/guides/providers.md
  • docs-site/src/content/docs/ru/guides/providers.md
  • docs-site/src/content/docs/tr/guides/providers.md
  • docs-site/src/content/docs/zh-cn/guides/providers.md
  • docs-site/src/content/docs/zh-tw/guides/providers.md
  • src/lib/bounded-body.ts
  • src/oauth/orcarouter.ts
  • tests/providers/orcarouter-provider.test.ts
  • tests/server/bounded-body.test.ts

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


📝 Walkthrough

Walkthrough

The OrcaRouter OAuth key exchange now limits response bodies to 64 KiB, applies shared timeout and cancellation handling, validates UTF-8 and JSON, masks body-read errors, and documents the behavior in localized provider guides.

Changes

OrcaRouter OAuth validation

Layer / File(s) Summary
Bounded body cancellation
src/lib/bounded-body.ts, tests/server/bounded-body.test.ts
Pre-aborted byte reads cancel the response body without waiting, preserve the abort reason, avoid pulling data, and leave the stream unlocked.
OAuth exchange response validation
src/oauth/orcarouter.ts, tests/providers/orcarouter-provider.test.ts
The exchange uses one 30-second signal for headers and body processing. It rejects bodies above 65,536 bytes, invalid UTF-8, malformed JSON, and body-read failures without exposing underlying errors. Tests cover cancellation, exact limits, stream cancellation, and timeout propagation.
Provider guide documentation
docs-site/src/content/docs/{guides,fr,ja,ko,ru,tr,zh-cn,zh-tw}/guides/providers.md
Localized guides document the response limit, timeout coverage, rejection behavior, inference scope, and unchanged scope validation.

Priority: ⬇️ Low

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

Suggested reviewers: lidge-jun, invalid-email-address

Sequence Diagram(s)

sequenceDiagram
  participant OrcaRouterOAuthFlow
  participant fetch
  participant readBoundedResponseBytes
  participant TextDecoder
  OrcaRouterOAuthFlow->>fetch: Send key exchange with shared 30-second signal
  fetch-->>OrcaRouterOAuthFlow: Return headers and response body
  OrcaRouterOAuthFlow->>readBoundedResponseBytes: Read at most 65536 bytes
  readBoundedResponseBytes-->>OrcaRouterOAuthFlow: Return bytes or abort/error
  OrcaRouterOAuthFlow->>TextDecoder: Decode with fatal UTF-8 validation
  TextDecoder-->>OrcaRouterOAuthFlow: Return JSON text
  OrcaRouterOAuthFlow->>OrcaRouterOAuthFlow: Parse JSON before saving the key
Loading

Merge Risk: ⚪ Minimal · up to d9fbb

OrcaRouter browser login now safely limits and validates token-exchange responses before saving credentials, while preserving cancellation behavior. The current change is ready to merge.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 4 functions across 4 files. (8 skipped: 8… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the primary change: limiting OrcaRouter OAuth key-exchange response bodies. It matches the implementation, tests, and documentation updates.
Full details: Docstring Coverage

Explanation

Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 4 functions across 4 files. (8 skipped: 8 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

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

@github-actions github-actions Bot added bug Something isn't working intake: hygiene-blocked Deterministic PR hygiene checks failed labels Sep 9, 2026
@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

⚠️ Deterministic hygiene checks failed.

  • unsponsored_surface — This changes an authentication, workflow, release-automation, or dependency surface. MAINTAINERS.md requires security review for these; ask a maintainer to apply maintainer-sponsored once they have reviewed it. Paths: src/oauth/orcarouter.ts.

@luvs01

luvs01 commented Sep 9, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

Please review immutable head d9fbb1e2a6804d117f316ccc35fb50a30252f63f, based on a7509fe00032f74d38ca073dcf701b90353b8283. The two commits cover the previously documented raw-byte-reader pre-abort cleanup follow-up and the OrcaRouter success-body bound. Check cancellation reason/cleanup ownership, non-reflection of non-abort body errors, exactly 64 KiB raw-byte admission, strict UTF-8 decoding, one 30-second header/body deadline, and retained key/user/scope/origin/reauthentication contracts. Tests use synthetic upstream responses and local callback fixtures; no live authentication or native-fetch leak claim is made. Focused suites passed (78 tests / 510 assertions), typecheck/privacy and eight-locale documentation validation passed. Full author CI is queued. Use available plain-text inspection without requiring nl; maintainer security review remains required before merge.

@coderabbitai

coderabbitai Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

head_commit='d9fbb1e2a6804d117f316ccc35fb50a30252f63f'
base_commit='a7509fe00032f74d38ca073dcf701b90353b8283'

printf '%s\n' '== checked-out revision =='
git rev-parse HEAD
printf '%s\n' '== requested commits =='
git cat-file -t "$head_commit"
git cat-file -t "$base_commit"

printf '%s\n' '== changed files =='
git diff --name-status "$base_commit" "$head_commit"

printf '%s\n' '== changed-file diff with zero context =='
git diff --no-ext-diff --unified=0 "$base_commit" "$head_commit" -- .

printf '%s\n' '== candidate implementation and test files =='
fd -t f . | rg -i '(orca|oauth|response|bounded|byte|reader|body|callback)'

printf '%s\n' '== symbol references =='
rg -n -i -C 3 \
  'readBoundedResponseBytes|OrcaRouter|TextDecoder|AbortSignal|timeout|key.exchange|keyExchange|scope|reauth' \
  . \
  -g '!node_modules/**' -g '!dist/**' -g '!build/**'

Length of output: 193112


🏁 Script executed:

#!/bin/bash
set -euo pipefail

head_commit='d9fbb1e2a6804d117f316ccc35fb50a30252f63f'
base_commit='a7509fe00032f74d38ca073dcf701b90353b8283'

printf '%s\n' '== revision check =='
printf 'HEAD: '; git rev-parse HEAD
printf 'base ancestor: '; git merge-base --is-ancestor "$base_commit" "$head_commit" && printf 'yes\n'
printf '%s\n' '== commits =='
git log --format='%H %s' --reverse "$base_commit..$head_commit"

printf '%s\n' '== changed paths =='
git diff --name-status "$base_commit" "$head_commit"

printf '%s\n' '== changed TypeScript paths =='
git diff --name-only "$base_commit" "$head_commit" -- '*.ts' '*.tsx'

printf '%s\n' '== TypeScript diff =='
git diff --no-ext-diff --unified=80 "$base_commit" "$head_commit" -- '*.ts' '*.tsx'

printf '%s\n' '== direct helper and OrcaRouter references outside devlog =='
rg -n -C 8 \
  'readBoundedResponseBytes|OrcaRouter|orca.?router|new TextDecoder|TextDecoder\(' \
  src tests \
  -g '*.ts' -g '*.tsx' 2>/dev/null || true

Length of output: 201430


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '== source outlines =='
ast-grep outline src/lib/bounded-body.ts --items all
ast-grep outline src/oauth/orcarouter.ts --items all

printf '%s\n' '== bounded byte reader implementation =='
match_line=$(rg -n 'function readBoundedResponseBytes|export async function readBoundedResponseBytes' src/lib/bounded-body.ts | head -1 | cut -d: -f1)
start=$((match_line > 30 ? match_line - 30 : 1))
end=$((match_line + 240))
sed -n "${start},${end}p" src/lib/bounded-body.ts

printf '%s\n' '== OrcaRouter exchange implementation =='
match_line=$(rg -n 'exchangeToken\s*\(' src/oauth/orcarouter.ts | head -1 | cut -d: -f1)
start=$((match_line > 60 ? match_line - 60 : 1))
end=$((match_line + 240))
sed -n "${start},${end}p" src/oauth/orcarouter.ts

printf '%s\n' '== exact source diff =='
git diff --no-ext-diff --unified=20 \
  a7509fe00032f74d38ca073dcf701b90353b8283 \
  d9fbb1e2a6804d117f316ccc35fb50a30252f63f \
  -- src/lib/bounded-body.ts src/oauth/orcarouter.ts

printf '%s\n' '== relevant test ranges =='
sed -n '440,575p' tests/server/bounded-body.test.ts
sed -n '240,405p' tests/providers/orcarouter-provider.test.ts

Length of output: 38517


🏁 Script executed:

#!/bin/bash
set -euo pipefail

head_commit='d9fbb1e2a6804d117f316ccc35fb50a30252f63f'
base_commit='a7509fe00032f74d38ca073dcf701b90353b8283'

printf '%s\n' '== OrcaRouter deadline helper and imports =='
sed -n '1,55p' src/oauth/orcarouter.ts

printf '%s\n' '== relevant exact line locations =='
rg -n \
  'cancelBodyWithoutWaiting|readBoundedResponseBytes|cancelWithoutWaiting|requestSignal|TOKEN_REQUEST_TIMEOUT_MS|BOUNDED_BODY_MAX_BYTES|TextDecoder\("utf-8", \{ fatal: true \}\)' \
  src/lib/bounded-body.ts src/oauth/orcarouter.ts

printf '%s\n' '== complete changed path list =='
git diff --name-status "$base_commit" "$head_commit"

printf '%s\n' '== whitespace validation =='
git diff --check "$base_commit" "$head_commit"

printf '%s\n' '== source-only change summary =='
git diff --stat "$base_commit" "$head_commit" -- src/lib/bounded-body.ts src/oauth/orcarouter.ts tests/server/bounded-body.test.ts tests/providers/orcarouter-provider.test.ts

Length of output: 6501


@luvs01 Review complete for d9fbb1e2a6804d117f316ccc35fb50a30252f63f against a7509fe00032f74d38ca073dcf701b90353b8283.

I found no actionable issue in the inspected changes.

  • src/lib/bounded-body.ts:134 cancels a pre-aborted original body before it rejects. It passes the original abort reason. It does not attach a reader or await cancellation.
  • src/lib/bounded-body.ts:208 cancels the reader on overflow or read failure. It observes rejected reads and releases the reader lock.
  • src/oauth/orcarouter.ts:151 creates one request signal. The same signal controls response headers and body consumption.
  • src/oauth/orcarouter.ts:176-185 applies the exact 65,536-byte limit. A body at the limit is accepted. The first byte over the limit causes cancellation and rejection.
  • src/oauth/orcarouter.ts:180-182 preserves the abort reason. Non-abort body-read failures map to the fixed non-reflective JSON error.
  • src/oauth/orcarouter.ts:189 uses TextDecoder("utf-8", { fatal: true }). Invalid UTF-8 cannot be silently replaced before JSON parsing.
  • The changed tests cover pre-abort cleanup, cancellation during reading, exact-limit admission, overflow without tail draining, malformed UTF-8, shared deadline identity, and body-error non-reflection.
  • git diff --check completed without whitespace errors.

The full CI result is still pending. Maintainer security review is still required before merge readiness.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@lidge-jun

Copy link
Copy Markdown
Owner

리뷰 · 우선순위 55 / 80

이 PR은 OrcaRouter 브라우저 로그인 키 교환이 성공 응답을 response.json()으로 통째로 읽던 자리를, 기존 raw-byte 헬퍼 readBoundedResponseBytes로 바꿔 최대 64 KiB(BOUNDED_BODY_MAX_BYTES)만 받고 UTF-8을 fatal로 파싱하게 만든다. 지금 devsrc/oauth/orcarouter.ts OrcaRouterOAuthFlow.exchangeToken은 헤더까지는 30초 시그널을 쓰지만, 본문은 길이·인코딩 한도 없이 JSON으로 펼친다. 악의적이거나 깨진 업스트림이 큰 바디·잘못된 UTF-8·스트림 꼬리를 붙이면 메모리·디코드 치환·로그 반사 위험이 남는다. 이 변경은 그 성공 경로만 조이면서, HTTP 실패 때 바디를 반사하지 않던 기존 계약과 “invalid JSON” 고정 문구(원인/업스트림 텍스트 미포함)를 유지한다.

같이 고친 src/lib/bounded-body.ts는 시그널이 이미 abort된 채 raw-byte 읽기가 들어오면, 리더를 붙이기 전에 원본 body.cancel을 시도하고 기다리지 않는다. 이건 #2398 쪽에서 문서화해 둔 공유 바이트 리더 follow-up이고, OrcaRouter 소비자가 그 갭을 실제로 밟아서 같이 닫은 형태다. 헤더 fetch와 바디 소비가 같은 requestSignal(30초)을 쓰도록 시그널을 한 번만 만들어 넘긴다. 회귀는 tests/providers/orcarouter-provider.test.ts에 64KiB 경계·초과·오픈 스트림 중단·malformed UTF-8·헤더 후 abort·바디 중 abort·단일 데드라인·바디 실패 비반사까지 넣고, tests/server/bounded-body.test.ts에 pre-abort cancel 행렬을 추가했다. providers 가이드 8개 로케일에 한도 문단도 넣었다.

현재 dev 방향(round-2 Lane A/B, 서비스 격리 #4152 등)과 직접 겹치지는 않지만, OAuth 키 교환은 보안 표면이라 위생 가치가 있다. 라벨 intake: hygiene-blocked가 이미 붙어 있고, hygiene 봇이 src/oauth/orcarouter.ts 변경에 maintainer-sponsored를 요구한다. 작성자도 Draft + 메인테이너 보안 리뷰 전제라고 적었다.

src/oauth/orcarouter.ts (exchangeToken catch) - readBoundedResponseBytes 실패를 abort가 아니면 전부 “invalid JSON”으로 접는다. 의도는 비반사인데, 한도 초과는 catch 밖 oversized 분기로 따로 나가므로 지금 구조는 맞다. 다만 타임아웃/네트워크성 바디 실패도 같은 문구로 보이니, 운영 디버깅 때 구분이 어렵다는 점은 알고 가면 좋다.
src/lib/bounded-body.ts (pre-abort cancel) - cancel이 reject/pending이어도 기다리지 않는 계약은 테스트로 고정됐다. 다른 소비자가 “cancel 완료를 await한다”고 가정하면 깨질 수 있으니, 이 헬퍼 주석/계약을 그대로 유지해야 한다.
docs-site/.../guides/providers.md (8로케일) - 문서만으로 동작이 바뀌진 않지만, 영문·번역 문단이 CI drift 게이트에 걸릴 수 있다. #4151 README drift 계열과 같은 계열 리스크다.
라벨 intake: hygiene-blocked - 코드 품질과 별개로 머지 전에 maintainer-sponsored가 필요하다. 스폰서 없이 Ready만 올려도 게이트에 막힌다.
Draft / CI - exact-head CI·자동 리뷰가 끝날 때까지 Draft 유지 선언과 맞다. 로컬 “All CI green” 체크는 비어 있다.

메인테이너의 판단이 필요한 지점

  • OAuth 표면이라 maintainer-sponsored를 붙일지(보안 리뷰 통과 여부)
  • 64 KiB가 키 페이로드에 충분한지, 아니면 OrcaRouter 실응답 상한을 문서/업스트림과 한번 더 맞출지
  • Lane A/B 배달과 병렬로 지금 스폰서할지, hygiene 백로그로 미룰지

너의 추천
보안 의도와 테스트 밀도는 좋다. Draft 유지한 채 exact-head CI를 보고, 메인테이너가 orcarouter.ts 교환 경로를 한 번 확인한 뒤 maintainer-sponsored를 붙여 머지하면 된다. 범위는 키 교환 바운드 + shared reader pre-abort에 국한된 채 유지하고, 다른 OAuth 프로바이더로 일반화하는 확장은 이 PR에 넣지 말 것.

이 댓글은 grok-bot이 작성했습니다

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

✅ READY

  • all PR quality gates passed; the review readiness checklist is complete.

Review readiness checklist

  • ✅ All CI tests are green on my local testing.
  • ✅ I pushed my PR to the latest dev commit.
  • ✅ I resolved all correct Codex and CodeRabbit findings.
  • ✅ My PR is ready for review.

4/4 boxes ticked.

This pull request is already Ready for Review.
The review-ready label marks this PR as ready; review automation runs independently.
Maintainers: @lidge-jun @Ingwannu

@Ingwannu Ingwannu left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Completed the requested bounded authentication-response review at this exact head. The successful key exchange is admitted by raw byte count before UTF-8 decode/JSON parse, uses one header/body deadline, and retains the key/user/scope validators. Non-abort read failures use a fixed message; pre-aborted reads cancel without acquiring a reader or waiting on cancellation. The fixture matrix actually observes byte boundaries, cancellation reason identity, lock release and non-reflection. No change was found to the credential destination, PKCE, or omitted-versus-invalid scope contract.

Author CI 34412474002 is successful for this head. The changed production files also have no base-to-current-dev delta at inspection. I am applying maintainer-sponsored for this reviewed authentication change. This is a scoped security review, not a live-provider login test, blanket repository audit, or merge. Keep required repository checks and the readiness/independent integration decision separate.

@Ingwannu Ingwannu added the maintainer-sponsored Maintainer sponsors this change to an auth, workflow, release, or dependency surface label Sep 10, 2026
@github-actions github-actions Bot removed the intake: hygiene-blocked Deterministic PR hygiene checks failed label Sep 10, 2026
@luvs01
luvs01 force-pushed the agent/orcarouter-bounded-response-20260910 branch from d9fbb1e to 4f709f1 Compare September 10, 2026 03:07
@luvs01
luvs01 marked this pull request as ready for review September 10, 2026 03:34
@luvs01
luvs01 requested a review from lidge-jun as a code owner September 10, 2026 03:34
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 10, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-10T05:56:43.751980Z 9e28465 Draft marked ready
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@luvs01
luvs01 force-pushed the agent/orcarouter-bounded-response-20260910 branch from 4f709f1 to 9e28465 Compare September 10, 2026 04:56
@github-actions
github-actions Bot marked this pull request as draft September 10, 2026 04:56
@luvs01
luvs01 marked this pull request as ready for review September 10, 2026 05:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working maintainer-sponsored Maintainer sponsors this change to an auth, workflow, release, or dependency surface review-ready

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants