[management] clean nul byte strings in peer meta (#8219)

This commit is contained in:
Pascal Fischer
2026-10-15 16:26:04 +02:00
committed by GitHub
parent 89c1b47e9c
commit dd830f0890
2 changed files with 68 additions and 18 deletions
@@ -0,0 +1,45 @@
package grpc
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/netbirdio/netbird/shared/management/proto"
)
func TestExtractPeerMeta_RemovesNulAndInvalidUTF8(t *testing.T) {
meta := extractPeerMeta(context.Background(), &proto.PeerSystemMeta{
Hostname: "host\x00name",
OS: "\x00\x00\x00\x00\x00\x00s",
OSVersion: "11\xff",
SysSerialNumber: "SN\x00\x00",
Environment: &proto.Environment{Cloud: "\x00aws"},
NetworkAddresses: []*proto.NetworkAddress{
{NetIP: "192.168.0.1/24", Mac: "e0:d5\x00"},
},
Files: []*proto.File{{Path: "C:\\x\x00.exe"}},
})
assert.Equal(t, "hostname", meta.Hostname)
assert.Equal(t, "s", meta.OS)
assert.Equal(t, "11", meta.OSVersion)
assert.Equal(t, "SN", meta.SystemSerialNumber)
assert.Equal(t, "aws", meta.Environment.Cloud)
assert.Equal(t, "e0:d5", meta.NetworkAddresses[0].Mac)
assert.Equal(t, "C:\\x.exe", meta.Files[0].Path)
}
func TestCleanClientString(t *testing.T) {
tests := map[string]string{
"": "",
"\x00\x00": "",
"Wïndows": "Wïndows",
"ssh-ed25519 AAA\x00": "ssh-ed25519 AAA",
"bad\xc3\x28utf8": "bad(utf8",
}
for in, want := range tests {
assert.Equal(t, want, cleanClientString(in), "input %q", in)
}
}
+23 -18
View File
@@ -553,9 +553,9 @@ func extractPeerMeta(ctx context.Context, meta *proto.PeerSystemMeta) nbpeer.Pee
return nbpeer.PeerSystemMeta{}
}
osVersion := meta.GetOSVersion()
osVersion := cleanClientString(meta.GetOSVersion())
if osVersion == "" {
osVersion = meta.GetCore()
osVersion = cleanClientString(meta.GetCore())
}
networkAddresses := make([]nbpeer.NetworkAddress, 0, len(meta.GetNetworkAddresses()))
@@ -567,36 +567,36 @@ func extractPeerMeta(ctx context.Context, meta *proto.PeerSystemMeta) nbpeer.Pee
}
networkAddresses = append(networkAddresses, nbpeer.NetworkAddress{
NetIP: netAddr,
Mac: addr.GetMac(),
Mac: cleanClientString(addr.GetMac()),
})
}
files := make([]nbpeer.File, 0, len(meta.GetFiles()))
for _, file := range meta.GetFiles() {
files = append(files, nbpeer.File{
Path: file.GetPath(),
Path: cleanClientString(file.GetPath()),
Exist: file.GetExist(),
ProcessIsRunning: file.GetProcessIsRunning(),
})
}
return nbpeer.PeerSystemMeta{
Hostname: meta.GetHostname(),
GoOS: meta.GetGoOS(),
Kernel: meta.GetKernel(),
Platform: meta.GetPlatform(),
OS: meta.GetOS(),
Hostname: cleanClientString(meta.GetHostname()),
GoOS: cleanClientString(meta.GetGoOS()),
Kernel: cleanClientString(meta.GetKernel()),
Platform: cleanClientString(meta.GetPlatform()),
OS: cleanClientString(meta.GetOS()),
OSVersion: osVersion,
WtVersion: meta.GetNetbirdVersion(),
UIVersion: meta.GetUiVersion(),
KernelVersion: meta.GetKernelVersion(),
WtVersion: cleanClientString(meta.GetNetbirdVersion()),
UIVersion: cleanClientString(meta.GetUiVersion()),
KernelVersion: cleanClientString(meta.GetKernelVersion()),
NetworkAddresses: networkAddresses,
SystemSerialNumber: meta.GetSysSerialNumber(),
SystemProductName: meta.GetSysProductName(),
SystemManufacturer: meta.GetSysManufacturer(),
SystemSerialNumber: cleanClientString(meta.GetSysSerialNumber()),
SystemProductName: cleanClientString(meta.GetSysProductName()),
SystemManufacturer: cleanClientString(meta.GetSysManufacturer()),
Environment: nbpeer.Environment{
Cloud: meta.GetEnvironment().GetCloud(),
Platform: meta.GetEnvironment().GetPlatform(),
Cloud: cleanClientString(meta.GetEnvironment().GetCloud()),
Platform: cleanClientString(meta.GetEnvironment().GetPlatform()),
},
Flags: nbpeer.Flags{
RosenpassEnabled: meta.GetFlags().GetRosenpassEnabled(),
@@ -618,6 +618,11 @@ func extractPeerMeta(ctx context.Context, meta *proto.PeerSystemMeta) nbpeer.Pee
}
}
// cleanClientString removes NUL bytes and invalid UTF-8 from a client-reported string, which Postgres rejects in text columns.
func cleanClientString(s string) string {
return strings.ToValidUTF8(strings.ReplaceAll(s, "\x00", ""), "")
}
func capabilitiesToInt32(caps []proto.PeerCapability) []int32 {
result := make([]int32, len(caps))
for i, c := range caps {
@@ -711,7 +716,7 @@ func (s *Server) Login(ctx context.Context, req *proto.EncryptedMessage) (*proto
peer, network, postureChecks, enableSSH, err := s.accountManager.LoginPeer(ctx, types.PeerLogin{
WireGuardPubKey: peerKey.String(),
SSHKey: string(sshKey),
SSHKey: cleanClientString(string(sshKey)),
Meta: peerMeta,
UserID: userID,
SetupKey: loginReq.GetSetupKey(),