Skip to content

[security] fix(app): validate stored MCP tool URLs#6826

Merged
c121914yu merged 1 commit intolabring:mainfrom
Hinotoi-agent:fix/mcp-url-ssrf-guards
Apr 28, 2026
Merged

[security] fix(app): validate stored MCP tool URLs#6826
c121914yu merged 1 commit intolabring:mainfrom
Hinotoi-agent:fix/mcp-url-ssrf-guards

Conversation

@Hinotoi-agent
Copy link
Copy Markdown
Contributor

Summary

This PR hardens the MCP tool URL boundary so the same internal-address validation is enforced when MCP URLs are saved and when stored MCP tools are executed from workflows.

  • Adds a shared MCP URL guard around the existing isInternalAddress() policy.
  • Reuses that guard on direct MCP preview/run endpoints.
  • Adds missing validation to MCP tool create/update paths before URLs are persisted.
  • Adds runtime defense-in-depth before workflow execution connects to stored MCP URLs.
  • Adds focused tests for blocking localhost MCP endpoints while allowing public endpoints.

Security issues covered

Issue Impact Severity
Stored MCP tool URLs were not validated consistently before persistence and workflow execution An authenticated user with MCP toolset permissions could persist an internal MCP endpoint and later cause the backend workflow runner to connect to it Medium

Before this PR

  • Direct MCP preview/run API paths checked isInternalAddress(url) before connecting to a user-supplied MCP server.
  • MCP tool create/update paths accepted and stored url without the same internal-address validation.
  • Workflow execution loaded stored MCP toolset URLs and constructed MCPClient instances without re-checking the stored URL.
  • The direct-call SSRF guard and stored workflow execution path could drift apart.

After this PR

  • MCP URL validation lives behind a shared assertMCPUrlNotInternal() helper.
  • Direct MCP preview/run endpoints continue to enforce the existing internal-address policy through that shared helper.
  • MCP tool create/update endpoints reject internal URLs before storing toolset configuration.
  • Workflow MCP execution paths revalidate stored URLs before connecting, covering pre-existing or indirectly inserted stored data.
  • Tests lock in localhost rejection and public endpoint allowance for the shared guard.

Why this matters

MCP server URLs are server-side network destinations. If a stored MCP URL is allowed to point at localhost, metadata services, or other blocked internal destinations, a later workflow run can make the FastGPT backend connect across a network boundary that the direct MCP preview/run endpoints already attempted to protect.

This patch keeps the existing FastGPT SSRF policy consistent across both admission-time and runtime MCP paths.

How this differs from #6640

PR #6640 added SSRF checks to the direct MCP and HTTP tool run/preview API paths.

This PR addresses the remaining stored-configuration and workflow-runtime variant:

  • mcp ssrf check #6640 guarded direct MCP requests in mcpTools/getTools.ts and mcpTools/runTool.ts.
  • This PR also validates MCP URLs before create/update persistence.
  • This PR also validates stored MCP URLs immediately before workflow execution constructs an MCPClient.

Both layers matter because saved tool configuration can outlive the original request path and can be executed later by workflow dispatch code.

Attack flow

Authenticated user with MCP toolset write permission
    -> creates or updates an MCP toolset with an internal URL
        -> workflow execution loads the stored toolset URL
            -> backend MCP client connects to the internal destination

Affected code

Issue Files
Inconsistent MCP URL validation projects/app/src/pages/api/core/app/mcpTools/create.ts, projects/app/src/pages/api/core/app/mcpTools/update.ts, packages/service/core/workflow/dispatch/child/runTool.ts, packages/service/core/workflow/dispatch/ai/agent/sub/tool/index.ts
Shared validation helper packages/service/core/app/mcp.ts
Regression coverage test/cases/service/core/app/mcp.test.ts

Root cause

Stored MCP URL validation drift:

  • The direct MCP request endpoints used the internal-address check, but the stored MCP configuration write paths did not.
  • Workflow execution trusted stored MCP URLs without applying the same network-boundary policy at runtime.

CVSS assessment

Issue CVSS v3.1 Vector
Stored MCP URL internal-address bypass 6.5 Medium CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:L

