Skip to content

Commit 402bd0e

Browse files
committed
fix(mongo): rewrite Buffer as ? during serialization
1 parent 7e39d04 commit 402bd0e

File tree

2 files changed

+115
-0
lines changed

2 files changed

+115
-0
lines changed

packages/node/src/integrations/tracing/mongo.ts

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

0 commit comments

Comments
 (0)