diff options
| author | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-07-27 21:27:10 +0200 |
|---|---|---|
| committer | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-07-27 21:27:10 +0200 |
| commit | 2901c73fde7ff19e8cdea00f933dc628d11c6ec6 (patch) | |
| tree | 5ea265b5f27c0dc05f888cb84ae345e1751307e7 /internal/calfeed/ical_test.go | |
| parent | 45e0b7652552867f7f99ffe22c968641510d5143 (diff) | |
| download | lectio-2901c73fde7ff19e8cdea00f933dc628d11c6ec6.tar.gz lectio-2901c73fde7ff19e8cdea00f933dc628d11c6ec6.zip | |
feat(calfeed): RFC-5545 iCal renderer with injection-safe escaping
Diffstat (limited to 'internal/calfeed/ical_test.go')
| -rw-r--r-- | internal/calfeed/ical_test.go | 52 |
1 files changed, 52 insertions, 0 deletions
diff --git a/internal/calfeed/ical_test.go b/internal/calfeed/ical_test.go new file mode 100644 index 0000000..fb7ee9a --- /dev/null +++ b/internal/calfeed/ical_test.go @@ -0,0 +1,52 @@ +package calfeed + +import ( + "strings" + "testing" + "time" +) + +func TestICalEscapeInjection(t *testing.T) { + // A malicious custom-calendar name must not be able to inject lines/props. + got := icalEscape("Evil\r\nBEGIN:VEVENT\nSUMMARY:hijack; a,b\\c") + if strings.ContainsAny(got, "\r\n") { + t.Fatalf("unescaped newline survived: %q", got) + } + for _, sub := range []string{`\n`, `\;`, `\,`, `\\`} { + if !strings.Contains(got, sub) { + t.Fatalf("missing escape %q in %q", sub, got) + } + } +} + +func TestICalStructure(t *testing.T) { + days := []DayView{{ + Date: "2026-01-06", Season: "time-after-epiphany", Week: 1, Colour: "white", + Observed: CelView{Name: "The Epiphany of the Lord", Rank: "class-1"}, + Readings: []ReadingView{{Part: "gospel", Citation: "Matt 2:1-12"}}, + }} + out := string(ICal("old", days, time.Date(2026, 7, 27, 12, 0, 0, 0, time.UTC))) + for _, want := range []string{ + "BEGIN:VCALENDAR", "VERSION:2.0", "PRODID:-//lectio//calendar//EN", + "BEGIN:VEVENT", "UID:2026-01-06-old@lectio", "DTSTART;VALUE=DATE:20260106", + "DTEND;VALUE=DATE:20260107", "SUMMARY:The Epiphany of the Lord", + "CATEGORIES:WHITE", "DTSTAMP:20260727T120000Z", "END:VEVENT", "END:VCALENDAR", + } { + if !strings.Contains(out, want) { + t.Fatalf("missing %q in:\n%s", want, out) + } + } + // injection attempt via day count: exactly one VEVENT + if strings.Count(out, "BEGIN:VEVENT") != 1 { + t.Fatalf("expected 1 VEVENT") + } +} + +func TestFoldLine(t *testing.T) { + long := "SUMMARY:" + strings.Repeat("x", 200) + for _, line := range strings.Split(foldLine(long), "\r\n") { + if len(line) > 75 { + t.Fatalf("line exceeds 75 octets: %d", len(line)) + } + } +} |
