Skip to content

Commit 03da541

Browse files
committed
modified few changes
Signed-off-by: Satya <[email protected]>
1 parent ae9eae2 commit 03da541

File tree

4 files changed

+74
-19
lines changed

4 files changed

+74
-19
lines changed

mcpgateway/config.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -495,9 +495,7 @@ def validate_database(self) -> None:
495495
db_dir.mkdir(parents=True)
496496

497497
# Validation patterns for safe display (configurable)
498-
validation_dangerous_html_pattern: str = (
499-
r"<(script|iframe|object|embed|link|meta|base|form|img|svg|video|audio|source|track|area|map|canvas|applet|frame|frameset|html|head|body|style)\b|</*(script|iframe|object|embed|link|meta|base|form|img|svg|video|audio|source|track|area|map|canvas|applet|frame|frameset|html|head|body|style)>"
500-
)
498+
validation_dangerous_html_pattern: str = r"<(script|iframe|object|embed|link|meta|base|form|img|svg|video|audio|source|track|area|map|canvas|applet|frame|frameset|html|head|body|style)\b|</*(script|iframe|object|embed|link|meta|base|form|img|svg|video|audio|source|track|area|map|canvas|applet|frame|frameset|html|head|body|style)>"
501499
validation_dangerous_js_pattern: str = r"(?i)(?:^|\s|[\"'`<>=])(javascript:|vbscript:|data:\s*[^,]*[;\s]*(javascript|vbscript)|\bon[a-z]+\s*=|<\s*script\b)"
502500

503501
validation_allowed_url_schemes: List[str] = ["http://", "https://", "ws://", "wss://"]

