fix: keep benchmark-range DNS blocked for provider fetches - #5608
qqzhangyanhua wants to merge 4 commits into
Conversation
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>
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>
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>
Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
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
| // 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)) |
There was a problem hiding this comment.
🔴 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.
|
The connect-time DNS-rebinding gap you're flagging is real, but it's pre-existing and broader than this PR: 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. |
|
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 |
Summary
baseURLvalues are fetched by the AI SDK, which does not usessrfSafeFetchor the connect-time SSRF dispatcher.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.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)198.18.0.12is rejected byvalidateProviderBaseUrl198.18.0.5after a public preflight fails withEAI_BLOCKED