Skip to content

fix(mcp): honor AUTH_ROLE_ADMIN and warn on permission-less protected tools#40659

Merged
rusackas merged 3 commits into
masterfrom
fix/mcp-authz-hardening
Jun 3, 2026
Merged

fix(mcp): honor AUTH_ROLE_ADMIN and warn on permission-less protected tools#40659
rusackas merged 3 commits into
masterfrom
fix/mcp-authz-hardening

Conversation

@rusackas
Copy link
Copy Markdown
Member

@rusackas rusackas commented Jun 2, 2026

SUMMARY

Two MCP authorization-hardening fixes:

  • FINDING-040user_has_permission() (superset/mcp_service/utils/permissions_utils.py) hardcoded the admin check to role name "Admin"/"admin", ignoring the configurable AUTH_ROLE_ADMIN. In deployments that rename the admin role, admins would be over-filtered (denied the field-level sensitive-data bypass) — a functionality regression, not an escalation. Now uses current_app.config["AUTH_ROLE_ADMIN"].
  • FINDING-039check_tool_permission() (superset/mcp_service/auth.py) silently allows a permission-protected tool that declares no class_permission_name. That's a supported configuration, so a blanket fail-closed would break legitimate tools — but an accidentally permission-less sensitive tool would fail open with no signal. Keep the allow behavior, but log a warning so the misconfiguration is visible.

TESTING INSTRUCTIONS

pytest tests/unit_tests/mcp_service/utils/test_permissions_utils.py

New test: the admin bypass follows AUTH_ROLE_ADMIN (a renamed admin role is honored; the hardcoded "Admin" no longer grants it).

ADDITIONAL INFORMATION

  • Has associated issue:
  • Required feature flags:
  • Changes UI
  • Includes DB Migration
  • Introduces new feature or API
  • Removes existing feature or API

🤖 Generated with Claude Code

@dosubot dosubot Bot added authentication Related to authentication authentication:RBAC Related to RBAC labels Jun 2, 2026
@bito-code-review
Copy link
Copy Markdown
Contributor

bito-code-review Bot commented Jun 2, 2026

Code Review Agent Run #819485

Actionable Suggestions - 0
Additional Suggestions - 1
  • superset/mcp_service/auth.py - 1
    • Missing test assertion for new warning · Line 122-130
      The diff adds valuable defensive logging that surfaces a security-relevant configuration gap. However, `test_check_tool_permission_no_class_permission_allows` at line 103 in `tests/unit_tests/mcp_service/test_auth_rbac.py` only asserts the return value and does not verify the new warning is emitted. Add a `caplog` assertion to ensure this logging behavior is covered by tests, preventing silent regressions.
Review Details
  • Files reviewed - 3 · Commit Range: 68ba87e..68ba87e
    • superset/mcp_service/auth.py
    • superset/mcp_service/utils/permissions_utils.py
    • tests/unit_tests/mcp_service/utils/test_permissions_utils.py
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@netlify
Copy link
Copy Markdown

netlify Bot commented Jun 2, 2026

Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit 11fcdc8
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/6a1f6cfb2c08f10007b7d0fe
😎 Deploy Preview https://deploy-preview-40659--superset-docs-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@codecov
Copy link
Copy Markdown

codecov Bot commented Jun 2, 2026

Codecov Report

❌ Patch coverage is 9.09091% with 10 lines in your changes missing coverage. Please review.
✅ Project coverage is 64.05%. Comparing base (3bbb35e) to head (9b8bfe2).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
superset/mcp_service/utils/permissions_utils.py 0.00% 7 Missing ⚠️
superset/mcp_service/auth.py 25.00% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #40659      +/-   ##
==========================================
- Coverage   64.05%   64.05%   -0.01%     
==========================================
  Files        2662     2662              
  Lines      143254   143261       +7     
  Branches    32941    32941              
==========================================
+ Hits        91764    91765       +1     
- Misses      49903    49909       +6     
  Partials     1587     1587              
