fix(ai): resolve the Kimi OAuth host from trusted env only - #3311
Conversation
Yeachan-Heo
left a comment
There was a problem hiding this comment.
Verdict: APPROVE — exact head 361522d031f3e2059032c5f8b638ae053e70b06f
External successor to closed #3278 (housekeeping-only closure; original verdict MERGE_READY, widest blast radius in that batch — the refresh-token-carrying OAuth-exchange call — independently re-verified here).
Checks performed:
- Content identity confirmed:
git diff 6789f2c5<#3278 exact head> 361522d0<#3311 exact head> -- packages/ai/src/utils/oauth/kimi.tsis empty — byte-identical to the version #3278's review validated (traced all threeresolveOAuthHost()call sites including therefresh_token-carrying exchange, verified alias precedence and empty-skip semantics preserved exactly via$pickCredentialEnv, zero credential logging across ten throw sites). - Rebase safety via three-way merge simulation: same pre-#3285 merge-base pattern as #3309/#3310;
git merge-treeconfirms clean landing of onlykimi.ts+ its own test/changelog files, no reversion of the merged env-trust refactor. - PR's own diff is 4 files / 113 lines, fully scoped to the OAuth host trust boundary.
- CI terminal on exact head: 16 SUCCESS, 5 SKIPPED, 0 pending, 0 failing.
- Mergeability:
MERGEABLE, clean pergit merge-tree. - No merge performed — read-only lane.
—
[repo owner's gaebal-gajae (clawdbot) 🦞]
361522d to
d3bf4c5
Compare
Yeachan-Heo
left a comment
There was a problem hiding this comment.
Verdict: APPROVE — exact head d3bf4c53b1bdab8076d4342aa6924bc7f1a16274
Rebase-only refresh of the prior exact-head review at 361522d031f3e2059032c5f8b638ae053e70b06f (APPROVE).
Checks performed:
- Content identity confirmed, not assumed:
diff <(git show 361522d0 --format= -- packages/ai/src/utils/oauth/kimi.ts packages/ai/test/fixtures/kimi-oauth-host-probe.ts packages/ai/test/kimi-oauth-host-trust.test.ts) <(git show d3bf4c53 --format= -- packages/ai/src/utils/oauth/kimi.ts packages/ai/test/fixtures/kimi-oauth-host-probe.ts packages/ai/test/kimi-oauth-host-trust.test.ts)is empty — the PR's own commit is byte-for-byte identical to the version the prior review validated. CHANGELOG.md is intentionally excluded from this file set: its diff context shifted because independently-mergeddevcommits inserted new entries above the same insertion point, which is expected rebase churn, not a content change to this PR's own line. - Rebase mechanics verified:
git merge-baseconfirms this PR carries the same single commit both before and after the rebase; every additional commit between the two heads is an independently-mergeddevcommit (own-PR-numbered), not new work folded into this PR. - Exact-head terminal CI: 16 success / 0 failure / 0 pending / 5 skipped (total 21). No pending/queued checks remain.
Prior technical analysis still applies verbatim to this head.
—
[repo owner's gaebal-gajae (clawdbot) 🦞]
d3bf4c5 to
4e700e7
Compare
4e700e7 to
a1026d9
Compare
resolveOAuthHost() read KIMI_CODE_OAUTH_HOST / KIMI_OAUTH_HOST through $env, the merged view that includes the caller's cwd/.env. That host is used for: :100 POST /api/oauth/device_authorization device flow start :169 POST /api/oauth/token authorization-code exchange :233 POST /api/oauth/token refresh, sending the refresh token So repository content could redirect the login flow: show the user an attacker-hosted verification URL, receive the code exchange, and receive the existing refresh token on the next refresh. Resolve through $pickCredentialEnv, the resolver this codebase already uses for material that must not come from the project .env. Precedence between the two names is preserved, and the launching shell plus GJC/user-owned .env files keep working. Regression: a child process with a controlled cwd proves a planted .env cannot set either name, both inherited names still apply, the primary name stays ahead of the legacy alias, and a planted .env cannot override an inherited host. Reverting the resolver fails exactly the two planted cases.
a1026d9 to
6d852a5
Compare
Yeachan-Heo
left a comment
There was a problem hiding this comment.
Verdict: APPROVE — exact head 6d852a5882e649b1f1e294f857cab36d035d6491.
Security/trust revalidation:
- The implementation and regression files are byte-identical to the previously approved head;
resolveOAuthHost()now uses$pickCredentialEnv, preserving primary/legacy alias precedence and inherited/user-owned configuration while excluding the caller project's.env. - All three credential-bearing OAuth destinations use the resolver: device authorization, authorization-code exchange, and refresh-token exchange.
- The semantic rebase lands on current
devparent4805960e2474322014ddb2b2a4f9661bd1063256; the stale release note was moved from released0.11.11toUnreleased. - Local focused trust test: 7/7 passed.
packages/aitypecheck passed. Biome and diff checks passed. - Exact-head CI is terminal: every reported check is SUCCESS or SKIPPED, including the focused Kimi trust test and
check:@gajae-code/ai. - Mergeability is
MERGEABLE/CLEAN, and the remote PR head was re-pinned immediately before this verdict.
No credential values were printed or persisted during review.
— GJC security/trust review, 2026-07-28
Successor to #3278, reopened per your triage note:
#3278 could not be reopened directly — GitHub returns 422 once the head has been force-pushed — so this is a fresh PR on the same branch, rebased onto current
dev.Why it should still land
resolveOAuthHost()readKIMI_CODE_OAUTH_HOST/KIMI_OAUTH_HOSTthrough$env. That host serves the device-authorization request the user is sent to (:100), the code exchange (:169), and the refresh call that carries the existing refresh token (:233).Your review on #3278 reached MERGE_READY after independently reproducing the boundary with your own probe rather than reusing mine, and recorded that the surface closure was total. No technical blocker was raised; the later
CHANGES_REQUESTEDwas the batch-triage note quoted above.State on this head
Rebased onto current
dev(zero commits behind), suite re-run green after the rebase. Everything else is byte-identical to the head you reviewed.If the no-merge lane was a deliberate policy call for external contributions rather than housekeeping, say so and I will stop resubmitting this class rather than keep adding noise.