Rationale:

  • The issue requires authenticated access with permission to create or manage an MCP toolset.
  • The impact depends on what internal services are reachable from the FastGPT backend and what the MCP response path exposes.
  • The patch is intentionally scoped to enforcing the existing internal-address policy consistently rather than introducing a new network policy.

Safe reproduction steps

  1. Run a FastGPT instance before this patch.
  2. Authenticate as a user who can create or manage MCP toolsets.
  3. Submit an MCP toolset create or update request with a URL such as http://localhost:3000/mcp or another destination that isInternalAddress() would reject.
  4. Observe that the create/update path can store the URL even though direct MCP preview/run paths reject the same URL.
  5. Add that stored MCP tool to a workflow and execute the workflow.
  6. Observe that workflow execution reaches MCPClient construction for the stored URL.

After this patch, the create/update request is rejected and workflow execution also re-checks stored MCP URLs before connecting.

Expected vulnerable behavior

  • The direct MCP preview/run endpoints reject internal URLs.
  • The MCP create/update endpoints do not apply the same validation before storing the URL.
  • The workflow runtime later attempts to connect to the stored URL through MCPClient.

Changes in this PR

  • Adds assertMCPUrlNotInternal() in the MCP service module.
  • Refactors direct MCP preview/run endpoints to use the shared helper.
  • Adds MCP URL validation before create/update persistence.
  • Adds runtime validation before new and legacy MCP workflow tool execution paths connect to stored URLs.
  • Adds regression tests for localhost rejection and public endpoint allowance.

Files changed

Category Files What changed
Shared guard packages/service/core/app/mcp.ts Adds assertMCPUrlNotInternal() using the existing isInternalAddress() / PRIVATE_URL_TEXT policy
Direct MCP endpoints projects/app/src/pages/api/core/app/mcpTools/getTools.ts, projects/app/src/pages/api/core/app/mcpTools/runTool.ts Reuses the shared guard
MCP persistence projects/app/src/pages/api/core/app/mcpTools/create.ts, projects/app/src/pages/api/core/app/mcpTools/update.ts Rejects internal MCP URLs before storing toolset configuration
Workflow runtime packages/service/core/workflow/dispatch/child/runTool.ts, packages/service/core/workflow/dispatch/ai/agent/sub/tool/index.ts Revalidates stored MCP URLs before constructing MCPClient
Tests test/cases/service/core/app/mcp.test.ts Adds focused guard coverage

Maintainer impact

  • The patch is narrow and reuses the existing SSRF policy helper.
  • Existing public MCP destinations remain allowed.
  • Internal destinations that were already blocked on direct MCP preview/run paths are now blocked consistently when saved and executed from workflows.
  • Runtime checks add defense-in-depth for older stored configuration or data inserted through alternate paths.

Fix rationale

The safest durable boundary is to validate MCP URLs both:

  1. before persistence, so new unsafe toolset configuration is not saved; and
  2. before runtime use, so old or alternate-path stored data cannot bypass the policy.

Centralizing this behind assertMCPUrlNotInternal() keeps future MCP entrypoints aligned with the existing FastGPT internal-address policy.

Type of change

  • Security fix
  • Tests
  • Documentation update
  • Refactor with no behavior change

Test plan

  • Focused MCP service test passes.
  • Prettier check passes on touched files.
  • ESLint passes on touched files.

Executed with:

  • corepack pnpm@9.15.9 vitest run test/cases/service/core/app/mcp.test.ts
  • corepack pnpm@9.15.9 prettier --config ./.prettierrc.js --check packages/service/core/app/mcp.ts projects/app/src/pages/api/core/app/mcpTools/getTools.ts projects/app/src/pages/api/core/app/mcpTools/runTool.ts projects/app/src/pages/api/core/app/mcpTools/create.ts projects/app/src/pages/api/core/app/mcpTools/update.ts packages/service/core/workflow/dispatch/child/runTool.ts packages/service/core/workflow/dispatch/ai/agent/sub/tool/index.ts test/cases/service/core/app/mcp.test.ts
  • corepack pnpm@9.15.9 eslint --ignore-path .eslintignore packages/service/core/app/mcp.ts projects/app/src/pages/api/core/app/mcpTools/getTools.ts projects/app/src/pages/api/core/app/mcpTools/runTool.ts projects/app/src/pages/api/core/app/mcpTools/create.ts projects/app/src/pages/api/core/app/mcpTools/update.ts packages/service/core/workflow/dispatch/child/runTool.ts packages/service/core/workflow/dispatch/ai/agent/sub/tool/index.ts test/cases/service/core/app/mcp.test.ts

