Skip to content

Commit fddea1a

Browse files
bokelleyclaude
andauthored
feat(adagents): ads.txt MANAGERDOMAIN fallback discovery (#704) (#705)
* feat(adagents): add ads.txt MANAGERDOMAIN fallback discovery (#704) Implements RFC 4175: when /.well-known/adagents.json returns 404 on a publisher, consult /ads.txt for a MANAGERDOMAIN= directive and attempt discovery one hop later on the manager. The fallback only kicks in for the directive form — pure-comment lines like `# managerdomain=` are rejected — and follows IAB last-wins resolution when a file declares multiple MANAGERDOMAIN entries. Manager-domain 404 is a terminal failure, not a silent pass. `AdAgentsValidationResult` and the `DiscoveryMethod` literal are added to the public API, exposing which path produced the data (direct, authoritative_location, or ads_txt_managerdomain) plus the resolved `manager_domain` for diagnostics. `fetch_adagents` transparently benefits from the fallback while keeping its dict return shape; callers who need provenance use the new `validate_adagents_domain`. Refs: adcontextprotocol/adcp#4173, adcontextprotocol/adcp#4175 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(adagents): close SSRF + size + redirect-404 gaps on MANAGERDOMAIN fallback Reviewer follow-ups on #705 / #704: - SSRF: a publisher-controlled MANAGERDOMAIN=169.254.169.254 (or any loopback / private / link-local literal) would have passed `_validate_publisher_domain` and forced the SDK into a cross-origin HTTPS GET against internal services. The fallback now routes every manager domain through `_ensure_safe_manager_domain`, which composes the existing `_validate_redirect_url` SSRF gate with the publisher domain validator. - ads.txt size: the body was passed to `_parse_managerdomains` with no cap, so a hostile publisher could force the SDK to buffer arbitrary data. Bodies over MAX_ADS_TXT_BYTES (1 MiB) are now discarded. - Redirect-target 404: a 404 from a followed authoritative_location target previously bubbled up as AdagentsNotFoundError and triggered the ads.txt MANAGERDOMAIN fallback on the original publisher, which conflates a broken pointer with a missing manifest. It now surfaces as AdagentsValidationError so the fallback only fires on the publisher's own direct 404. Also documents the publisher-scope caveat on `validate_adagents_domain` — callers wiring `ads_txt_managerdomain` results into authorization decisions must verify the manager's adagents.json explicitly scopes the source publisher (per the TS reference's `hasExplicitPublisherScope` gate). Adding that gate as a default is tracked as a follow-up. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 2d1ae2f commit fddea1a

4 files changed

Lines changed: 806 additions & 57 deletions

File tree

src/adcp/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,17 @@
1111
from importlib.metadata import version as _pkg_version
1212

1313
from adcp.adagents import (
14+
AdAgentsValidationResult,
1415
AuthorizationContext,
16+
DiscoveryMethod,
1517
domain_matches,
1618
fetch_adagents,
1719
fetch_agent_authorizations,
1820
get_all_properties,
1921
get_all_tags,
2022
get_properties_by_agent,
2123
identifiers_match,
24+
validate_adagents_domain,
2225
verify_agent_authorization,
2326
verify_agent_for_property,
2427
)
@@ -805,9 +808,12 @@ def get_adcp_version() -> str:
805808
# Configuration types
806809
"PushNotificationConfig",
807810
# Adagents validation
811+
"AdAgentsValidationResult",
808812
"AuthorizationContext",
813+
"DiscoveryMethod",
809814
"fetch_adagents",
810815
"fetch_agent_authorizations",
816+
"validate_adagents_domain",
811817
"verify_agent_authorization",
812818
"verify_agent_for_property",
813819
"domain_matches",

0 commit comments

Comments
 (0)