Skip to content

fix(providers): pin Azure OpenAI/Anthropic endpoints to validated IP#5060

Merged
waleedlatif1 merged 5 commits into
stagingfrom
worktree-fix+azure-provider-ssrf-pinning
Jun 15, 2026
Merged

fix(providers): pin Azure OpenAI/Anthropic endpoints to validated IP#5060
waleedlatif1 merged 5 commits into
stagingfrom
worktree-fix+azure-provider-ssrf-pinning

Conversation

@waleedlatif1

Copy link
Copy Markdown
Collaborator

Summary

  • Fix TOCTOU DNS-rebinding SSRF in the Azure OpenAI and Azure Anthropic providers: both validated request.azureEndpoint with validateUrlWithDNS but then discarded the resolved IP and handed the raw hostname to the SDK/fetch, which re-resolved DNS at connect time — letting a short-TTL rebinding host pass validation (public IP) then connect to an internal IP (e.g. 169.254.169.254).
  • Add createPinnedFetch(resolvedIP) in input-validation.server.ts: a standard fetch routed through an undici Agent with a pinned DNS lookup, so the connection goes to the validated IP while the hostname is preserved for TLS SNI / Host header. Because the lookup always returns the validated IP, redirect hops can't rebind to an internal address either. Dispatchers are pooled by IP (LRU, max 64) for keep-alive reuse — same pattern as lib/mcp/pinned-fetch.ts.
  • Wire the pinned fetch into both providers, but only on the user-supplied-endpoint path: Azure Anthropic's new Anthropic({ fetch }), Azure OpenAI's chat-completions new AzureOpenAI({ fetch }), and both Responses-API configs via a new optional ResponsesProviderConfig.fetch (defaults to global fetch).
  • Trusted server-env endpoints and all non-Azure providers are unchanged.

Type of Change

  • Bug fix (security)

Testing

  • New createPinnedFetch unit suite (8 tests): pinned lookup returns the validated IP for any hostname (IPv4/IPv6), dispatcher forwarding + init preservation, pooling/eviction.
  • New Azure OpenAI + Azure Anthropic wiring suites (8 tests): user endpoint pins; trusted-env endpoint does not; blocked validation throws before any client/fetch.
  • typecheck clean, check:api-validation passes, full security + provider suites green (453 tests).

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

@vercel

vercel Bot commented Jun 15, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Jun 15, 2026 8:11pm

Request Review

@cursor

cursor Bot commented Jun 15, 2026

Copy link
Copy Markdown

PR Summary

High Risk
Changes outbound HTTP for user-controlled Azure endpoints and consolidates SSRF pinning used by MCP and LLM providers—security-sensitive, though scoped to the user-endpoint path with fail-closed validation.

Overview
Fixes a DNS-rebinding (TOCTOU) SSRF gap where Azure OpenAI and Azure Anthropic ran validateUrlWithDNS on user azureEndpoint values but still connected via the SDK/global fetch, which could re-resolve to a different IP at connect time.

Adds shared createPinnedFetch(resolvedIP) in input-validation.server.ts: undici Agent + pinned lookup so traffic uses the validated IP while keeping the original hostname for TLS SNI/Host. MCP transport, OAuth probe, and SSRF-guarded MCP fetch now call this helper instead of the removed createMcpPinnedFetch (and its per-IP agent pool) in lib/mcp/pinned-fetch.ts.

For user-supplied Azure endpoints only: after validation, providers require a resolvedIP, build createPinnedFetch, and pass it into the Anthropic client, AzureOpenAI chat completions, and Responses API via new optional ResponsesProviderConfig.fetch (used by executeResponsesProviderRequest instead of global fetch). Trusted env-configured endpoints are unchanged.

New/updated unit tests cover pinned fetch behavior and Azure pinning paths (pin vs env vs blocked vs missing IP).

Reviewed by Cursor Bugbot for commit 3b4ef1d. Configure here.

@greptile-apps