Flag Coverage Δ
hive 39.80% <9.09%> (-0.01%) ⬇️
mysql 58.47% <9.09%> (-0.01%) ⬇️
postgres 58.55% <9.09%> (-0.01%) ⬇️
presto 41.39% <9.09%> (-0.01%) ⬇️
python 60.03% <9.09%> (-0.01%) ⬇️
sqlite 58.16% <9.09%> (-0.01%) ⬇️
unit 100.00% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment thread superset/mcp_service/auth.py Outdated
Comment thread superset/mcp_service/utils/permissions_utils.py
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR hardens MCP authorization behavior by (1) honoring Superset’s configurable admin role name (AUTH_ROLE_ADMIN) when applying admin bypass logic in MCP field-level permission filtering, and (2) surfacing potentially dangerous RBAC misconfiguration for MCP tools by warning when a protected tool declares no class_permission_name.

Changes:

  • Update MCP field-permission admin detection to use current_app.config["AUTH_ROLE_ADMIN"] instead of hardcoded "Admin".
  • Add a warning log when check_tool_permission() encounters a tool without class_permission_name (while still allowing access).
  • Add a unit test ensuring the admin bypass follows AUTH_ROLE_ADMIN when the admin role is renamed.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
superset/mcp_service/utils/permissions_utils.py Uses configured AUTH_ROLE_ADMIN for the admin bypass in MCP field-level permission checks.
superset/mcp_service/auth.py Logs a warning when a protected tool has no RBAC class permission metadata, while maintaining backward-compatible allow behavior.
tests/unit_tests/mcp_service/utils/test_permissions_utils.py Adds coverage for honoring AUTH_ROLE_ADMIN in the MCP permission helper.

Comment thread superset/mcp_service/utils/permissions_utils.py Outdated
Comment thread tests/unit_tests/mcp_service/utils/test_permissions_utils.py Outdated
Comment thread tests/unit_tests/mcp_service/utils/test_permissions_utils.py Outdated
Copy link
Copy Markdown
Contributor

@aminghadersohi aminghadersohi left a comment

Choose a reason for hiding this comment

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

Code Review — PR #40659

HEAD SHA: 68ba87ec43291b8a72640515b258058be447167e

The two fixes are directionally correct. Two issues should be addressed in this PR rather than deferred to follow-ups.


HIGH — Per-call WARNING log will flood production logs

superset/mcp_service/auth.py, lines 126–130

check_tool_permission is called on every authenticated MCP tool invocation (both async wrapper line 656 and sync wrapper line 703 in mcp_auth_hook). The new logger.warning fires on every such call for any protect=True tool that has no class_permission_name. If any deployed tool has that configuration, every user request emits a WARNING, burying real permission-denial alerts and producing significant log churn in production.

The signal is right; the frequency and level are wrong. Two options:

  • Preferred: move to registration time — warn once when the tool is registered, not per-call
  • Fallback: downgrade to logger.debug so it's opt-in rather than default noise

This was flagged by the codeant reviewer and rusackas acknowledged a follow-up. Given it's an operational hazard on every tool call, it should ship fixed in this PR.


MEDIUM — New test asserts False for incidental reasons, not RBAC correctness

tests/unit_tests/mcp_service/utils/test_permissions_utils.py, lines 46–51

not_admin = Mock()
not_admin.roles = [_role("Admin")]
assert user_has_permission(not_admin, "can_read", "Chart") is False

This passes because security_manager.has_access is not mocked and raises/returns False in the unit test context — not because the RBAC check correctly evaluated the user's permissions. A future change to the exception handling could make this assertion pass or fail for unrelated reasons.

Fix: mock security_manager.has_access to return False and assert it was called:

with patch("superset.mcp_service.utils.permissions_utils.security_manager") as mock_sm:
    mock_sm.has_access.return_value = False
    assert user_has_permission(not_admin, "can_read", "Chart") is False
    mock_sm.has_access.assert_called_once()

