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
b565d497f9). 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 <contact@dolicraft.com>
Co-authored-by: Dolicraft <contact@dolicraft.com>
This commit is contained in:
w3houde 2026-05-31 04:14:50 +07:00 committed by GitHub
parent 099f88a208
commit c7ff62a0ea
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -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) {