Skip to content

fix: keep benchmark-range DNS blocked for provider fetches - #5608

Open
qqzhangyanhua wants to merge 4 commits into
BuilderIO:mainfrom
qqzhangyanhua:fix/fake-ip-provider-endpoint-ssrf
Open

qqzhangyanhua wants to merge 4 commits into
BuilderIO:mainfrom
qqzhangyanhua:fix/fake-ip-provider-endpoint-ssrf

Conversation

@qqzhangyanhua

@qqzhangyanhua qqzhangyanhua commented Sep 22, 2026

Copy link
Copy Markdown

Summary

  • Custom provider baseURL values are fetched by the AI SDK, which does not use ssrfSafeFetch or the connect-time SSRF dispatcher.
  • Validation therefore keeps rejecting a hostname whose DNS answer is in 198.18.0.0/15. Allowing that answer would let a user-controlled name pass the check and later connect to a reserved address, including Clash/mihomo fake-ip space when that range is actually reachable.
  • Literal addresses in that range, and connect-time answers in that range, stay blocked as well.

Test plan

  • pnpm --filter @agent-native/core exec vitest run src/extensions/url-safety.spec.ts src/agent/engine/provider-endpoint-validation.spec.ts (36 passed)
  • A DNS answer of 198.18.0.12 is rejected by validateProviderBaseUrl
  • A connect-time answer of 198.18.0.5 after a public preflight fails with EAI_BLOCKED

Clash/mihomo fake-ip rewrites public hostnames into 198.18.0.0/15, so the SSRF guard rejected legitimate custom provider URLs. Keep blocking that range only when it is written literally.

Co-authored-by: Cursor <cursoragent@cursor.com>
builder-io-integration[bot]

This comment was marked as outdated.

Saving a provider URL can ignore a 198.18.0.0/15 DNS answer, but the TCP SSRF dispatcher dials that address and still cannot tell fake-ip from a real target.

Co-authored-by: Cursor <cursoragent@cursor.com>
builder-io-integration[bot]

This comment was marked as outdated.

Provider base URLs are fetched by the AI SDK, which does not use the SSRF dispatcher. A hostname that resolves into 198.18.0.0/15 must fail validation instead of being stored and dialed later.

Co-authored-by: Cursor <cursoragent@cursor.com>
@qqzhangyanhua qqzhangyanhua changed the title fix: accept public endpoints when DNS returns fake-ip fix: keep benchmark-range DNS blocked for provider fetches Sep 22, 2026
Co-authored-by: Cursor <cursoragent@cursor.com>

@builder-io-integration builder-io-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Builder reviewed your changes and found 1 potential issue 🔴

Review Details

Incremental Code Review Summary

The latest commit correctly restores benchmark-range blocking in both the shared URL safety logic and provider endpoint validation. The two earlier review comments covering those explicit exemptions have been resolved. The added tests also cover rejecting a 198.18.0.0/15 preflight answer and blocking the same range at connect time.

Risk: High — this remains security-sensitive SSRF and DNS validation code.

New Finding

  • 🔴 High: Provider endpoint validation still performs only a one-time DNS preflight, while the validated URL is later sent through the AI SDK’s normal fetch path without the connect-time SSRF dispatcher. A hostname can resolve publicly during validation and rebind to loopback, metadata, or another private address before the model request; the actual provider transport must use the guarded dispatcher or pin and authorize the validated address.

No UI code changed, so browser verification is not applicable.

🧪 Browser testing: Skipped — PR only modifies backend/security utility code and tests, no UI impact

Comment on lines +29 to 32
// Keep 198.18.0.0/15 blocked here. The saved URL is fetched by the AI SDK,
// which does not use the SSRF dispatcher, so a hostname that resolves into
// that range would be dialed.
(await isBlockedExtensionUrlWithDns(normalized))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔴 Protect provider requests against DNS rebinding

This performs a one-time DNS preflight, but the validated URL is later fetched by the AI SDK without the connect-time SSRF dispatcher. The hostname can resolve publicly here and rebind to loopback, metadata, or another private address before the model request; use the guarded dispatcher for the provider transport or pin and authorize the resolved address.

Additional Info
This is distinct from the resolved benchmark-range exemption comments: those comments were fixed by restoring the default closed behavior.

Fix in Builder

@qqzhangyanhua

Copy link
Copy Markdown
Author

The connect-time DNS-rebinding gap you're flagging is real, but it's pre-existing and broader than this PR: validateProviderBaseUrl()'s one-time preflight was never backed by ssrfSafeFetch/createSsrfSafeDispatcher on the actual provider request path, for any custom endpoint — not just the 198.18.0.0/15 case this PR touches. Confirmed unchanged on main at aba8c52 before this PR's diff.

Keeping this PR scoped to the fake-ip false positive (both prior findings on that are fixed and tested). Tracking the connect-time guard for provider requests separately in #5610.

Copy link
Copy Markdown
Contributor

Thanks for the PR, and for opening #5610 for the broader DNS-rebinding issue. I agree that should stay a separate follow-up.

For #5608, could you align the PR with the behavior we want to merge? The current diff adds tests and comments, while the 198.18.0.0/15 block already exists in the base, so runtime behavior is unchanged. If the intent is to lock in the fail-closed behavior, please frame it as regression coverage and update the title and changeset. If the goal is to fix the fake-IP false positive, please include the runtime change and tests that preserve the SSRF protection.

This branch has not been deployed

No deployments
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.

2 participants