mox/mtasts/parse_test.go
Mechiel Lukkien cb229cb6cf
mox!
2023-01-30 14:27:06 +01:00

237 lines
9.8 KiB
Go

package mtasts
import (
"reflect"
"testing"
"github.com/mjl-/mox/dns"
)
func TestRecord(t *testing.T) {
good := func(txt string, want Record) {
t.Helper()
r, _, err := ParseRecord(txt)
if err != nil {
t.Fatalf("parse: %s", err)
}
if !reflect.DeepEqual(r, &want) {
t.Fatalf("want %#v, got %#v", want, *r)
}
}
bad := func(txt string) {
t.Helper()
r, _, err := ParseRecord(txt)
if err == nil {
t.Fatalf("parse, expected error, got record %v", r)
}
}
good("v=STSv1; id=20160831085700Z;", Record{Version: "STSv1", ID: "20160831085700Z"})
good("v=STSv1; \t id=20160831085700Z \t;", Record{Version: "STSv1", ID: "20160831085700Z"})
good("v=STSv1; id=a", Record{Version: "STSv1", ID: "a"})
good("v=STSv1; id=a; more=a; ext=2", Record{Version: "STSv1", ID: "a", Extensions: []Pair{{"more", "a"}, {"ext", "2"}}})
bad("v=STSv0")
bad("v=STSv10")
bad("v=STSv2")
bad("v=STSv1") // missing id
bad("v=STSv1;") // missing id
bad("v=STSv1; ext=1") // missing id
bad("v=STSv1; id=") // empty id
bad("v=STSv1; id=012345678901234567890123456789012") // id too long
bad("v=STSv1; id=test-123") // invalid id
bad("v=STSv1; id=a; more=") // empty value in extension
bad("v=STSv1; id=a; a12345678901234567890123456789012=1") // extension name too long
bad("v=STSv1; id=a; 1%=a") // invalid extension name
bad("v=STSv1; id=a; test==") // invalid extension name
bad("v=STSv1; id=a;;") // additional semicolon
const want = `v=STSv1; id=a; more=a; ext=2`
record := Record{Version: "STSv1", ID: "a", Extensions: []Pair{{"more", "a"}, {"ext", "2"}}}
got := record.String()
if got != want {
t.Fatalf("record string, got %q, want %q", got, want)
}
}
func TestParsePolicy(t *testing.T) {
good := func(s string, want Policy) {
t.Helper()
p, err := ParsePolicy(s)
if err != nil {
t.Fatalf("parse policy: %s", err)
}
if !reflect.DeepEqual(p, &want) {
t.Fatalf("want %v, got %v", want, p)
}
}
good(`version: STSv1
mode: testing
mx: mx1.example.com
mx: mx2.example.com
mx: mx.backup-example.com
max_age: 1296000
`,
Policy{
Version: "STSv1",
Mode: ModeTesting,
MX: []STSMX{
{Domain: dns.Domain{ASCII: "mx1.example.com"}},
{Domain: dns.Domain{ASCII: "mx2.example.com"}},
{Domain: dns.Domain{ASCII: "mx.backup-example.com"}},
},
MaxAgeSeconds: 1296000,
},
)
good("version: STSv1\nmode: enforce \nmx: *.example.com \nmax_age: 0 \n",
Policy{
Version: "STSv1",
Mode: ModeEnforce,
MX: []STSMX{
{Wildcard: true, Domain: dns.Domain{ASCII: "example.com"}},
},
MaxAgeSeconds: 0,
},
)
good("version:STSv1\r\nmode:\tenforce\r\nmx: \t\t *.example.com\nmax_age: 1\nmore:ext e ns ion",
Policy{
Version: "STSv1",
Mode: ModeEnforce,
MX: []STSMX{
{Wildcard: true, Domain: dns.Domain{ASCII: "example.com"}},
},
MaxAgeSeconds: 1,
Extensions: []Pair{{"more", "ext e ns ion"}},
},
)
bad := func(s string) {
t.Helper()
p, err := ParsePolicy(s)
if err == nil {
t.Fatalf("parsing policy did not fail: %v", p)
}
}
bad("") // missing version
bad("version:STSv0\nmode:none\nmax_age:0") // bad version
bad("version:STSv10\nmode:none\nmax_age:0") // bad version
bad("version:STSv2\nmode:none\nmax_age:0") // bad version
bad("version:STSv1\nmax_age:0\nmx:example.com") // missing mode
bad("version:STSv1\nmode:none") // missing max_age
bad("version:STSv1\nmax_age:0\nmode:enforce") // missing mx for mode
bad("version:STSv1\nmax_age:0\nmode:testing") // missing mx for mode
bad("max_age:0\nmode:none") // missing version
bad("version:STSv1\nmode:none\nmax_age:01234567890") // max_age too long
bad("version:STSv1\nmode:bad\nmax_age:1") // bad mode
bad("version:STSv1\nmode:none\nmax_age:a") // bad max_age
bad("version:STSv1\nmode:enforce\nmax_age:0\nmx:") // missing value
bad("version:STSv1\nmode:enforce\nmax_age:0\nmx:*.*.example") // bad mx
bad("version:STSv1\nmode:enforce\nmax_age:0\nmx:**.example") // bad mx
bad("version:STSv1\nmode:enforce\nmax_age:0\nmx:**.example-") // bad mx
bad("version:STSv1\nmode:enforce\nmax_age:0\nmx:test.example-") // bad mx
bad("version:STSv1\nmode:none\nmax_age:0\next:") // empty extension
bad("version:STSv1\nmode:none\nmax_age:0\na12345678901234567890123456789012:123") // long extension name
bad("version:STSv1\nmode:none\nmax_age:0\n_bad:test") // bad ext name
bad("version:STSv1\nmode:none\nmax_age:0\nmx: møx.example") // invalid u-label in mx
policy := Policy{
Version: "STSv1",
Mode: ModeTesting,
MX: []STSMX{
{Domain: dns.Domain{ASCII: "mx1.example.com"}},
{Domain: dns.Domain{ASCII: "mx2.example.com"}},
{Domain: dns.Domain{ASCII: "mx.backup-example.com"}},
},
MaxAgeSeconds: 1296000,
}
want := `version: STSv1
mode: testing
max_age: 1296000
mx: mx1.example.com
mx: mx2.example.com
mx: mx.backup-example.com
`
got := policy.String()
if got != want {
t.Fatalf("policy string, got %q, want %q", got, want)
}
}
func FuzzParseRecord(f *testing.F) {
f.Add("v=STSv1; id=20160831085700Z;")
f.Add("v=STSv1; \t id=20160831085700Z \t;")
f.Add("v=STSv1; id=a")
f.Add("v=STSv1; id=a; more=a; ext=2")
f.Add("v=STSv0")
f.Add("v=STSv10")
f.Add("v=STSv2")
f.Add("v=STSv1") // missing id
f.Add("v=STSv1;") // missing id
f.Add("v=STSv1; ext=1") // missing id
f.Add("v=STSv1; id=") // empty id
f.Add("v=STSv1; id=012345678901234567890123456789012") // id too long
f.Add("v=STSv1; id=test-123") // invalid id
f.Add("v=STSv1; id=a; more=") // empty value in extension
f.Add("v=STSv1; id=a; a12345678901234567890123456789012=1") // extension name too long
f.Add("v=STSv1; id=a; 1%=a") // invalid extension name
f.Add("v=STSv1; id=a; test==") // invalid extension name
f.Add("v=STSv1; id=a;;") // additional semicolon
f.Fuzz(func(t *testing.T, s string) {
r, _, err := ParseRecord(s)
if err == nil {
_ = r.String()
}
})
}
func FuzzParsePolicy(f *testing.F) {
f.Add(`version: STSv1
mode: testing
mx: mx1.example.com
mx: mx2.example.com
mx: mx.backup-example.com
max_age: 1296000
`)
f.Add(`version: STSv1
mode: enforce
mx: *.example.com
max_age: 0
`)
f.Add("version:STSv1\r\nmode:\tenforce\r\nmx: \t\t *.example.com\nmax_age: 1\nmore:ext e ns ion")
f.Add("") // missing version
f.Add("version:STSv0\nmode:none\nmax_age:0") // bad version
f.Add("version:STSv10\nmode:none\nmax_age:0") // bad version
f.Add("version:STSv2\nmode:none\nmax_age:0") // bad version
f.Add("version:STSv1\nmax_age:0\nmx:example.com") // missing mode
f.Add("version:STSv1\nmode:none") // missing max_age
f.Add("version:STSv1\nmax_age:0\nmode:enforce") // missing mx for mode
f.Add("version:STSv1\nmax_age:0\nmode:testing") // missing mx for mode
f.Add("max_age:0\nmode:none") // missing version
f.Add("version:STSv1\nmode:none\nmax_age:0 ") // trailing whitespace
f.Add("version:STSv1\nmode:none\nmax_age:01234567890") // max_age too long
f.Add("version:STSv1\nmode:bad\nmax_age:1") // bad mode
f.Add("version:STSv1\nmode:none\nmax_age:a") // bad max_age
f.Add("version:STSv1\nmode:enforce\nmax_age:0\nmx:") // missing value
f.Add("version:STSv1\nmode:enforce\nmax_age:0\nmx:*.*.example") // bad mx
f.Add("version:STSv1\nmode:enforce\nmax_age:0\nmx:**.example") // bad mx
f.Add("version:STSv1\nmode:enforce\nmax_age:0\nmx:**.example-") // bad mx
f.Add("version:STSv1\nmode:enforce\nmax_age:0\nmx:test.example-") // bad mx
f.Add("version:STSv1\nmode:none\nmax_age:0\next:") // empty extension
f.Add("version:STSv1\nmode:none\nmax_age:0\next:abc ") // trailing space
f.Add("version:STSv1\nmode:none\nmax_age:0\next:a\t") // invalid char
f.Add("version:STSv1\nmode:none\nmax_age:0\na12345678901234567890123456789012:123") // long extension name
f.Add("version:STSv1\nmode:none\nmax_age:0\n_bad:test") // bad ext name
f.Fuzz(func(t *testing.T, s string) {
r, err := ParsePolicy(s)
if err == nil {
_ = r.String()
}
})
}