package parsemail import ( "bytes" "io" "strings" "testing" "github.com/emersion/go-message" ) // XSS Test Cases func TestSanitizeHTML(t *testing.T) { testCases := []struct { name string input string expected string }{ { name: "Simple script tag", input: ``, expected: ``, }, { name: "Image with onerror - should remove dangerous attributes", input: ``, expected: ``, // 保留图片元素并移除事件处理属性 }, { name: "SVG with onload", input: ``, expected: ``, }, { name: "Iframe with javascript", input: ``, expected: ``, }, { name: "Safe HTML content", input: `

Hello World!

`, expected: `

Hello World!

`, }, { name: "Link with javascript protocol - removes dangerous href", input: `Click me`, expected: `Click me`, // bluemonday removes entire link when href is dangerous }, { name: "Style tag with javascript", input: ``, expected: ``, }, { name: "Object tag", input: ``, expected: ``, }, { name: "Form tag", input: `
`, expected: ``, }, { name: "Mixed content with XSS - keeps safe div tags", input: `

Safe content

More safe content
`, expected: `

Safe content

More safe content
`, // div is now allowed, script removed }, { name: "Event handlers in various tags - removes handlers but keeps tag", input: `
Click me
`, expected: `
Click me
`, // div is kept, onclick handler removed }, { name: "Data URLs with javascript - removes dangerous link", input: `Link`, expected: `Link`, // dangerous data URL removes the link }, { name: "Meta refresh with javascript", input: ``, expected: ``, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { result := SanitizeHTML(tc.input) if result != tc.expected { t.Errorf("SanitizeHTML(%q) = %q, want %q", tc.input, result, tc.expected) } }) } } func TestSanitizeText(t *testing.T) { testCases := []struct { name string input string expected string }{ { name: "Script tag in text", input: ``, expected: ``, }, { name: "HTML tags in text", input: `

Hello World!

`, expected: `Hello World!`, }, { name: "Plain text", input: `Hello World! This is plain text.`, expected: `Hello World! This is plain text.`, }, { name: "Text with special characters", input: `Hello & goodbye < > " '`, expected: `Hello & goodbye < > " '`, }, { name: "Image tag in text", input: ``, expected: ``, }, { name: "Mixed HTML and text", input: `Normal text more text`, expected: `Normal text more text`, }, { name: "Unicode characters", input: `Hello 世界 🌍`, expected: `Hello 世界 🌍`, }, { name: "Multiple script tags", input: `Text`, expected: `Text`, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { result := SanitizeText(tc.input) if result != tc.expected { t.Errorf("SanitizeText(%q) = %q, want %q", tc.input, result, tc.expected) } }) } } func TestBuildUser_XSS(t *testing.T) { testCases := []struct { name string input string expectedEmail string expectedName string shouldBeNil bool }{ { name: "Normal user", input: `John Doe `, expectedEmail: "john@example.com", expectedName: "John Doe", shouldBeNil: false, }, { name: "Simple email only", input: `test@example.com`, expectedEmail: "test@example.com", expectedName: "", shouldBeNil: false, }, { name: "User with quotes", input: `"John O'Connor" `, expectedEmail: "john@example.com", expectedName: "John O'Connor", shouldBeNil: false, }, { name: "JavaScript in name - should be filtered", input: `javascript:alert('xss') `, expectedEmail: "js@example.com", expectedName: "javascript:alert('xss')", shouldBeNil: false, }, { name: "Event handler in name - quotes removed in sanitization", input: `onclick="alert('xss')" `, expectedEmail: "click@example.com", expectedName: "onclick= alert('xss')", shouldBeNil: false, }, { name: "HTML in email address - should be filtered", input: ``, expectedEmail: "script>test@example.com`) h.Set("To", "to@example.com") h.Set("Subject", `Test Subject`) h.Set("Date", "Mon, 02 Jan 2006 15:04:05 -0700") h.SetContentType("multipart/alternative", nil) w, err := message.CreateWriter(&b, h) if err != nil { t.Fatal(err) } var htmlHeader message.Header htmlHeader.SetContentType("text/html", nil) htmlPart, err := w.CreatePart(htmlHeader) if err != nil { t.Fatal(err) } io.WriteString(htmlPart, `

Hello

Safe content

`) htmlPart.Close() var textHeader message.Header textHeader.SetContentType("text/plain", nil) textPart, err := w.CreatePart(textHeader) if err != nil { t.Fatal(err) } io.WriteString(textPart, `Hello World`) textPart.Close() w.Close() email := mustParseEmail(t, []string{"to@example.com"}, &b, b.Len()) if email.From.EmailAddress != "from@example.com" { t.Errorf("Expected From.EmailAddress to be 'from@example.com', got: %q", email.From.EmailAddress) } if email.From.Name != "Safe Name" { t.Errorf("Expected From.Name to be 'Safe Name', got: %q", email.From.Name) } expectedSubject := "Test Subject" if email.Subject != expectedSubject { t.Errorf("Expected Subject to be %q, got: %q", expectedSubject, email.Subject) } htmlContent := string(email.HTML) if strings.Contains(htmlContent, "malicious.txt` sanitizedFilename := SanitizeText(dangerousFilename) if strings.Contains(sanitizedFilename, "` sanitizedContentType := SanitizeText(dangerousContentType) if strings.Contains(sanitizedContentType, "`, testType: "html", }, { name: "Base64 encoded script", input: ``, testType: "html", }, { name: "CSS expression - should be removed", input: `
Test
`, testType: "html", }, { name: "HTML entities script", input: `<script>alert('xss')</script>`, testType: "html", }, { name: "Multiple attack vectors", input: ``, testType: "html", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { var result string if tc.testType == "html" { result = SanitizeHTML(tc.input) } else { result = SanitizeText(tc.input) } // 检查结果中不应包含危险的内容 dangerousPatterns := []string{ "`) h.Set("To", "to@example.com") h.Set("Subject", `Important Email`) h.Set("Date", "Mon, 02 Jan 2006 15:04:05 -0700") h.SetContentType("text/plain", nil) w, err := message.CreateWriter(&b, h) if err != nil { t.Fatal(err) } io.WriteString(w, `Email body with content`) w.Close() email := mustParseEmail(t, []string{"to@example.com"}, &b, b.Len()) if strings.Contains(email.Subject, "
safe
`) if err := writer.Close(); err != nil { t.Fatal(err) } email := mustParseEmail(t, []string{"to@example.com"}, &body, body.Len()) htmlContent := strings.ToLower(string(email.HTML)) for _, forbidden := range []string{"