Skip to content

Commit 8a434d8

Browse files
committed
fix(ci): correct ruff lint errors and Python 3.10 syntax in security patch
- Remove module-level research docstrings from card_resolver.py and default_request_handler.py (originals have none; D415 lint failure) - Add missing docstrings to five push-notification handler methods that existed in the original SDK (D102 lint failure) - Fix import ordering in card_resolver.py and url_validation.py (I001) - Convert double-quoted string literals to single quotes throughout both patched source files (Q000; project uses quote-style = single) - Restore parentheses in except clause: except (asyncio.CancelledError, GeneratorExit): Bare tuple syntax was added in Python 3.14; CI targets Python 3.10 where it raises SyntaxError (ruff had silently removed the parens)
1 parent 8c73b03 commit 8a434d8

5 files changed

Lines changed: 235 additions & 161 deletions

File tree

src/a2a/client/card_resolver.py

Lines changed: 16 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,3 @@
1-
"""Patched version of a2a/client/card_resolver.py
2-
3-
Fix for A2A-SSRF-01: validate AgentCard.url before returning the card.
4-
5-
Diff summary vs. original (v0.3.25):
6-
+ import A2ASSRFValidationError, validate_agent_card_url from a2a.utils.url_validation
7-
+ call validate_agent_card_url(agent_card.url) after model_validate()
8-
+ wrap in try/except to raise A2AClientJSONError with a clear SSRF message
9-
+ validate additional_interfaces[*].url as well (same attack surface)
10-
11-
Target file: src/a2a/client/card_resolver.py
12-
"""
13-
141
import json
152
import logging
163

@@ -19,8 +6,6 @@
196

207
import httpx
218

22-
from pydantic import ValidationError
23-
249
from a2a.client.errors import (
2510
A2AClientHTTPError,
2611
A2AClientJSONError,
@@ -29,11 +14,11 @@
2914
AgentCard,
3015
)
3116
from a2a.utils.constants import AGENT_CARD_WELL_KNOWN_PATH
32-
33-
# ---- NEW IMPORT (fix for A2A-SSRF-01) ----
34-
from a2a.utils.url_validation import A2ASSRFValidationError, validate_agent_card_url
35-
36-
# -------------------------------------------
17+
from a2a.utils.url_validation import (
18+
A2ASSRFValidationError,
19+
validate_agent_card_url,
20+
)
21+
from pydantic import ValidationError
3722

3823

3924
logger = logging.getLogger(__name__)
@@ -55,8 +40,8 @@ def __init__(
5540
base_url: The base URL of the agent's host.
5641
agent_card_path: The path to the agent card endpoint, relative to the base URL.
5742
"""
58-
self.base_url = base_url.rstrip("/")
59-
self.agent_card_path = agent_card_path.lstrip("/")
43+
self.base_url = base_url.rstrip('/')
44+
self.agent_card_path = agent_card_path.lstrip('/')
6045
self.httpx_client = httpx_client
6146

6247
async def get_agent_card(
@@ -89,9 +74,9 @@ async def get_agent_card(
8974
if not relative_card_path:
9075
path_segment = self.agent_card_path
9176
else:
92-
path_segment = relative_card_path.lstrip("/")
77+
path_segment = relative_card_path.lstrip('/')
9378

94-
target_url = f"{self.base_url}/{path_segment}"
79+
target_url = f'{self.base_url}/{path_segment}'
9580

9681
try:
9782
response = await self.httpx_client.get(
@@ -101,13 +86,13 @@ async def get_agent_card(
10186
response.raise_for_status()
10287
agent_card_data = response.json()
10388
logger.info(
104-
"Successfully fetched agent card data from %s: %s",
89+
'Successfully fetched agent card data from %s: %s',
10590
target_url,
10691
agent_card_data,
10792
)
10893
agent_card = AgentCard.model_validate(agent_card_data)
10994

110-
# ---- FIX: A2A-SSRF-01 -- validate card.url before returning ----
95+
# Validate card.url before returning (fix for A2A-SSRF-01).
11196
# Without this check, any caller who controls the card endpoint
11297
# can redirect all subsequent RPC calls to an internal address.
11398
try:
@@ -117,30 +102,29 @@ async def get_agent_card(
117102
validate_agent_card_url(iface.url)
118103
except A2ASSRFValidationError as e:
119104
raise A2AClientJSONError(
120-
f"AgentCard from {target_url} failed SSRF URL validation: {e}"
105+
f'AgentCard from {target_url} failed SSRF URL validation: {e}'
121106
) from e
122-
# -----------------------------------------------------------------
123107

124108
if signature_verifier:
125109
signature_verifier(agent_card)
126110

127111
except httpx.HTTPStatusError as e:
128112
raise A2AClientHTTPError(
129113
e.response.status_code,
130-
f"Failed to fetch agent card from {target_url}: {e}",
114+
f'Failed to fetch agent card from {target_url}: {e}',
131115
) from e
132116
except json.JSONDecodeError as e:
133117
raise A2AClientJSONError(
134-
f"Failed to parse JSON for agent card from {target_url}: {e}"
118+
f'Failed to parse JSON for agent card from {target_url}: {e}'
135119
) from e
136120
except httpx.RequestError as e:
137121
raise A2AClientHTTPError(
138122
503,
139-
f"Network communication error fetching agent card from {target_url}: {e}",
123+
f'Network communication error fetching agent card from {target_url}: {e}',
140124
) from e
141125
except ValidationError as e:
142126
raise A2AClientJSONError(
143-
f"Failed to validate agent card structure from {target_url}: {e.json()}"
127+
f'Failed to validate agent card structure from {target_url}: {e.json()}'
144128
) from e
145129

146130
return agent_card

0 commit comments

Comments
 (0)