package main
import (
"fmt"
"io"
"log"
"net/http"
"strings"
)
func splitArgs(value string) []string {
var parts []string
var current strings.Builder
quote := rune(0)
escaped := false
flush := func() {
part := strings.TrimSpace(current.String())
if part != "" {
parts = append(parts, part)
}
current.Reset()
}
for _, ch := range value {
switch {
case escaped:
current.WriteRune(ch)
escaped = false
case ch == '\\':
current.WriteRune(ch)
escaped = true
case quote != 0:
current.WriteRune(ch)
if ch == quote {
quote = 0
}
case ch == '\'' || ch == '"':
current.WriteRune(ch)
quote = ch
case ch == ',':
flush()
default:
current.WriteRune(ch)
}
}
flush()
return parts
}
func formatArgs(values []string) string {
parts := make([]string, 0, len(values))
for _, value := range values {
parts = append(parts, splitArgs(value)...)
}
return strings.Join(parts, ", ")
}
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(r.Body)
if err != nil {
log.Printf("method=%s path=%s query=%q body_read_error=%q", r.Method, r.URL.Path, r.URL.RawQuery, err.Error())
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
r.Body.Close()
log.Printf("method=%s path=%s query=%q body=%q", r.Method, r.URL.Path, r.URL.RawQuery, string(body))
if r.Method != http.MethodGet {
w.WriteHeader(http.StatusNoContent)
return
}
query := r.URL.Query()
funcName := strings.TrimSpace(query.Get("func"))
args := formatArgs(query["args"])
if args == "" {
fmt.Fprintf(w, "{{%s()}}", funcName)
return
}
fmt.Fprintf(w, "{{%s(%s)}}", funcName, args)
})
log.Fatal(http.ListenAndServe(":8088", nil))
}
Using evaluation as part of unresolved-variable detection means helper functions can run in validation paths, which is unsafe.
Recipes:
Template:
Server:
Impact: