types: fix nil panics in Owner() and TailscaleUserID() for orphaned nodes

Owner() on a non-tagged node with nil User returns an invalid
UserView that panics when Name() is called. Add a guard to return
an empty UserView{} when the user is not valid.

TailscaleUserID() calls UserID().Get() without checking Valid()
first, which panics on orphaned nodes (no tags, no UserID). Add a
validity check to return 0 for this invalid state.

Callers should check Owner().Valid() before accessing fields.
This commit is contained in:
Kristoffer Dalby
2026-04-08 12:25:50 +00:00
committed by Kristoffer Dalby
parent 3037e5eee0
commit 4064f13bda

View File

@@ -789,12 +789,18 @@ func (nv NodeView) MarshalZerologObject(e *zerolog.Event) {
// Owner returns the owner for display purposes.
// For tagged nodes, returns TaggedDevices. For user-owned nodes, returns the user.
// Returns an invalid UserView if the node is in an orphaned state (no tags, no user).
// Callers should check .Valid() on the result before accessing fields.
func (nv NodeView) Owner() UserView {
if nv.IsTagged() {
return TaggedDevices.View()
}
return nv.User()
if user := nv.User(); user.Valid() {
return user
}
return UserView{}
}
func (nv NodeView) IPs() []netip.Addr {
@@ -996,6 +1002,7 @@ func (nv NodeView) TypedUserID() UserID {
// TailscaleUserID returns the user ID to use in Tailscale protocol.
// Tagged nodes always return TaggedDevices.ID, user-owned nodes return their actual UserID.
// Returns 0 for nodes in an orphaned state (no tags, no UserID).
func (nv NodeView) TailscaleUserID() tailcfg.UserID {
if !nv.Valid() {
return 0
@@ -1006,6 +1013,10 @@ func (nv NodeView) TailscaleUserID() tailcfg.UserID {
return tailcfg.UserID(int64(TaggedDevices.ID))
}
if !nv.UserID().Valid() {
return 0
}
//nolint:gosec // G115: UserID values are within int64 range
return tailcfg.UserID(int64(nv.UserID().Get()))
}