diff --git a/pkg/ebpf/common/mongo_detect_transform.go b/pkg/ebpf/common/mongo_detect_transform.go index 0da36d3ea3..abf1528b39 100644 --- a/pkg/ebpf/common/mongo_detect_transform.go +++ b/pkg/ebpf/common/mongo_detect_transform.go @@ -549,11 +549,18 @@ 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) { @@ -561,7 +568,7 @@ func findDoubleInBson(doc bson.D, key string) (float64, bool) { 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 } diff --git a/pkg/ebpf/common/mongo_detect_transform_test.go b/pkg/ebpf/common/mongo_detect_transform_test.go index 59f1a7ac45..48bf4a92ce 100644 --- a/pkg/ebpf/common/mongo_detect_transform_test.go +++ b/pkg/ebpf/common/mongo_detect_transform_test.go @@ -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"}}, }, }, } @@ -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{