greptile-apps Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR closes a TOCTOU DNS-rebinding SSRF vulnerability in the Azure OpenAI and Azure Anthropic providers: both previously validated the user-supplied endpoint with validateUrlWithDNS but then discarded the resolved IP and passed the raw hostname to the SDK, allowing a short-TTL rebinding attack to switch from a public IP (passing validation) to an internal address at connect time.

  • Adds createPinnedFetch(resolvedIP) in input-validation.server.ts — wraps undici's fetch with an Agent whose DNS lookup function always returns the pre-validated IP, so every connection (including SDK-internal retries and redirects) is pinned to the safe address.
  • Wires the pinned fetch into both Azure providers on the user-supplied-endpoint path only; server-env endpoints remain untouched.
  • Consolidates the MCP client, OAuth probe, and revoke paths to use the same createPinnedFetch, removing the now-redundant createMcpPinnedFetch and its LRU pool from pinned-fetch.ts.

Confidence Score: 5/5

The change is safe to merge: the SSRF fix is correctly applied to all three Azure OpenAI code paths and to the Anthropic provider, the fail-closed guard prevents silent bypass when resolvedIP is absent, and trusted server-env endpoints are deliberately left untouched.

The core security logic — pinning DNS resolution via an undici Agent before handing the URL to the SDK — is sound and well-tested. All direct fetch calls in core.ts were replaced with fetchImpl, and both Azure providers propagate the pinned function through every branch. The only regression introduced is that createSsrfGuardedMcpFetch now creates a new Agent per request (losing the former LRU-pooled keep-alive reuse), but this affects only low-frequency MCP OAuth operations and carries no correctness or security impact.

apps/sim/lib/mcp/pinned-fetch.ts — the inner-closure placement of createPinnedFetch means MCP OAuth guard requests do not reuse connections; all other files are clean.

Important Files Changed

Filename Overview
apps/sim/lib/core/security/input-validation.server.ts Adds createPinnedFetch: creates an undici Agent with a pinned DNS lookup per call; correctly returns a standard fetch-compatible function with double-casts for type bridge.
apps/sim/lib/mcp/pinned-fetch.ts Removes createMcpPinnedFetch and its LRU agent pool; createSsrfGuardedMcpFetch now calls createPinnedFetch per-request inside the closure, losing cross-request connection reuse.
apps/sim/providers/azure-anthropic/index.ts Properly validates user endpoint, fails closed if resolvedIP is absent, and threads the pinned fetch into the Anthropic SDK client; trusted-env path is unchanged.
apps/sim/providers/azure-openai/index.ts Correctly propagates pinnedFetch to all three code paths (chat completions, Responses full-URL, Responses base-URL); fail-closed guard for missing resolvedIP is in place.
apps/sim/providers/openai/core.ts Adds optional fetch field to ResponsesProviderConfig; all three direct fetch(config.endpoint, …) calls correctly replaced with fetchImpl; global fetch fallback is sound.
apps/sim/lib/mcp/client.ts One-line import swap from createMcpPinnedFetch to createPinnedFetch; correct — Agent is created once per MCP client connection, so keep-alive reuse is preserved here.

Reviews (7): Last reviewed commit: "refactor(security): drop pinned-fetch ag..." | Re-trigger Greptile

Comment thread apps/sim/providers/azure-anthropic/index.ts Outdated
Comment thread apps/sim/providers/azure-openai/index.ts Outdated
Comment thread apps/sim/lib/core/security/input-validation.server.ts Outdated
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

Thanks @greptile-apps — addressed in c8c4867:

  • resolvedIP! non-null assertion (P2, both providers): replaced with an explicit fail-closed guard — if validation passes but no resolvedIP is present, we throw rather than calling createPinnedFetch(undefined). For a security fix, failing closed (no request) is the correct behavior since proceeding unpinned would reintroduce the SSRF. Added test coverage for this branch in both provider suites.
  • Duplicated pool/pinned-fetch logic vs lib/mcp/pinned-fetch.ts (P2): intentionally deferred. Consolidating would require createMcpPinnedFetch to import the shared helper from input-validation.server.ts, which breaks lib/mcp/pinned-fetch.test.ts (it mocks that module to export only createPinnedLookup). The duplication is ~15 lines and self-contained; I'd rather not expand the blast radius of a security PR with a cross-module refactor. Happy to do it as a follow-up.

