fix(security): harden MCP client against SSRF, env leakage, npx package exec, and oversized responses#9351
fix(security): harden MCP client against SSRF, env leakage, npx package exec, and oversized responses#9351rishipandey9399 wants to merge 1 commit into
Conversation
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
There was a problem hiding this comment.
Hey - I've found 1 issue, and left some high level feedback:
- The SSRF guard currently resolves hostnames on every call to
validate_mcp_url; consider caching resolution/allow/deny decisions per hostname (or URL) to avoid repeatedgetaddrinfocalls on hot paths. - In
_validate_stdio_argsfor package runners, the allowlist comparison is a strict string equality on non-flag args; if you expect commonnpxpatterns likepkg@versionor scoped commands, it may be worth normalizing or parsing package specs more flexibly so legitimate configurations are not inadvertently blocked.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The SSRF guard currently resolves hostnames on every call to `validate_mcp_url`; consider caching resolution/allow/deny decisions per hostname (or URL) to avoid repeated `getaddrinfo` calls on hot paths.
- In `_validate_stdio_args` for package runners, the allowlist comparison is a strict string equality on non-flag args; if you expect common `npx` patterns like `pkg@version` or scoped commands, it may be worth normalizing or parsing package specs more flexibly so legitimate configurations are not inadvertently blocked.
## Individual Comments
### Comment 1
<location path="tests/unit/test_mcp_client_security.py" line_range="61" />
<code_context>
+ assert "PATH" not in merged
+
+
+class TestNpxPackageAllowlist:
+ def test_unrestricted_by_default(self, monkeypatch):
+ monkeypatch.delenv("ASTRBOT_MCP_NPX_ALLOWED_PACKAGES", raising=False)
</code_context>
<issue_to_address>
**suggestion (testing):** Extend `TestNpxPackageAllowlist` to cover bunx/uvx and the case where only flags (no packages) are passed with an allowlist configured.
The allowlist logic is shared by `npx`, `bunx`, and `uvx`, but the tests only cover `npx`. Please add parallel cases for `bunx` and `uvx` so we verify all runners enforce the same restrictions. In addition, when an allowlist is set and the args consist only of flags (no non-`-`-prefixed entries), `_validate_stdio_args` currently raises; a specific test for this flags‑only scenario will ensure that behavior is preserved and that misconfigured invocations cannot skip package checks.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| assert "PATH" not in merged | ||
|
|
||
|
|
||
| class TestNpxPackageAllowlist: |
There was a problem hiding this comment.
suggestion (testing): Extend TestNpxPackageAllowlist to cover bunx/uvx and the case where only flags (no packages) are passed with an allowlist configured.
The allowlist logic is shared by npx, bunx, and uvx, but the tests only cover npx. Please add parallel cases for bunx and uvx so we verify all runners enforce the same restrictions. In addition, when an allowlist is set and the args consist only of flags (no non---prefixed entries), _validate_stdio_args currently raises; a specific test for this flags‑only scenario will ensure that behavior is preserved and that misconfigured invocations cannot skip package checks.
Fixes #9304
Modifications / 改动点
astrbot/core/agent/mcp_client.py的 MCP 客户端存在 4 个相关的安全问题(#9304),本 PR 逐一修复:astrbot/core/utils/ssrf_guard.pywithvalidate_mcp_url(), which resolves the configured MCP server hostname and rejects private/loopback/link-local/reserved/multicast addresses (blocks things like169.254.169.254,127.0.0.1,10.x,192.168.x). Wired into both_quick_test_mcp_connection()implementations (mcp_client.pyand the duplicate used by the dashboard's test-connection endpoint infunc_tool_manager.py) and intoMCPClient._do_connect()as defense in depth, so the real connection path is covered even independently of the quick test. Opt-out viaASTRBOT_MCP_ALLOW_PRIVATE_NETWORK_URLS=1for deployments that intentionally run MCP servers on an internal network.mcpSDK (>=1.8.0, as pinned) already restricts the default subprocess environment on non-Windows to a safe subset (get_default_environment()), but AstrBot's own_merge_environment_variables()explicitly copied all ofos.environ(API keys, tokens, etc.) into the subprocess environment on Windows. Replaced the full-environ copy with a curated_WINDOWS_SAFE_ENV_VARSallowlist (PATH,PATHEXT,SYSTEMROOT,TEMP, …) needed for executable resolution, preserving the intent of the original Windows fix (fix(windows): inherit all system environment variables into MCP runner for Windows #7054) without forwarding secrets to MCP subprocesses.npx/bunx/uvxviaASTRBOT_MCP_NPX_ALLOWED_PACKAGES(comma-separated package specs), matching the fix suggested in the issue. Disabled by default so existing configurations are unaffected.ASTRBOT_MCP_MAX_RESPONSE_TEXT_LENGTH, default 200k characters) that truncates oversized text blocks returned fromMCPTool.call(), guarding against memory/context exhaustion from a malicious or compromised MCP server.Tests added in
tests/unit/test_mcp_client_security.pycover the SSRF guard (blocking/allowing), the Windows env-merge fix (asserts secrets are not leaked), the npx allowlist (default-open, blocks when configured), and response truncation.Screenshots or Test Results / 运行截图或测试结果
Checklist / 检查清单
requirements.txtandpyproject.toml. / 我确保没有引入新依赖库,或者引入了新依赖库的同时将其添加到requirements.txt和pyproject.toml文件相应位置。🤖 Generated with Claude Code
Summary by Sourcery
Harden MCP client and related tooling against SSRF, environment-variable leakage, arbitrary package execution, and oversized tool responses.
Bug Fixes:
Tests: