From c7ff62a0eaccb62b49e774edb5dc0e92e1a802bb Mon Sep 17 00:00:00 2001 From: w3houde Date: Sun, 31 May 2026 04:14:50 +0700 Subject: [PATCH] Fix #38103 Surface SSRF block reason in webhook failure message (#38517) Webhooks targeting a host on a private/reserved range, on the local network or on a known cloud metadata server have been blocked since the GHSA-hh5p-m24x-fwx2 SSRF hardening landed in 23.0.2 (commit b565d497f9f). getURLContent() returns http_code 400 with a textual explanation in $response['content'], but the webhook trigger only logged "httpcode=400 curl_error_no=" without that content, so the operator could not tell their webhook had been blocked on purpose versus a generic network failure and could not discover the $dolibarr_allow_localurl_for_webhooks flag that re-enables the local-host case. Append $response['content'] to the error message when the failure matches the SSRF-block signature (curl_error_no empty, http_code exactly 400, content non-empty). The textual hint emitted by isIPAllowed() is then visible both in syslog and in the WebhookHistory record. The security fix itself is not changed. Reported by @whoisbarry. Signed-off-by: Dolicraft Co-authored-by: Dolicraft --- .../interface_95_modWebhook_WebhookTriggers.class.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/htdocs/core/triggers/interface_95_modWebhook_WebhookTriggers.class.php b/htdocs/core/triggers/interface_95_modWebhook_WebhookTriggers.class.php index 5044088c06d..77074431854 100644 --- a/htdocs/core/triggers/interface_95_modWebhook_WebhookTriggers.class.php +++ b/htdocs/core/triggers/interface_95_modWebhook_WebhookTriggers.class.php @@ -133,6 +133,14 @@ class InterfaceWebhookTriggers extends DolibarrTriggers $nbPosts++; } else { $errormsg = "The WebHook for triggercode ".$action." failed to do the GET URL ".$tmpobject->url." with httpcode=".(!empty($response['http_code']) ? $response['http_code'] : "")." curl_error_no=".(!empty($response['curl_error_no']) ? $response['curl_error_no'] : ""); + // getURLContent returns http_code 400 with an explanation in $response['content'] + // when the host is rejected by the SSRF protection (private/reserved range, local + // network, metadata server, ...). Surface that explanation so the operator can + // either fix the target URL or enable $dolibarr_allow_localurl_for_webhooks in + // conf.php when the target is on a trusted local network. + if (empty($response['curl_error_no']) && !empty($response['http_code']) && $response['http_code'] == 400 && !empty($response['content'])) { + $errormsg .= " - ".$response['content']; + } $errorforhistory ++; if ($tmpobject->type == Target::TYPE_BLOCKING) {