fix(web): run URL SSRF checks off the event loop in async paths

Add async_is_safe_url() wrapping is_safe_url via asyncio.to_thread, and route
all async SSRF call sites through it: web_extract_tool, the vision/video
preflight checks, and both download redirect guards. socket.getaddrinfo blocks;
calling it inline from async tool paths froze the event loop for the duration of
DNS resolution.

vision_tools: split _validate_image_url into _image_url_shape_ok (no DNS) +
sync _validate_image_url (for sync callers/tests) + async _validate_image_url_async.

Widened beyond the original PR #3691 to sibling async sites that also blocked
the loop (second redirect guard, video preflight).

Salvage of #3691 by @Kewe63 — surgically re-applied onto current main because
the original branch was too stale to cherry-pick cleanly (would have reverted
the web_crawl_tool refactor).

Co-authored-by: Kewe63 <kewe.3217@gmail.com>
This commit is contained in:
kewe63
2026-06-04 18:04:47 -07:00
committed by Teknium
parent 46b2afc56b
commit c60952ba94
7 changed files with 72 additions and 31 deletions
+4 -2
View File
@@ -372,7 +372,8 @@ class TestVisionDispatchLoopSafety:
side_effect=lambda url, dest, **kw: _write_fake_image(dest),
),
patch(
"tools.vision_tools._validate_image_url",
"tools.vision_tools._validate_image_url_async",
new_callable=AsyncMock,
return_value=True,
),
patch(
@@ -416,7 +417,8 @@ class TestVisionDispatchLoopSafety:
side_effect=lambda url, dest, **kw: _write_fake_image(dest),
),
patch(
"tools.vision_tools._validate_image_url",
"tools.vision_tools._validate_image_url_async",
new_callable=AsyncMock,
return_value=True,
),
patch(