mirror of
https://github.com/henrygd/beszel.git
synced 2026-09-21 17:07:47 +02:00
fix(hub): reject null systemd service entries to prevent hub panic
This commit is contained in:
@@ -330,6 +330,11 @@ func createSystemdStatsRecords(app core.App, data []*systemd.Service, systemId s
|
|||||||
|
|
||||||
valueStrings := make([]string, 0, len(data))
|
valueStrings := make([]string, 0, len(data))
|
||||||
for i, service := range data {
|
for i, service := range data {
|
||||||
|
// Agent payloads can contain null entries. Reject the snapshot before
|
||||||
|
// executing any queries so existing service records remain intact.
|
||||||
|
if service == nil {
|
||||||
|
return fmt.Errorf("null systemd service at index %d", i)
|
||||||
|
}
|
||||||
suffix := fmt.Sprintf("%d", i)
|
suffix := fmt.Sprintf("%d", i)
|
||||||
valueStrings = append(valueStrings, fmt.Sprintf("({:id%[1]s}, {:system}, {:name%[1]s}, {:state%[1]s}, {:sub%[1]s}, {:cpu%[1]s}, {:cpuPeak%[1]s}, {:memory%[1]s}, {:memPeak%[1]s}, {:updated})", suffix))
|
valueStrings = append(valueStrings, fmt.Sprintf("({:id%[1]s}, {:system}, {:name%[1]s}, {:state%[1]s}, {:sub%[1]s}, {:cpu%[1]s}, {:cpuPeak%[1]s}, {:memory%[1]s}, {:memPeak%[1]s}, {:updated})", suffix))
|
||||||
params["id"+suffix] = makeStableHashId(systemId, service.Name)
|
params["id"+suffix] = makeStableHashId(systemId, service.Name)
|
||||||
|
|||||||
@@ -3,9 +3,11 @@
|
|||||||
package systems_test
|
package systems_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/fxamacker/cbor/v2"
|
||||||
"github.com/henrygd/beszel/internal/entities/system"
|
"github.com/henrygd/beszel/internal/entities/system"
|
||||||
"github.com/henrygd/beszel/internal/entities/systemd"
|
"github.com/henrygd/beszel/internal/entities/systemd"
|
||||||
"github.com/henrygd/beszel/internal/hub/systems"
|
"github.com/henrygd/beszel/internal/hub/systems"
|
||||||
@@ -15,6 +17,42 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestCreateRecordsRejectsNullSystemdService(t *testing.T) {
|
||||||
|
hub, user := tests.GetHubWithUser(t)
|
||||||
|
defer hub.Cleanup()
|
||||||
|
records, err := tests.CreateSystems(hub, 1, user.Id, "paused")
|
||||||
|
require.NoError(t, err)
|
||||||
|
sys, err := hub.GetSystemManager().GetSystem(records[0].Id)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, systems.CreateSystemdStatsRecords(hub, []*systemd.Service{
|
||||||
|
{Name: "existing.service", State: systemd.StatusFailed},
|
||||||
|
}, records[0].Id))
|
||||||
|
|
||||||
|
for _, services := range []string{`[null]`, `[{"name":"new.service"},null]`, `[null,{"name":"new.service"}]`} {
|
||||||
|
for _, encoding := range []string{"json", "cbor"} {
|
||||||
|
t.Run(encoding+"/"+services, func(t *testing.T) {
|
||||||
|
var data system.CombinedData
|
||||||
|
require.NoError(t, json.Unmarshal([]byte(`{"systemd":`+services+`}`), &data))
|
||||||
|
if encoding == "cbor" {
|
||||||
|
encoded, err := cbor.Marshal(data)
|
||||||
|
require.NoError(t, err)
|
||||||
|
data = system.CombinedData{}
|
||||||
|
require.NoError(t, cbor.Unmarshal(encoded, &data))
|
||||||
|
}
|
||||||
|
_, err := sys.CreateRecords(&data)
|
||||||
|
require.ErrorContains(t, err, "null systemd service")
|
||||||
|
var names []string
|
||||||
|
require.NoError(t, hub.DB().Select("name").From("systemd_services").
|
||||||
|
Where(dbx.HashExp{"system": records[0].Id}).Column(&names))
|
||||||
|
assert.Equal(t, []string{"existing.service"}, names)
|
||||||
|
count, err := hub.CountRecords("system_stats", dbx.HashExp{"system": records[0].Id})
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Zero(t, count, "invalid snapshot must roll back system stats")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCreateRecordsHandlesSystemdAlertLifecycle(t *testing.T) {
|
func TestCreateRecordsHandlesSystemdAlertLifecycle(t *testing.T) {
|
||||||
hub, user := tests.GetHubWithUser(t)
|
hub, user := tests.GetHubWithUser(t)
|
||||||
defer hub.Cleanup()
|
defer hub.Cleanup()
|
||||||
|
|||||||
Reference in New Issue
Block a user