Skip to content

fix(push): harden SSRF (CGNAT/benchmark + create-time URL reject) - #394

Open
SashaMIT wants to merge 3 commits into
a2aproject:mainfrom
SashaMIT:fix/push-ssrf-cgnat-benchmark
Open

fix(push): harden SSRF (CGNAT/benchmark + create-time URL reject)#394
SashaMIT wants to merge 3 commits into
a2aproject:mainfrom
SashaMIT:fix/push-ssrf-cgnat-benchmark

Conversation

@SashaMIT

@SashaMIT SashaMIT commented Aug 9, 2026

Copy link
Copy Markdown

Summary

  • Dial-time: block RFC 6598 CGNAT and RFC 2544 benchmarking ranges in isBlockedIP (parity with Python ipaddress.is_private).
  • Create/save-time: reject non-http(s) and private/loopback literal hosts when storing push configs (CreateTaskPushConfig and SendMessage embed). Dial-time guard remains for DNS rebinding.
  • push.StoreConfig.AllowPrivateNetworks opts out for trusted internal deployments (paired with sender AllowPrivateNetworks).

Threat model: a caller who can create/embed push configs can already choose the webhook URL; dial-time already blocks private POSTs. This is fail-fast write-path + scheme reject (parity with a2a-python create-time / a2a-js#645), not a new authority class.

Test plan

  • go test ./a2asrv/push/ ./a2asrv/ -count=1
  • Revert-tested: disabling private reject makes loopback/localhost Save cases fail their want-err assertions
  • ITK + FailOnPush tests opt into AllowPrivateNetworks on the store

SashaMIT and others added 2 commits August 9, 2026 14:40
net.IP.IsPrivate covers RFC 1918/4193 only. Also reject RFC 6598
100.64/10 and RFC 2544 198.18/15 so push webhooks cannot target those
non-global spaces (aligned with Python ipaddress non-global floor).
Fail closed on non-http(s) and private/loopback literals when storing
push configs (CreateTaskPushConfig and SendMessage embed). Dial-time
SSRF remains; StoreConfig.AllowPrivateNetworks opts out for trusted
internal use. Parity with a2a-python/js create-time guards.
@SashaMIT SashaMIT changed the title fix(push): block CGNAT and benchmarking ranges in SSRF guard fix(push): harden SSRF (CGNAT/benchmark + create-time URL reject) Aug 11, 2026
Comment thread a2asrv/push/store.go Outdated
return err
}
scheme := strings.ToLower(u.Scheme)
if scheme != "http" && scheme != "https" {

@yarolegovich yarolegovich Aug 16, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should not be done. nothing in the spec says pushes must be done over http(s). In an internal deployment I might have topic://name push configs for automatic mq event publishing. http push sender should just ignore non-http configs

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right. The spec does not require HTTP, and topic:// is a real internal config.

Create/save now keeps non-http(s) URLs. HTTPPushSender skips them instead of POSTing. Private/loopback reject still applies to http(s) only.

448c527

Create/save no longer requires http(s). The spec does not, and
internal deployments use schemes like topic:// for MQ. HTTPPushSender
skips those URLs instead of POSTing. Private/loopback reject still
applies to http(s) only.
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.

2 participants