This was noted by Copilot and acknowledged as a follow-up — it should be in this PR.


MEDIUM — Silent behavioral change: lowercase "admin" role loses bypass

superset/mcp_service/utils/permissions_utils.py, line 100

The old check accepted both "Admin" and "admin". The new check only matches AUTH_ROLE_ADMIN (default "Admin"). Any deployment with a FAB role literally named "admin" (lowercase) will silently lose the MCP field-level admin bypass. The fix is correct — lowercase "admin" never should have bypassed — but the behavioral change should be noted in UPDATING.md or called out explicitly in the PR description as a breaking change for that edge case.


MEDIUM — config["AUTH_ROLE_ADMIN"] subscript fails silently via broad except

superset/mcp_service/utils/permissions_utils.py, line 97

FAB always sets AUTH_ROLE_ADMIN (default "Admin"), so a KeyError is unlikely. But if it ever occurs, it's silently swallowed by except Exception as e and user_has_permission returns False with only a generic "Error checking permission" log. Using .get("AUTH_ROLE_ADMIN", "Admin") would be more explicit about the fallback and avoid the silent path for a configuration problem.


NIT — Inline import lacks a comment

superset/mcp_service/utils/permissions_utils.py, line 92

from flask import current_app is deferred inside the function body. This follows an existing pattern in the file (from flask import g, from superset import security_manager), but a brief comment explaining why it's inline (app context not guaranteed at module import time) would help future readers.


Scan coverage (all 19 run)

Scans 2–19 came back clean: no SQL injection, no missing auth decorators, no hardcoded credentials, no dangerous builtins, no deprecated APIs, no f-strings in logger calls, no module-level config access, no unused imports.


Praise

  • Correctly removing the loose "admin" alias is the right call — only the configured AUTH_ROLE_ADMIN should bypass, not an arbitrary lowercase variant.
  • All new logger calls use lazy %s formatting.
  • try/finally in the test correctly prevents config pollution between tests.
  • Keeping the allow behavior in check_tool_permission when class_permission_name is absent is correct — fail-closing would break legitimate tools that use protect=True without an RBAC class mapping.
  • PR description cleanly separates the two findings with their motivations.

@aminghadersohi aminghadersohi self-requested a review June 2, 2026 04:35
@aminghadersohi aminghadersohi dismissed their stale review June 2, 2026 04:35

Switching to comment-only review

aminghadersohi

This comment was marked as duplicate.

aminghadersohi

This comment was marked as duplicate.

aminghadersohi

This comment was marked as duplicate.

@sadpandajoe sadpandajoe added the merge-if-green If approved and tests are green, please go ahead and merge it for me label Jun 2, 2026
@bito-code-review
Copy link
Copy Markdown
Contributor

bito-code-review Bot commented Jun 2, 2026

Code Review Agent Run #91fdc4

Actionable Suggestions - 0
Filtered by Review Rules

Bito filtered these suggestions based on rules created automatically for your feedback. Manage rules.

  • tests/unit_tests/mcp_service/utils/test_permissions_utils.py - 1
Review Details
  • Files reviewed - 3 · Commit Range: 68ba87e..0bfab4f
    • superset/mcp_service/auth.py
    • superset/mcp_service/utils/permissions_utils.py
    • tests/unit_tests/mcp_service/utils/test_permissions_utils.py
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@rusackas rusackas moved this from Needs Review to Approved and/or Merged in Superset Review Help Wanted Jun 2, 2026
claude and others added 2 commits June 2, 2026 16:53
…ed tools

- user_has_permission() hardcoded the admin role check to "Admin"/"admin",
  ignoring AUTH_ROLE_ADMIN. Deployments that rename the admin role would have
  admins over-filtered (denied the field-level sensitive-data bypass). Use the
  configured AUTH_ROLE_ADMIN.
