Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/missionmatch / internal/platform/imapclient/parse_test.go |
| Date | Not specified in source |
| Repository | Janady13/missionmatch |
| Revision | 720d47fae476b0d38477ae4401697d372b1ea2bc |
| Branch | feature/opportunity-to-outcome |
| Path or archive member | internal/platform/imapclient/parse_test.go |
| Original SHA-256 | 3f405c7b582db6282bec8de2bf7c4a4b1e104e2661a0315a74c558d536bd0d74 |
| Git blob SHA | f34eeed67ed790f5157444d6bc96e1e8b21999d7 |
| Reading-copy SHA-256 | 3f405c7b582db6282bec8de2bf7c4a4b1e104e2661a0315a74c558d536bd0d74 |
Recovered source transcript
Download complete source text ↓185 lines · 5.8 KB
package imapclient
import (
"strings"
"testing"
"time"
)
// A recruiter's contact list is full of names like "Smith, Jane", and splitting naively on every comma turns
// one recipient into two, one of which is garbage.
func TestAddressListSplitsOnlyOutsideQuotes(t *testing.T) {
got := addresses(`"Smith, Jane" <jane@x.test>, bob@y.test, "Doe, John" <john@z.test>`)
want := []string{"jane@x.test", "bob@y.test", "john@z.test"}
if len(got) != len(want) {
t.Fatalf("got %v, want %v", got, want)
}
for i := range want {
if got[i] != want[i] {
t.Errorf("position %d: got %q want %q", i, got[i], want[i])
}
}
}
func TestBareAddress(t *testing.T) {
cases := map[string]string{
"jane@x.test": "jane@x.test",
" Jane Smith <Jane@X.TEST> ": "jane@x.test",
"<bob@y.test>": "bob@y.test",
`"Quoted Name" <q@z.test>`: "q@z.test",
"Jane Smith": "",
"": "",
"no-at-sign": "",
"trailing@": "",
"@leading.test": "",
"has space@x.test": "",
strings.Repeat("a", 400) + "@x.co": "",
}
for in, want := range cases {
if got := bareAddress(in); got != want {
t.Errorf("bareAddress(%q) = %q, want %q", in, got, want)
}
}
}
// Header folding: a long recipient list wraps, and getting this wrong truncates it at the first wrap.
func TestFoldedHeadersAreJoined(t *testing.T) {
block := []string{
"Message-ID: <abc123@mail.test>",
"From: Dana <dana@agency.test>",
"To: one@x.test,",
" two@x.test,",
"\tthree@x.test",
"Subject: A long subject that",
" continues on the next line",
"Date: Mon, 14 Jul 2026 09:15:00 -0400",
}
h, ok := parseHeaderBlock(block)
if !ok {
t.Fatal("a well formed block was rejected")
}
if len(h.To) != 3 {
t.Errorf("folded recipient list truncated: %v", h.To)
}
if !strings.Contains(h.Subject, "continues on the next line") {
t.Errorf("folded subject truncated: %q", h.Subject)
}
if h.From != "dana@agency.test" {
t.Errorf("from = %q", h.From)
}
if h.MessageID != "abc123@mail.test" {
t.Errorf("message id should have its angle brackets stripped, got %q", h.MessageID)
}
if h.Date.IsZero() {
t.Error("a valid date did not parse")
}
}
// A message with no identifier cannot be deduplicated, so logging it would log it again on every sync.
func TestMessageWithoutIDIsRejected(t *testing.T) {
if _, ok := parseHeaderBlock([]string{"From: a@b.test", "Subject: no id"}); ok {
t.Error("a message with no Message-ID must be rejected, it cannot be deduplicated")
}
if _, ok := parseHeaderBlock([]string{"Message-ID: <" + strings.Repeat("x", 500) + ">"}); ok {
t.Error("an absurdly long Message-ID must be rejected")
}
if _, ok := parseHeaderBlock(nil); ok {
t.Error("an empty block must be rejected")
}
}
// Every function here handles text a stranger chose. None may panic, hang, or allocate without bound.
func TestParsingIsTotalOnHostileInput(t *testing.T) {
hostile := []string{
"", ":", "::::", strings.Repeat(",", 5000), strings.Repeat("<", 5000),
strings.Repeat(`"`, 5000), "To: " + strings.Repeat("a@b.test,", 5000),
"Message-ID: <ok@test>\x00\x01", "Subject: " + strings.Repeat("x", 100000),
"From: <<<>>>", "Date: not a date at all",
}
for _, h := range hostile {
func() {
defer func() {
if r := recover(); r != nil {
t.Errorf("panicked on %q: %v", clip(h, 40), r)
}
}()
_, _ = parseHeaderBlock([]string{h})
_ = addresses(h)
_ = firstAddress(h)
_ = parseDate(h)
_ = decodeIfEncoded(h)
_ = parseFetch([]string{h})
}()
}
// A recipient list is bounded even when the header is not.
if n := len(addresses("To: " + strings.Repeat("a@b.test,", 5000))); n > 64 {
t.Errorf("recipient list not bounded: %d", n)
}
}
func TestParseDateForms(t *testing.T) {
for _, v := range []string{
"Mon, 14 Jul 2026 09:15:00 -0400",
"14 Jul 2026 09:15:00 -0400",
"Mon, 14 Jul 2026 09:15:00 -0400 (EDT)",
"Mon, 14 Jul 2026 09:15:00 GMT",
} {
if parseDate(v).IsZero() {
t.Errorf("failed to parse %q", v)
}
}
// An unreadable date is the zero time, not an error: the message still happened.
if !parseDate("yesterday afternoon").IsZero() {
t.Error("an unparseable date should yield the zero time")
}
}
// A newline inside a command is command injection against the IMAP session, exactly as a newline in a mail
// header is header injection.
func TestQuoteStripsControlCharacters(t *testing.T) {
got := quote("user\r\nA001 DELETE \"INBOX\"")
if strings.ContainsAny(got, "\r\n") {
t.Errorf("quote left a line break in %q", got)
}
if got := quote(`a"b\c`); got != `"a\"b\\c"` {
t.Errorf("quote escaping wrong: %s", got)
}
}
// parseFetch must separate messages and must not run away on a server that never closes one.
func TestParseFetchSeparatesMessages(t *testing.T) {
lines := []string{
"* 1 FETCH (BODY[HEADER.FIELDS (MESSAGE-ID FROM)] {60}",
"Message-ID: <one@test>",
"From: a@b.test",
")",
"* 2 FETCH (BODY[HEADER.FIELDS (MESSAGE-ID FROM)] {60}",
"Message-ID: <two@test>",
"From: c@d.test",
")",
}
got := parseFetch(lines)
if len(got) != 2 {
t.Fatalf("expected two messages, got %d", len(got))
}
if got[0].MessageID != "one@test" || got[1].MessageID != "two@test" {
t.Errorf("messages were not separated: %+v", got)
}
// A message block that never closes is bounded rather than accumulating forever.
runaway := []string{"* 1 FETCH ("}
for i := 0; i < 5000; i++ {
runaway = append(runaway, "X-Filler: value")
}
if r := parseFetch(runaway); len(r) > 1 {
t.Errorf("an unterminated block produced %d messages", len(r))
}
}
func TestTimeParsingIsStable(t *testing.T) {
got := parseDate("Mon, 14 Jul 2026 09:15:00 -0400")
want := time.Date(2026, 7, 14, 9, 15, 0, 0, time.FixedZone("", -4*3600))
if !got.Equal(want) {
t.Errorf("got %v want %v", got, want)
}
}