[bug-hunter] Heartbeat api monitor expands dotted params keys instead of preserving literals
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 78/100
Research direction
Start with x-pack/heartbeat/cmd/root.go:79-80 and the config path used by x-pack/heartbeat/monitors/api/api.go:44. Compare the existing dotted-key tests in x-pack/heartbeat/cmd/root_test.go:100-156 with the reproduction test described in the issue, then run the focused Go test. Done means the API monitor preserves params["subdomain.example.com"] literally without nested expansion.
Written by the indexing model from the issue text.
Description
Impact
x-pack Heartbeat type: api monitors that pass dotted params keys (for example "subdomain.example.com") do not receive the original literal key. The key is dot-expanded into nested objects, which breaks journey code expecting params["subdomain.example.com"].
This is user-impacting because api monitors reuse the browser source-job/params pipeline, but only browser streams get dotted-key preservation during config transform.
Reproduction Steps
- In
/home/runner/work/beats/beats/x-pack/heartbeat/cmd, add this new test file:
package cmd
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/types/known/structpb"
"github.com/elastic/elastic-agent-client/v7/pkg/client"
"github.com/elastic/elastic-agent-client/v7/pkg/proto"
)
func TestReproAPIParamsDottedKeys(t *testing.T) {
var rawIn proto.UnitExpectedConfig
err := readRawIn("testdata/simple-browser.in.json", &rawIn)
require.NoError(t, err, "failed to read fixture")
sourceMap := rawIn.GetSource().AsMap()
streams, ok := sourceMap["streams"].([]interface{})
require.True(t, ok, "expected streams slice")
require.NotEmpty(t, streams, "expected at least one stream")
stream, ok := streams[0].(map[string]interface{})
require.True(t, ok, "expected stream map")
stream["type"] = "api"
stream["params"] = map[string]interface{}{
"subdomain.example.com": "value2",
}
rawIn.Source, err = structpb.NewStruct(sourceMap)
require.NoError(t, err, "failed to rebuild proto source")
cfg, err := heartbeatCfg(&rawIn, &client.AgentInfo{ID: "abc7d0a8-ce04-4663-95da-ff6d537c268f", Version: "8.13.1"})
require.NoError(t, err, "heartbeatCfg returned error")
got, err := cfgToArrMap(cfg)
require.NoError(t, err, "failed to unpack cfg")
require.NotEmpty(t, got, "expected at least one monitor config")
params, ok := got[0]["params"].(map[string]interface{})
require.True(t, ok, "expected params map")
assert.Equal(t, "value2", params["subdomain.example.com"], "api monitor params dotted key should be preserved literally")
assert.NotContains(t, params, "subdomain", "api monitor params dotted key should not be expanded")
}
- Run:
go test -count=1 -run TestReproAPIParamsDottedKeys ./x-pack/heartbeat/cmd
Expected vs Actual
Expected: For type: api, dotted keys under params are preserved literally, so params["subdomain.example.com"] == "value2".
Actual: Dotted key is expanded to nested structure; the literal key is missing.
Actual failing output:
--- FAIL: TestReproAPIParamsDottedKeys (0.00s)
repro_api_params_test.go:43:
Error: Not equal:
expected: string("value2")
actual : <nil>(<nil>)
Messages: api monitor params dotted key should be preserved literally
repro_api_params_test.go:44:
Error: map[string]interface {}{"subdomain":map[string]interface {}{"example":map[string]interface {}{"com":"value2"}}} should not contain "subdomain"
Messages: api monitor params dotted key should not be expanded
FAIL
Failing Test
(identical to the reproduction test above)
Evidence
x-pack/heartbeat/cmd/root.go:79-80only extracts params forkind == "browser":if kind, _ := stream["type"].(string); !ok || kind != "browser" { continue }
x-pack/heartbeat/monitors/api/api.go:44reuses browser source job:sj, err := browser.NewSourceJob(cfg)
x-pack/heartbeat/cmd/root_test.go:100-156contains dotted-key preservation tests only for browser streams, not api streams.
What is this? | From workflow: Bug Hunter
Give us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.
- expires on Aug 6, 2026, 11:51 AM UTC
- Dominant language
- Go
- Stars
- 12.7k
- Forks
- 5k
- Avg merge
- 2d 1h
- Merged PRs (30d)
- 305
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from elastic/beats
-
[bug-hunter] http_endpoint skips options_response_code validation for empty options_headers map Openneeds_team
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
-
needs_team
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
needs_team
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
-
needs_team
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
-
docs Team:Elastic-Agent-Data-Plane
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
Similar issues
-
Difficulty 1/5 Under an hour Newbie friendliness 84/100
-
enhancement needs triage
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
-
kind/cleanup
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
kubernetes-sigs/kueue#15947 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
sympozium-ai/sympozium#627 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 86/100