T09 · Insecure Skill Coding Practices
- Location
scripts/mcp_server.py:342- Finding
Unrestricted URL Fetching Enables Server-Side Request Forgery
- Content
View full analysis
Vulnerability Details
File Location:
scripts/mcp_server.py:342-368, with the request sink inscripts/page_fetcher.py:66-92and redirect handling inscripts/http_client.py:213-220
Vulnerability Type: Server-Side Request Forgery (SSRF)
Risk Level: MediumVulnerable Code
scripts/mcp_server.py:342-368:python async def _tool_fetch(args: Dict[str, Any], config: Dict[str, Any]) -> Dict[str, Any]: """执行 fetch 工具""" url = args.get("url", "") if not url: return {"error": "url 参数必填"} timeout = args.get("timeout", 10) try: from page_fetcher import fetch_and_extract except ImportError: from .page_fetcher import fetch_and_extract # 读取代理配置 proxy = None privacy_cfg = (config.get("privacy", {}) or {}) strict_cfg = privacy_cfg.get("strict", {}) or {} proxy = strict_cfg.get("proxy", None) try: _html, text = fetch_and_extract(url, timeout=timeout, proxy=proxy) if text: return { "url": url, "text": text, "success": True, "length": len(text), }scripts/page_fetcher.py:66-92:python def fetch_page(url: str, timeout: int = 10, proxy: Optional[str] = None) -> Optional[str]: """ 抓取网页 HTML 复用 http_client 的 UA 池/代理/重试 """ import asyncio try: import aiohttp from http_client import RequestContext, fetch_text, build_connector except ImportError: from .http_client import RequestContext, fetch_text, build_connector import aiohttp ctx = RequestContext( headers={}, proxy=proxy, timeout=timeout, ) async def _do(): async with aiohttp.ClientSession(connector=build_connector(1)) as session: return await fetch_text(session, url, ctx) try: return asyncio.run(_do()) except Exception: return Nonescripts/http_client.py:213-220:text ...[truncated 2866 chars]- Remediation
View remediation
Remediation Suggestions
- Accept only explicitly supported schemes, preferably
httpsand, where necessary,http. Reject user-information URLs and all other schemes. - Resolve the destination hostname before connecting and reject every address in loopback, private, link-local, multicast, reserved, unspecified, and documentation ranges for both IPv4 and IPv6.
- Disable automatic redirects or validate every redirect target with the same scheme, hostname, port, and resolved-address policy.
- Mitigate DNS rebinding by ensuring the validated address is the address actually used for the connection. Revalidate after DNS cache expiration and on every redirect.
- Apply an explicit destination-port policy and block common internal administration and metadata-service destinations.
- Treat URLs obtained from search results and caller-supplied synthesis objects as untrusted. Consider limiting automatic synthesis retrieval to validated public URLs from expected search results.
- Before forwarding fetched content to an LLM provider, clearly enforce the configured disclosure policy and avoid forwarding content retrieved from non-public destinations.
- Add regression tests covering direct loopback URLs, private IPv4 ranges, IPv6 loopback and unique-local ranges, link-local metadata addresses, alternate textual IP encodings, DNS rebinding scenarios, and public-to-private redirects.
- Accept only explicitly supported schemes, preferably
