Sonarqube podman file update and teams pattern#2155
Merged
crivetimihai merged 4 commits intomainfrom Jan 18, 2026
Merged
Conversation
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
- Add pattern validation for team names (alphanumeric, space, underscore, period, dash) - Escape team data in templates and backend HTML generation - Extend validate_no_xss to check JavaScript patterns - Add validation to admin team update endpoint - Document team name rules and migration path Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
kcostell06
pushed a commit
to kcostell06/mcp-context-forge
that referenced
this pull request
Feb 24, 2026
* Fix Sonar compose Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * feat(security): Add strict team name validation and output escaping - Add pattern validation for team names (alphanumeric, space, underscore, period, dash) - Escape team data in templates and backend HTML generation - Extend validate_no_xss to check JavaScript patterns - Add validation to admin team update endpoint - Document team name rules and migration path Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * Add teams validation sonar Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * Update sonarqube podman file Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> --------- Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
aidbutlr
added a commit
to aidbutlr/mcp-context-forge
that referenced
this pull request
Mar 3, 2026
* Refactor team management: Unified UI for view and manage, cursor-based pagination, and search on name or email (#2067)
* unified team member view
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>
* Fix selection
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>
* Fix member search
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>
* Remove extra logging
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>
* Update search to non dom users
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>
* lint-web fixes
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>
* black fix
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>
* team member cleanup
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>
* Segregate members and non members and fix pagination
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>
* Fix search
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>
* Linting fixes
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>
* Fix tests
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>
* Make list team members return paginated result by default
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>
* linting fixes
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>
* Fix cursor use in auth_service list users
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>
* Remove unused param
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>
* Add index on emai_user name
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>
* Fix non paginated response for list team members API
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>
* Update migration script
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>
* Fix failing doctest
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>
* Use next_cursor with alias
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>
* Remove print statements in alembic script
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>
* Fix close manage members
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>
* refactor: remove dead code _cursor_paginate_users method
Remove unused _cursor_paginate_users() method from EmailAuthService
that was made obsolete when list_users() was refactored to use
unified_paginate() for both cursor and page-based pagination.
Also remove:
- Tests for the dead code method
- Unused imports (base64, orjson, and_, Select)
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix: address pagination safety and role preservation issues
Critical fixes:
- Add loadedMembers tracking to prevent accidental removal of members
from unloaded pages during infinite scroll pagination
- Only remove members who were visible in the form AND unchecked
High priority fixes:
- Preserve member roles when rendering search results (prevent owner demotion)
- Add loadedMembers hidden input in JS search to match template behavior
- Use fetchWithAuth instead of bare fetch for bearer token support
Low priority fixes:
- Fix owner_count to use count_team_owners() for accurate Last Owner badge
across all pages, not just current page
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix: implement proper cursor pagination for EmailUser
EmailUser uses email as primary key (not id), so the generic
unified_paginate cursor using (created_at, id) doesn't work.
Changes:
- Add custom cursor pagination in list_users() using (created_at, email)
as cursor fields for correct keyset pagination
- Add warning log in unified_paginate when model lacks 'id' field
to help diagnose cursor pagination issues with other models
- Keep page-based pagination using unified_paginate (works correctly)
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix: add debounce and HTMX reinitialization for search
- Add debouncedServerSideUserSearch() wrapper with 300ms delay to
prevent excessive API calls on each keystroke
- Call htmx.process() after innerHTML replacement when clearing search
to reinitialize HTMX intersect triggers for infinite scroll
- Export debounced function for use in templates
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix: move imports to top level and add missing template
Move base64, orjson, and_ imports to module top level to satisfy pylint
C0415 (import-outside-toplevel).
Add team_members_selector.html template for the user selector with team
context, used when render=selector in admin users partial endpoint.
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix: team member cursor pagination, checkbox preservation, and tests
- Add created_at property alias to EmailTeamMember for cursor pagination
compatibility with unified_paginate
- Fix ORDER BY in get_team_members to use (joined_at DESC, id DESC) for
cursor-based pagination and (full_name, email) for page-based
- Preserve checkbox states during server-side user search to prevent
in-progress add/remove selections from being lost
- Fix admin.html users list per_page from 10 to 20 for consistency
- Add cursor pagination tests for EmailUser list_users method testing
the (created_at, email) keyset implementation
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix: cursor pagination, access control, and UI consistency issues
- Implement custom cursor pagination for team members using (joined_at, id)
keyset instead of relying on unified_paginate's (created_at, id) which
doesn't work for EmailTeamMember
- Implement custom cursor pagination for list_users_not_in_team using
(created_at, email) keyset for EmailUser
- Fix backward compatibility check to only trigger when all pagination
params (cursor, page, limit) are None
- Update legacy add-members route from non-existent /admin/teams/{id}/users/partial
to /admin/users/partial?render=selector&team_id={id}
- Allow teams.manage_members permission for /users/search endpoint so team
owners can search users to add to their teams
- Add email fallback for name display in team_users_selector.html and
team_members_selector.html templates
- Remove unused created_at property alias from EmailTeamMember
- Add unit tests for team member cursor-based pagination
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix: make non-members list use same per_page as members section
Both members and non-members sections in the team management UI should
honor the same per_page setting from the view context for consistency.
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix: per_page consistency, legacy search URL, and limit cap
- Add data-per-page attributes to team member containers and read them
in JS search reset to maintain consistent page sizes
- Fix legacy add-members search URL from non-existent
/admin/teams/{id}/users/search to /admin/users/search
- Revert users list per_page from 20 back to 50 to match main branch
- Add le=settings.pagination_max_page_size constraint to teams router
limit parameter and cap page_size in service for defense-in-depth
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* Linting
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix: prettier formatting in admin.js
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
---------
Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
* feature/password-enforcement-1843 (#2066)
* feature/password-enforcement-1843
Signed-off-by: NAYANAR <nayana.r7813@gmail.com>
* pylint fix
Signed-off-by: NAYANAR <nayana.r7813@gmail.com>
* bandit fix
Signed-off-by: NAYANAR <nayana.r7813@gmail.com>
* added a small visible indicator on the page Password policy enabled password_policy_enabled so you can immediately see the effective value the server used when rendering the page
Signed-off-by: NAYANAR <nayana.r7813@gmail.com>
* flake8 fix
Signed-off-by: NAYANAR <nayana.r7813@gmail.com>
* fix pytest
Signed-off-by: NAYANAR <nayana.r7813@gmail.com>
* fix flake8
Signed-off-by: NAYANAR <nayana.r7813@gmail.com>
* feat: add migration for password_changed_at column
Adds the missing Alembic migration to create the password_changed_at
column in the email_users table, required for password age tracking
and expiry enforcement.
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix: address password enforcement logic issues
Fixes 6 issues found during code review:
1. Default password enforcement bypass - moved needs_password_change
inside the require_password_change_for_default_password check
2. password_change_required flag bypass - made the flag authoritative,
no longer ignored based on password age (age is only for expiry)
3. Admin UI ignores policy toggle - added password_policy_enabled
check to validate_password_strength function
4. Admin update missing timestamp - added password_changed_at update
in admin user update paths (router and service layer)
5. Migration no backfill - added backfill logic to set
password_changed_at = created_at for existing users
6. .env.example missing docs - added all new password enforcement
configuration options
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
---------
Signed-off-by: NAYANAR <nayana.r7813@gmail.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
* 2072 - MCP Registry "Add Server" button flow (#2081)
* 2072 - MCP Registry "Add Server" button flow
- Backend now detects HTMX requests via HX-Request header and returns HTML fragments instead of JSON
- Replaced broken htmx-indicator pattern with hx-on::before-request and hx-on::after-request event handlers
- Implemented three button states: success (green), OAuth warning (yellow), error with retry (red)
- Fixed settings.root_path → settings.app_root_path bug
- Added hx-disabled-elt to prevent double-clicks
- Added comprehensive test coverage for OAuth registration and HTMX endpoints
Signed-off-by: Gabriel Costa <gabrielcg@proton.me>
* Flake8 fix
Signed-off-by: Gabriel Costa <gabrielcg@proton.me>
* fix: add proper HTML escaping and fix HTMX tests
- Add html.escape() for server_id, message, and error values in
HTML fragments to prevent XSS vulnerabilities
- Fix HTMX tests to use __wrapped__ to bypass RBAC decorator
- Apply Black formatting and isort to test file
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix: address HTMX registration flow issues
- High: Use HX-Trigger-After-Swap header for success refresh instead of
inline hx-on::after-request. Error buttons no longer get wiped by
auto-refresh since they don't include the trigger header.
- Medium: Add oauth_required field to CatalogServerRegisterResponse schema
and use it instead of brittle string matching on message content.
- Low: Add hx-on::response-error handlers to restore button state on
transport/network failures.
Also adds event listener in template for catalogRegistrationSuccess trigger.
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix: address remaining HTMX registration issues
- Medium: OAuth registrations no longer trigger delayed refresh, so the
yellow "OAuth Config Required" warning persists instead of being
replaced by "Add Server" on refresh
- Low: Event listener uses window guard to prevent re-attachment when
partial is refreshed multiple times
- Low: HTTP errors (401/403/500) now show orange "Request Failed - Click
to Retry" button with visual feedback instead of silently resetting
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* Lint fix
Signed-off-by: Gabriel Costa <gabrielcg@proton.me>
* style: remove unnecessary else after return (pylint R1705)
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix: OAuth servers persist as registered after refresh
- Add requires_oauth_config field to CatalogServer schema
- Query all gateways (enabled and disabled) to track registration status
- Disabled gateways are marked as requires_oauth_config=True
- Template shows yellow "OAuth Config Required" state for disabled servers
- Add debounce to prevent multiple refreshes when registering rapidly
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix: only mark OAuth servers as requires_oauth_config
- Query auth_type from Gateway to distinguish OAuth from other disabled
servers (only disabled + auth_type='oauth' = requires_oauth_config)
- Re-enable refresh for OAuth success since template now handles state
- Revert unrelated formatting change in migration file
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix: distinguish unconfigured OAuth servers from manually disabled ones
- Set auth_type="oauth" when registering OAuth servers (was None)
- Check oauth_config IS NULL to identify servers needing OAuth setup
- Manually disabled OAuth servers (with oauth_config populated) now
correctly show "Already Registered" instead of "OAuth Config Required"
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* test: add unit tests for requires_oauth_config classification
Test cases for the get_catalog_servers method:
- Disabled OAuth server with oauth_config=None → requires_oauth_config=True
- Disabled OAuth server with oauth_config populated → requires_oauth_config=False
- Enabled OAuth server → requires_oauth_config=False
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix: use named logger for duplicate header warning
Change logging.warning() to logger.warning() for duplicate header key
warnings. This ensures proper log capture in pytest's caplog fixture,
fixing flaky test in CI.
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
---------
Signed-off-by: Gabriel Costa <gabrielcg@proton.me>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
* 2077 - Reorder action buttons (#2090)
* 2077 - Reorder action buttons
Signed-off-by: Gabriel Costa <gabrielcg@proton.me>
* fix: Update search indices after moving Actions column to first position
Update JavaScript table filter functions to reflect the new column order
where Actions is at index 0 instead of being the last column. This fixes
search/filter functionality that was broken by the column reorder.
Affected functions:
- filterGatewaysTable: skip Actions(0) and S.No.(1)
- filterServerTable: skip Actions(0), Icon(1), S.No.(2)
- filterToolsTable: skip Actions(0), S.No.(1)
- filterResourcesTable: skip Actions(0)
- filterPromptsTable: skip Actions(0), S.No.(1)
- filterA2AAgentsTable: skip Actions(0), ID(1)
- simpleGatewaySearch: skip Actions(0), S.No.(1)
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
---------
Signed-off-by: Gabriel Costa <gabrielcg@proton.me>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
* 2080 - Adjust Show inactive toggle (#2099)
* 2080 - Adjust Show inactive toggle
Fix the Show inactive toggle behaviour on the MCP Server Gateway and
Virtual Servers - Soon to be added to other tabs
Signed-off-by: Gabriel Costa <gabrielcg@proton.me>
* Web lint fix
Signed-off-by: Gabriel Costa <gabrielcg@proton.me>
* fix: restore HTMX-based toggle for servers/gateways with proper state preservation
- Revert from client-side to server-side filtering via HTMX to fix pagination
- Fix fallback ID lookup to include servers and gateways tables
- Preserve team_id and per_page URL params when toggling inactive filter
- Preserve and reapply search term after HTMX content swap
- Remove unused applyServerFilter/applyGatewayFilter functions
Addresses issues with sparse pages and search state loss when toggling
the "Show inactive" checkbox on servers and gateways tabs.
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
---------
Signed-off-by: Gabriel Costa <gabrielcg@proton.me>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
* Add pagination for teams listing and update search to server side (#2089)
* feat(teams): add server-side pagination and search for teams listing
Implement unified pagination support for teams management:
Service Layer:
- Update TeamManagementService.list_teams to use unified_paginate
- Support cursor, page, per_page, search_query, and visibility_filter
- Add get_all_team_ids method for admin lookups
- Add composite index on (name, id) for optimized search/pagination
API Router:
- Refactor list_teams endpoint with proper pagination flags
- Add CursorPaginatedTeamsResponse schema
Frontend (HTMX):
- Add teams_partial.html template for HTMX partial updates
- Add teams_selector_items.html for infinite-scroll dropdowns
- Implement server-side team search in admin.js
Testing:
- Update router tests to explicitly pass include_pagination=False
- Update service tests to match new unified pagination signature
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix(teams): address code review findings for pagination
Fixes identified in code review:
High Priority:
- Fix cursor pagination ordering: use created_at DESC for cursor mode,
name ASC for page mode to match unified_paginate expectations
- Fix team action refresh: use /admin/teams/partial instead of legacy URL
- Remove inline filterTeams/filterByRelationship that override server-side versions
Medium Priority:
- Add get_teams_count() for accurate total in API responses
- URL encode q and relationship params in base_url construction
- URL encode q param in teams_selector_items.html template
- Fix admin controls: admins see manage/edit/delete for all teams (not "Request to Join")
- Add 500 limit to discover_public_teams for memory safety
- Fix non-admin pagination to respect page parameter
- Enable description search for consistency with in-memory filter
Low Priority:
- Fix error fallback links with consistent per_page values
- Fix loading indicator timing for HTMX (let HTMX handle indicator)
- Revert accidental per_page=20→10 changes in non-teams endpoints
- Add test for include_pagination=True cursor response path
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
---------
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
* Dependency update
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* feat: restore OAuth actions in gateways partial (#2083)
Add "Authorize" and "Fetch Tools" buttons for OAuth-enabled gateways,
matching the main admin interface layout.
Use |tojson filter for gateway.name in onclick handler to prevent
potential XSS from gateway names containing quotes or backslashes.
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix: Handle None message in session registry and missing tool_runs tracking (#2086)
Fix two critical bugs in core session and agent services:
1. Session Registry (session_registry.py)
- Added null check for message content before passing to orjson.loads()
- Prevents crash when _session_message contains None value
- Logs warning instead of raising exception on edge case
2. Tool Execution (mcp_client_chat_service.py)
- Fixed race condition where on_tool_end event arrives before on_tool_start
- Implemented buffering for out-of-order tool events with TTL (30s) and cap (100)
- Out-of-order events are buffered and reconciled when start event arrives
- Tracks dropped tool ends (TTL-expired or buffer-full) with cap (200)
- Later on_tool_start or on_tool_error clears from dropped set (prevents false orphans)
- Emits aggregated tool_error at stream end for all orphan/dropped ends (de-duplicated)
- on_tool_error clears buffered ends to avoid emitting both error and end
- Uses UUID for aggregated error ID to avoid run_id collisions
- Refactored output extraction into helper function
Tests added:
- test_respond_memory_backend_with_none_message_content: Tests None message handling
- test_chat_events_reconciles_out_of_order_tool_events: Tests event reconciliation
- test_chat_events_emits_error_for_orphan_tool_ends: Tests orphan error emission
- test_chat_events_tool_error_clears_buffered_end: Tests error/buffer consistency
- test_chat_events_buffer_full_drops_included_in_error: Tests buffer overflow tracking
- test_chat_events_ttl_expiry_included_in_error: Tests TTL expiry tracking
- test_chat_events_dropped_then_start_clears_from_dropped: Tests false orphan prevention
- test_chat_events_dropped_then_error_clears_from_dropped: Tests error clears dropped
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
* feat(auth): decouple JWT issuer verification from audience verification (#2097)
Add a new JWT_ISSUER_VERIFICATION configuration setting (default: true) to provide
granular control over JWT issuer claim validation, independent of audience
verification.
Previously, verify_jwt_token would unconditionally pass the issuer parameter
to PyJWT decode, enforcing presence and validity of the 'iss' claim even when
users intended to disable strict checks. This prevented accepting tokens with
varying or missing issuers, which is critical for:
- Dynamic Client Registration (DCR)
- Multi-tenant setups
- Custom authentication flows
Changes:
- Add jwt_issuer_verification setting (default: true) to mcpgateway/config.py
- Conditionally include issuer in decode_kwargs when verification is enabled
- Update all documentation (README, values.yaml, docs/) with the new setting
- Add .env.example entry for consistency
- Add unit test for issuer verification skip scenario
- Update all doctests with jwt_issuer_verification mock
Closes #1792
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
* 1792 jwt aud iss fix (#2107)
* feat(auth): decouple JWT issuer verification from audience verification
Add a new JWT_ISSUER_VERIFICATION configuration setting (default: true) to provide
granular control over JWT issuer claim validation, independent of audience
verification.
Previously, verify_jwt_token would unconditionally pass the issuer parameter
to PyJWT decode, enforcing presence and validity of the 'iss' claim even when
users intended to disable strict checks. This prevented accepting tokens with
varying or missing issuers, which is critical for:
- Dynamic Client Registration (DCR)
- Multi-tenant setups
- Custom authentication flows
Changes:
- Add jwt_issuer_verification setting (default: true) to mcpgateway/config.py
- Conditionally include issuer in decode_kwargs when verification is enabled
- Update all documentation (README, values.yaml, docs/) with the new setting
- Add .env.example entry for consistency
- Add unit test for issuer verification skip scenario
- Update all doctests with jwt_issuer_verification mock
Closes #1792
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* Linting
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
---------
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* tests: minor fixes to plugins performance profiler (#2105)
* tests: minor fixes to plugins performance profiler
Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>
* chore: fix yamlint issues
Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>
* chore: fix trailing whitespace in performance test
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix: restore disabled status for plugins in performance config
Restore the original disabled mode for plugins that were unintentionally
enabled during YAML reformatting:
- RateLimiterPlugin
- PrivacyNoticeInjector
- WebhookNotification
- ContentModeration (also restore ibm_granite provider config)
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix: restore original context handling in performance profiler
Revert to passing GlobalContext directly instead of pre-creating a
shared PluginContext. The original behavior provides realistic
per-request profiling because:
- Each iteration gets a fresh PluginContext via auto-wrapping
- Stateful plugins (caches, circuit breakers) don't accumulate state
- Profile times reflect real-world per-request performance
Also removes the exception swallowing so failures properly surface
as ERROR in the summary table rather than computing averages from
partial iteration data.
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
---------
Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
* Add cache auth/crypto key material and derived objects (#2041)
* cache auth/crypto key material and derived objects
Signed-off-by: Shoumi <shoumimukherjee@gmail.com>
* lint fixes
Signed-off-by: Shoumi <shoumimukherjee@gmail.com>
* fixes for test
Signed-off-by: Shoumi <shoumimukherjee@gmail.com>
* fix pylint code duplication
Signed-off-by: Shoumi <shoumimukherjee@gmail.com>
* fix linting uses
Signed-off-by: Shoumi <shoumimukherjee@gmail.com>
* Lint
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
---------
Signed-off-by: Shoumi <shoumimukherjee@gmail.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
* Feature/qr code server (#1652)
* Initial commit for qr-code-server, created using scaffold script
* Added config.yaml based on requirements.
* feat: Implement configuration management for QR code generation - load and validade
* fix: Update dependencies in pyproject.toml for QR code server
added qrcode
* Refactored config.py
-Removed extra comments
-Added info when loaded
-Removed wrong validation
* Added dev requirements to test and lint
* Add qr generation logic
- implement create_qr
- implemented simple happy path unit test
* "Fix lint issues in config.py"
* Improved qr_generator
- Added base64 output
- Added error handling
- Added png, svg and ascii formats
- Added tests to cover generator changes
* Create a resolve_output_path function
- It will check if user input has filename on it
- check if filename.ext has the correct ext
- It creates the output folder if it does not exist
* Fixed resolve output path not creating correct path
* Added ImageAscii class to convert pillow image to ascii
- save method added to allow use as qrcode image
* Refactored ImageAscii to be compatible to qrcode image factory
* Refactored file.utils to not raise error and added filename + extension as output
* Refactored generator
- Uses qrcode image factory to generate images
- Removed ascii logic from generator
- improved output path handling
- remove code used to run locally
* Added tests for generator, covering 100%
- test for multiple formats PNG, SVG, ASCII
- test for error handling
- test create qr base64
* rename main folder from qr-code-serve to qr_code_server,
Added a server test to list tools
* Fixed generator test creating output path
* Fixed resolve output path to address filename without extension, it manage the following cases
- case 1: output_path is a folder
- case 2: output_path has file extension
- case 3: output_path has filename without extension
- case 4: output_path does not have filename
* Removed qr code file generated by a test
* Added validator and decoder file with request validation model
* Improved image utils to handle batch qr code generation
- Decoupled qr image generation from the create_qr_code
- Improved Ascii save to handle BytesIO for batch zip generation
* Improved QRcode batch generator and added tests
- Handle errors
- Added tests to get 100% coverage
- Test manually to check created files
- changed ascii filename to have extension ascii
* Refactored Image utils
- Created a image generator to handle batch
- handled errors
- changed ascii format to have ascii extension
* Added create_batch_qr_codes to server and test_server
* Added dependencies for QR code Decoder
* Fixed bugs in generator and improved test coverage
- Fixed ascii not encoding in base64
- Added validation to BatchQRgeneration
- Fixed lint
* Added a load image for decoder
- loads from file image
- loads from base64
- load multiple types
* Added max data lenght validation for Qr generator
* Added max image size error for decoder
* Added a function to decode string bytes size to bytes
* Added decoder validation for image format
* Removed hard coded supported formats
Added in progress decoder
* Added request validations for validator
- target_version
- error correction
* Handled max concurrent requests using semaphore
- use max request from config
- queue max size 3*max request config
* Fixed folder and file handling for generator
- Added tests for edge cases
* Improved convert to bytes function
- Added tests for edge cases
- raise value error when cannot decode
* Fixed double quotes inside double quotes in rstrip
* Refactored test_generator and linted
- replaced TemporaryDirectory() with tmp_path fixture
* Added default values from config in generator
* Added QR validator Request model
* Refactored and linted image utils
* Refactored and improved generator
- Added config default to Generation Request model
- fixed data validation
- improved logs
* Refactored and cleaned generator errors
- cleaned tmp_path
- added base64 fail to generate test
- cleaned unused imports
* Addeed functions to estimate bits and size
- check smalles fitting version
- added table size per version and error correction
- function to estimate encoded bits for string and version
* Added validator tests and bits table size reference
* Added validate qr code to server
- Fixed lint
* Improved load_image
- Improved image size validation before b64 decoding
- Added image size validation after decoding
* Added decoder qr code
- Accept path to image or base64 image
- preprocess image using cv2 gaussian blur and treshold
- decode images with multiple codes
* Fixed bug where zipped png files were corrupted
- save image as Bytes buffer before adding to zip
- Added test to check if file is image
* change project name script to qr-code-server
* Added qr code decoder in server
Also added health check endpoint
* Added semaphore server tests
* Fixed batch generator missing format input
- Added generator test to server test
- Added Batch generator to server test
* Renamed internal function decode_qr_code to qr_decode
* Added test decode qr tool in server tests
* Added tests to call tools from server
* Fix small images not decoding
- Created a preprocessing function
- Improved logs
- Added a resize small images when preprocessing
- moved preprocessing to load image function in image utils
* Improved decoder log when cannot decode image or find file
* Added different types of qr images for decoding tests
* Improved decoder tests
- Added multiple qr code decode test
- Added multiple type tests
* Added test for multiple tools running concurrently
* Added more tests in server.py to reach > 90% coverage
* Improved test coverage for config.
- Fixed config not loading default values
* Removed Cache generated qr code config
* Linted and removed health endpoit as it is not on requirements
* Fixed wrong command in make format
* Linted and formatted the code
* Fixed image utils types and lint
* Fixed types and lint for decoder
* Added return type to qr_decode function
* Fixed types in validador
* Improved types in decoder
* Improved types and return tyoes in generator
* changed how to handle error correction in Qrgeneration config
* change BytesIO to Any in Ascii save image
* Normalized tools Output using Model
* Created Comprehensive Readme
* Normalized output for server errors using models
* Improved logs for server
- Added Log when successfull
- added log for success false
* Removed files from BatchQRresult
* Capped border at 100 to avoid long request and computer freezing
* Improved Readme with notes on Border and Colors
* Fixed lint and long lines
* Formatted and linted files for PR
* fix(qr-code-server): correct file extension handling in resolve_output_path
Fix edge cases in file path resolution:
- Handle filenames with non-matching extensions (e.g., file.jpg -> file.jpg.png)
- Handle filenames ending with dot (e.g., weird. -> weird.png)
- Only skip extension appending when existing extension matches target format
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix(qr-code-server): address security and correctness issues
Security fixes:
- Fix Containerfile build order (copy src/ before pip install)
- Add path traversal validation for naming_pattern to prevent zip-slip
Correctness fixes:
- Populate file_path, zip_file_path, output_directory in responses
- Use config values for batch defaults (size, output_directory, zip_output)
- Use config.decoding.preprocessing_enabled as default
- Fix wrong request names in logging (decode/validate used batch name)
- Convert NumPy arrays to lists for JSON serialization
- Fix README to show correct zip filename (qr.zip not qr_batch.zip)
- Fix test using wrong field name (version -> target_version)
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix(qr-code-server): improve validation and container compatibility
Validation fixes:
- Preserve whitespace in QR data (don't strip during validation)
- Clamp border to valid range [0, 100] (was only capping max)
- Add image_format validation against config.supported_image_formats
- Consistent data length validation in batch (use actual length)
Container fixes:
- Switch to opencv-python-headless (works on slim containers)
Test additions:
- Add tests for naming_pattern path traversal rejection
- Add tests for border validation clamping
Code cleanup:
- Remove unnecessary UTF-8 encoding declarations (ruff UP009)
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix(qr-code-server): ensure batch uses config for border and error_correction
Fixes:
- Add border and error_correction to BatchQRGenerationRequest model
- Pass border/error_correction to index_image_generator
- Add format validation to single QR generation (consistency with batch)
- Update server.py to expose border/error_correction for batch
Documentation:
- Update README examples to match actual output format
- Document that image_format is validation-only (auto-detection used)
- Add border/error_correction parameters to batch docs
Test fix:
- Use == instead of is for string comparison in format test
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
---------
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
* tests: add plugin smoke tests for instantiation and hook invocation (#2102)
* tests: add basic smoke tests for all plugins to check they initialize and run on all defined hooks
Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>
* fix: yamlint issues
Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>
* fix(tests): correct misleading class names in plugin smoke tests
Rename test classes from 'Disabled' to reflect that they test enabled
plugins in permissive mode, not disabled plugins:
- TestDisabledPluginsInstantiation -> TestPluginInstantiation
- TestDisabledPluginsHookInvocation -> TestPluginHookInvocation
- TestAllDisabledPluginsTogether -> TestAllPluginsTogether
Update associated docstrings for consistency.
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
---------
Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
* fix: type mismatches and missing dependency checks in plugins (#2104)
* fix: type mismatches and missing dependency checks in plugins
Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>
* chore: lint issues
Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>
---------
Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>
* chore: Update dependency-review scope, bump dependencies for Dependabot findings (#2093)
Signed-off-by: Jonathan Springer <jps@s390x.com>
* Closes #1731 (#2122)
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix flaky a2a test
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* MCP authentication controls and team membership validation (#2126)
* feat: add MCP authentication controls and team membership validation
Implement configurable authentication and consistent authorization for MCP
endpoints:
- Add MCP_REQUIRE_AUTH config option to require Bearer tokens on /mcp endpoints
(default: false allows public-only access for unauthenticated requests)
- Add team membership validation to MCP auth matching REST behavior with 60s
cache TTL - users removed from teams lose access immediately
- Extend access checks to template resources ensuring visibility and team
scoping applies consistently across all resource types
Configuration:
- MCP_REQUIRE_AUTH=false (default): unauthenticated requests get public-only access
- MCP_REQUIRE_AUTH=true: all /mcp requests require valid Bearer token
Updated: config.py, streamablehttp_transport.py, resource_service.py,
README.md, Helm charts, ADR-004, .env.example
Tests: 34 authorization tests, 60 MCP transport tests
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* feat: add visibility-based access control for A2A agents and resource templates
Implement consistent authorization controls across A2A agents and resource
templates, matching the existing tools/resources/prompts access patterns.
Changes:
- Add _apply_visibility_filter() and _check_agent_access() to A2AAgentService
for listing, get, and invoke operations
- Add _apply_visibility_filter() to ResourceService for template listing
- Fix visibility parameter precedence bug where schema defaults overrode
endpoint parameters
- Add Authorization header to nginx cache key to prevent cross-user data
leakage
- Add MCP transport tests for team membership validation
- Fix flaky circuit breaker test timing
Access control rules:
- token_teams=None: Admin unrestricted access
- token_teams=[]: Public-only access (no owner access to private entities)
- token_teams=[...]: Team-scoped access with owner access to private entities
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
---------
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* feat: add JWT token lifecycle management with JTI support (#2132)
- Add automatic JTI (JWT ID) generation to all tokens for revocation support
- Add REQUIRE_JTI configuration option to enforce JTI claims
- Log warning when tokens without JTI are accepted (for migration visibility)
- Add platform admin bootstrap authentication logging for audit trails
- Update DummySettings in doctests with require_jti attribute
Closes #2127
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* feat: add REQUIRE_USER_IN_DB configuration option (#2140)
* feat: add REQUIRE_USER_IN_DB configuration option
- Add require_user_in_db setting to enforce database user existence
- When enabled, disables platform admin bootstrap mechanism
- Add rejection logging with security_event for audit trails
- Add startup warning for ephemeral storage without strict mode
- Fix: verify DB existence even when user is cached (prevents cache bypass)
- Add unit tests for all auth paths: fallback, batched, and cache-hit (5 new tests)
- Update README.md with REQUIRE_JTI and REQUIRE_USER_IN_DB docs
- Update Helm chart values.yaml and README.md
- Update config.py docstring with new environment variables
Closes #2128
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* chore: add environment variable overrides to docker-compose
Add shell environment variable overrides for:
- AUTH_CACHE_ENABLED (default: true)
- REQUIRE_JTI (default: false)
- REQUIRE_USER_IN_DB (default: false)
- LOG_LEVEL (default: ERROR)
This allows flexibility in testing and production deployments.
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix: resolve pylint W1404 implicit string concatenation warning
Combine the two adjacent string literals into a single string
to avoid the implicit-str-concat pylint warning.
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
---------
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* feat: add environment isolation warnings and optional env claim validation (#2141) (#2143)
- Add EMBED_ENVIRONMENT_IN_TOKENS config to include env claim in gateway JWTs
- Add VALIDATE_TOKEN_ENVIRONMENT config to reject tokens with mismatched env claim
- Add startup warnings for default JWT_ISSUER/JWT_AUDIENCE in non-dev environments
- Update documentation, schema files, and Helm chart values
- Fix pre-existing doctest failures in verify_credentials.py
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* alembic location fix
Signed-off-by: Keval Mahajan <mahajankeval23@gmail.com>
* linting
Signed-off-by: Keval Mahajan <mahajankeval23@gmail.com>
* Prevent HTTP connection exhaustion and expose pool metrics (#2123)
* use shared httpx client and expose pool metrics
Signed-off-by: Shoumi <shoumimukherjee@gmail.com>
* expose pool limits & metrics
Signed-off-by: Shoumi <shoumimukherjee@gmail.com>
* fix bandit issue
Signed-off-by: Shoumi <shoumimukherjee@gmail.com>
* fix: preserve original timeout behavior and correct docstring in get_pool_stats
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
---------
Signed-off-by: Shoumi <shoumimukherjee@gmail.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
* unused import removal
Signed-off-by: Keval Mahajan <mahajankeval23@gmail.com>
* update docstrings
Signed-off-by: Keval Mahajan <mahajankeval23@gmail.com>
* feat: Add EntraID role mapping for SSO authentication (#2129)
* improved role mappings
Signed-off-by: Keval Mahajan <mahajankeval23@gmail.com>
* Issue #2054 quality review improvements
Signed-off-by: Jonathan Springer <jps@s390x.com>
* feat: enhance EntraID role mapping with id_token parsing and admin sync
- Parse groups/roles from id_token (Microsoft userinfo doesn't return them)
- Sync is_admin on login (upgrade-only, preserves manual grants)
- Smart merge for provider_metadata on bootstrap (DB values preserved)
- Add ADR-034 documenting design decisions
- Use orjson for consistency with codebase
- Fix documentation default value for sso_entra_default_role
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* feat: add group overage detection, front-channel logout, and doc improvements
EntraID SSO enhancements:
- Add group overage detection with warning when >200 groups in token
- Add GET support to /admin/logout for OIDC front-channel logout
- Move sso-entra-role-mapping.md to docs/docs/manage/ for MkDocs nav
Documentation fixes (from review):
- Fix front-channel logout URL (now supported with GET handler)
- Fix ID tokens checkbox guidance (not required for auth code flow)
- Fix admin API endpoints (/auth/email/admin/users)
- Fix step ordering (8.4 Group Claims before 8.5 Role Mapping)
- Fix .env JSON examples to single-line format
- Fix log message examples with actual commands
- Add note that certificate auth is not yet supported
- Add free developer account setup instructions
- Add ADR-034 to navigation
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix: resolve alembic multiple heads and add migration safety checks
- Add merge migration to combine gateway_refresh and sso_provider_metadata heads
- Add fresh database detection (skip when gateways missing) to multiple migrations
- Improve UUID migration (356a2d4eed6f) with robust integer type detection:
- Use exact type matching to avoid false positives (POINT, INTERVAL)
- Handle MySQL modifiers (UNSIGNED, ZEROFILL)
- Support all PostgreSQL serial types (SERIAL, BIGSERIAL, SMALLSERIAL)
- Fail-safe on partial/inconsistent migration states
- Enhance a2a_agents migration (77243f5bfce5):
- Add MySQL backfill support with JSON_UNQUOTE/JSON_EXTRACT
- Fail on orphaned tool_id values with cleanup instructions
- Add tools table existence check
- Add table/column existence checks before schema modifications
- Use batch mode consistently for SQLite FK support
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix: restore migration parent and add MySQL/MariaDB JSON support
- Restore a8f3b2c1d4e5 down_revision to original parent 77243f5bfce5
(was incorrectly changed to 5f3c681b05e1 during path relocation)
- Fix u5f6g7h8i9j0 to handle MySQL/MariaDB which don't support
server_default on JSON columns: add as nullable, backfill, alter to NOT NULL
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix: check FK constraint exists before drop in database indexes migration
The migration was failing when upgrading from v0.9.0 because it tried to
drop FK constraint 'fk_email_team_member_history_team_member_id' which
doesn't exist in older database schemas. In PostgreSQL, this aborts the
transaction and causes all subsequent migration commands to fail.
Added _fk_constraint_exists() helper that uses SQLAlchemy inspector to
check if the constraint exists before attempting to drop/recreate it.
The migration now gracefully skips the FK update for older schemas.
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
---------
Signed-off-by: Keval Mahajan <mahajankeval23@gmail.com>
Signed-off-by: Jonathan Springer <jps@s390x.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Co-authored-by: Keval Mahajan <mahajankeval23@gmail.com>
Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
* Security: Dependabot 73 (#2146)
Signed-off-by: Jonathan Springer <jps@s390x.com>
* feat: Add RFC 8707 resource parameter support for JWT access tokens (#2151)
* feat: Add RFC 8707 resource parameter support for JWT access tokens
Adds support for RFC 8707 Resource Indicators to enable OAuth providers
to return JWT tokens instead of opaque tokens.
## Problem
OAuth providers that support both token types (e.g., BetterAuth) return
opaque tokens by default. JWT tokens are only issued when the `resource`
parameter is present in the token request, per RFC 8707.
Without this parameter, MCP servers expecting JWT tokens fail with
"Invalid Compact JWS" errors when attempting to verify opaque tokens.
## Solution
- Set `resource` to `gateway.url` in /authorize endpoint
- Pass `resource` through to /callback endpoint for token exchange
- Include `resource` in both authorization URL and token request
The resource parameter identifies the MCP server (resource server) as
the intended token audience, signaling the OAuth provider to mint a
JWT token for that specific resource.
## Testing
- All 4,791 tests pass
- Updated test verifies resource parameter is included
- Manually verified JWT tokens (3 parts) returned vs opaque (1 part)
Signed-off-by: Brad McNew <mcnewbrad@gmail.com>
* docs: Update CHANGELOG for RFC 8707 OAuth resource parameter support
Signed-off-by: Brad McNew <mcnewbrad@gmail.com>
* fix: Address RFC 8707 review findings for OAuth resource parameter
- Add resource parameter to refresh_token flow to maintain JWT token type
- Add URL normalization per RFC 8707 (strip fragment and query components)
- Handle list values for multiple resource parameters with doseq=True
- Always recompute resource from gateway.url to handle URL changes
- Add resource parameter in token_storage_service during token refresh
- Add tests for callback resource verification and URL normalization
- Update CHANGELOG to note provider-specific configuration may be needed
Addresses review findings:
1. Refresh-token flow now includes resource parameter
2. URLs are normalized per RFC 8707 Section 2 requirements
3. Multiple resource values are properly encoded
4. Stale resource values are avoided by recomputing at request time
5. Test coverage expanded for callback and normalization
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix: Address additional RFC 8707 review findings
- Respect pre-configured resource values instead of always overwriting
- Validate absolute URI requirement (must have scheme and netloc)
- Support multiple resource parameters with list of tuples encoding
- Fix type signature for _normalize_resource_url to Optional[str]
- Add tests for relative URI rejection and scheme-only URLs
- Update token_storage_service to respect pre-configured resource
This ensures:
1. Custom/pre-registered resource values are preserved
2. Multiple resources are properly encoded per RFC 8707
3. Invalid URIs (relative, no scheme) are rejected with warning
4. Backwards compatibility with existing tests and behavior
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix: Address third round of RFC 8707 review findings
- Normalize existing resource values in token_storage_service refresh path
- Fix double normalization in list handling (compute once, filter once)
- Add tests for multiple resource parameter encoding (list of tuples)
- Document policy: only hierarchical URIs supported (URNs rejected)
- Document policy: query components stripped per RFC 8707 SHOULD NOT
This ensures consistent normalization across authorize/callback/refresh paths
and adds explicit test coverage for the multiple resource encoding behavior.
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix: Address fourth round of RFC 8707 review findings
- Support URN-style absolute URIs (only require scheme, not netloc)
- Add preserve_query parameter: strip for auto-derived, preserve for explicit config
- Add warnings when configured resources are dropped during normalization
- Add warnings when all list resources normalize to empty
- Consistent normalization across authorize/callback/refresh paths
Tests added:
- URN support (urn:example:app)
- file:// URI support
- preserve_query=True behavior
- Fragment always stripped even with preserve_query=True
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix: Add missing Returns in docstring
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
---------
Signed-off-by: Brad McNew <mcnewbrad@gmail.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
* Sonarqube podman file update and teams pattern (#2155)
* Fix Sonar compose
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* feat(security): Add strict team name validation and output escaping
- Add pattern validation for team names (alphanumeric, space, underscore, period, dash)
- Escape team data in templates and backend HTML generation
- Extend validate_no_xss to check JavaScript patterns
- Add validation to admin team update endpoint
- Document team name rules and migration path
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* Add teams validation sonar
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* Update sonarqube podman file
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
---------
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix: Enable CORS preflight for /servers/{id}/mcp endpoints (#2153)
* fix: Enable CORS preflight for /servers/{id}/mcp endpoints (#2152)
Allow browser-based MCP clients to connect by:
- Bypassing auth for CORS preflight (OPTIONS + Origin + Access-Control-Request-Method)
- Routing /mcp paths through middleware stack so CORSMiddleware handles preflight
Closes #2152
Signed-off-by: Brad McNew <mcnewbrad@gmail.com>
* fix: Remove duplicate test_call_tool_success function
Remove duplicate function definition that was causing ruff F811 error.
Keep the better implementation using @asynccontextmanager pattern.
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix: Address review findings for CORS preflight PR
- Add SSE-aware compression middleware to skip compression for /mcp
paths when json_response_enabled=False (SSE mode). This prevents
buffering/breaking of streaming responses while still compressing
JSON responses in the default mode.
- Fix CORS test to properly validate against actual CORSMiddleware
config instead of patching settings after app startup. Add test
for disallowed origin rejection.
- Update MCPPathRewriteMiddleware docstring to reflect actual behavior
(rewrites to /mcp/ with trailing slash, not /mcp).
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix: Prevent double token scoping for /mcp requests
Use request.state flag to track if scoping has already been done.
This is more robust than checking modified_path because:
- request.state persists across all middleware invocations
- The flag is set before any scoping work is done
The previous approach using modified_path didn't work because
the scope["modified_path"] check couldn't reliably detect the
second pass through the middleware stack.
Closes #2160
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
---------
Signed-off-by: Brad McNew <mcnewbrad@gmail.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
* feat: add OAuth 2.0 protected resource metadata for virtual servers (RFC 9728) (#2029)
* feat: add OAuth 2.0 protected resource metadata for virtual servers (RFC 9728)
Add support for MCP clients to discover OAuth authorization servers via
RFC 9728 Protected Resource Metadata. This enables browser-based SSO
authentication flows for virtual servers.
Changes:
- Add oauth_authorization_server and oauth_scopes fields to Server model
- Implement /.well-known/oauth-protected-resource/{server_name} endpoint
- Add admin UI fields for configuring OAuth settings on virtual servers
- Include database migration for new OAuth fields
Closes #2022
Signed-off-by: Jonathan Springer <jps@s390x.com>
* Updates to ensure \'.well-known\' is published at the virtual_server level
Signed-off-by: Jonathan Springer <jps@s390x.com>
* feat: add well-known endpoints for virtual servers (RFC 9728)
Add well-known URI endpoints at /servers/{server_id}/.well-known/* to support:
- oauth-protected-resource (RFC 9728 OAuth Protected Resource Metadata)
- robots.txt, security.txt, ai.txt, dnt-policy.txt
Changes:
- Create mcpgateway/routers/server_well_known.py with virtual server endpoints
- Extract get_base_url_with_protocol() and get_well_known_file_content() as
shared helpers in well_known.py
- Add comprehensive E2E tests (14 test scenarios)
The well-known files use the same configuration as the root-level endpoints,
with server validation to ensure only public, enabled servers expose metadata.
Closes #2022
Signed-off-by: Jonathan Springer <jps@s390x.com>
* fix: update unit tests for get_db override and merge alembic heads
- Fix TestServerRouterOAuthProtectedResource tests to override both
main.get_db and db.get_db (workaround for redundant definitions, see #2147)
- Merge alembic migration heads to resolve "multiple heads" error
Signed-off-by: Jonathan Springer <jps@s390x.com>
* chore: update flake8 config, fix import, and expand test README
- Add DAR ignore for all test files in .flake8
- Split import statement in session_registry.py for clarity
- Expand tests/README.md with comprehensive test suite documentation
Signed-off-by: Jonathan Springer <jps@s390x.com>
* fix: correct user context dict in unit tests for RBAC decorator
Fix unit tests that pass user parameter to functions decorated with
@require_permission. The RBAC decorator expects a dict with "email"
and "db" keys, not a simple string.
Changes:
- test_server_service.py: Fix mock setup for OAuth config update tests
to properly mock db.execute() and add permission service patch
- test_admin.py: Pass user as dict with email and db keys to satisfy
RBAC decorator requirements
- test_admin.py: Fix pre-existing flake8 issues (unused imports/vars)
These were pre-existing test issues that surfaced during testing.
Signed-off-by: Jonathan Springer <jps@s390x.com>
* fix: add pylint disable for import-outside-toplevel
Signed-off-by: Jonathan Springer <jps@s390x.com>
* fix: address PR review findings
- Use sa.false() instead of "0" for boolean default in migration
for PostgreSQL compatibility
- Move synchronous DB query to threadpool using asyncio.to_thread()
to avoid blocking the event loop in async path
Signed-off-by: Jonathan Springer <jps@s390x.com>
* fix: rebase OAuth migration onto current main alembic head
Update down_revision from 77243f5bfce5 to b9e496e91e71 and remove
the stale merge migration to maintain linear alembic history.
Signed-off-by: Jonathan Springer <jps@s390x.com>
* fix: disable OAuth when authorization server is missing
Prevent inconsistent state where oauth_enabled=True but oauth_config=None
by logging a warning and disabling OAuth when no authorization server is
provided in the admin form.
Signed-off-by: Jonathan Springer <jps@s390x.com>
* fix: Address review findings for OAuth protected resource metadata
Security fixes:
- Add well_known_enabled check to server-scoped OAuth endpoint to respect
global toggle configuration (server_well_known.py:65-67)
- Check well_known_enabled before database access in server well-known
files to prevent information leakage about server existence
(server_well_known.py:114-116)
Feature fixes:
- Add OAuth field handling to admin server edit handler - previously
OAuth settings were silently dropped on edit (admin.py:2210-2248)
- Populate OAuth fields in JavaScript edit modal from server data
(admin.js:6084-6132)
- Fix OAuth disable logic ordering in server_service to ensure config
is cleared when oauth_enabled=False even if oauth_config is also
provided in the same update (server_service.py:1225-1240)
Test coverage:
- Add TestWellKnownDisabledScenarios class with 3 tests for
well_known_enabled=false scenarios (test_oauth_protected_resource.py)
- Add test_disable_oauth_clears_config_even_when_both_provided to
verify OAuth disable logic fix (test_server_service.py)
- Add 3 admin OAuth edit tests: enable, disable, and missing auth
server scenarios (test_admin.py)
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
---------
Signed-off-by: Jonathan Springer <jps@s390x.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
* Fix regex empty match and clean up docstring examples (S5842, S6739) Closes #2166 (#2168)
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* Orjson - closes #2113 (#2169)
* Orjson - closes #2113
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* Orjson - closes #2113
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
---------
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* Update dependencies
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix: Use async I/O instead of blocking calls in async functions (S749… (#2174)
* fix: Use async I/O instead of blocking calls in async functions (S7493, S7487)
Replace synchronous file I/O and subprocess calls in async functions with
non-blocking alternatives using asyncio.to_thread() and FileResponse.
Changes:
- catalog_service.py: Use asyncio.to_thread for YAML file reads
- grpc_service.py: Use asyncio.to_thread for TLS cert reads
- translate_grpc.py: Use asyncio.to_thread for TLS cert reads
- cli_export_import.py: Use asyncio.to_thread for export/import file I/O
- admin.py: Use FileResponse for log/bundle downloads (streams async)
- dagger_deploy.py: Use asyncio.to_thread for subprocess.run git calls
- python_deploy.py: Wrap _build_component/_run_command with asyncio.to_thread
- virus_total_checker.py: Add retry-capable sync upload helper for httpx
- clamav_plugin.py: Use asyncio.to_thread for file reads
Also fixes test mock to use Path.write_bytes instead of builtins.open.
Closes #2164
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix: suppress bandit B105 false positives for non-password strings
Add nosec B105 comments to suppress false positives where bandit
incorrectly flags:
- Example placeholder tokens in OpenAPI schema documentation
- Public OAuth2 endpoint URLs containing "token" in path
- Boolean flag "is_token_revoked" and None token assignments
All are clearly not hardcoded passwords.
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
---------
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* Event-Driven Gateway Refresh for Pooled Sessions with Notifications (#2071)
* centralized notification service integration with session pooling
Signed-off-by: Keval Mahajan <mahajankeval23@gmail.com>
* update doctests
Signed-off-by: Keval Mahajan <mahajankeval23@gmail.com>
* notification service test cases
Signed-off-by: Keval Mahajan <mahajankeval23@gmail.com>
* linting fixes
Signed-off-by: Keval Mahajan <mahajankeval23@gmail.com>
* e2e test cases updation
Signed-off-by: Keval Mahajan <mahajankeval23@gmail.com>
* adr for notification service
Signed-off-by: Keval Mahajan <mahajankeval23@gmail.com>
* fix: improve type annotations in notification_service.py
- Add MessageHandlerCallback type alias for proper callable typing
- Fix asyncio.Task type parameter (Task[None])
- Add typed factory function for Set[NotificationType] default
- Add explicit type annotations to capability dict processing
- Add pyright ignore for intentional protected member access
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix: address review feedback for notification service
Security & Correctness Fixes:
- Add per-gateway lock acquisition before calling _refresh_gateway_tools_resources_prompts
to prevent race conditions with manual/auto refresh operations
- Implement flag merging during debounce window - subsequent notifications
now merge their include_resources/include_prompts flags instead of being dropped
- Add tracking of pending refresh flags to support flag merging
Documentation:
- Update ADR-034 status from "Planned" to "Accepted"
- Document known limitation: session pool keys by URL/identity, not gateway_id,
so multiple gateways sharing the same URL may have notification attribution issues
- Add limitation note to notification_service.py module docstring
Test Updates:
- Fix mock configuration for _get_refresh_lock (synchronous, returns asyncio.Lock)
- Add test for lock-held skip behavior
- Update e2e tests with proper lock mocking
Closes review feedback from PR #2071
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
* fix: add gateway_id to session pool key for correct notification attribution
Previously, session pool keys were (user_identity, url, identity_hash, transport_type).
If multiple gateways shared the same URL/auth, notifications would be attributed to
whichever gateway first created the session, causing the wrong gateway to be refreshed.
Changes:
- Add gateway_id as 5th element of PoolKey tuple
- Add gateway_id field to PooledSession dataclass
- Update _make_pool_key to include gateway_id parameter
- Update acquire/release to use gateway_id in pool key reconstruction
- Update _create_session to store gateway_id on PooledSession
- Update get_metrics to unpack 5-element pool key
- Add "Gateway Isolation" section to MCPSessionPool docstring
- Update ADR to note slightly reduced session reuse as trade-off
- Remove "Known Limitations" section since issue is now fixed
- Update all tests to use 5-element pool keys
The trade-off is slightly reduced session reuse when multiple gateways have
different gateway_ids but share the same URL/auth. This is acceptable because
correctness of notification attribution is more important than session reuse
efficiency for thi…
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.
Fix sonarqube podman and lint issues, add teams pattern