1. Embedding model: support custom provider
- Add "custom" entry to EMBEDDING_VENDORS with supports_dim_param=False
- Parse custom:<id> credentials and model fallback in agent_initializer
- Expand custom_providers as custom:<id> entries in Web UI dropdown
2. Vision model: support custom provider
- Add custom:<id> routing in _route_by_provider_id
- Add _build_custom_provider reading credentials from custom_providers
- Expand custom_providers in Web UI dropdown, add validation in _set_vision
3. Fix memory_get Windows path validation bug!
- str.startswith(path+'/') always False on Windows due to backslashes, So All Users can not use "memory_get" tool in Windows.
- Use os.path.realpath + os.sep, consistent with MemoryService
4. Fix historical needsModel:false bug preventing embedding provider switch
- Change embedding needsModel to true in console.js
- Support custom:<id> resolution in cow_cli /memory status, also for adding custom provider support
Closes#2908, Closes#2880
web_fetch fetched any http/https URL a model emitted, checking only the
scheme. It performed requests.get(..., allow_redirects=True) with no
hostname resolution check and no private/loopback/link-local/cloud-metadata
filtering, and never re-validated redirect targets. A model (including one
under prompt injection) could make CowAgent fetch 127.0.0.1, RFC1918,
169.254.169.254 or other internal endpoints and return their bodies into
the conversation; a public URL could also 302-bounce into a private target.
The repo already shipped an SSRF validator for the vision tool
(Vision._validate_url_safe). Extract that logic into a shared helper
(agent/tools/utils/url_safety.py) and reuse it:
- execute() now validates the URL before dispatching to either fetch path.
- A new _safe_get() helper disables auto-redirect and follows redirects
manually, re-validating every hop so a public URL cannot bounce into an
internal address. Both the webpage and document fetch paths use it.
- Vision._validate_url_safe now delegates to the shared helper (public API
unchanged), so both URL-consuming tools share one guard.
Stdlib only (ipaddress, socket, urllib.parse); no new dependency. Adds
tests/test_security_ssrf_web_fetch.py covering loopback, cloud-metadata,
RFC1918 and a public->loopback redirect.
Sink: agent/tools/web_fetch/web_fetch.py (_fetch_webpage / _fetch_document).
Signed-off-by: christop <825583681@qq.com>
1. Vision SSRF (#2878, #2872):
Add _validate_url_safe() that resolves the target hostname via DNS and
rejects any IP in private (RFC1918), loopback, link-local, or reserved
ranges before requests.get() is called. This blocks attacks that use
attacker-controlled image URLs to probe internal services or cloud
metadata endpoints (169.254.169.254).
2. Skill install path traversal (#2873):
Add _safe_skill_dir() that validates the skill name cannot escape the
skills/ root directory. Rejects names containing '..', absolute paths,
and any resolved path that falls outside the custom_dir boundary.
Applied to _add_url(), _add_package(), and delete().
Both fixes include comprehensive unit tests (19 test cases) covering
blocked patterns, edge cases, and allowed legitimate usage.
Closes#2878Closes#2873
Ref: #2872
- Add call_vision method to all bot implementations (DashScope, Claude,
Gemini, ZhipuAI, MiniMax, Doubao, Moonshot, OpenAICompatibleBot)
using each vendor's native multimodal API format
- Remove call_with_tools/call_vision from Bot base class to fix MRO
shadowing issue with OpenAICompatibleBot mixin
- Refactor vision tool provider resolution: MainModel → other configured
models (auto-discovered) → OpenAI → LinkAI, with automatic fallback
- Return actual model name used in call_vision responses
- Sync config.json API keys to .env bidirectionally on startup
- Fix bot instance cache to detect bot_type/use_linkai config changes
- Add SSE reconnection support for web console
- Preserve image path hints in Gemini text for correct vision tool calls
- Update docs/tools/vision.mdx