Token usage

  • discovery tokens: partial/unknown
  • validation tokens: partial/unknown
  • duplicate-check tokens: partial/unknown
  • PR/writeup tokens: partial/unknown
  • total tokens: partial/unknown
  • notes: exact token telemetry was not available in this session.

Disclosure notes

  • This PR is intentionally bounded to a stored MCP URL validation variant adjacent to the already-public MCP SSRF hardening in mcp ssrf check #6640.
  • It does not claim unauthenticated access or account takeover.
  • It reuses the existing FastGPT internal-address policy rather than changing the policy itself.
  • No unrelated files are changed.

@cla-assistant
Copy link
Copy Markdown

cla-assistant Bot commented Apr 27, 2026

CLA assistant check
All committers have signed the CLA.

@cla-assistant
Copy link
Copy Markdown

cla-assistant Bot commented Apr 27, 2026

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

Copy link
Copy Markdown
Collaborator

@c121914yu c121914yu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review: [security] fix(app): validate stored MCP tool URLs

需求理解

该 PR 目标是把 MCP URL 的内网地址校验从直接预览/调用入口扩展到 MCP 工具集创建、更新和工作流运行时,避免已保存的 MCP URL 绕过现有 SSRF 防护。

逻辑验证

  • 创建 MCP 工具集时传入 localhost:会在持久化前被 assertMCPUrlNotInternal() 拒绝。
  • 更新 MCP 工具集时传入 localhost:会在写入 MongoApp/MongoAppVersion 前被拒绝。
  • 工作流执行已保存的新旧 MCP 工具:构造 MCPClient 前会重新校验 URL。
  • 公网 MCP URL:按现有 isInternalAddress() 策略允许。
  • 公网 URL 30x 到内网:当前仍可绕过,见行级评论。

问题汇总

🔴 严重问题 1 个:MCP 实际连接阶段未防重定向到内网,安全边界仍可被公共跳转 URL 绕过。

测试情况

我在独立 worktree 中安装依赖后尝试运行 corepack pnpm@9.15.9 vitest run test/cases/service/core/app/mcp.test.ts,Vitest 在收集阶段因依赖 buffer-equal-constant-time 读取 Buffer.prototype 失败,测试文件未执行到;因此本地未能完成单测复跑。

审查结论

需修改:请先补齐 MCP transport 请求阶段的重定向防护,并增加 public URL redirect 到 localhost/metadata 的回归测试。

Comment thread packages/service/core/app/mcp.ts
@c121914yu c121914yu force-pushed the fix/mcp-url-ssrf-guards branch from 24dd880 to f673cfd Compare April 28, 2026 10:15
@github-actions
Copy link
Copy Markdown

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 14.09% 1139 / 8081
🔵 Statements 14.08% 1194 / 8475
🔵 Functions 12.57% 245 / 1948
🔵 Branches 12.07% 536 / 4438
File CoverageNo changed files found.
Generated in workflow #44 for commit f673cfd by the Vitest Coverage Report Action

@github-actions
Copy link
Copy Markdown

Admin Preview Image Ready!

registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-pro-pr:f673cfd84a97a1e779c7b48f4ace9c6f48140785

@c121914yu c121914yu merged commit c1c6b95 into labring:main Apr 28, 2026
7 checks passed
@github-actions
Copy link
Copy Markdown

Build Successful - Preview code-sandbox Image for this PR:

registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-pr:code-sandbox_f673cfd84a97a1e779c7b48f4ace9c6f48140785

@github-actions
Copy link
Copy Markdown

Build Successful - Preview fastgpt Image for this PR:

registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-pr:fastgpt_f673cfd84a97a1e779c7b48f4ace9c6f48140785

@github-actions
Copy link
Copy Markdown

Build Successful - Preview mcp_server Image for this PR:

registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-pr:mcp_server_f673cfd84a97a1e779c7b48f4ace9c6f48140785

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants