Skip to content

Commit 6d1a251

Browse files
authored
fix(mongo): rewrite Buffer as ? during serialization (#14071)
1 parent 188ece7 commit 6d1a251

File tree

3 files changed

+122
-8
lines changed
  • dev-packages/node-integration-tests/suites/tracing/mongodb
  • packages/node

3 files changed

+122
-8
lines changed

Diff for: dev-packages/node-integration-tests/suites/tracing/mongodb/test.ts

+4-8
Original file line numberDiff line numberDiff line change
@@ -71,12 +71,10 @@ describe('MongoDB experimental Test', () => {
7171
'db.connection_string': expect.any(String),
7272
'net.peer.name': expect.any(String),
7373
'net.peer.port': expect.any(Number),
74-
'db.statement':
75-
'{"title":"?","_id":{"_bsontype":"?","id":{"0":"?","1":"?","2":"?","3":"?","4":"?","5":"?","6":"?","7":"?","8":"?","9":"?","10":"?","11":"?"}}}',
74+
'db.statement': '{"title":"?","_id":{"_bsontype":"?","id":"?"}}',
7675
'otel.kind': 'CLIENT',
7776
},
78-
description:
79-
'{"title":"?","_id":{"_bsontype":"?","id":{"0":"?","1":"?","2":"?","3":"?","4":"?","5":"?","6":"?","7":"?","8":"?","9":"?","10":"?","11":"?"}}}',
77+
description: '{"title":"?","_id":{"_bsontype":"?","id":"?"}}',
8078
op: 'db',
8179
origin: 'auto.db.otel.mongo',
8280
}),
@@ -162,12 +160,10 @@ describe('MongoDB experimental Test', () => {
162160
'db.connection_string': expect.any(String),
163161
'net.peer.name': expect.any(String),
164162
'net.peer.port': expect.any(Number),
165-
'db.statement':
166-
'{"endSessions":[{"id":{"_bsontype":"?","sub_type":"?","position":"?","buffer":{"0":"?","1":"?","2":"?","3":"?","4":"?","5":"?","6":"?","7":"?","8":"?","9":"?","10":"?","11":"?","12":"?","13":"?","14":"?","15":"?"}}}]}',
163+
'db.statement': '{"endSessions":[{"id":{"_bsontype":"?","sub_type":"?","position":"?","buffer":"?"}}]}',
167164
'otel.kind': 'CLIENT',
168165
},
169-
description:
170-
'{"endSessions":[{"id":{"_bsontype":"?","sub_type":"?","position":"?","buffer":{"0":"?","1":"?","2":"?","3":"?","4":"?","5":"?","6":"?","7":"?","8":"?","9":"?","10":"?","11":"?","12":"?","13":"?","14":"?","15":"?"}}}]}',
166+
description: '{"endSessions":[{"id":{"_bsontype":"?","sub_type":"?","position":"?","buffer":"?"}}]}',
171167
op: 'db',
172168
origin: 'auto.db.otel.mongo',
173169
}),

Diff for: packages/node/src/integrations/tracing/mongo.ts

+46
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,58 @@ export const instrumentMongo = generateInstrumentOnce(
1111
INTEGRATION_NAME,
1212
() =>
1313
new MongoDBInstrumentation({
14+
dbStatementSerializer: _defaultDbStatementSerializer,
1415
responseHook(span) {
1516
addOriginToSpan(span, 'auto.db.otel.mongo');
1617
},
1718
}),
1819
);
1920

