Vendored deer-flow upstream (bytedance/deer-flow) plus prompt-injection hardening: - New deerflow.security package: content_delimiter, html_cleaner, sanitizer (8 layers — invisible chars, control chars, symbols, NFC, PUA, tag chars, horizontal whitespace collapse with newline/tab preservation, length cap) - New deerflow.community.searx package: web_search, web_fetch, image_search backed by a private SearX instance, every external string sanitized and wrapped in <<<EXTERNAL_UNTRUSTED_CONTENT>>> delimiters - All native community web providers (ddg_search, tavily, exa, firecrawl, jina_ai, infoquest, image_search) replaced with hard-fail stubs that raise NativeWebToolDisabledError at import time, so a misconfigured tool.use path fails loud rather than silently falling back to unsanitized output - Native client back-doors (jina_client.py, infoquest_client.py) stubbed too - Native-tool tests quarantined under tests/_disabled_native/ (collect_ignore_glob via local conftest.py) - Sanitizer Layer 7 fix: only collapse horizontal whitespace, preserve newlines and tabs so list/table structure survives - Hardened runtime config.yaml references only the searx-backed tools - Factory overlay (backend/) kept in sync with deer-flow tree as a reference / source See HARDENING.md for the full audit trail and verification steps.
94 lines
2.9 KiB
Python
94 lines
2.9 KiB
Python
"""Core behavior tests for MCP client server config building."""
|
|
|
|
import pytest
|
|
|
|
from deerflow.config.extensions_config import ExtensionsConfig, McpServerConfig
|
|
from deerflow.mcp.client import build_server_params, build_servers_config
|
|
|
|
|
|
def test_build_server_params_stdio_success():
|
|
config = McpServerConfig(
|
|
type="stdio",
|
|
command="npx",
|
|
args=["-y", "my-mcp-server"],
|
|
env={"API_KEY": "secret"},
|
|
)
|
|
|
|
params = build_server_params("my-server", config)
|
|
|
|
assert params == {
|
|
"transport": "stdio",
|
|
"command": "npx",
|
|
"args": ["-y", "my-mcp-server"],
|
|
"env": {"API_KEY": "secret"},
|
|
}
|
|
|
|
|
|
def test_build_server_params_stdio_requires_command():
|
|
config = McpServerConfig(type="stdio", command=None)
|
|
|
|
with pytest.raises(ValueError, match="requires 'command' field"):
|
|
build_server_params("broken-stdio", config)
|
|
|
|
|
|
@pytest.mark.parametrize("transport", ["sse", "http"])
|
|
def test_build_server_params_http_like_success(transport: str):
|
|
config = McpServerConfig(
|
|
type=transport,
|
|
url="https://example.com/mcp",
|
|
headers={"Authorization": "Bearer token"},
|
|
)
|
|
|
|
params = build_server_params("remote-server", config)
|
|
|
|
assert params == {
|
|
"transport": transport,
|
|
"url": "https://example.com/mcp",
|
|
"headers": {"Authorization": "Bearer token"},
|
|
}
|
|
|
|
|
|
@pytest.mark.parametrize("transport", ["sse", "http"])
|
|
def test_build_server_params_http_like_requires_url(transport: str):
|
|
config = McpServerConfig(type=transport, url=None)
|
|
|
|
with pytest.raises(ValueError, match="requires 'url' field"):
|
|
build_server_params("broken-remote", config)
|
|
|
|
|
|
def test_build_server_params_rejects_unsupported_transport():
|
|
config = McpServerConfig(type="websocket")
|
|
|
|
with pytest.raises(ValueError, match="unsupported transport type"):
|
|
build_server_params("bad-transport", config)
|
|
|
|
|
|
def test_build_servers_config_returns_empty_when_no_enabled_servers():
|
|
extensions = ExtensionsConfig(
|
|
mcp_servers={
|
|
"disabled-a": McpServerConfig(enabled=False, type="stdio", command="echo"),
|
|
"disabled-b": McpServerConfig(enabled=False, type="http", url="https://example.com"),
|
|
},
|
|
skills={},
|
|
)
|
|
|
|
assert build_servers_config(extensions) == {}
|
|
|
|
|
|
def test_build_servers_config_skips_invalid_server_and_keeps_valid_ones():
|
|
extensions = ExtensionsConfig(
|
|
mcp_servers={
|
|
"valid-stdio": McpServerConfig(enabled=True, type="stdio", command="npx", args=["server"]),
|
|
"invalid-stdio": McpServerConfig(enabled=True, type="stdio", command=None),
|
|
"disabled-http": McpServerConfig(enabled=False, type="http", url="https://disabled.example.com"),
|
|
},
|
|
skills={},
|
|
)
|
|
|
|
result = build_servers_config(extensions)
|
|
|
|
assert "valid-stdio" in result
|
|
assert result["valid-stdio"]["transport"] == "stdio"
|
|
assert "invalid-stdio" not in result
|
|
assert "disabled-http" not in result
|