[security] fix(multi-user): deny MCP tools until explicitly granted#579
Merged
pancacake merged 1 commit intoJun 21, 2026
Merged
Conversation
Collaborator
|
Thanks for your contribution! |
pancacake
added a commit
that referenced
this pull request
Jun 21, 2026
…grant UI Follow-up refinements on top of #579 (deny MCP tools until explicitly granted), tightening decoupling and front/back consistency: - Decouple: extract the duplicated `metadata.source == "partner"` check (deferred-tools, exec gate, tool composition, cron owner routing) into a single `AgenticChatPipeline._is_partner_turn` helper, so the partner authority-via-metadata contract has one definition instead of four inline copies. - Frontend parity: the backend now denies MCP for non-admin users when the grant omits `mcp_tools`, but GrantEditor still showed `null` as "Default · all" / "all MCP". Make ModeSwitch's default-mode label caller-supplied, label the MCP section "Default · none" / "no MCP", and add a one-line rationale so admins are not misled into granting nothing. Built-in optional tools keep `null = all`; only MCP tools fail closed. Tests: ruff clean; pytest multi_user/mcp/partners/chat (545 passed); web tsc + eslint clean. Co-Authored-By: Claude Opus 4.8 <[email protected]>
vaskoyudha
added a commit
to vaskoyudha/deeptutor-for-programmer-fork
that referenced
this pull request
Jul 25, 2026
vaskoyudha
added a commit
to vaskoyudha/deeptutor-for-programmer-fork
that referenced
this pull request
Jul 25, 2026
…grant UI Follow-up refinements on top of HKUDS#579 (deny MCP tools until explicitly granted), tightening decoupling and front/back consistency: - Decouple: extract the duplicated `metadata.source == "partner"` check (deferred-tools, exec gate, tool composition, cron owner routing) into a single `AgenticChatPipeline._is_partner_turn` helper, so the partner authority-via-metadata contract has one definition instead of four inline copies. - Frontend parity: the backend now denies MCP for non-admin users when the grant omits `mcp_tools`, but GrantEditor still showed `null` as "Default · all" / "all MCP". Make ModeSwitch's default-mode label caller-supplied, label the MCP section "Default · none" / "no MCP", and add a one-line rationale so admins are not misled into granting nothing. Built-in optional tools keep `null = all`; only MCP tools fail closed. Tests: ruff clean; pytest multi_user/mcp/partners/chat (545 passed); web tsc + eslint clean. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR hardens the multi-user MCP tool boundary so configured MCP tools are not exposed to real non-admin users unless an administrator explicitly grants those tool names.
mcp_toolsis absent from the user's grant.enabled_tools: nullstill follows the default tool pool.Security issues covered
Before this PR
mcp_toolsresolved to unrestricted MCP access.load_toolsfor those names.After this PR
mcp_toolsgrant receive an empty MCP allowlist.Why this matters
MCP tools execute on the backend side through configured MCP servers. In deployments that enable powerful MCP servers, a model-selected
load_toolscall should not be treated as the user's authorization to invoke host-capability tools. This patch makes the server-side grant resolver enforce an explicit MCP permission boundary for real non-admin users.How this differs from related issue/PR
This is in the same general trust-boundary family as the older TutorBot shell/filesystem reports, but it covers the newer deferred MCP tool path rather than those legacy TutorBot tool implementations.
The relevant boundary here is:
Attack flow
Affected code
mcp_toolsdeeptutor/multi_user/tool_access.py,deeptutor/multi_user/grants.py,deeptutor/agents/chat/agentic_pipeline.py,tests/multi_user/test_tool_access.pyRoot cause
Issue 1: Missing deny-by-default MCP grant semantics
Noneas unrestricted for both ordinary optional tools and MCP tools.CVSS assessment
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:NRationale:
Safe reproduction steps
mcp_toolsgrant.Expected vulnerable behavior
mcp_toolsgrant should not expose deployment-wide MCP tools to non-admin users.set()for a non-admin user withoutmcp_tools, while still returning the explicit allowlist when an admin grants tool names.Changes in this PR
allowed_mcp_tools()so real non-admin users fail closed whenmcp_toolsis absent.Noneis not read as universally unrestricted across built-in tools and MCP tools.Files changed
deeptutor/multi_user/tool_access.pyset()instead of unrestricted; docs clarify split semanticsdeeptutor/multi_user/grants.pydeeptutor/agents/chat/agentic_pipeline.pytests/multi_user/test_tool_access.pyMaintainer impact
mcp_toolsgrant before MCP tools are listed or loadable.Fix rationale
MCP tools are deployment-defined backend capabilities, not ordinary UI-toggleable helper tools. A missing grant should therefore be interpreted conservatively for real non-admin users. Keeping administrator access unrestricted and preserving explicit partner metadata whitelists keeps the change narrow while closing the host-capability boundary for ordinary users.
Type of change
Test plan
python3 -m pytest -q tests/multi_user/test_tool_access.py tests/services/mcp/test_mcp_config.pypython3 -m compileall -q deeptutor/multi_user deeptutor/agents/chat tests/multi_user/test_tool_access.pygit diff --checkdocker run --rm -v "$PWD":/work -w /work python:3.11 bash -lc 'python -m pip install -q --upgrade pip && python -m pip install -q -e .[dev] && python -m pytest -q tests/multi_user/test_tool_access.py tests/services/mcp/test_mcp_config.py'Results:
22 passed22 passedDisclosure notes