- Move remaining beach packages and embedded admin assets\n- Rewrite framework, example, build, and gate paths
129 lines
4.2 KiB
Go
129 lines
4.2 KiB
Go
package postcard
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
"testing/fstest"
|
|
)
|
|
|
|
func TestMailRecipientsAndSafety(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
fsys := fstest.MapFS{
|
|
"views/mail/hello.htm": {Data: []byte(`subject = "Witaj {{ .Name }}"
|
|
layout = "default"
|
|
==
|
|
Witaj **{{ .Name }}**.
|
|
|
|
[Strona]({{ .URL }})
|
|
`)},
|
|
}
|
|
cat := NewCatalog()
|
|
if err := cat.Register("golem15.hello", fsys, []string{"golem15.hello::mail.hello"}, nil); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
drv := NewMemoryDriver()
|
|
mail := NewMailer(cat, drv, Options{From: "from@example.test"})
|
|
ctx := context.Background()
|
|
|
|
if err := mail.Send(ctx, Message{
|
|
Template: "golem15.hello::mail.hello",
|
|
To: []string{"ada@example.test"},
|
|
Cc: []string{"cc@example.test"},
|
|
Bcc: []string{"bcc@example.test"},
|
|
ReplyTo: "reply@example.test",
|
|
Vars: map[string]any{"Name": "Ada", "URL": "https://example.test"},
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got := drv.Messages()
|
|
if len(got) != 1 {
|
|
t.Fatalf("stored %d messages", len(got))
|
|
}
|
|
msg := got[0]
|
|
if len(msg.To) != 1 || msg.To[0] != "ada@example.test" {
|
|
t.Fatalf("To = %v", msg.To)
|
|
}
|
|
if len(msg.Cc) != 1 || msg.Cc[0] != "cc@example.test" {
|
|
t.Fatalf("Cc = %v", msg.Cc)
|
|
}
|
|
if len(msg.Bcc) != 1 || msg.Bcc[0] != "bcc@example.test" {
|
|
t.Fatalf("Bcc = %v", msg.Bcc)
|
|
}
|
|
if msg.ReplyTo != "reply@example.test" {
|
|
t.Fatalf("ReplyTo = %q", msg.ReplyTo)
|
|
}
|
|
if msg.Subject != "Witaj Ada" {
|
|
t.Fatalf("subject = %q", msg.Subject)
|
|
}
|
|
if !strings.Contains(msg.Text, "Witaj **Ada**.") {
|
|
t.Fatalf("text = %q", msg.Text)
|
|
}
|
|
if !strings.Contains(msg.HTML, "<strong>Ada</strong>") || !strings.Contains(msg.HTML, `href="https://example.test"`) {
|
|
t.Fatalf("html = %q", msg.HTML)
|
|
}
|
|
|
|
t.Run("raw tags markdown links and dangerous urls stay out of html", func(t *testing.T) {
|
|
cases := []map[string]any{
|
|
{"Name": `<script>alert(1)</script>`, "URL": "https://example.test"},
|
|
{"Name": "Ada", "URL": "javascript:alert(1)"},
|
|
{"Name": "Ada", "URL": "vbscript:msgbox(1)"},
|
|
{"Name": "Ada", "URL": "data:text/html;base64,PGh0bWw+"},
|
|
{"Name": `<iframe src="https://evil.test"></iframe>`, "URL": "https://example.test"},
|
|
}
|
|
for _, vars := range cases {
|
|
drv := NewMemoryDriver()
|
|
mail := NewMailer(cat, drv, Options{From: "from@example.test"})
|
|
err := mail.Send(ctx, Message{
|
|
Template: "golem15.hello::mail.hello",
|
|
To: []string{"ada@example.test"},
|
|
Vars: vars,
|
|
})
|
|
if err != nil {
|
|
if strings.Contains(err.Error(), "unsafe") || strings.Contains(err.Error(), "dangerous") {
|
|
continue
|
|
}
|
|
t.Fatalf("unexpected send error for %v: %v", vars, err)
|
|
}
|
|
html := strings.ToLower(drv.Messages()[0].HTML)
|
|
for _, banned := range []string{"<script", "<iframe", "javascript:", "vbscript:", "data:text/html"} {
|
|
if strings.Contains(html, banned) {
|
|
t.Fatalf("unsafe %q leaked for %v:\n%s", banned, vars, html)
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestMailRegistrationBoundaries(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("reserved default alias", func(t *testing.T) {
|
|
err := NewCatalog().Register("golem15.hello", fstest.MapFS{}, nil, map[string]string{
|
|
"default": "golem15.hello::mail.layouts.default",
|
|
})
|
|
if err == nil || !strings.Contains(err.Error(), "reserved") {
|
|
t.Fatalf("reserved alias error = %v", err)
|
|
}
|
|
})
|
|
t.Run("template not owned by plugin", func(t *testing.T) {
|
|
err := NewCatalog().Register("golem15.hello", fstest.MapFS{}, []string{"golem15.other::mail.hello"}, nil)
|
|
if err == nil || !strings.Contains(err.Error(), "golem15.hello") || !strings.Contains(err.Error(), "golem15.other::mail.hello") {
|
|
t.Fatalf("ownership error = %v", err)
|
|
}
|
|
})
|
|
t.Run("invalid dotted name", func(t *testing.T) {
|
|
err := NewCatalog().Register("golem15.hello", fstest.MapFS{}, []string{"golem15.hello::mail.hello/../x"}, nil)
|
|
if err == nil || !strings.Contains(err.Error(), "invalid mail name") {
|
|
t.Fatalf("invalid name error = %v", err)
|
|
}
|
|
})
|
|
t.Run("missing template names plugin and dotted name", func(t *testing.T) {
|
|
err := NewCatalog().Register("golem15.hello", fstest.MapFS{}, []string{"golem15.hello::mail.missing"}, nil)
|
|
if err == nil || !strings.Contains(err.Error(), "golem15.hello::mail.missing") {
|
|
t.Fatalf("missing template error = %v", err)
|
|
}
|
|
})
|
|
}
|