← Back to PRs

#18712: feat(hooks): add responseUrl callback with security hardening

by shanefirek open 2026-02-17 00:35 View on GitHub →
gateway commands agents stale size: S
## Summary - Adds optional `responseUrl` and `responseSecret` fields to hook agent payloads - When `responseUrl` is set, OpenClaw POSTs the agent result (status, summary, outputText, error) back to the URL after the agent turn completes - When `responseSecret` is set, the callback includes an `X-OpenClaw-Signature` HMAC-SHA256 header for verification - SSRF protection: HTTPS-only, blocks private/reserved IP ranges (RFC 1918, loopback, link-local, IPv6 ULA) - 10-second timeout on callback requests via AbortController - Fields propagate through the full mapping pipeline (normalizeHookMapping, buildActionFromMapping, mergeAction) so mapping-triggered hooks also fire callbacks - Added `responseUrl` and `responseSecret` to `HookMappingConfig` type ## Test plan - [x] Direct `/hooks/agent` payload with `responseUrl` — callback fires on completion - [x] Direct `/hooks/agent` payload with `responseUrl` + `responseSecret` — callback includes HMAC signature - [ ] Mapping-triggered hook with `responseUrl` in config — callback fires - [ ] SSRF: `responseUrl` pointing to private IP returns error - [ ] SSRF: `responseUrl` with HTTP (not HTTPS) returns error - [ ] Timeout: unresponsive `responseUrl` doesn't block indefinitely <!-- greptile_comment --> <h3>Greptile Summary</h3> Adds optional `responseUrl` / `responseSecret` fields to hook agent payloads, enabling POST callbacks with HMAC-SHA256 signing after agent turns complete. Fields propagate through the full mapping pipeline. - **SSRF protection is bypassable**: The custom hostname-based validation in `src/gateway/server/hooks.ts` only checks literal URL strings, not resolved IPs. Attacker-controlled domains resolving to private IPs (DNS rebinding) bypass the check entirely. The codebase already has robust SSRF protection in `src/infra/net/ssrf.ts` (`resolvePinnedHostnameWithPolicy` + `fetchWithSsrFGuard`) with DNS pinning — this should be used instead of the custom regex-based approach. - **No early validation of `responseUrl`**: Invalid or SSRF-blocked URLs are silently accepted at request time (202 response), only failing later in async callback. Callers get no feedback that their callback URL is bad. - **Callback logic duplication**: The ~40-line POST callback block is copy-pasted between success and error paths, increasing maintenance risk. <h3>Confidence Score: 2/5</h3> - The SSRF protection is insufficient and bypassable via DNS rebinding; this should be addressed before merging. - The PR introduces a useful feature but the custom SSRF validation is vulnerable to DNS rebinding attacks, where an attacker-controlled domain resolves to private/internal IPs after passing the hostname string check. The codebase already has a robust SSRF solution (src/infra/net/ssrf.ts) with DNS pinning that should be used instead. Additionally, responseUrl validation happens only at callback time rather than at request intake, providing no feedback to callers. - src/gateway/server/hooks.ts requires the most attention — its SSRF validation logic needs to be replaced with the existing robust implementation from src/infra/net/ssrf.ts <sub>Last reviewed commit: 1cf4706</sub> <!-- greptile_other_comments_section --> <sub>(3/5) Reply to the agent's comments like "Can you suggest a fix for this @greptileai?" or ask follow-up questions!</sub> <!-- /greptile_comment -->

Most Similar PRs