diff --git a/server/channels/app/users/profile_picture.go b/server/channels/app/users/profile_picture.go index 63c0c556127..13e1b9bcdbd 100644 --- a/server/channels/app/users/profile_picture.go +++ b/server/channels/app/users/profile_picture.go @@ -135,7 +135,14 @@ func createProfileImage(username string, userID string, initialFont string) ([]b h.Write([]byte(userID)) seed := h.Sum32() - initial := string(strings.ToUpper(username)[0]) + // Guard against empty/whitespace-only usernames which can occur when user records + // bypass model validation (e.g. LDAP/SAML sync, direct DB inserts, data migrations). + // See: api4.getProfileImage → app.GetProfileImage → GetDefaultProfileImage → createProfileImage + trimmedUsername := strings.TrimSpace(username) + initial := "?" + if trimmedUsername != "" { + initial = string([]rune(strings.ToUpper(trimmedUsername))[0]) + } font, err := getFont(initialFont) if err != nil { diff --git a/server/channels/app/users/profile_picture_test.go b/server/channels/app/users/profile_picture_test.go index 6d2bc40030c..3054c07949f 100644 --- a/server/channels/app/users/profile_picture_test.go +++ b/server/channels/app/users/profile_picture_test.go @@ -24,3 +24,21 @@ func TestCreateProfileImage(t *testing.T) { require.Equal(t, colorful, img.At(1, 1), "Failed to create correct color") } + +func TestCreateProfileImage_EmptyUsername(t *testing.T) { + t.Run("empty username should not panic", func(t *testing.T) { + require.NotPanics(t, func() { + b, err := createProfileImage("", "eo1zkdr96pdj98pjmq8zy35wba", "nunito-bold.ttf") + require.NoError(t, err) + require.NotEmpty(t, b) + }) + }) + + t.Run("whitespace-only username should not panic", func(t *testing.T) { + require.NotPanics(t, func() { + b, err := createProfileImage(" ", "eo1zkdr96pdj98pjmq8zy35wba", "nunito-bold.ttf") + require.NoError(t, err) + require.NotEmpty(t, b) + }) + }) +}