Overview
The CVE-2026-34984 real-world advisory describes a DNS exfiltration risk in External Secrets Operator (ESO) where user-authored templating could leak secret data via DNS lookups. In vulnerable versions (2.2.0 and below), the v2 template engine exposed a getHostByName function to user-controlled templates through Sprig's functional map, allowing a templated ExternalSecret to trigger DNS queries with secret-derived values. The controller process performed these lookups, enabling data exfiltration in environments where untrusted users could author templated resources. This is a confidentiality issue (CWE-200) and highlights how object-level access patterns can enable powerful operations when combined with template rendering in a Go-based service. The issue was fixed in ESO version 2.3.0, and the vulnerability demonstrates how similar patterns can arise in Go (Gin) applications if template execution is coupled with insecure, user-controlled inputs or function maps.
Affected Versions
2.2.0 and below
Code Fix Example
Go (Gin) API Security Remediation
// Vulnerable pattern (Go + Gin)
package main
import (
"bytes"
"net"
"net/http"
"strings"
"text/template"
"github.com/gin-gonic/gin"
)
type RenderRequest struct {
Template string
Data map[string]string
Role string
}
func renderTemplateVulnerable(tmplStr string, data map[string]string) (string, error) {
funcMap := template.FuncMap{
// Danger: exposes DNS lookup capability to templates
"getHostByName": func(host string) (string, error) {
ips, err := net.LookupHost(host)
if err != nil {
return "", err
}
return strings.Join(ips, ","), nil
},
}
t, err := template.New("tmpl").Funcs(funcMap).Parse(tmplStr)
if err != nil {
return "", err
}
var b bytes.Buffer
if err := t.Execute(&b, data); err != nil {
return "", err
}
return b.String(), nil
}
func renderTemplateFixed(tmplStr string, data map[string]string) (string, error) {
t, err := template.New("tmpl").Parse(tmplStr)
if err != nil {
return "", err
}
var b bytes.Buffer
if err := t.Execute(&b, data); err != nil {
return "", err
}
return b.String(), nil
}
func main() {
r := gin.Default()
r.POST("/vuln/render", func(c *gin.Context) {
var req RenderRequest
if err := c.BindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
out, err := renderTemplateVulnerable(req.Template, req.Data)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"result": out})
})
r.POST("/fixed/render", func(c *gin.Context) {
var req RenderRequest
if err := c.BindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// RBAC-based access control example: require admin role to render templates
if req.Role != "admin" {
c.JSON(http.StatusForbidden, gin.H{"error": "forbidden"})
return
}
// Fixed: no dangerous functions exposed in the template engine
out, err := renderTemplateFixed(req.Template, req.Data)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"result": out})
})
r.Run(":8080")
}