Skip to content

Commit 41db465

Browse files
committed
fix(node): Guard against invalid maxSpanWaitDuration values
1 parent f962eef commit 41db465

File tree

2 files changed

+103
-1
lines changed

2 files changed

+103
-1
lines changed

packages/node/src/sdk/initOtel.ts

+25-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
import { GLOBAL_OBJ, SDK_VERSION, consoleSandbox, logger } from '@sentry/core';
1111
import { SentryPropagator, SentrySampler, SentrySpanProcessor } from '@sentry/opentelemetry';
1212
import { createAddHookMessageChannel } from 'import-in-the-middle';
13+
import { DEBUG_BUILD } from '../debug-build';
1314
import { getOpenTelemetryInstrumentationToPreload } from '../integrations/tracing';
1415
import { SentryContextManager } from '../otel/contextManager';
1516
import type { EsmLoaderHookOptions } from '../types';
@@ -18,6 +19,9 @@ import type { NodeClient } from './client';
1819

1920
declare const __IMPORT_META_URL_REPLACEMENT__: string;
2021

22+
// About 277h - this must fit into new Array(len)!
23+
const MAX_MAX_SPAN_WAIT_DURATION = 1_000_000;
24+
2125
/**
2226
* Initialize OpenTelemetry for Node.
2327
*/
@@ -138,7 +142,7 @@ export function setupOtel(client: NodeClient): BasicTracerProvider {
138142
forceFlushTimeoutMillis: 500,
139143
spanProcessors: [
140144
new SentrySpanProcessor({
141-
timeout: client.getOptions().maxSpanWaitDuration,
145+
timeout: _getSpanProcessorTimeout(client.getOptions().maxSpanWaitDuration),
142146
}),
143147
],
144148
});
@@ -152,6 +156,26 @@ export function setupOtel(client: NodeClient): BasicTracerProvider {
152156
return provider;
153157
}
154158

159+
/** Just exported for tests. */
160+
export function _getSpanProcessorTimeout(maxSpanWaitDuration: number | undefined): number | undefined {
161+
if (maxSpanWaitDuration == null) {
162+
return undefined;
163+
}
164+
165+
// We guard for a max. value here, because we create an array with this length
166+
// So if this value is too large, this would fail
167+
if (maxSpanWaitDuration > MAX_MAX_SPAN_WAIT_DURATION) {
168+
DEBUG_BUILD &&
169+
logger.warn(`\`maxSpanWaitDuration\` is too high, using the maximum value of ${MAX_MAX_SPAN_WAIT_DURATION}`);
170+
return MAX_MAX_SPAN_WAIT_DURATION;
171+
} else if (maxSpanWaitDuration <= 0 || Number.isNaN(maxSpanWaitDuration)) {
172+
DEBUG_BUILD && logger.warn('`maxSpanWaitDuration` must be a positive number, using default value instead.');
173+
return undefined;
174+
}
175+
176+
return maxSpanWaitDuration;
177+
}
178+
155179
/**
156180
* Setup the OTEL logger to use our own logger.
157181
*/
+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { logger } from '@sentry/core';
2+
import { _getSpanProcessorTimeout } from '../../src/sdk/initOtel';
3+
4+
describe('_getSpanProcessorTimeout', () => {
5+
beforeEach(() => {
6+
jest.clearAllMocks();
7+
});
8+
9+
it('works with undefined', () => {
10+
const loggerWarnSpy = jest.spyOn(logger, 'warn').mockImplementation(() => {});
11+
const timeout = _getSpanProcessorTimeout(undefined);
12+
expect(timeout).toBe(undefined);
13+
expect(loggerWarnSpy).not.toHaveBeenCalled();
14+
});
15+
16+
it('works with positive number', () => {
17+
const loggerWarnSpy = jest.spyOn(logger, 'warn').mockImplementation(() => {});
18+
const timeout = _getSpanProcessorTimeout(10);
19+
expect(timeout).toBe(10);
20+
expect(loggerWarnSpy).not.toHaveBeenCalled();
21+
});
22+
23+
it('works with 0', () => {
24+
const loggerWarnSpy = jest.spyOn(logger, 'warn').mockImplementation(() => {});
25+
const timeout = _getSpanProcessorTimeout(0);
26+
expect(timeout).toBe(undefined);
27+
expect(loggerWarnSpy).toHaveBeenCalledTimes(1);
28+
expect(loggerWarnSpy).toHaveBeenCalledWith(
29+
'`maxSpanWaitDuration` must be a positive number, using default value instead.',
30+
);
31+
});
32+
33+
it('works with negative number', () => {
34+
const loggerWarnSpy = jest.spyOn(logger, 'warn').mockImplementation(() => {});
35+
const timeout = _getSpanProcessorTimeout(-10);
36+
expect(timeout).toBe(undefined);
37+
expect(loggerWarnSpy).toHaveBeenCalledTimes(1);
38+
expect(loggerWarnSpy).toHaveBeenCalledWith(
39+
'`maxSpanWaitDuration` must be a positive number, using default value instead.',
40+
);
41+
});
42+
43+
it('works with -Infinity', () => {
44+
const loggerWarnSpy = jest.spyOn(logger, 'warn').mockImplementation(() => {});
45+
const timeout = _getSpanProcessorTimeout(-Infinity);
46+
expect(timeout).toBe(undefined);
47+
expect(loggerWarnSpy).toHaveBeenCalledTimes(1);
48+
expect(loggerWarnSpy).toHaveBeenCalledWith(
49+
'`maxSpanWaitDuration` must be a positive number, using default value instead.',
50+
);
51+
});
52+
53+
it('works with Infinity', () => {
54+
const loggerWarnSpy = jest.spyOn(logger, 'warn').mockImplementation(() => {});
55+
const timeout = _getSpanProcessorTimeout(Infinity);
56+
expect(timeout).toBe(1_000_000);
57+
expect(loggerWarnSpy).toHaveBeenCalledTimes(1);
58+
expect(loggerWarnSpy).toHaveBeenCalledWith('`maxSpanWaitDuration` is too high, using the maximum value of 1000000');
59+
});
60+
61+
it('works with large number', () => {
62+
const loggerWarnSpy = jest.spyOn(logger, 'warn').mockImplementation(() => {});
63+
const timeout = _getSpanProcessorTimeout(1_000_000_000);
64+
expect(timeout).toBe(1_000_000);
65+
expect(loggerWarnSpy).toHaveBeenCalledTimes(1);
66+
expect(loggerWarnSpy).toHaveBeenCalledWith('`maxSpanWaitDuration` is too high, using the maximum value of 1000000');
67+
});
68+
69+
it('works with NaN', () => {
70+
const loggerWarnSpy = jest.spyOn(logger, 'warn').mockImplementation(() => {});
71+
const timeout = _getSpanProcessorTimeout(NaN);
72+
expect(timeout).toBe(undefined);
73+
expect(loggerWarnSpy).toHaveBeenCalledTimes(1);
74+
expect(loggerWarnSpy).toHaveBeenCalledWith(
75+
'`maxSpanWaitDuration` must be a positive number, using default value instead.',
76+
);
77+
});
78+
});

0 commit comments

Comments
 (0)