@greptile-apps

greptile-apps Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Both fixes look correct and well-reasoned.

The fail-closed guard is exactly right — if validateUrlWithDNS ever returns isValid: true with no resolvedIP (edge case in a future code path, or an unexpected resolver behavior), throwing is strictly safer than proceeding unpinned and silently reintroducing the TOCTOU window. The error messages are also clear enough to diagnose in practice.

The duplication deferral rationale holds. Touching lib/mcp/pinned-fetch.ts imports in a security PR just to save ~15 lines would drag in test-mock changes and expand review surface for no security benefit. A follow-up issue to track the consolidation would be worth filing so it doesn't quietly accumulate.

Both concerns addressed — LGTM.

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile review

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor 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.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit c8c4867. Configure here.

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile review

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor 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.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 081d0e4. Configure here.

@waleedlatif1 waleedlatif1 changed the title fix(providers): pin Azure OpenAI/Anthropic endpoints to validated IP (TOCTOU SSRF) fix(providers): pin Azure OpenAI/Anthropic endpoints to validated IP Jun 15, 2026
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile review

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@waleedlatif1 waleedlatif1 force-pushed the worktree-fix+azure-provider-ssrf-pinning branch from 081d0e4 to 112734e Compare June 15, 2026 19:51

@cursor cursor 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.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 112734e. Configure here.

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile review

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor 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.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 112734e. Configure here.

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile review

@waleedlatif1 waleedlatif1 force-pushed the worktree-fix+azure-provider-ssrf-pinning branch from 112734e to 3b4ef1d Compare June 15, 2026 20:11
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor 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.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 3b4ef1d. Configure here.

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile review

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor 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.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 3b4ef1d. Configure here.

@waleedlatif1 waleedlatif1 merged commit 02022e9 into staging Jun 15, 2026
15 checks passed
@waleedlatif1 waleedlatif1 deleted the worktree-fix+azure-provider-ssrf-pinning branch June 15, 2026 20:59
waleedlatif1 added a commit that referenced this pull request Jun 16, 2026
…er node:net in client bundle

The browser build broke with `Cannot find module 'node:net'`. Server-only
SSRF code in `input-validation.server.ts` (`dns/promises`, and since PR #5060
`undici` → `node:net`/`node:tls`) is statically reachable from the client
bundle via the tool/connector registries, which the workflow editor imports
for metadata. Node networking builtins have no browser shim, so Turbopack
cannot compile them for the client.

Two changes:

1. Split each connector's client-safe declarative metadata into a sibling
   `meta.ts` (`<name>ConnectorMeta`), mirroring the `XBlockMeta` /
   `BLOCK_META_REGISTRY` pattern. `connectors/registry.ts` is now the
   client-safe `CONNECTOR_META_REGISTRY` (+ `getConnectorMeta` /
   `getAllConnectorMeta`); the full registry with runtime fns moves to
   `connectors/registry.server.ts`. Client components consume the meta
   registry; the sync engine and knowledge API routes consume the server
   registry. This removes connectors from the client's server-only graph.
   Connector metadata is byte-for-byte identical before/after; runtime fns
   are untouched.

2. Extend the existing #4899 `turbopack.resolveAlias` browser stub — which
   already mapped `dns`/`dns/promises` to an empty module for the browser —
   to also cover `net`/`tls` (+ `node:` variants), since `undici` now pulls
   those in. The remaining tool/provider definitions still reach
   `input-validation.server` server-side; the browser-only stub keeps those
   Node builtins out of the client bundle while the real modules stay on the
   server, so SSRF validation and IP pinning are unaffected.

Connector authoring/validation skills updated to teach the meta.ts split.
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.

1 participant