fix(desktop): validate generic oauth token responses - #5511
Conversation
|
📌 Same maintainer note applies here — full version on #5509. #5507, #5509, and this PR are the desktop arm of the same fix series (path-boundary / strict-parse / validation). Please fold them into a single "desktop hardening" PR where it makes sense, and reuse existing shared helpers rather than adding new per-file copies (duplication list is in the #5509 comment). The empty- Thanks! 🙏 |
|
@qwen-code /triage |
|
Thanks for the PR! Template looks good ✓ — all required sections present, bilingual, clear test plan. On direction: this is a straightforward correctness/security fix — On approach: the scope feels right for what it claims — +212/-13 across 2 files, focused entirely on OAuth token response validation. New helpers ( One note: the existing maintainer comment on this PR already flags the consolidation ask across #5507/#5509/#5511. That's a process concern, not a correctness one — this PR stands on its own merits. Whether to merge it separately or fold it into a broader desktop-hardening PR is a maintainer call. Moving on to code review. 🔍 中文说明感谢贡献! 模板完整 ✓ —— 所有必填段落齐全,双语,测试计划清晰。 方向:这是一个直接的正确性/安全修复 —— 方案:对于所声称的目标来说范围合理 —— 2 个文件,+212/-13,完全聚焦于 OAuth token response 校验。新增的 helper( 注意:此 PR 上已有的维护者评论提出了 #5507/#5509/#5511 合并的要求。这是流程上的考虑,不是正确性问题 —— 本 PR 本身是有价值的。是单独合并还是并入更广泛的 desktop-hardening PR,由维护者决定。 进入代码审查 🔍 — Qwen Code · qwen3.7-max |
Code ReviewBefore reading the diff, my independent proposal for fixing "generic OAuth accepts malformed token responses" would have been:
The PR's approach matches and exceeds this baseline. The helpers are small, focused, and private to the file. The No critical blockers. No AGENTS.md violations. The code is minimal, focused, and does exactly what the PR says it does. TestingThis PR modifies internal OAuth token response validation — not user-facing TUI behavior. There's no way to drive OAuth token exchange through the CLI without a real OAuth provider, so unit tests and type/lint checks are the appropriate verification here. All green — 10/10 tests pass, typecheck clean, prettier clean. 中文说明代码审查在读 diff 之前,我对"generic OAuth 接受格式错误的 token response"这个问题的独立方案是:
PR 的方案与基线一致并有所超越。helper 函数小而专注,文件内私有。 无关键阻塞。无 AGENTS.md 违规。代码最小、聚焦,完全实现了 PR 所声称的功能。 测试本 PR 修改的是内部 OAuth token response 校验逻辑,非用户可见的 TUI 行为。无法在没有真实 OAuth provider 的情况下通过 CLI 驱动 OAuth token exchange,因此单元测试和类型/lint 检查是合适的验证方式。 全部通过 —— 10/10 测试通过,typecheck 干净,prettier 干净。 — Qwen Code · qwen3.7-max |
|
Stepping back to look at the whole picture. This is a clean, focused security fix. The bug is real — My independent proposal before reading the diff was essentially the same approach — the PR didn't miss anything obvious, and it added a couple of niceties I wouldn't have thought of (the The existing maintainer comment flags consolidation across #5507/#5509/#5511 as a process preference. That's fair — but it's a grouping decision, not a code quality concern. This PR stands on its own merits. Approving. ✅ 中文说明退后一步看全貌。 这是一个干净、聚焦的安全修复。bug 是真实存在的 —— 读 diff 前我的独立方案基本是一样的思路 —— PR 没有遗漏明显的东西,还加了一些我不会想到的细节( 已有的维护者评论提出了 #5507/#5509/#5511 合并的流程偏好。这合理 —— 但这是归组决定,不是代码质量问题。本 PR 本身是有价值的。 批准 ✅ — Qwen Code · qwen3.7-max |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship. ✅
What this PR does
access_token.access_tokenvalidation for generic OAuth refresh responses.expires_infrom JSON and form-encoded token responses without partial parsing.expires_in: 0as an immediate expiry instead of treating it as missing.Why it's needed
SourceCredentialManager.exchangeAndStore()savesresult.accessToken!after a successful OAuth exchange. The generic OAuth exchange path could returnsuccess: trueeven when the token response was missingaccess_token, which could mark a source authenticated with an invalid credential.The generic OAuth path also used
parseIntforexpires_in, so malformed values like"3600abc"were accepted as3600. Token lifetimes should only accept whole, safe, non-negative seconds.Reviewer Test Plan
How to verify
exchangeGenericOAuth: missing or emptyaccess_tokenshould returnsuccess: false.refreshGenericOAuthToken: missing or emptyaccess_tokenshould still throw.expires_inparsing: JSON numbers and form-encoded strings should be accepted only when they are safe non-negative integers.expires_in: 0: it should produce an immediateexpiresAt, notundefined.bun test packages/desktop/packages/shared/src/auth/__tests__/generic-oauth.test.tsfrom the repo root.bun run typecheck:sharedfrompackages/desktop.npx prettier --check packages/desktop/packages/shared/src/auth/generic-oauth.ts packages/desktop/packages/shared/src/auth/__tests__/generic-oauth.test.ts.git diff --check.Evidence (Before & After)
Before: a generic OAuth token exchange response like
{"refresh_token":"r"}could be treated as successful, andexpires_in: "3600abc"was accepted as3600. After: missingaccess_tokenfails exchange/refresh, malformedexpires_inis rejected, and valid JSON/form-encoded token responses keep working.Tested on
Environment (optional)
Local desktop workspace after
bun install --cwd packages/desktop --frozen-lockfile; rootnpm ci --ignore-scriptswas used for tooling.Risk & Scope
access_token;expires_in, when present, must be a whole non-negative second count.Linked Issues
Fixes #5510
中文说明
What this PR does
access_token时直接拒绝。access_token校验。expires_in做严格解析,不再部分解析。expires_in: 0为立即过期,而不是当成缺失。Why it's needed
SourceCredentialManager.exchangeAndStore()会在 OAuth exchange 成功后保存result.accessToken!。generic OAuth exchange 路径以前即使 token response 缺少access_token,也可能返回success: true,从而把 source 标记为已认证并保存无效 credential。generic OAuth 还用
parseInt解析expires_in,所以"3600abc"这类异常值会被当成3600。token lifetime 应该只接受安全、非负、完整的秒数整数。Reviewer Test Plan
How to verify
exchangeGenericOAuth:缺少或为空的access_token应该返回success: false。refreshGenericOAuthToken:缺少或为空的access_token仍应该抛错。expires_in解析:JSON number 和 form-encoded string 只有在安全非负整数时才接受。expires_in: 0:应该生成立即过期的expiresAt,而不是undefined。git diff --check。Evidence (Before & After)
修复前:
{"refresh_token":"r"}这类 generic OAuth exchange response 可能被当作成功,expires_in: "3600abc"会被当成3600。修复后:缺少access_token的 exchange/refresh 会失败,格式错误的expires_in会被拒绝,合法 JSON/form-encoded token response 继续可用。Tested on
Environment (optional)
本地 desktop workspace,已运行
bun install --cwd packages/desktop --frozen-lockfile;为了 tooling 也运行了 rootnpm ci --ignore-scripts。Risk & Scope
access_token;如果返回expires_in,必须是完整的非负秒数整数。Linked Issues
Fixes #5510
AI Assistance Disclosure
I used Codex to review the changes, sanity-check the implementation against existing patterns, and help spot potential edge cases.