Initial commit
CalDAV and CardDAV checkers sharing a single Go module. Discovery follows RFC 6764 (/.well-known + SRV/TXT), authenticated probes cover principal, home-set, collections and a minimal REPORT query on top of go-webdav. Common shape in internal/dav/; CalDAV adds a scheduling rule. Surfaces its context URL (and each secure-SRV target) as TLS endpoints via the EndpointDiscoverer interface, so the dedicated TLS checker can pick them up without re-parsing observations. HTML report foregrounds common misconfigs (well-known returning 200, missing SRV, plaintext-only SRV, missing DAV capability, skipped auth phase) as action-item callouts before the full phase breakdown.
This commit is contained in:
commit
7d5535fddf
39 changed files with 3179 additions and 0 deletions
98
internal/dav/endpoints_test.go
Normal file
98
internal/dav/endpoints_test.go
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
package dav
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
tlsct "git.happydns.org/checker-tls/contract"
|
||||
)
|
||||
|
||||
// parseAll decodes DiscoverEntries output via the TLS contract. Malformed
|
||||
// entries fail the test so we notice drift quickly.
|
||||
func parseAll(t *testing.T, obs *Observation) []tlsct.TLSEndpoint {
|
||||
t.Helper()
|
||||
entries := DiscoverEntries(obs)
|
||||
eps, warnings := tlsct.ParseEntries(entries)
|
||||
if len(warnings) != 0 {
|
||||
t.Fatalf("unexpected decode warnings: %v", warnings)
|
||||
}
|
||||
out := make([]tlsct.TLSEndpoint, len(eps))
|
||||
for i, e := range eps {
|
||||
if e.Ref == "" {
|
||||
t.Errorf("entry %d has empty Ref", i)
|
||||
}
|
||||
out[i] = e.Endpoint
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func TestDiscoverEntries_contextURLOnly(t *testing.T) {
|
||||
obs := &Observation{
|
||||
Discovery: DiscoveryResult{ContextURL: "https://dav.example.com/caldav/"},
|
||||
}
|
||||
got := parseAll(t, obs)
|
||||
if len(got) != 1 {
|
||||
t.Fatalf("got %d endpoints, want 1: %+v", len(got), got)
|
||||
}
|
||||
if got[0].Host != "dav.example.com" || got[0].Port != 443 {
|
||||
t.Errorf("unexpected endpoint: %+v", got[0])
|
||||
}
|
||||
// Direct TLS; no STARTTLS upgrade.
|
||||
if got[0].STARTTLS != "" {
|
||||
t.Errorf("STARTTLS = %q, want empty (direct TLS)", got[0].STARTTLS)
|
||||
}
|
||||
// SNI must be set unconditionally, even when it is equal to Host.
|
||||
if got[0].SNI != "dav.example.com" {
|
||||
t.Errorf("SNI = %q, want dav.example.com", got[0].SNI)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDiscoverEntries_nonDefaultPort(t *testing.T) {
|
||||
obs := &Observation{
|
||||
Discovery: DiscoveryResult{ContextURL: "https://dav.example.com:8443/caldav/"},
|
||||
}
|
||||
got := parseAll(t, obs)
|
||||
if len(got) != 1 || got[0].Port != 8443 {
|
||||
t.Fatalf("unexpected: %+v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDiscoverEntries_srvTargets(t *testing.T) {
|
||||
// SRV pointing to a different name than the domain → we must surface
|
||||
// the SRV target too, because that's the hostname the cert needs to
|
||||
// cover.
|
||||
obs := &Observation{
|
||||
Discovery: DiscoveryResult{
|
||||
ContextURL: "https://dav.example.com/caldav/",
|
||||
SecureSRV: []SRVRecord{
|
||||
{Target: "dav-backend-1.example.net", Port: 443},
|
||||
{Target: "dav-backend-2.example.net", Port: 443},
|
||||
{Target: "dav.example.com", Port: 443}, // duplicate of context → deduped
|
||||
},
|
||||
},
|
||||
}
|
||||
got := parseAll(t, obs)
|
||||
if len(got) != 3 {
|
||||
t.Fatalf("expected 3 unique endpoints, got %d: %+v", len(got), got)
|
||||
}
|
||||
hosts := map[string]bool{}
|
||||
for _, e := range got {
|
||||
hosts[e.Host] = true
|
||||
if e.SNI != e.Host {
|
||||
t.Errorf("endpoint %+v: SNI=%q, want %q (equal to Host)", e, e.SNI, e.Host)
|
||||
}
|
||||
}
|
||||
for _, want := range []string{"dav.example.com", "dav-backend-1.example.net", "dav-backend-2.example.net"} {
|
||||
if !hosts[want] {
|
||||
t.Errorf("missing host %q in %+v", want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDiscoverEntries_emptyOnNoContextURL(t *testing.T) {
|
||||
if got := DiscoverEntries(&Observation{}); got != nil {
|
||||
t.Errorf("expected nil, got %+v", got)
|
||||
}
|
||||
if got := DiscoverEntries(nil); got != nil {
|
||||
t.Errorf("expected nil for nil obs, got %+v", got)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue