feat(adagents): ads.txt MANAGERDOMAIN fallback discovery (#704)#705
Merged
Conversation
bokelley
added a commit
that referenced
this pull request
May 12, 2026
… 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>
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>
… 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>
d67c7b3 to
9e0ab34
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #704.
Summary
MANAGERDOMAINfallback foradagents.jsondiscovery. When/.well-known/adagents.jsonreturns 404, consults/ads.txtfor aMANAGERDOMAIN=directive and attempts discovery on the manager.AdAgentsValidationResultand theDiscoveryMethodliteral ('direct' | 'authoritative_location' | 'ads_txt_managerdomain') to the public API, with a newvalidate_adagents_domain()that returns provenance (discovery_method,manager_domain).fetch_adagentstransparently benefits from the fallback while keeping its existing dict return shape.Behavior
MANAGERDOMAIN=directive form only —# managerdomain=comment lines are rejected.MANAGERDOMAINlines: last-wins (IAB resolution per adcp#4173).discovery_methodandmanager_domainpreserved for diagnostics (not a silent pass).errors.Test plan
tests/test_adagents.py::TestParseManagerdomains— directive parsing, case-insensitivity, comment rejection, inline-comment handling, order preservation.tests/test_adagents.py::TestValidateAdagentsDomain— direct, authoritative_location, ads_txt_managerdomain, duplicate-last-wins, manager 404 terminal failure, cycle detection, comment-form not followed.tests/test_public_api.py::test_public_api_surface_matches_snapshot— snapshot regenerated for the three new exports.Refs: adcontextprotocol/adcp#4173, adcontextprotocol/adcp#4175
🤖 Generated with Claude Code