Injection

Go (Gin) Injection Guide: Template Vetting [Jun 2026] [CVE-2026-33531]

[Updated Jun 2026] Updated CVE-2026-33531

Overview

The CVE-2026-33531 advisory describes a path traversal vulnerability in InvenTree's Python-based template engine, where a staff-level user who can upload or edit templates could craft template tags that read arbitrary files from the server filesystem. This class of vulnerability arises when a template engine evaluates user-modifiable templates and exposes functions that access the filesystem, allowing an attacker to traverse paths and fetch sensitive files outside the application directory. While this CVE targets a Python project, the underlying risk is template injection combined with unsafe file-access, demonstrated by file reads via crafted template content. The impact can be severe, including exposure of configuration, credentials, and project data, and is mitigated by ensuring templates are not user-controlled and by removing or safely restricting any file-system access from template functions. The report explicitly references CVE-2026-33531 (CWE-89: Improper Neutralization of Special Elements used in an SQL Command, extended here to template injection risk) and notes that patched versions exist (1.2.6 and 1.3.0+ for InvenTree). In Go (Gin) contexts, this vulnerability manifests as template injection risks when user-controlled templates or unsafe template functions can read arbitrary server files. To prevent this, Go apps should avoid evaluating untrusted templates, or constrain templates to a fixed, non-user-modifiable set, and remove any file-access operations from template-supplied code paths. Implementing a strict allowlist of templates and precompiling them removes the attack surface while preserving the intended rendering behavior.

Affected Versions

Prior to 1.2.6; patched in 1.2.6 and 1.3.0+ for the InvenTree project (CVE-2026-33531)

Code Fix Example

Go (Gin) API Security Remediation
VULNERABLE PATTERN (Go Gin, risky):
package main

import (
  "bytes"
  "net/http"
  "os"
  "text/template"

  "github.com/gin-gonic/gin"
)

func main() {
  r := gin.Default()
  // Dangerous: user-supplied template is parsed with a function that can read files
  r.GET("/render", func(c *gin.Context) {
    tmpl := c.Query("tmpl") // attacker-controlled content
    t, err := template.New("tmpl").Funcs(template.FuncMap{
      "readFile": func(path string) string {
        b, _ := os.ReadFile(path)
        return string(b)
      },
    }).Parse(tmpl)
    if err != nil {
      c.String(http.StatusBadRequest, "template parse error")
      return
    }
    var out bytes.Buffer
    if err := t.Execute(&out, map[string]string{"Name": "World"}); err != nil {
      c.String(http.StatusInternalServerError, "render error")
      return
    }
    c.String(http.StatusOK, out.String())
  })
  r.Run()
}

FIXED PATTERN (Go Gin):
package main

import (
  "bytes"
  "log"
  "net/http"
  "github.com/gin-gonic/gin"
  "text/template"
)

func main() {
  // Precompile allowed templates (no user-supplied template parsing)
  templates := map[string]*template.Template{
    "hello": template.Must(template.New("hello").Parse("Hello, {{.Name}}!")),
  }

  r := gin.Default()
  r.GET("/render", func(c *gin.Context) {
    tmplName := c.Query("tmpl")
    t, ok := templates[tmplName]
    if !ok {
      c.String(http.StatusBadRequest, "unknown template")
      return
    }
    var buf bytes.Buffer
    if err := t.Execute(&buf, map[string]string{"Name": "World"}); err != nil {
      c.String(http.StatusInternalServerError, "render error")
      return
    }
    c.String(http.StatusOK, buf.String())
  })
  if err := r.Run(); err != nil {
    log.Fatal(err)
  }
}

CVE References

Choose which optional cookies to allow. You can change this any time.