#18712: feat(hooks): add responseUrl callback with security hardening
gateway
commands
agents
stale
size: S
Cluster:
Security Enhancements and Guardrails
## 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
#6405: feat(security): Add HTTP API security hooks for plugin scanning
by masterfung · 2026-02-01
77.4%
#7091: feat: add pre-answer hooks system for automatic context injection
by dizhaky · 2026-02-02
75.5%
#7580: feat: add message:received internal hook with prompt injection
by rodrigoschott · 2026-02-03
74.2%
#15732: [AI-assisted] feat: emit agent:response internal hook after replies
by zontasticality · 2026-02-13
74.0%
#10663: feat(hooks): add agentId support for webhook routing
by nityatrish · 2026-02-06
73.3%
#19565: feat: add agent lifecycle hook events (session, message, error)
by tag-assistant · 2026-02-17
72.8%
#10383: feat(hooks): add agentId to hook mappings
by quentintou · 2026-02-06
72.7%
#14222: core: add needsApproval to before_tool_call; move AgentShield to ex...
by Eventedge · 2026-02-11
72.2%
#12635: Gateway: add inbound webhook dispatch framework
by jhs129 · 2026-02-09
72.2%
#6630: feat(hooks): add agent:turn_start and agent:turn_end lifecycle events
by drdigital13 · 2026-02-01
72.0%