21+
/**
22+
* Replaces values in document with '?', hiding PII and helping grouping.
23+
*/
24+
export function _defaultDbStatementSerializer(commandObj: Record<string, unknown>): string {
25+
const resultObj = _scrubStatement(commandObj);
26+
return JSON.stringify(resultObj);
27+
}
28+
29+
function _scrubStatement(value: unknown): unknown {
30+
if (Array.isArray(value)) {
31+
return value.map(element => _scrubStatement(element));
32+
}
33+
34+
if (isCommandObj(value)) {
35+
const initial: Record<string, unknown> = {};
36+
return Object.entries(value)
37+
.map(([key, element]) => [key, _scrubStatement(element)])
38+
.reduce((prev, current) => {
39+
if (isCommandEntry(current)) {
40+
prev[current[0]] = current[1];
41+
}
42+
return prev;
43+
}, initial);
44+
}
45+
46+
// A value like string or number, possible contains PII, scrub it
47+
return '?';
48+
}
49+
50+
function isCommandObj(value: Record<string, unknown> | unknown): value is Record<string, unknown> {
51+
return typeof value === 'object' && value !== null && !isBuffer(value);
52+
}
53+
54+
function isBuffer(value: unknown): boolean {
55+
let isBuffer = false;
56+
if (typeof Buffer !== 'undefined') {
57+
isBuffer = Buffer.isBuffer(value);
58+
}
59+
return isBuffer;
60+
}
61+
62+
function isCommandEntry(value: [string, unknown] | unknown): value is [string, unknown] {
63+
return Array.isArray(value);
64+
}
65+
2066
const _mongoIntegration = (() => {
2167
return {
2268
name: INTEGRATION_NAME,
+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { MongoDBInstrumentation } from '@opentelemetry/instrumentation-mongodb';
2+
3+
import {
4+
_defaultDbStatementSerializer,
5+
instrumentMongo,
6+
mongoIntegration,
7+
} from '../../../src/integrations/tracing/mongo';
8+
import { INSTRUMENTED } from '../../../src/otel/instrument';
9+
10+
jest.mock('@opentelemetry/instrumentation-mongodb');
11+
12+
describe('Mongo', () => {
13+
beforeEach(() => {
14+
jest.clearAllMocks();
15+
delete INSTRUMENTED.Mongo;
16+
17+
(MongoDBInstrumentation as unknown as jest.SpyInstance).mockImplementation(() => {
18+
return {
19+
setTracerProvider: () => undefined,
20+
setMeterProvider: () => undefined,
21+
getConfig: () => ({}),
22+
setConfig: () => ({}),
23+
enable: () => undefined,
24+
};
25+
});
26+
});
27+
28+
it('defaults are correct for instrumentMongo', () => {
29+
instrumentMongo();
30+
31+
expect(MongoDBInstrumentation).toHaveBeenCalledTimes(1);
32+
expect(MongoDBInstrumentation).toHaveBeenCalledWith({
33+
dbStatementSerializer: expect.any(Function),
34+
responseHook: expect.any(Function),
35+
});
36+
});
37+
38+
it('defaults are correct for mongoIntegration', () => {
39+
mongoIntegration().setupOnce!();
40+
41+
expect(MongoDBInstrumentation).toHaveBeenCalledTimes(1);
42+
expect(MongoDBInstrumentation).toHaveBeenCalledWith({
43+
responseHook: expect.any(Function),
44+
dbStatementSerializer: expect.any(Function),
45+
});
46+
});
47+
48+
describe('_defaultDbStatementSerializer', () => {
49+
it('rewrites strings as ?', () => {
50+
const serialized = _defaultDbStatementSerializer({
51+
find: 'foo',
52+
});
53+
expect(JSON.parse(serialized).find).toBe('?');
54+
});
55+
56+
it('rewrites nested strings as ?', () => {
57+
const serialized = _defaultDbStatementSerializer({
58+
find: {
59+
inner: 'foo',
60+
},
61+
});
62+
expect(JSON.parse(serialized).find.inner).toBe('?');
63+
});
64+
65+
it('rewrites Buffer as ?', () => {
66+
const serialized = _defaultDbStatementSerializer({
67+
find: Buffer.from('foo', 'utf8'),
68+
});
69+
expect(JSON.parse(serialized).find).toBe('?');
70+
});
71+
});
72+
});

0 commit comments

Comments
 (0)