mcpgateway/services/gateway_service.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1148,10 +1148,11 @@ async def _initialize_gateway(self, url: str, authentication: Optional[Dict[str,
11481148
>>> import asyncio
11491149
>>> async def test_params():
11501150
... try:
1151-
... await service._initialize_gateway("invalid://url")
1151+
... await service._initialize_gateway("http://invalid://url")
11521152
... except Exception as e:
1153-
... return "Failed" in str(e) or "GatewayConnectionError" in str(type(e).__name__)
1154-
>>> asyncio.run(test_params())
1153+
... return "True" if ("Failed" in str(e) or "GatewayConnectionError" in str(type(e).__name__)) else "False"
1154+
1155+
>>> print (asyncio.run(test_params()))
11551156
True
11561157
11571158
>>> # Test default parameters

mcpgateway/validators.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,7 @@ class SecurityValidator:
5252
"""Configurable validation with MCP-compliant limits"""
5353

5454
# Configurable patterns (from settings)
55-
DANGEROUS_HTML_PATTERN = (
56-
settings.validation_dangerous_html_pattern
57-
) # Default: '<(script|iframe|object|embed|link|meta|base|form|img|svg|video|audio|source|track|area|map|canvas|applet|frame|frameset|html|head|body|style)\b|</*(script|iframe|object|embed|link|meta|base|form|img|svg|video|audio|source|track|area|map|canvas|applet|frame|frameset|html|head|body|style)>'
55+
DANGEROUS_HTML_PATTERN = settings.validation_dangerous_html_pattern # Default: '<(script|iframe|object|embed|link|meta|base|form|img|svg|video|audio|source|track|area|map|canvas|applet|frame|frameset|html|head|body|style)\b|</*(script|iframe|object|embed|link|meta|base|form|img|svg|video|audio|source|track|area|map|canvas|applet|frame|frameset|html|head|body|style)>'
5856
DANGEROUS_JS_PATTERN = settings.validation_dangerous_js_pattern # Default: javascript:|vbscript:|on\w+\s*=|data:.*script
5957
ALLOWED_URL_SCHEMES = settings.validation_allowed_url_schemes # Default: ["http://", "https://", "ws://", "wss://"]
6058

tests/unit/mcpgateway/services/test_gateway_service.py

Lines changed: 68 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from __future__ import annotations
1717

1818
# Standard
19+
import asyncio
1920
from datetime import datetime, timezone
2021
from unittest.mock import AsyncMock, MagicMock, Mock
2122

@@ -265,10 +266,10 @@ async def test_ssl_verification_bypass(self, gateway_service, monkeypatch):
265266
pass
266267

267268
# ────────────────────────────────────────────────────────────────────
268-
# Validate Gateway URL Auth Failure
269+
# Validate Gateway URL Auth Failure - 401
269270
# ────────────────────────────────────────────────────────────────────
270271
@pytest.mark.asyncio
271-
async def test_validate_auth_failure(self, gateway_service, monkeypatch):
272+
async def test_validate_auth_failure_401(self, gateway_service, monkeypatch):
272273
# Mock the response object to be returned inside the async with block
273274
response_mock = MagicMock()
274275
response_mock.status_code = 401
@@ -297,6 +298,39 @@ async def test_validate_auth_failure(self, gateway_service, monkeypatch):
297298
# Expect False due to 401
298299
assert result is False
299300

301+
# ────────────────────────────────────────────────────────────────────
302+
# Validate Gateway URL Auth Failure - 403
303+
# ────────────────────────────────────────────────────────────────────
304+
@pytest.mark.asyncio
305+
async def test_validate_auth_failure_403(self, gateway_service, monkeypatch):
306+
# Mock the response object to be returned inside the async with block
307+
response_mock = MagicMock()
308+
response_mock.status_code = 403
309+
response_mock.headers = {"content-type": "text/event-stream"}
310+
311+
# Create an async context manager mock that returns response_mock
312+
stream_context = MagicMock()
313+
stream_context.__aenter__ = AsyncMock(return_value=response_mock)
314+
stream_context.__aexit__ = AsyncMock(return_value=None)
315+
316+
# Mock the AsyncClient to return this context manager from .stream()
317+
client_mock = MagicMock()
318+
client_mock.stream = AsyncMock(return_value=stream_context)
319+
client_mock.aclose = AsyncMock()
320+
321+
# Mock ResilientHttpClient to return this client
322+
resilient_client_mock = MagicMock()
323+
resilient_client_mock.client = client_mock
324+
resilient_client_mock.aclose = AsyncMock()
325+
326+
monkeypatch.setattr("mcpgateway.services.gateway_service.ResilientHttpClient", MagicMock(return_value=resilient_client_mock))
327+
328+
# Run the method
329+
result = await gateway_service._validate_gateway_url(url="http://example.com", headers={}, transport_type="SSE")
330+
331+
# Expect False due to 401
332+
assert result is False
333+
300334
# ────────────────────────────────────────────────────────────────────
301335
# Validate Gateway URL Connection Error
302336
# ────────────────────────────────────────────────────────────────────
@@ -324,14 +358,6 @@ async def test_validate_connectivity_failure(self, gateway_service, monkeypatch)
324358

325359
assert result is False
326360

327-
# ────────────────────────────────────────────────────────────────────
328-
# Validate Gateway URL Bulk Connections Validation
329-
# ────────────────────────────────────────────────────────────────────
330-
@pytest.mark.asyncio
331-
async def test_bulk_concurrent_validation(self, gateway_service, monkeypatch):
332-
# TODO
333-
pass
334-
335361
# ───────────────────────────────────────────────────────────────────────────
336362
# Validate Gateway - StreamableHTTP with mcp-session-id & redirected-url
337363
# ───────────────────────────────────────────────────────────────────────────
@@ -371,6 +397,38 @@ async def test_streamablehttp_redirect(self, gateway_service, monkeypatch):
371397
# assert result is True
372398
pass
373399

400+
# ───────────────────────────────────────────────────────────────────────────
401+
# Validate Gateway URL - Bulk Concurrent requests Validation
402+
# ───────────────────────────────────────────────────────────────────────────
403+
@pytest.mark.asyncio
404+
async def test_bulk_concurrent_validation(self, gateway_service, monkeypatch):
405+
urls = [f"http://gateway{i}.com" for i in range(20)]
406+
407+
# Simulate a successful stream context
408+
stream_context = AsyncMock()
409+
stream_context.__aenter__.return_value.status_code = 200
410+
stream_context.__aenter__.return_value.headers = {"content-type": "text/event-stream"}
411+
stream_context.__aexit__.return_value = AsyncMock()
412+
413+
# Mock client to return the above stream context
414+
mock_client = MagicMock()
415+
mock_client.stream.return_value = stream_context
416+
mock_client.aclose = AsyncMock()
417+
418+
# ResilientHttpClient mock returns a .client and .aclose
419+
resilient_client_mock = MagicMock()
420+
resilient_client_mock.client = mock_client
421+
resilient_client_mock.aclose = AsyncMock()
422+
423+
# Patch ResilientHttpClient where it’s used in your module
424+
monkeypatch.setattr("mcpgateway.services.gateway_service.ResilientHttpClient", MagicMock(return_value=resilient_client_mock))
425+
426+
# Run the validations concurrently
427+
results = await asyncio.gather(*[gateway_service._validate_gateway_url(url, {}, "SSE") for url in urls])
428+
429+
# All should be True (validation success)
430+
assert all(results)
431+
374432
# ────────────────────────────────────────────────────────────────────
375433
# LIST / GET
376434
# ────────────────────────────────────────────────────────────────────

0 commit comments

Comments
 (0)