package cardigann import ( "bytes" "fmt" "strings" "text/template" ) // SearchQuery represents a search query to be templated into request URLs and inputs. type SearchQuery struct { Keywords string MediaType string } // TemplateContext provides the data available to Cardigann templates. type TemplateContext struct { Query SearchQuery Config map[string]string Categories []string } // ApplyTemplate processes a Go template string with the sandboxed Cardigann FuncMap. // The FuncMap contains ONLY "replace" to prevent SSRF or file access via templates. func ApplyTemplate(name, tpl string, ctx interface{}) (string, error) { tmpl, err := template.New(name).Funcs(sandboxedFuncMap()).Parse(tpl) if err != nil { return "", fmt.Errorf("parse template %q: %w", name, err) } var buf bytes.Buffer if err := tmpl.Execute(&buf, ctx); err != nil { return "", fmt.Errorf("execute template %q: %w", name, err) } return buf.String(), nil } // sandboxedFuncMap returns a template FuncMap containing ONLY safe functions. // SECURITY: No file, network, environment, or exec access allowed. // Threat model T-10-02, T-10-06: FuncMap contains ONLY "replace". func sandboxedFuncMap() template.FuncMap { return template.FuncMap{ "replace": func(old, new, src string) string { return strings.ReplaceAll(src, old, new) }, } }