Files

92 lines
2.9 KiB
Go

package diagnostics
import (
"bytes"
"context"
"encoding/json"
"io"
"log/slog"
"net"
"os"
"path/filepath"
"strings"
"syscall"
"testing"
)
func TestExportPersistsOnlyAllowlistedFields(t *testing.T) {
t.Parallel()
var output bytes.Buffer
path := filepath.Join(t.TempDir(), "diagnostics.jsonl")
r := &Recorder{path: path, logger: slog.New(slog.NewJSONHandler(&output, nil))}
const secret = "password-token-cookie-SID-secret"
r.Record(Event{Code: "ip_fetch_failed", Target: "https://user:" + secret + "@private.test/?token=" + secret, Failure: secret, Address: secret, Family: secret, Network: &Network{Nameservers: []string{"127.0.0.11", secret}, DockerUpstreams: []string{secret}, Routes: []Route{{secret, secret, secret, secret}}}})
r.Record(Event{Code: secret, Target: secret})
r.Record(Event{Code: "ip_fetch_failed", Address: "fe80::1%" + secret, DNSResolver: "fe80::2%" + secret, Network: &Network{Nameservers: []string{"fe80::3%" + secret}}})
reloaded := &Recorder{path: path, logger: slog.New(slog.NewTextHandler(io.Discard, nil))}
encoded, err := json.Marshal(reloaded.Snapshot())
if err != nil {
t.Fatal(err)
}
file, err := os.ReadFile(path)
if err != nil {
t.Fatal(err)
}
for _, data := range []string{string(encoded), string(file), output.String()} {
if strings.Contains(data, secret) || strings.Contains(data, "private.test") {
t.Fatalf("secret escaped allowlist: %s", data)
}
}
if !strings.Contains(string(encoded), "127.0.0.11") || !strings.Contains(string(encoded), "ip_fetch_failed") {
t.Fatal("technical evidence lost")
}
info, err := os.Stat(path)
if err != nil || info.Mode().Perm() != 0600 {
t.Fatalf("unsafe diagnostic permissions: %v %v", info, err)
}
}
func TestDiagnosticRotationIsBoundedAndReloadable(t *testing.T) {
t.Parallel()
path := filepath.Join(t.TempDir(), "diagnostics.jsonl")
file, err := os.Create(path)
if err != nil {
t.Fatal(err)
}
if err = file.Truncate(maxFileSize); err != nil {
t.Fatal(err)
}
file.Close()
r := &Recorder{path: path, logger: slog.New(slog.NewTextHandler(io.Discard, nil))}
r.Record(Event{Code: "update_recovered", Attempt: 20})
info, err := os.Stat(path + ".1")
if err != nil || info.Size() != maxFileSize {
t.Fatalf("rotation: %v %v", info, err)
}
events := r.Snapshot()
if len(events) != 1 || events[0].Attempt != 20 {
t.Fatalf("new event lost after rotation: %#v", events)
}
}
func TestNetworkFailuresAndRouteDecoding(t *testing.T) {
t.Parallel()
for _, test := range []struct {
err error
want string
}{
{&net.DNSError{IsTemporary: true}, "dns_server_failure"},
{&net.DNSError{IsTimeout: true}, "dns_timeout"},
{syscall.EHOSTUNREACH, "no_route"},
{syscall.ECONNREFUSED, "connection_refused"},
{context.DeadlineExceeded, "timeout"},
} {
if got := Failure(test.err); got != test.want {
t.Errorf("failure=%s want %s", got, test.want)
}
}
if got := routeIP("010012AC"); got != "172.18.0.1" {
t.Fatalf("default gateway decoded incorrectly: %s", got)
}
}