fix(mcp): remove dead team authorization and harden dataset lookup - #319
fix(mcp): remove dead team authorization and harden dataset lookup#319atomicdjt wants to merge 2 commits into
Conversation
Signed-off-by: David Turner <davidelsey9513@gmail.com>
Rosetta Triage ReviewSummary: Fixes #295 — under Priority: High (security fix — closes a cross-tenant read-authorization bypass). Findings:
Caveats:
Automated triage by Rosetta agent |
There was a problem hiding this comment.
Thank you, @atomicdjt. This one needs a different change than the issue asked for.
ROSETTA_READ_POLICY=team is dead code. can_read returns True for any aia- name before reaching _evaluate (authorizer.py:43-45), and all three call sites pass call_ctx.config.instruction_dataset, which is INSTRUCTION_DATASET_TEMPLATE = "aia-{version}" (constants.py:87, config.py:425-426). No MCP tool accepts a dataset parameter. So _evaluate never receives a project-* name. Neither the #295 bypass nor this fix can execute.
Project datasets were removed from the product. The team policy and project-* handling are leftovers. No code supports them.
Our ask: delete them instead of hardening them. What is dead:
Code:
services/authorizer.py— thePOLICY_TEAMbranch,_check_team_membership,_get_team_api, theteam_apiconstructor parameter,_normalize_email(used only on that path), and theproject-*line in the class docstringservices/_ragflow_team_api.py— the whole file.authorizer.pyis its only callerconstants.py:76,78—POLICY_TEAMand its entry inVALID_POLICIEStests/test_authorizer.py— the team-policy casestests/test_ragflow_team_api.py— the whole file
Documentation that advertises the setting, and should go with it:
src/rosetta-mcp-server/README.md—ROSETTA_READ_POLICYappears in two env tables as a supported runtime settingdocs/MCP-ARCHITECTURE.mddocs/web/docs/mcp-architecture.md— the published website mirrordocs/PATTERNS/policy-based-authorization.md— built entirely on this policy, and it also documents acan_writethat does not existdocs/PATTERNS/env-backed-dataclass-config.md— itsAuthorizerexample is already stale- the ownership section you added to
docs/mcp/RAGFLOW.mdbecomes moot once the caller is gone
all and none stay. read_policy keeps working; team stops being a valid value and falls back to the default.
Keep the DatasetLookup change. It is the valuable half. get_id and get_dataset are on live paths. Before this change remember() was last-write-wins on a name collision, so a dataset named aia-r3 in another visible tenant could shadow the instruction dataset and be served as instructions. Your ambiguity marking turns that into a deny. Neither #295 nor the triage mentions it. Keep it here, or split it into its own PR if you prefer a narrower diff — your call.
For the record, the work itself checks out: 362 MCP, 39 focused, 56 CLI tests pass, mypy clean on 55 files. Your numbers reproduce exactly. Ten refutation attempts found no surviving and no newly introduced bypass; email and tenant-id normalization, the aia- short-circuit, ID-path access and pending invites all behave as described. The analysis was sound. The target was dead code, and that is on the issue, not on you.
Signed-off-by: David Turner <davidelsey9513@gmail.com>
|
@isolomatov-gd Thank you for the correction. Follow-up commit
The PR title/body now describe the 30-file net diff rather than the superseded tenant-hardening approach. Final local validation is 38 focused, 339 MCP, and 56 CLI tests; MyPy is clean on 54 files; package builds/Twine checks passed. The new GitHub MCP build/test and CodeQL checks are all green. Ready for re-review. |
Summary
This follow-up aligns PR #319 with the current MCP product surface and the maintainer review:
teamread-policy branch and its RAGFlow team API helper;allandnone; unsupported values such asteamnow fall back to the existing default (all);DatasetLookupcollision fix so duplicate visible names are ambiguous instead of last-write-wins;Root cause and resulting behavior
All three registered MCP tools authorize only
call_ctx.config.instruction_dataset, which isaia-{version}.Authorizer.can_read()short-circuitsaia-*datasets before policy evaluation, and no MCP tool accepts a caller-selected dataset. The team/project authorization path therefore could not execute.The remaining behavior is intentionally small:
aia-*instruction datasets are always readable.allornone; unknown policy values deny insideAuthorizer.ROSETTA_READ_POLICYvalues back to the existing default (all).list_datasets()retains all visible IDs.Removed
rosetta_mcp/services/_ragflow_team_api.pyDatasetLookup validation
Tests cover both duplicate-name insertion orders, ID lookup under ambiguity, repeated observation of the same ID, cache invalidation/refresh, and preservation of the full visible dataset list.
A refutation mutation temporarily restored last-write-wins behavior: 3 targeted tests failed as expected. Restoring ambiguity detection returned all 10 DatasetLookup tests to green. No mutation-only change was committed.
Validation
Passed locally on the final net diff:
src/validate-types.shPython validationgit diff --checkandgit fsck --no-danglingupstream/mainEnvironment-limited checks:
verify_mcp.pyverified the in-memory surface (3 tools and 1 resource template) but its 13 data-backed assertions could not run withoutROSETTA_API_KEY; no live RAGFlow pass is claimed.src/hooks/node_modules/tscare unavailable. Its runnable Python checks were executed directly above.node_modulesdirectories are absent.Net diff
Against the current merge base with
main: 30 files changed, 114 insertions, 400 deletions.Fixes #295