fix: validate push notification URLs before dispatch - #1169
Conversation
Client-set push config URLs were posted to by the server without any scheme or IP checks, letting a caller point the server at private, link-local, or cloud metadata hosts. Reject non-http(s) schemes, hostless URLs, private/link-local/multicast/reserved literal IPs, and known cloud metadata endpoints. Loopback addresses stay allowed so local notification receivers keep working.
🧪 Code Coverage (vs
|
| Base | PR | Delta | |
|---|---|---|---|
| src/a2a/server/tasks/base_push_notification_sender.py | 94.44% | 96.92% | 🟢 +2.48% |
| Total | 93.00% | 93.02% | 🟢 +0.02% |
Generated by coverage-comment.yml
|
Heads-up: #1164 by @SashaMIT (filed the same day) covers the same fix — validating push-notification URLs in Maintainers: these two are duplicates — consider merging one and closing the other to avoid split review cycles. Happy to help converge either way. |
|
Thanks @kuangmi-bit — agreed these overlap. #1164 is the survivor on our side (same dispatch-time URL validation, plus the redirect-client rejection and integration coverage that already took a review pass). Happy for maintainers to close this PR as a duplicate of #1164 to keep review on one branch. |
Thanks @kuangmi-bit and @SashaMIT for the heads-up. Some context on our side: this PR comes from a test-framework effort we are building to exercise the A2A protocol across the SDKs. The findings were filed as a single batch on the same day, so the individual submissions were not cross-checked against each other before reporting. Regarding the overlap with #1164: at the time we filed this report, #1164 had not passed all of its required checks, so we did not treat it as an existing, mergeable fix to deduplicate against — and a PR that has not passed all checks cannot be accepted anyway. The two submissions were made independently and target the same problem from the same finding. That said, I have no objection to whatever review path the maintainers prefer — if they want #1164 to remain the single surviving branch, this PR can be closed as a duplicate. |
Summary
Client-set push config URLs were POSTed to by the server without any
validation, so a caller who can create a push notification config can
point the server at loopback, private-network, link-local, or
cloud-metadata hosts.
Root cause
In
src/a2a/server/tasks/base_push_notification_sender.py,_dispatch_notificationpassespush_info.urlstraight toself._client.post(url, ...)— the URL comes from aTaskPushNotificationConfigthat callers can create, and no scheme orIP checks exist before the request is sent.
Fix
Added
_validate_push_url()and call it before POSTing:http/httpsschemes are allowedmulticast are rejected
169.254.169.254,metadata.google.internal,metadata.azure.com) are blocked explicitlyInvalid URLs fail the notification dispatch gracefully (logged and
skipped), the same failure path as a normal HTTP error.
Testing
cloud-metadata hostnames, non-http schemes, hostless URLs, and the
public-IP allow path.
./scripts/lint.shpasses (ruff + ty).tests/server/tasks/test_push_notification_sender.py— 15 passed.