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
17 changes: 12 additions & 5 deletions pkg/ebpf/common/mongo_detect_transform.go
Original file line number Diff line number Diff line change
Expand Up @@ -549,19 +549,26 @@ func findIntInBson(doc bson.D, key string) (int, bool) {
if !found {
return 0, false
}
intValue, ok := value.(int) // MongoDB uses int32 for integer values
if !ok {
return 0, false

// bson.Unmarshal stores MongoDB int32 as Go int32 and int64 as Go int64.
switch v := value.(type) {
case int32:
return int(v), true
case int64:
return int(v), true
case int:
return v, true
}
return intValue, true

return 0, false
}

func findDoubleInBson(doc bson.D, key string) (float64, bool) {
value, found := findInBson(doc, key)
if !found {
return 0, false
}
doubleValue, ok := value.(float64) // MongoDB uses int32 for integer values
doubleValue, ok := value.(float64) // MongoDB uses float64 (double) for floating-point values
if !ok {
return 0, false
}
Expand Down
40 changes: 39 additions & 1 deletion pkg/ebpf/common/mongo_detect_transform_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,9 @@ func TestGetMongoInfoErrorRequest(t *testing.T) {
ResponseSections: []mongoSection{
{
Type: sectionTypeBody,
Body: bson.D{bson.E{Key: "ok", Value: float64(0)}, bson.E{Key: "errmsg", Value: "some error"}, bson.E{Key: "code", Value: 12345}, bson.E{Key: "codeName", Value: "SomeError"}},
// Use int32 as bson.Unmarshal produces int32 for MongoDB int32 fields,
// not plain Go int. Using plain int here would bypass the real code path.
Body: bson.D{bson.E{Key: "ok", Value: float64(0)}, bson.E{Key: "errmsg", Value: "some error"}, bson.E{Key: "code", Value: int32(12345)}, bson.E{Key: "codeName", Value: "SomeError"}},
},
},
}
Expand All @@ -430,6 +432,42 @@ func TestGetMongoInfoErrorRequest(t *testing.T) {
assert.Equal(t, "SomeError", res.ErrorCodeName, "Expected ErrorCodeName to be 'SomeError'")
}

// TestGetMongoInfoErrorCodeFromBsonRoundTrip verifies that error codes are correctly
// parsed when the BSON body comes from a real marshal/unmarshal round-trip, as happens
// in production (parseBodySection calls bson.Unmarshal which yields int32, not int).
// With the old findIntInBson (value.(int)), this test silently produced ErrorCode=0.
func TestGetMongoInfoErrorCodeFromBsonRoundTrip(t *testing.T) {
// Simulate the production path: marshal a document with an integer code field,
// then unmarshal it back into bson.D. bson.Unmarshal stores MongoDB int32 as
// Go int32, so the result is bson.E{Key: "code", Value: int32(11000)}, not int.
raw, err := bson.Marshal(bson.D{
{Key: "ok", Value: float64(0)},
{Key: "errmsg", Value: "E11000 duplicate key error"},
{Key: "code", Value: int32(11000)},
{Key: "codeName", Value: "DuplicateKey"},
})
require.NoError(t, err)

var body bson.D
require.NoError(t, bson.Unmarshal(raw, &body))

mongoRequest := MongoRequestValue{
RequestSections: []mongoSection{
{Type: sectionTypeBody, Body: bson.D{{Key: commInsert, Value: "col"}, {Key: "$db", Value: "db"}}},
},
ResponseSections: []mongoSection{
{Type: sectionTypeBody, Body: body},
},
}

res, err := getMongoInfo(&mongoRequest)
require.NoError(t, err)
assert.False(t, res.Success)
assert.Equal(t, "E11000 duplicate key error", res.Error)
assert.Equal(t, 11000, res.ErrorCode, "error code must be parsed from int32 BSON value")
assert.Equal(t, "DuplicateKey", res.ErrorCodeName)
}

func TestGetMongoInfoNoResponseSectionShouldBeSuccess(t *testing.T) {
mongoRequest := MongoRequestValue{
RequestSections: []mongoSection{
Expand Down