Skip to content

fix(oauth): thread scoped fetch through high-level clients#2390

Open
bokelley wants to merge 1 commit into
mainfrom
codex/scoped-fetch-high-level
Open

fix(oauth): thread scoped fetch through high-level clients#2390
bokelley wants to merge 1 commit into
mainfrom
codex/scoped-fetch-high-level

Conversation

@bokelley

Copy link
Copy Markdown
Contributor

Summary

  • thread scoped fetch through AdCPClient, TaskExecutor, ProtocolClient, OAuth refresh and discovery, MCP/A2A, comply, and storyboard paths
  • define scoped fetch as a trusted/pinned network-policy boundary and avoid cross-policy cache reuse
  • cover high-level OAuth, static MCP, client-credentials, A2A, and storyboard paths with tests that fail if global fetch is used

Root cause

PR #2381 exposed the hook at the low-level transport, but high-level callers could not consistently supply or retain it across discovery, authentication, and protocol calls.

Validation

  • npm run build:lib
  • npm run typecheck
  • 34 focused scoped-fetch and SSRF tests
  • 153 affected tests from the implementation pass

Fixes #2386
Fixes adcontextprotocol/adcp#5962

@bokelley
bokelley enabled auto-merge (squash) July 23, 2026 20:15
@bokelley bokelley changed the title Thread scoped fetch through high-level clients fix(oauth): thread scoped fetch through high-level clients Jul 23, 2026
.filter(t => t.inputSchema?.properties)
.map(t => [t.name, t.inputSchema!.properties as Record<string, unknown>])
);
if (!usesScopedFetch) this.cachedToolSchemas = discoveredToolSchemas;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Medium: Under scoped fetch, this.cachedToolSchemas is never populated, which breaks the invariant at L2243 (adaptRequest "runs after detectServerVersion so cachedCapabilities/tool schemas are populated"). executeTaskdetectServerVersiongetCapabilities (which returns without caching here) → adaptRequest reads this.cachedToolSchemas (L2287) and finds nothing, so it takes the fail-open path: no undeclared-field stripping and no v2.5 adapter alias reconciliation. That reconciliation is exactly what prevents the documented Wonderstruck rejection (brand_manifest string into a brand object slot, L2295). A hosted runner using scoped fetch against a v2/v2.5 seller loses it silently. Thread discoveredToolSchemas into the adapt path per call rather than skipping it entirely — tenant isolation only requires not caching, not discarding the schema for the current call.

@aao-secretariat aao-secretariat Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Ladon verdict: Approve

Approve — request-scoped fetchFn threaded cleanly through the high-level clients, OAuth discovery/refresh, MCP/A2A calls, and compliance/storyboard runners.

What I checked:

  • SSRF boundary is handled honestly: ssrfSafeFetch still validates scheme + resolved address and marks connectionPinned: false when the caller's trustedFetchFn assumes DNS pinning, with delegation documented at the public TransportOptions.fetchFn surface.
  • Multi-tenant isolation: caches are correctly bypassed under scoped fetch.
  • Changeset present (.changeset/scoped-fetch-high-level.md) covering the src/lib/** changes.
  • No witness-not-translator violations, no reimplemented HTTP/SSE transport.

Medium findings

  • src/lib/core/SingleAgentClient.ts:4058 — Scoped fetch skips cachedToolSchemas, disabling adaptRequest field-stripping and v2.5 alias reconciliation. A quality/adaptation degradation on the scoped-fetch path, worth a follow-up.

Decision path: no critical/high findings (row 1 no); gated_paths false (row 2 no); high_risk true but all reasons are (modified) — no (deleted) (row 3 no); the single medium finding is category tool-schema/adaptation, not data-loss/schema/infra (row 4 no); high_risk (modified) + medium would fire row 5 — this is a modified high-risk file (SingleAgentClient.ts)... note: the medium finding is on SingleAgentClient.ts, which is NOT in the high_risk_reasons list (auth, protocols/* are). Row 5 requires a medium finding AND a modified high-risk-path file; the medium is not on a high-risk-path file, so row 5 does not fire. No prior escalate (row 6 no); no team gate (row 7 no); only 1 medium finding, under the 3-medium threshold (row 8 no). Falls through to row 9 → approve.

Medium findings

  • src/lib/core/SingleAgentClient.ts:4058 — Scoped fetch skips cachedToolSchemas, disabling adaptRequest field-stripping and v2.5 alias reconciliation

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(oauth): thread scoped fetch through AdCPClient and testing runners agent OAuth: inject safe fetch into SDK-managed refresh

1 participant