fix: harden DynDNS recovery and publish v1.5.1
This commit is contained in:
1 parent
3383b91e49
commit
81def77e3c
37 files changed
+2415
-250
No files matched your search
@@ -0,0 +1,91 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user