Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changes/47388-getclientconfig-nil-map-panic
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Fixed a server panic ("assignment to entry in nil map") when a host checked in for its osquery config while its agent options had a null `config`.
7 changes: 7 additions & 0 deletions server/service/osquery.go
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,13 @@ func (svc *Service) GetClientConfig(ctx context.Context) (map[string]interface{}
if err != nil {
return nil, newOsqueryError("internal error: parse base configuration: " + err.Error())
}
if config == nil {
// Unmarshaling the JSON literal `null` (e.g. agent options with
// "config": null) sets the map to nil rather than leaving it empty.
// Re-initialize so later assignments (e.g. config["packs"]) don't
// panic with "assignment to entry in nil map".
config = make(map[string]any)
}
}

packConfig := fleet.Packs{}
Expand Down
46 changes: 46 additions & 0 deletions server/service/osquery_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,52 @@ func TestGetClientConfig(t *testing.T) {
)
}

// TestGetClientConfigNullConfig is a regression test for a panic
// ("assignment to entry in nil map") that occurred when a host's agent options
// had a null "config" and the host also had packs/scheduled queries. See
// https://github.com/fleetdm/fleet/issues/47388.
func TestGetClientConfigNullConfig(t *testing.T) {
ds := new(mock.Store)

ds.TeamAgentOptionsFunc = func(ctx context.Context, teamID uint) (*json.RawMessage, error) {
return nil, nil
}
ds.ListPacksForHostFunc = func(ctx context.Context, hid uint) ([]*fleet.Pack, error) {
return []*fleet.Pack{{ID: 1, Name: "pack_by_label"}}, nil
}
ds.ListScheduledQueriesInPackFunc = func(ctx context.Context, pid uint) (fleet.ScheduledQueryList, error) {
return []*fleet.ScheduledQuery{
{Name: "time", Query: "select * from time", Interval: 30, Removed: new(false)},
}, nil
}
ds.ListScheduledQueriesForAgentsFunc = func(ctx context.Context, teamID *uint, hostID *uint, queryReportsDisabled bool) ([]*fleet.Query, error) {
return nil, nil
}
// Global agent options with a null config. This unmarshals into a nil map,
// which previously caused a panic once packs were added to the config.
ds.AppConfigFunc = func(ctx context.Context) (*fleet.AppConfig, error) {
return &fleet.AppConfig{AgentOptions: new(json.RawMessage(`{"config":null}`))}, nil
}
ds.UpdateHostFunc = func(ctx context.Context, host *fleet.Host) error {
return nil
}

svc, ctx := newTestService(t, ds, nil, nil)
ctx = hostctx.NewContext(ctx, &fleet.Host{ID: 1})

conf, err := svc.GetClientConfig(ctx)
require.NoError(t, err)
assert.JSONEq(t, `{
"pack_by_label": {
"queries":{
"time":{"query":"select * from time","interval":30,"removed":false}
}
}
}`,
string(conf["packs"].(json.RawMessage)),
)
}

func TestAgentOptionsForHost(t *testing.T) {
ds := new(mock.Store)
svc, ctx := newTestService(t, ds, nil, nil)
Expand Down
Loading