Security Improvement: Add SSRF protection for Push Notification webhooks and authorization checks for Task operations
@rohityan đang làm issue này rồi.
Từ ngày 11/3/2026.
Đánh giá
- Độ khó
- 4/5
- Thời gian dự kiến
- 3-5 ngày
- Mức phù hợp với người mới
- 45/100
- Loại issue
- Tính năng
- Độ rõ ràng
- Đặc tả rõ ràng
- Mức độ hoạt động
- Đình trệ
- Công nghệ
- python
- Lĩnh vực
- backend-api-design, security
Hướng nghiên cứu
Chi tiết issue nêu rõ hai lỗ hổng bảo mật: SSRF trong các webhook thông báo push và thiếu cơ chế ủy quyền cho các thao tác Task. Trước tiên, hãy kiểm tra các tệp được đề cập: src/a2a/server/tasks/base_push_notification_sender.py để xác thực URL và src/a2a/server/request_handlers/default_request_handler.py để ủy quyền Task. Xem lại model Task trong src/a2a/types.py để hiểu cấu trúc dữ liệu. Bản sửa bao gồm việc thêm logic xác thực và sửa model Task để thêm trường owner, sau đó cập nhật các request handler để thực thi các kiểm tra. Có thể xem là hoàn tất khi các bài kiểm tra bảo mật mới đều vượt qua và bảo đảm không có hồi quy trong chức năng hiện có.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Summary
The current SDK implementation has two architectural gaps that could lead to security issues in production deployments:
- Push Notification webhook URLs are used without SSRF protections — any URL provided via
PushNotificationConfigis passed directly tohttpx.post()with no validation - Task operations have no authorization layer — tasks are looked up by ID only, allowing any client to access, cancel, or modify any other client's tasks
These are not obscure edge cases — they affect the default behavior that every developer inherits when building on this SDK.
Issue 1: SSRF via Push Notification Webhooks
Affected code:
src/a2a/server/tasks/base_push_notification_sender.py(lines 53-62)
url = push_info.url # user-controlled, no validation
response = await self._client.post(
url,
json=notification.model_dump(mode="json", exclude_none=True),
headers=headers,
)
The URL from PushNotificationConfig.url (defined in src/a2a/types.py, line 840) is stored and used without any validation:
- No scheme restriction (allows
file://,gopher://, etc.) - No IP/hostname blocklist (allows
127.0.0.1,169.254.169.254, internal hostnames) - No DNS rebinding protection
- No redirect policy
Both InMemoryPushNotificationConfigStore.set_info() and DatabasePushNotificationConfigStore.set_info() store the URL as-is.
Impact: A malicious client can register a push notification config with an internal URL (e.g., cloud metadata endpoint, internal services) and trigger SSRF when the server sends notifications.
Suggested fix:
- Validate URL scheme (allow only
https://, optionallyhttp://) - Resolve the hostname and reject private/loopback IP ranges (
127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,169.254.0.0/16,::1, link-local) - Consider adding a configurable allowlist/blocklist for webhook destinations
- Disable or limit redirects on the HTTP client
Issue 2: Cross-Client Task IDOR (Missing Authorization)
Affected code:
src/a2a/server/request_handlers/default_request_handler.pyon_get_task()(line 117)on_cancel_task()(line 131)on_message_send()(line 289)on_resubscribe_to_task()(line 513)on_set_task_push_notification_config()(line 461)on_get_task_push_notification_config()(line 484)on_delete_task_push_notification_config()(line 581)
All task operations retrieve tasks using only the task ID:
task: Task | None = await self.task_store.get(params.id, context)
Although ServerCallContext (defined in src/a2a/server/context.py) is passed through, it is never used for authorization checks. The Task model (src/a2a/types.py, lines 1855-1887) has no owner/user field, making ownership checks impossible even if a developer wanted to add them.
The InMemoryTaskStore.get() and DatabaseTaskStore.get() implementations both look up tasks by ID alone with no authorization logic.
Impact: In any multi-client deployment, Client A can read, cancel, or modify tasks belonging to Client B simply by guessing or enumerating task IDs.
Suggested fix:
- Add an
owner(orclient_id) field to theTaskmodel - Populate it from
ServerCallContext.userwhen a task is created - Check ownership in
TaskStore.get()/cancel()/ etc., or inDefaultRequestHandlerbefore returning results - At minimum, provide a hook or middleware interface so developers can plug in their own authorization logic without forking the SDK
Why this matters for an SDK
While input validation is always partly the developer's responsibility, an SDK/reference implementation sets the pattern that developers follow. Currently:
- The default path is insecure — a developer has to actively work to add these protections
- There are no hooks, middleware, or configuration options to enable these protections
- The official samples and documentation don't warn about these gaps
- As a reference implementation, this code will be copied and adapted by many downstream projects
Adding basic protections (or at minimum, configurable validation hooks) in the SDK itself would significantly reduce the attack surface across the entire A2A ecosystem.
Environment
- a2a-python version: latest main branch (commit fa14dbf)
- Python: 3.12+
- Ngôn ngữ chính
- Python
- Star
- 2.2k
- Fork
- 496
- Merge trung bình
- 3 ngày 15 giờ
- Pull request đã merge (30 ngày)
- 20
Hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của a2aproject/a2a-python
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
a2aproject/a2a-python#1261 · 1 người được giao ·
-
component: server status:awaiting response
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
a2aproject/a2a-python#1237 · 1 bình luận · 1 người được giao ·
-
[Bug]: In-memory push store keeps the caller object so later mutations rewrite stored webhooks Đang mởcomponent: server status:awaiting response status:stale
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
a2aproject/a2a-python#1215 · 2 bình luận · 1 người được giao ·
-
component: server status:awaiting response status:stale
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
a2aproject/a2a-python#1205 · 3 bình luận · 1 người được giao ·
-
component: server status:awaiting response status:stale
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
a2aproject/a2a-python#1204 · 2 bình luận · 1 người được giao ·
Tất cả issue của a2aproject/a2a-python
Issue tương tự
-
triage/confirmed
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 88/100
agentscope-ai/agentscope#2775 ·
-
comp/desktop P3 type/bug
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 92/100
NousResearch/hermes-agent#118866 ·
-
bug
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 90/100
apache/cloudstack#14222 ·
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
-
bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100