Skip to content
IBMGHSA-c7vv-9h9c-fvj4

ContextForge SSRF protection bypass via outbound HTTP redirects (Tool/Gateway/A2A invocation paths)

HighPublished Aug 25, 2026

ContextForge validates Tool, Gateway, and A2A URLs for SSRF at registration/update time, but the Python outbound clients used later for those URLs follow redirects by default. A user who can create and invoke one of those resources can submit a public URL that passes validation, then return a redirect to a blocked destination such as cloud metadata, localhost, or an RFC1918 service. The redirected Location is not revalidated. At commit f855e54d5b7bc1c91b977574a03b91eff6b86bb6. ## Source evidence - mcpgateway/common/validators.py:1289-1290 — _validate_ssrf() called only for the hostname parsed from the submitted URL. - mcpgateway/common/validators.py:1417-1451 — blocks configured metadata networks, localhost, and private ranges. - mcpgateway/schemas.py:563-579 — validates ToolCreate.url. - mcpgateway/schemas.py:2851-2862 — validates GatewayCreate.url. - mcpgateway/schemas.py:4601-4612, :4926-4937 — validates A2A endpoint URLs. - mcpgateway/services/http_client_service.py:120-126 — shared httpx.AsyncClient created with follow_redirects=True. - mcpgateway/services/http_client_service.py:304-367 — isolated clients default follow_redirects=True. - mcpgateway/services/tool_service.py:9...

GitHub advisory

Affected versions

PackageAffectedFixed in
mcp-context-forge
Product
< v1.0.2v1.0.2
Details and references

ContextForge validates Tool, Gateway, and A2A URLs for SSRF at registration/update time, but the Python outbound clients used later for those URLs follow redirects by default. A user who can create and invoke one of those resources can submit a public URL that passes validation, then return a redirect to a blocked destination such as cloud metadata, localhost, or an RFC1918 service. The redirected Location is not revalidated. At commit f855e54d5b7bc1c91b977574a03b91eff6b86bb6. ## Source evidence - mcpgateway/common/validators.py:1289-1290 — _validate_ssrf() called only for the hostname parsed from the submitted URL. - mcpgateway/common/validators.py:1417-1451 — blocks configured metadata networks, localhost, and private ranges. - mcpgateway/schemas.py:563-579 — validates ToolCreate.url. - mcpgateway/schemas.py:2851-2862 — validates GatewayCreate.url. - mcpgateway/schemas.py:4601-4612, :4926-4937 — validates A2A endpoint URLs. - mcpgateway/services/http_client_service.py:120-126 — shared httpx.AsyncClient created with follow_redirects=True. - mcpgateway/services/http_client_service.py:304-367 — isolated clients default follow_redirects=True. - mcpgateway/services/tool_service.py:933-940 — REST tool HTTP client built without disabling redirects. - mcpgateway/services/tool_service.py:4957-4983 — invokes REST tools against stored URL. - mcpgateway/services/gateway_service.py:3719-3722, :3735, :3805, :5666-5680, :5833-5846 — gateway/SSE/StreamableHTTP clients keep redirects enabled. - mcpgateway/services/a2a_service.py:1942-1943, :2255, :2327 — Python A2A requests use shared client. ## Positive control (proves the maintainers know how to disable redirects) - mcpgateway/services/openapi_service.py:61-65 — explicitly disables redirects to prevent SSRF bypass. - mcpgateway/services/openapi_service.py:80-84 — uses get_isolated_http_client(..., follow_redirects=False). - mcpgateway/services/http_client_service.py:335-336 — documents that follow_redirects=False should be used for SSRF-sensitive requests. ## RBAC reachability - mcpgateway/main.py:4927-4935 — POST /tools behind tools.create. - mcpgateway/main.py:6496-6503 — POST /gateways behind gateways.create. - mcpgateway/main.py:4478-4487 — POST /a2a behind a2a.create. - mcpgateway/alembic/versions/v1a2b3c4d5e6_assign_default_viewer_role_to_existing_users.py:51-110 — gives team-admin/developer roles the relevant create + execute permissions. ## PoC Local non-destructive primitive imports the project validator, blocks a direct metadata URL, accepts a public-looking URL, then demonstrates that an httpx client configured with follow_redirects=True follows a 302 to metadata. ```text direct metadata blocked: direct metadata URL contains IP address blocked by SSRF protection (network: 169.254.169.254/32) attacker URL accepted by preflight validator: https://example.com/redirect final status: 200 requests made: - https://example.com/redirect - http://169.254.169.254/latest/meta-data/iam/security-credentials/ ``` ## Root cause Preflight URL validation runs once at registration/update time. The runtime httpx client uses default follow_redirects=True. Redirect Location is not revalidated. The maintainers' own openapi_service.py demonstrates the correct pattern (follow_redirects=False) — the issue is that pattern is not applied across REST tool, gateway health/discovery, SSE/StreamableHTTP, and A2A code paths. ## Auth boundary Boundary: server-side egress scope on ContextForge — outbound requests from the server should be limited to operator-authorized destinations, not attacker-redirected internal targets. Respected at: openapi_service.py:61-65 + :80-84 (disables redirects + revalidates). Violated at: tool_service.py:4957-4983, gateway_service.py:3719-3846, a2a_service.py:1942-2327. SSRF-sensitive outbound clients allow redirects without per-hop revalidation. ## Impact In a multi-user deployment where developer or team-admi

Severity from
GitHub (reviewed advisory)
Weakness
CWE-918

More IBM advisories

All IBM

Critical advisories by email

Wednesdays: the week’s critical and high advisories in the AI and data stack, with the fixed versions. Only in weeks that have some.

Double opt-in. Unsubscribe any time.