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-
141import json
152import logging
163
196
207import httpx
218
22- from pydantic import ValidationError
23-
249from a2a .client .errors import (
2510 A2AClientHTTPError ,
2611 A2AClientJSONError ,
2914 AgentCard ,
3015)
3116from 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
3924logger = 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