diff --git a/data.go b/data.go index 448e0a9..1e9e4c4 100644 --- a/data.go +++ b/data.go @@ -81,6 +81,9 @@ func SetForTest(agents []Agent) (restore func()) { if a.NodeID == 0 { continue } + if a.Hostname == "" { + continue + } idx[a.NodeID] = a.Hostname } mu.Lock() @@ -120,6 +123,9 @@ func Load(raw []byte) error { if a.NodeID == 0 { continue // 0 is reserved / would silently match unset fields } + if a.Hostname == "" { + continue // empty hostname: missing required field — drop + } idx[a.NodeID] = a.Hostname } mu.Lock() diff --git a/zz_test.go b/zz_test.go index dd25ff9..fadf207 100644 --- a/zz_test.go +++ b/zz_test.go @@ -75,6 +75,43 @@ func TestMalformedRejected(t *testing.T) { } } +func TestEmptyHostnameSkipped(t *testing.T) { + t.Parallel() + + // Entry with non-zero node_id but empty hostname must be dropped, + // so a missing hostname field can't produce an empty-string trust name. + restore := SetForTest([]Agent{ + {Hostname: "", NodeID: 7}, + {Hostname: "valid", NodeID: 9}, + }) + defer restore() + + if _, ok := IsTrusted(7); ok { + t.Fatal("IsTrusted(7): empty-hostname entry must not match") + } + if _, ok := IsTrusted(9); !ok { + t.Fatal("IsTrusted(9): valid entry should still match") + } +} + +func TestLoadEmptyHostnameSkipped(t *testing.T) { + // Not parallel: calls Load() which mutates shared global state. + if err := Load([]byte(`{"agents":[ + {"hostname":"","node_id":7}, + {"hostname":"valid","node_id":9} + ]}`)); err != nil { + t.Fatalf("Load must succeed when skipping empty-hostname entries: %v", err) + } + if _, ok := IsTrusted(7); ok { + t.Fatal("empty-hostname entry must not become trusted after Load") + } + if _, ok := IsTrusted(9); !ok { + t.Fatal("valid entry must still be trusted after Load") + } + // Restore embedded list for downstream tests. + _ = Load(embeddedJSON) +} + func TestAllReturnsCopy(t *testing.T) { t.Parallel() restore := SetForTest([]Agent{{Hostname: "a", NodeID: 1}})