- check_tool_permission() silently allows a permission-protected tool that
  declares no class_permission_name. This is a supported configuration, but a
  protected tool with an accidentally-omitted permission would fail open with
  no signal. Keep the allow behavior but log a warning so the misconfiguration
  is visible.

Add a test verifying the admin bypass follows AUTH_ROLE_ADMIN.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Resolves the three review threads that were previously answered with
"follow-up" promises:

- check_tool_permission: warn ONCE per tool (not on every protected-tool
  call) when a tool declares no class_permission_name — keeps the
  fail-open visibility without the per-call WARNING noise.
- user_has_permission: the AUTH_ROLE_ADMIN bypass now also honors the admin
  role when it is inherited via group membership (User.groups -> Group.roles),
  not just directly-assigned roles.
- test_user_has_permission_admin_uses_configured_role_name: explicitly mock
  security_manager.has_access to return False and assert it is called,
  instead of relying on incidental Mock behavior.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@rusackas rusackas force-pushed the fix/mcp-authz-hardening branch from c960c91 to 11fcdc8 Compare June 2, 2026 23:53
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@bito-code-review
Copy link
Copy Markdown
Contributor

bito-code-review Bot commented Jun 3, 2026

Code Review Agent Run #5e05ed

Actionable Suggestions - 0
Additional Suggestions - 2
  • superset/mcp_service/utils/permissions_utils.py - 1
    • Code Structure: Use security_manager helper · Line 97-103
      The admin-role check manually iterates `user.roles` and `user.groups`, duplicating logic that FAB's `security_manager.get_user_roles(user)` already handles via SQL joins on the `user_group` association table. The canonical pattern in `SupersetSecurityManager.is_admin()` (line 3743) uses `get_user_roles()` instead. Aligning here improves consistency and avoids divergence risk if FAB's role resolution logic changes.
      Code suggestion
      --- a/superset/mcp_service/utils/permissions_utils.py
      +++ b/superset/mcp_service/utils/permissions_utils.py
       @@ -94,14 +94,10 @@ def user_has_permission(
                # admin role name rather than hardcoding "Admin", so deployments that
                # rename the admin role (AUTH_ROLE_ADMIN) still grant admins the bypass.
                admin_role_name = current_app.config["AUTH_ROLE_ADMIN"]
      -        # Collect role names granted directly AND inherited via group
      -        # membership (FAB users can receive the admin role through a group),
      -        # so a group-admin still gets the bypass.
      -        role_names = {role.name for role in getattr(user, "roles", None) or []}
      -        for group in getattr(user, "groups", None) or []:
      -            role_names.update(role.name for role in getattr(group, "roles", None) or [])
      -        if admin_role_name in role_names:
      +        from superset import security_manager
      +
      +        if admin_role_name in {r.name for r in security_manager.get_user_roles(user)}:
                    return True
  • tests/unit_tests/mcp_service/utils/test_permissions_utils.py - 1
    • Testing: Missing group-inheritance test · Line 32-61
      The test for AUTH_ROLE_ADMIN covers direct role assignment (line 43-46) but lacks coverage for group-inherited admin roles. Since the diff adds group-based role resolution, add a test case like: mock a user with no direct roles but one group containing an admin role, then assert user_has_permission returns True.
Review Details
  • Files reviewed - 3 · Commit Range: f1b6054..9b8bfe2
    • superset/mcp_service/auth.py
    • superset/mcp_service/utils/permissions_utils.py
    • tests/unit_tests/mcp_service/utils/test_permissions_utils.py
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@rusackas rusackas merged commit 0b9764a into master Jun 3, 2026
59 checks passed
@rusackas rusackas deleted the fix/mcp-authz-hardening branch June 3, 2026 04:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

authentication:RBAC Related to RBAC authentication Related to authentication merge-if-green If approved and tests are green, please go ahead and merge it for me size/M

Projects

Status: Approved and/or Merged

Development

Successfully merging this pull request may close these issues.

6 participants