Skip to content

fix(rbac): gate integrations routes with RequirePermission (EVO-1956)#19

Merged
gomessguii merged 1 commit into
developfrom
marcelo/evo-1956-python-rbac-gaps
Jul 4, 2026
Merged

fix(rbac): gate integrations routes with RequirePermission (EVO-1956)#19
gomessguii merged 1 commit into
developfrom
marcelo/evo-1956-python-rbac-gaps

Conversation

@marcelogorutuba

Copy link
Copy Markdown
Member

Summary

Closes the Python processor half of the EVO-1956 RBAC audit (2 🔴 HIGH findings). Before this PR, every authenticated user (including the default agent role, which lacks admin integrations.* perms) could reach the bulk integrations endpoint and every provider endpoint in the processor — controllers had no RequirePermission gate, only the auth middleware ran.

  • integrations_routes.py bulk endpoint gated with integrations.read
  • 12 provider modules — github, google-calendar, google-sheets, notion, linear, monday, atlassian, asana, hubspot, paypal, canva, supabase — 82 handlers gated by HTTP verb / operation semantics:
    verb / path permission
    GET / (config), GET /discover-tools, GET /calendars, GET /spreadsheets integrations.read
    POST/GET /discover, POST /authorization, POST /callback integrations.connect
    POST /availability (calendar query) integrations.read
    POST /events (calendar mutation) integrations.create
    PUT "" (save config) integrations.update
    DELETE "" (disconnect) integrations.disconnect
  • OAuth callback_router endpoints intentionally NOT gated — browser redirect from external OAuth provider (github.com, google, etc.) never carries a CRM bearer; gating them would break the OAuth flow. The 12 callback endpoints remain unauthenticated by design.
  • mcp_integration_base.create_mcp_router() factory (currently 0 callers in the repo) left untouched — per-provider handlers are what's actually wired via main.py.
  • Contract test tests/unit/test_integrations_rbac_gates.py locks the invariants: (1) every main-router endpoint has RequirePermission("integrations", <verb>) where <verb> is in the auth-service resource_actions_config.rb allow-list; (2) every callback_router endpoint has no permission gate.

Validation

  • evo-ai-processor-community: python -m pytest tests/unit/test_integrations_rbac_gates.py25/25 pass
  • evo-ai-processor-community: python -c "import ast; [ast.parse(open(f).read()) for f in glob.glob('src/api/*_routes.py') + ['src/api/mcp_integration_base.py']]" → all parse OK

Changed Files

  • src/api/integrations_routes.py
  • src/api/github_routes.py
  • src/api/google_calendar_routes.py
  • src/api/google_sheets_routes.py
  • src/api/notion_routes.py
  • src/api/linear_routes.py
  • src/api/monday_routes.py
  • src/api/atlassian_routes.py
  • src/api/asana_routes.py
  • src/api/hubspot_routes.py
  • src/api/paypal_routes.py
  • src/api/canva_routes.py
  • src/api/supabase_routes.py
  • tests/unit/test_integrations_rbac_gates.py (new)

Linked Issue

  • EVO-1956

Notes for Reviewer

  • The 2 remaining halves of EVO-1956 (CRM Rails MEDIUMs / Go core-service trailing-slash ai_folders) will land as separate PRs.
  • Own-goal caught during self-review: integrations.execute isn't seeded on any role in resource_actions_config.rb — my initial default mapping for POST subresources would have 403'd Google Calendar's availability and events endpoints for everyone including admins. Fixed pre-commit; the contract test's ALLOWED_ACTIONS allow-list now locks this class of regression.
  • Second own-goal: my initial batch-injector produced verb-dependent divergence on discover_oauth (7 providers use POST → connect, 3 providers use GET → read for the same semantic operation). Unified all 10 to connect — same OAuth-flow step, same permission.

🤖 Generated with Claude Code

Closes the Python processor half of the EVO-1956 RBAC audit (2 HIGH):
the bulk integrations endpoint and the 12 provider modules were fully
reachable by any authenticated user because none of the handlers carried
a RequirePermission gate.

- integrations_routes.py bulk endpoint gated with integrations.read
- 12 provider modules (github, google-calendar, google-sheets, notion,
  linear, monday, atlassian, asana, hubspot, paypal, canva, supabase):
  82 handlers gated by HTTP verb / operation semantics (read / connect /
  update / create / disconnect)
- OAuth callback_routers intentionally NOT gated (browser redirect from
  external OAuth provider — carries no CRM bearer by design)
- Contract test locks in the invariants: every main-router endpoint
  gated with resource=integrations, action ∈ allow-list; every
  callback_router endpoint remains unauthenticated

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Sorry @marcelogorutuba, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@gomessguii gomessguii merged commit 11bf0b9 into develop Jul 4, 2026
6 of 7 checks passed
@gomessguii gomessguii deleted the marcelo/evo-1956-python-rbac-gaps branch July 4, 2026 00:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants