fix(plugins): guard integration plugins against SSRF

Merged
JI
2mo ago
+1085-204

Hardens the integration plugins that make outbound HTTP requests to a URL supplied at registration time.

> cldev integration registered add slack-webhook --name clslack --opt webhook=http://localhost/my/webhook
DBG using config file path="/Users/jiparis/Library/Application Support/chainloop/config.devel.toml"
WRN API contacted in insecure mode
ERR validation error: error validating a webhook: error making request: Post "http://localhost/my/webhook": blocked outbound request: "localhost" resolves to non-public address ::1

A new SSRF-safe HTTP client in the plugin SDK resolves the destination, refuses addresses that are not publicly routable, and dials the address it validated, so DNS cannot answer differently between the check and the connection. Because the check runs at dial time, every redirect hop is validated as well. Rejected ranges cover loopback, private and unique local addresses, link-local (and with it the cloud metadata endpoints), CGNAT, reserved and benchmarking ranges, IPv4-mapped and IPv4-compatible IPv6, and the IPv6 transition ranges that embed an IPv4 address.

The Slack and Discord webhooks only ever target their public services, so they always refuse non-public destinations.

The generic webhook and Dependency-Track plugins accept an arbitrary destination that a deployment may well run inside its own network, so their behaviour is left to a new control plane setting, plugins_network_policy.block_private_targets, exposed in the Helm chart as controlplane.pluginsNetworkPolicy.blockPrivateTargets. It defaults to off, so existing deployments that point those plugins at an internal service keep working.

AI disclosure: this contribution was produced with the assistance of Claude Code.