Skip to content

fix(security): normalize /v1 paths for rate limit tier matching - #6879

Open
prakhar-singh1928 wants to merge 2 commits into
mainfrom
fix/rate-limit-v1-tier-matching
Open

prakhar-singh1928 wants to merge 2 commits into
mainfrom
fix/rate-limit-v1-tier-matching

Conversation

@prakhar-singh1928

@prakhar-singh1928 prakhar-singh1928 commented Sep 18, 2026

Copy link
Copy Markdown
Collaborator

Pull Request

🔗 Related Issue

Closes #


📝 Summary

Normalize request paths in RateLimitMiddleware by stripping root path, API aliases, and /v1 prefixes before pattern matching.

Previously, RateLimitMiddleware compiled regex patterns against unversioned paths (e.g., ^/auth/email/..., ^/(tokens|oauth|rbac)(/|$)), while endpoints were mounted under /v1. As a result, requests to versioned routes fell through to the default LOW tier (500 rpm) instead of matching their intended CRITICAL (10 rpm) or HIGH (30 rpm) tiers. Normalizing the path before evaluating tier rules aligns RateLimitMiddleware with TokenScopingMiddleware and restores correct rate-limiting enforcement.


📏 Reviewability

  • This PR has one clear purpose
  • The linked issue is not labeled triage
  • Unrelated bugs or improvements are tracked in separate issues/PRs
  • Tests are included with the code they validate
  • If AI-assisted, I understand and can explain the generated changes

🏷️ Type of Change

  • Bug fix
  • Feature / Enhancement
  • Documentation
  • Refactor
  • Chore (deps, CI, tooling)
  • Other (describe below)

🧪 Verification

Ran unit tests and static analysis linters across the modified files and test suite:

Check Command Status
Lint suite make ruff && make interrogate && make pylint && make bandit Passed
Unit tests uv run pytest tests/unit/mcpgateway/middleware/test_rate_limit_middleware.py Passed
Coverage ≥ 80% make coverage Passed

✅ Checklist

  • Code formatted (make black isort pre-commit)
  • Tests added/updated for changes
  • Documentation updated (if applicable)
  • No secrets or credentials committed

📓 Notes (optional)

Added parameterized unit test test_endpoint_tier_matches_v1_versioned_paths to tests/unit/mcpgateway/middleware/test_rate_limit_middleware.py verifying /v1-prefixed requests correctly match CRITICAL, CRITICAL_SSO, CRITICAL_INVITATION, SESSION_REFRESH, HIGH, HIGH_APPBRIDGE, MEDIUM, and LOW tiers.

Normalize request paths in RateLimitMiddleware by stripping root path,
API aliases, and /v1 prefixes before pattern matching. This prevents
versioned endpoints from falling through to the LOW tier default.

Signed-off-by: prakhar-singh1928 <prakhar.singh1928@ibm.com>
…ormalization

Remove dead version guards from tier patterns that were unreachable after
_normalize_path_for_matching strips /v1 from every path before matching:
- SESSION_REFRESH: ^(/v1)?/auth/refresh$ -> ^/auth/refresh$
- CRITICAL_INVITATION: ^/(?:v1/)?teams/... -> ^/teams/...

Extract path normalization in dispatch so it runs once per request.
Introduce _get_tier_for_normalized and _get_tier_name_for_normalized as
the inner matching primitives; get_endpoint_tier and _get_tier_name
delegate to them, preserving the public API. Update the forged-HMAC test
to patch _get_tier_for_normalized, which is the method dispatch now calls
directly.

Add deny-path regression assertions to test_endpoint_tier_matches_v1_versioned_paths
confirming that /v1/auth/email/login, /v1/tokens, /v1/oauth/token, and
/v1/rbac/roles never return the 500 rpm LOW default.

Signed-off-by: prakhar-singh1928 <prakhar.singh1928@ibm.com>

This branch has not been deployed

No deployments
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.

1 participant