@@ -903,13 +903,21 @@ <h2 id="examples">Examples</h2>
903903 # Track visited URLs to detect loops
904904 visited_urls: set[str] = set()
905905
906+ is_redirect = False
907+
906908 for depth in range(MAX_REDIRECT_DEPTH + 1):
907909 # Check for redirect loop
908910 if url in visited_urls:
909- raise AdagentsValidationError(f"Circular redirect detected: {url} already visited")
911+ raise AdagentsValidationError(
912+ "Circular redirect detected in authoritative_location chain"
913+ )
910914 visited_urls.add(url)
911915
912- data = await _fetch_adagents_url(url, timeout, user_agent, client)
916+ # Use the caller's client for the initial fetch only. Redirect targets
917+ # use a fresh client to avoid leaking credentials to third-party URLs.
918+ fetch_client = None if is_redirect else client
919+
920+ data = await _fetch_adagents_url(url, timeout, user_agent, fetch_client)
913921
914922 # Check if this is a redirect. A response with authoritative_location but no
915923 # authorized_agents indicates a redirect. If both are present, authorized_agents
@@ -925,6 +933,9 @@ <h2 id="examples">Examples</h2>
925933 f"authoritative_location must be an HTTPS URL, got: {authoritative_url!r}"
926934 )
927935
936+ # Validate the redirect target is not a private/reserved address
937+ _validate_redirect_url(authoritative_url)
938+
928939 # Check if we've exceeded max depth
929940 if depth >= MAX_REDIRECT_DEPTH:
930941 raise AdagentsValidationError(
@@ -933,6 +944,7 @@ <h2 id="examples">Examples</h2>
933944
934945 # Follow the redirect
935946 url = authoritative_url
947+ is_redirect = True
936948 continue
937949
938950 # We have the final data with authorized_agents (or both fields present,
@@ -1315,6 +1327,9 @@ <h2 id="raises">Raises</h2>
13151327<pre><code class="python">def get_all_properties(adagents_data: dict[str, Any]) -> list[dict[str, Any]]:
13161328 """Extract all properties from adagents.json data.
13171329
1330+ Handles all authorization types: inline_properties, property_ids,
1331+ property_tags, and publisher_properties.
1332+
13181333 Args:
13191334 adagents_data: Parsed adagents.json data
13201335
@@ -1331,6 +1346,10 @@ <h2 id="raises">Raises</h2>
13311346 if not isinstance(authorized_agents, list):
13321347 raise AdagentsValidationError("adagents.json must have 'authorized_agents' array")
13331348
1349+ top_level_properties = adagents_data.get("properties", [])
1350+ if not isinstance(top_level_properties, list):
1351+ top_level_properties = []
1352+
13341353 properties = []
13351354 for agent in authorized_agents:
13361355 if not isinstance(agent, dict):
@@ -1340,20 +1359,17 @@ <h2 id="raises">Raises</h2>
13401359 if not agent_url:
13411360 continue
13421361
1343- agent_properties = agent.get("properties", [])
1344- if not isinstance(agent_properties, list):
1345- continue
1362+ agent_properties = _resolve_agent_properties(agent, top_level_properties)
13461363
1347- # Add each property with the agent URL for reference
13481364 for prop in agent_properties:
1349- if isinstance(prop, dict):
1350- # Create a copy and add agent_url
1351- prop_with_agent = {**prop, "agent_url": agent_url}
1352- properties.append(prop_with_agent)
1365+ prop_with_agent = {**prop, "agent_url": agent_url}
1366+ properties.append(prop_with_agent)
13531367
13541368 return properties</code></pre>
13551369</details>
13561370<div class="desc"><p>Extract all properties from adagents.json data.</p>
1371+ <p>Handles all authorization types: inline_properties, property_ids,
1372+ property_tags, and publisher_properties.</p>
13571373<h2 id="args">Args</h2>
13581374<dl>
13591375<dt><strong><code>adagents_data</code></strong></dt>
@@ -1597,12 +1613,10 @@ <h2 id="example">Example</h2>
15971613 if not isinstance(authorized_agents, list):
15981614 raise AdagentsValidationError("adagents.json must have 'authorized_agents' array")
15991615
1600- # Get top-level properties for reference-based authorization types
16011616 top_level_properties = adagents_data.get("properties", [])
16021617 if not isinstance(top_level_properties, list):
16031618 top_level_properties = []
16041619
1605- # Normalize the agent URL for comparison
16061620 normalized_agent_url = normalize_url(agent_url)
16071621
16081622 for agent in authorized_agents:
@@ -1613,48 +1627,10 @@ <h2 id="example">Example</h2>
16131627 if not agent_url_from_json:
16141628 continue
16151629
1616- # Match agent URL (protocol-agnostic)
16171630 if normalize_url(agent_url_from_json) != normalized_agent_url:
16181631 continue
16191632
1620- # Found the agent - determine authorization type
1621- authorization_type = agent.get("authorization_type", "")
1622-
1623- # Handle inline_properties (properties array directly on agent)
1624- if authorization_type == "inline_properties" or "properties" in agent:
1625- properties = agent.get("properties", [])
1626- if not isinstance(properties, list):
1627- return []
1628- return [p for p in properties if isinstance(p, dict)]
1629-
1630- # Handle property_ids (filter top-level properties by property_id)
1631- if authorization_type == "property_ids":
1632- authorized_ids = set(agent.get("property_ids", []))
1633- return [
1634- p
1635- for p in top_level_properties
1636- if isinstance(p, dict) and p.get("property_id") in authorized_ids
1637- ]
1638-
1639- # Handle property_tags (filter top-level properties by tags)
1640- if authorization_type == "property_tags":
1641- authorized_tags = set(agent.get("property_tags", []))
1642- return [
1643- p
1644- for p in top_level_properties
1645- if isinstance(p, dict) and set(p.get("tags", [])) & authorized_tags
1646- ]
1647-
1648- # Handle publisher_properties (cross-domain references)
1649- # Returns the selector objects; caller must resolve against other domains
1650- if authorization_type == "publisher_properties":
1651- publisher_props = agent.get("publisher_properties", [])
1652- if not isinstance(publisher_props, list):
1653- return []
1654- return [p for p in publisher_props if isinstance(p, dict)]
1655-
1656- # No recognized authorization type - return empty
1657- return []
1633+ return _resolve_agent_properties(agent, top_level_properties)
16581634
16591635 return []</code></pre>
16601636</details>
0 commit comments