fix(msteams): token refresh hangs past deadline when DNS preflight stalls#111317
fix(msteams): token refresh hangs past deadline when DNS preflight stalls#111317hugenshen wants to merge 1 commit into
Conversation
|
Codex review: needs maintainer review before merge. Reviewed July 19, 2026, 11:38 AM ET / 15:38 UTC. Summary PR surface: Source +33, Tests +58. Total +91 across 3 files. Reproducibility: yes. from source and posted exact-head proof: a never-resolving guarded DNS lookup on the delegated refresh path now rejects with Review metrics: none identified. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Next step before merge
Security Review detailsBest possible solution: Land the focused guard-owned timeout fix after the usual merge-refresh check, retaining the regression test that proves preflight stalls abort before an HTTP request is dispatched. Do we have a high-confidence way to reproduce the issue? Yes, from source and posted exact-head proof: a never-resolving guarded DNS lookup on the delegated refresh path now rejects with Is this the best way to solve the issue? Yes: using the shared SSRF guard’s own AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against a53461b2b155. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +33, Tests +58. Total +91 across 3 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
|
Added standalone exact-head runtime proof (non-Vitest) on PR body updated with the transcript. @clawsweeper re-review |
|
@clawsweeper re-review Please finish review against the standalone exact-head non-Vitest proof already posted and reflected in the PR body ( Standalone proof comment: #111317 (comment) |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
What Problem This Solves
Fixes an issue where Microsoft Teams delegated token refresh / exchange could hang past the 10s token-fetch deadline when SSRF DNS/proxy preflight never completes.
fetchMSTeamsTokensonly aborted viaAbortSignal.timeout()oninit.signal, which does not cover guarded-fetch preflight, so a stalled lookup can leave the Teams channel unable to refresh auth and stay connected.Why This Change Was Made
Pass
MSTEAMS_DEFAULT_TOKEN_FETCH_TIMEOUT_MSas top-levelfetchWithSsrFGuard({ timeoutMs }), matching siblinggraph.ts/ Graph attachment callers and the post-#105549 guard-owned timeout contract. RemoveAbortSignal.timeout()fromRequestInit. Optional test-onlyfetchImpl/lookupFn/timeoutMshooks let the refresh entry point exercise the real guarded-fetch owner without mocking it away.User Impact
Before: Teams token refresh could hang indefinitely during DNS/proxy preflight despite the 10s token-fetch budget, leaving the channel disconnected after token expiry.
After: Refresh/exchange rejects with
TimeoutError/request timed outwhen preflight stalls, and HTTPfetchis never called.Evidence
bb797ba40c8751caaa17bc6aa7e23d2e13da1949extensions/msteams/src/oauth.token.tsrefreshMSTeamsDelegatedTokens→fetchMSTeamsTokens→fetchWithSsrFGuard({ timeoutMs })with never-resolvinglookupFnextensions/msteams/src/oauth.token.preflight-timeout.test.ts+ updatedoauth.test.ts(asserts top-leveltimeoutMs, noinit.signal)graph.tsalready forwards guardtimeoutMsUnit + preflight proof (exit 0)
Standalone exact-head runtime proof (exit 0, non-Vitest)
PROOF_ROOT=. PROOF_HEAD=$(git rev-parse HEAD) PROOF_TIMEOUT_MS=80 \ node --import tsx /tmp/msteams-token-preflight-timeout-proof.mjsReal behavior proof
Behavior or issue addressed: MS Teams token refresh/exchange no longer hangs when SSRF DNS/proxy preflight never completes; deadline is owned by
fetchWithSsrFGuardtimeoutMs.Canonical reachability path: Teams auth refresh →
refreshMSTeamsDelegatedTokens→fetchMSTeamsTokens→fetchWithSsrFGuard({ timeoutMs: MSTEAMS_DEFAULT_TOKEN_FETCH_TIMEOUT_MS })→ preflight lookup abort before HTTP dispatch.Boundary crossed: Azure AD token endpoint DNS/proxy preflight → local guarded-fetch timeout →
TimeoutError(request timed out).Shared helper / provider constraint check: Reuses
fetchWithSsrFGuardtop-leveltimeoutMs(notinit.signal/AbortSignal.timeout()). Aligns withextensions/msteams/src/graph.tsand fix(feishu): pass timeoutMs through app-registration guarded fetch #105549. Default remainsMSTEAMS_DEFAULT_TOKEN_FETCH_TIMEOUT_MS(10s). No new config/env.Real environment tested: macOS, Node via
node --import tsxstandalone harness on exact headbb797ba40c8751caaa17bc6aa7e23d2e13da1949(plus Vitest regression).Exact steps or command run after this patch:
PROOF_ROOT=. PROOF_HEAD=$(git rev-parse HEAD) PROOF_TIMEOUT_MS=80 node --import tsx /tmp/msteams-token-preflight-timeout-proof.mjsEvidence after fix: Standalone transcript above:
timed_out=true name=TimeoutError message=request timed out elapsed_ms=112 fetch_called=0thenPASS.Observed result after fix: Stalled preflight rejects with
TimeoutError/request timed outbefore HTTP dispatch (fetch_called=0).What was not tested: Live Azure AD token refresh with real tenant credentials; full 10s production-floor duration (proof uses an 80ms stand-in).
Fix classification: Root cause fix
AI-assisted (Cursor)
I understand what the code does
Change is focused and does not mix unrelated concerns