Skip to content

fix: validate push notification URLs before dispatch - #1169

Open
ez-lbz wants to merge 1 commit into
a2aproject:mainfrom
ez-lbz:main
Open

fix: validate push notification URLs before dispatch#1169
ez-lbz wants to merge 1 commit into
a2aproject:mainfrom
ez-lbz:main

Conversation

@ez-lbz

@ez-lbz ez-lbz commented Aug 7, 2026

Copy link
Copy Markdown

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_notification passes push_info.url straight to
self._client.post(url, ...) — the URL comes from a
TaskPushNotificationConfig that callers can create, and no scheme or
IP checks exist before the request is sent.

Fix

Added _validate_push_url() and call it before POSTing:

  • only http/https schemes are allowed
  • a hostname is required
  • literal IPs that are private, loopback, link-local, unspecified, or
    multicast are rejected
  • known cloud metadata endpoints (169.254.169.254,
    metadata.google.internal, metadata.azure.com) are blocked explicitly

Invalid URLs fail the notification dispatch gracefully (logged and
skipped), the same failure path as a normal HTTP error.

Testing

  • Added unit tests covering blocked private/loopback/link-local IPs,
    cloud-metadata hostnames, non-http schemes, hostless URLs, and the
    public-IP allow path.
  • ./scripts/lint.sh passes (ruff + ty).
  • tests/server/tasks/test_push_notification_sender.py — 15 passed.

@ez-lbz
ez-lbz requested a review from a team as a code owner August 7, 2026 02:02
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.
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown

🧪 Code Coverage (vs main)

⬇️ Download Full Report

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

@kuangmi-bit

Copy link
Copy Markdown

Heads-up: #1164 by @SashaMIT (filed the same day) covers the same fix — validating push-notification URLs in _dispatch_notification against loopback/private/link-local/metadata hosts. #1164 additionally ships integration coverage (tests/integration/push_notifications/agent_app.py) and has already iterated on review feedback (rejecting follow_redirects=True clients outright, among other points).

Maintainers: these two are duplicates — consider merging one and closing the other to avoid split review cycles. Happy to help converge either way.

@SashaMIT

SashaMIT commented Aug 9, 2026

Copy link
Copy Markdown

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.

@ez-lbz

ez-lbz commented Aug 9, 2026

Copy link
Copy Markdown
Author

Heads-up: #1164 by @SashaMIT (filed the same day) covers the same fix — validating push-notification URLs in _dispatch_notification against loopback/private/link-local/metadata hosts. #1164 additionally ships integration coverage (tests/integration/push_notifications/agent_app.py) and has already iterated on review feedback (rejecting follow_redirects=True clients outright, among other points).

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 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants