package web import ( "strings" "testing" "git.fabledsword.com/bvandeusen/minstrel/internal/config" ) const sampleIndexHTML = ` {{ .AppName }} ` func TestApplyBrandingTemplate_Defaults(t *testing.T) { out, err := applyBrandingTemplate(sampleIndexHTML, config.BrandingConfig{ AppName: "Minstrel", Description: "Self-hosted music server.", }) if err != nil { t.Fatalf("apply: %v", err) } body := string(out) if !strings.Contains(body, "Minstrel") { t.Errorf("missing default title; body=%s", body) } } func TestApplyBrandingTemplate_CustomBranding(t *testing.T) { out, err := applyBrandingTemplate(sampleIndexHTML, config.BrandingConfig{ AppName: "Family Jukebox", Description: "Our home library.", }) if err != nil { t.Fatalf("apply: %v", err) } body := string(out) if !strings.Contains(body, "Family Jukebox") { t.Errorf("custom AppName not in title; body=%s", body) } if !strings.Contains(body, `og:title" content="Family Jukebox"`) { t.Errorf("custom AppName not in og:title; body=%s", body) } if !strings.Contains(body, `appName: "Family Jukebox"`) { t.Errorf("custom AppName not in inline window.__MINSTREL__; body=%s", body) } } func TestApplyBrandingTemplate_XSSEscape_ScriptContext(t *testing.T) { // Operator config is trusted, but defensive escaping is still required: // an operator who doesn't realize their value is interpolated into HTML // shouldn't be able to accidentally break out of contexts. out, err := applyBrandingTemplate(sampleIndexHTML, config.BrandingConfig{ AppName: "", Description: "ok", }) if err != nil { t.Fatalf("apply: %v", err) } body := string(out) // With html/template escaping intact, the inline-script block has // exactly one . If escaping silently regressed (e.g. someone // swapped html/template for text/template), the AppName payload would // inject a second one and break out of the JS-string context. if got := strings.Count(body, ""); got != 1 { t.Errorf("expected exactly one in body (escaping intact), got %d; body=%q", got, body) } // And the JS object literal's closing `};` should still live inside // the script block — i.e. the block didn't get truncated by the payload. scriptOpen := strings.Index(body, "") if scriptClose < 0 { t.Fatalf("inline script block has no close; body=%q", body) } scriptBody := body[scriptOpen : scriptOpen+scriptClose] if !strings.Contains(scriptBody, "};") { t.Errorf("inline script block truncated before object literal closed: %q", scriptBody) } } func TestApplyBrandingTemplate_XSSEscape_AttrContext(t *testing.T) { out, err := applyBrandingTemplate(sampleIndexHTML, config.BrandingConfig{ AppName: `">`, Description: "ok", }) if err != nil { t.Fatalf("apply: %v", err) } body := string(out) // The unescaped HTML should NOT appear as a live tag in attribute context. if strings.Contains(body, `">`) { t.Errorf("unescaped attribute breakout: %s", body) } }