rss email digests over ssh because you're a cool kid herald.dunkirk.sh
go rss rss-reader ssh charm

test: add tests to verify HTML rendering and sanitization

Tests confirm that:
- HTML in feeds renders correctly (not escaped as text)
- Unsafe tags like <script> are stripped
- Style attributes are removed
- Safe HTML formatting (p, strong, a, etc.) is preserved

Claude b1c4239a 3a44027a

+95
+95
email/render_test.go
··· 1 + package email 2 + 3 + import ( 4 + "strings" 5 + "testing" 6 + "time" 7 + ) 8 + 9 + func TestRenderDigest_HTMLNotEscaped(t *testing.T) { 10 + // Create test data with HTML content 11 + data := &DigestData{ 12 + ConfigName: "Test Config", 13 + TotalItems: 1, 14 + FeedGroups: []FeedGroup{ 15 + { 16 + FeedName: "Test Feed", 17 + FeedURL: "https://example.com/feed", 18 + Items: []FeedItem{ 19 + { 20 + Title: "Test Article", 21 + Link: "https://example.com/article", 22 + Content: "<p>This is a <strong>test</strong> article with <a href='https://example.com'>a link</a>.</p>", 23 + Published: time.Now(), 24 + }, 25 + }, 26 + }, 27 + }, 28 + } 29 + 30 + // Render with inline mode enabled 31 + htmlOutput, _, err := RenderDigest(data, true, 30, false, false) 32 + if err != nil { 33 + t.Fatalf("RenderDigest failed: %v", err) 34 + } 35 + 36 + // Debug: print actual output 37 + t.Logf("HTML Output:\n%s", htmlOutput) 38 + 39 + // Verify HTML is NOT escaped (should contain actual tags, not &lt; entities) 40 + if strings.Contains(htmlOutput, "&lt;p&gt;") { 41 + t.Error("HTML is being escaped - found &lt;p&gt; instead of <p>") 42 + } 43 + if strings.Contains(htmlOutput, "&lt;strong&gt;") { 44 + t.Error("HTML is being escaped - found &lt;strong&gt; instead of <strong>") 45 + } 46 + 47 + // Verify HTML tags are present (not escaped) 48 + if !strings.Contains(htmlOutput, "<p>This is a <strong>test</strong>") { 49 + t.Error("HTML tags are not being rendered - content appears to be escaped") 50 + } 51 + } 52 + 53 + func TestRenderDigest_UnsafeHTMLStripped(t *testing.T) { 54 + // Create test data with unsafe HTML content 55 + data := &DigestData{ 56 + ConfigName: "Test Config", 57 + TotalItems: 1, 58 + FeedGroups: []FeedGroup{ 59 + { 60 + FeedName: "Test Feed", 61 + FeedURL: "https://example.com/feed", 62 + Items: []FeedItem{ 63 + { 64 + Title: "Test Article", 65 + Link: "https://example.com/article", 66 + Content: "<p>Safe content</p><script>alert('xss')</script><p style='color:red'>No styles</p>", 67 + Published: time.Now(), 68 + }, 69 + }, 70 + }, 71 + }, 72 + } 73 + 74 + // Render with inline mode enabled 75 + htmlOutput, _, err := RenderDigest(data, true, 30, false, false) 76 + if err != nil { 77 + t.Fatalf("RenderDigest failed: %v", err) 78 + } 79 + 80 + // Debug: print actual output 81 + t.Logf("HTML Output:\n%s", htmlOutput) 82 + 83 + // Verify script tags are removed 84 + if strings.Contains(htmlOutput, "<script>") { 85 + t.Error("Unsafe <script> tags were not stripped") 86 + } 87 + if strings.Contains(htmlOutput, "alert('xss')") { 88 + t.Error("Script content was not removed") 89 + } 90 + 91 + // Verify safe content remains 92 + if !strings.Contains(htmlOutput, "<p>Safe content</p>") { 93 + t.Error("Safe HTML content was incorrectly removed") 94 + } 95 + }