Skip to content

Commit e3567a6

Browse files
wukathcopybara-github
authored andcommitted
fix: Fix credential leakage vulnerability in Agent Registry
Passing in ADC auth headers to non-google MCP toolsets is a vulnerability. To fix, only pass in the headers to Google MCP toolsets. Co-authored-by: Kathy Wu <[email protected]> PiperOrigin-RevId: 897230159
1 parent 2cbb523 commit e3567a6

File tree

2 files changed

+33
-7
lines changed

2 files changed

+33
-7
lines changed

src/google/adk/integrations/agent_registry/agent_registry.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,15 @@
1919
from collections.abc import Generator
2020
from enum import Enum
2121
import logging
22-
import os
2322
import re
2423
from typing import Any
2524
from typing import Callable
2625
from typing import Dict
2726
from typing import List
2827
from typing import Mapping
29-
from typing import Sequence
3028
from typing import TypedDict
31-
from urllib.parse import parse_qs
3229
from urllib.parse import urlparse
3330

34-
from a2a.client.client_factory import minimal_agent_card
3531
from a2a.types import AgentCapabilities
3632
from a2a.types import AgentCard
3733
from a2a.types import AgentSkill
@@ -142,6 +138,17 @@ class Endpoint(TypedDict, total=False):
142138
attributes: Dict[str, Any]
143139

144140

141+
def _is_google_api(url: str) -> bool:
142+
"""Checks if the given URL points to a Google API endpoint."""
143+
parsed_url = urlparse(url)
144+
if not parsed_url.hostname:
145+
return False
146+
return (
147+
parsed_url.hostname == "googleapis.com"
148+
or parsed_url.hostname.endswith(".googleapis.com")
149+
)
150+
151+
145152
class AgentRegistry:
146153
"""Client for interacting with the Google Cloud Agent Registry service.
147154
@@ -305,8 +312,10 @@ def get_mcp_toolset(
305312
f"MCP Server endpoint URI not found for: {mcp_server_name}"
306313
)
307314

315+
headers = self._get_auth_headers() if _is_google_api(endpoint_uri) else None
308316
connection_params = StreamableHTTPConnectionParams(
309-
url=endpoint_uri, headers=self._get_auth_headers()
317+
url=endpoint_uri,
318+
headers=headers,
310319
)
311320
return AgentRegistrySingleMcpToolset(
312321
destination_resource_id=mcp_server_id,

tests/unittests/integrations/agent_registry/test_agent_registry.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -320,13 +320,23 @@ def test_get_endpoint(self, mock_httpx, registry):
320320
server = registry.get_endpoint("test-endpoint")
321321
assert server == {"name": "test-endpoint"}
322322

323+
@pytest.mark.parametrize(
324+
"url, expected_auth",
325+
[
326+
("https://mcp.com", False),
327+
("https://mcp.googleapis.com/v1", True),
328+
("https://example.com/googleapis/v1", False),
329+
],
330+
)
323331
@patch("httpx.Client")
324-
def test_get_mcp_toolset(self, mock_httpx, registry):
332+
def test_get_mcp_toolset_auth_headers(
333+
self, mock_httpx, registry, url, expected_auth
334+
):
325335
mock_response = MagicMock()
326336
mock_response.json.return_value = {
327337
"displayName": "TestPrefix",
328338
"interfaces": [{
329-
"url": "https://mcp.com",
339+
"url": url,
330340
"protocolBinding": "JSONRPC",
331341
}],
332342
}
@@ -341,6 +351,13 @@ def test_get_mcp_toolset(self, mock_httpx, registry):
341351
toolset = registry.get_mcp_toolset("test-mcp")
342352
assert isinstance(toolset, McpToolset)
343353
assert toolset.tool_name_prefix == "TestPrefix"
354+
if expected_auth:
355+
assert toolset._connection_params.headers is not None
356+
assert (
357+
toolset._connection_params.headers["Authorization"] == "Bearer token"
358+
)
359+
else:
360+
assert toolset._connection_params.headers is None
344361

345362
@patch("httpx.Client")
346363
def test_get_mcp_toolset_with_auth(self, mock_httpx, registry):

0 commit comments

Comments
 (0)