VYPR
Medium severityNVD Advisory· Published Feb 20, 2026· Updated Apr 15, 2026

CVE-2026-26957

CVE-2026-26957

Description

Libredesk is a self-hosted customer support desk application. Versions prior to 1.0.2-0.20260215211005-727213631ce6 fail to validate destination URLs for webhooks, allowing an attacker posing as an authenticated "Application Admin" to force the server to make HTTP requests to arbitrary internal destinations. This could compromise the underlying cloud infrastructure or internal corporate network where the service is hosted. This issue has been fixed in version 1.0.2-0.20260215211005-727213631ce6.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/abhinavxd/libredeskGo
< 1.0.2-0.20260215211005-727213631ce61.0.2-0.20260215211005-727213631ce6

Affected products

1

Patches

1
727213631ce6

SSRF protection to webhooks

https://github.com/abhinavxd/libredeskAbhinav RautFeb 15, 2026via ghsa
5 files changed · +28 0
  • cmd/init.go+1 0 modified
    @@ -934,6 +934,7 @@ func initWebhook(db *sqlx.DB, i18n *i18n.I18n) *webhook.Manager {
     		QueueSize:     ko.MustInt("webhook.queue_size"),
     		Timeout:       ko.MustDuration("webhook.timeout"),
     		EncryptionKey: ko.MustString("app.encryption_key"),
    +		AllowedHosts:  ko.Strings("webhook.allowed_hosts"),
     	})
     	if err != nil {
     		log.Fatalf("error initializing webhook manager: %v", err)
    
  • config.sample.toml+2 0 modified
    @@ -119,6 +119,8 @@ workers = 5
     queue_size = 10000
     # HTTP timeout for webhook requests
     timeout = "15s"
    +# CIDR ranges allowed to bypass SSRF protection (e.g. ["10.0.0.0/8"])
    +allowed_hosts = []
     
     [conversation]
     # How often to check for conversations to unsnooze
    
  • go.mod+1 0 modified
    @@ -3,6 +3,7 @@ module github.com/abhinavxd/libredesk
     go 1.25.0
     
     require (
    +	github.com/abhinavxd/ssrfguard v0.1.0
     	github.com/casbin/casbin/v2 v2.99.0
     	github.com/coreos/go-oidc/v3 v3.11.0
     	github.com/disintegration/imaging v1.6.2
    
  • go.sum+2 0 modified
    @@ -2,6 +2,8 @@ cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2Qx
     cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
     filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
     filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
    +github.com/abhinavxd/ssrfguard v0.1.0 h1:Ns/llAQ63uGFehxSvhCd+WGDKmBEEmIH+E1AW1CGgWM=
    +github.com/abhinavxd/ssrfguard v0.1.0/go.mod h1:eNVubb+m/r3KrKWYdG6hxzeAfj+t2ZmZss4V/x7D6Ws=
     github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 h1:uvdUDbHQHO85qeSydJtItA4T55Pw6BtAejd0APRJOCE=
     github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
     github.com/alicebob/miniredis/v2 v2.32.1 h1:Bz7CciDnYSaa0mX5xODh6GUITRSx+cVhjNoOR4JssBo=
    
  • internal/webhook/webhook.go+22 0 modified
    @@ -14,6 +14,7 @@ import (
     	"io"
     	"net"
     	"net/http"
    +	"net/netip"
     	"sync"
     	"time"
     
    @@ -22,6 +23,7 @@ import (
     	"github.com/abhinavxd/libredesk/internal/envelope"
     	"github.com/abhinavxd/libredesk/internal/version"
     	"github.com/abhinavxd/libredesk/internal/webhook/models"
    +	"github.com/abhinavxd/ssrfguard"
     	"github.com/jmoiron/sqlx"
     	"github.com/knadh/go-i18n"
     	"github.com/lib/pq"
    @@ -57,6 +59,7 @@ type Opts struct {
     	QueueSize     int
     	Timeout       time.Duration
     	EncryptionKey string
    +	AllowedHosts  []string // CIDR prefixes allowed to bypass SSRF protection
     }
     
     // DeliveryTask represents a webhook delivery task
    @@ -86,6 +89,10 @@ func New(opts Opts) (*Manager, error) {
     		return nil, err
     	}
     
    +	// Parse allowed host CIDRs for SSRF exceptions.
    +	allowed := parseAllowedHosts(opts.AllowedHosts, opts.Lo)
    +	guard := ssrfguard.New(allowed...)
    +
     	return &Manager{
     		q:             q,
     		lo:            opts.Lo,
    @@ -98,6 +105,7 @@ func New(opts Opts) (*Manager, error) {
     				DialContext: (&net.Dialer{
     					Timeout:   3 * time.Second,
     					KeepAlive: 30 * time.Second,
    +					Control:   guard.Control,
     				}).DialContext,
     				TLSHandshakeTimeout:   3 * time.Second,
     				ResponseHeaderTimeout: 3 * time.Second,
    @@ -404,3 +412,17 @@ func (m *Manager) getWebhooksByEvent(event string) ([]models.Webhook, error) {
     
     	return webhooks, nil
     }
    +
    +// parseAllowedHosts parses CIDR strings into netip.Prefix slices.
    +func parseAllowedHosts(hosts []string, lo *logf.Logger) []netip.Prefix {
    +	var prefixes []netip.Prefix
    +	for _, h := range hosts {
    +		prefix, err := netip.ParsePrefix(h)
    +		if err != nil {
    +			lo.Warn("ignoring invalid webhook `allowed_hosts` entry", "entry", h, "error", err)
    +			continue
    +		}
    +		prefixes = append(prefixes, prefix)
    +	}
    +	return prefixes
    +}
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

4

News mentions

0

No linked articles in our index yet.