Skip to content

Commit 016e66d

Browse files
committed
fix: re-patch all random API calls
1 parent 0467fa5 commit 016e66d

File tree

12 files changed

+55
-31
lines changed

12 files changed

+55
-31
lines changed

packages/core/src/client.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/* eslint-disable max-lines */
2+
import { _INTERNAL_safeMathRandom } from '.';
23
import { getEnvelopeEndpointWithUrlEncodedAuth } from './api';
34
import { DEFAULT_ENVIRONMENT } from './constants';
45
import { getCurrentScope, getIsolationScope, getTraceContextFromScope } from './currentScopes';
@@ -1288,7 +1289,7 @@ export abstract class Client<O extends ClientOptions = ClientOptions> {
12881289
// 0.0 === 0% events are sent
12891290
// Sampling for transaction happens somewhere else
12901291
const parsedSampleRate = typeof sampleRate === 'undefined' ? undefined : parseSampleRate(sampleRate);
1291-
if (isError && typeof parsedSampleRate === 'number' && Math.random() > parsedSampleRate) {
1292+
if (isError && typeof parsedSampleRate === 'number' && _INTERNAL_safeMathRandom() > parsedSampleRate) {
12921293
this.recordDroppedEvent('sample_rate', 'error');
12931294
return rejectedSyncPromise(
12941295
_makeDoNotSendEventError(

packages/core/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,4 +516,6 @@ export type {
516516
export {
517517
withRandomSafeContext as _INTERNAL_withRandomSafeContext,
518518
type RandomSafeContextRunner as _INTERNAL_RandomSafeContextRunner,
519+
safeMathRandom as _INTERNAL_safeMathRandom,
520+
safeDateNow as _INTERNAL_safeDateNow,
519521
} from './utils/randomSafeContext';

packages/core/src/integrations/mcp-server/correlation.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export function storeSpanForRequest(transport: MCPTransport, requestId: RequestI
4646
spanMap.set(requestId, {
4747
span,
4848
method,
49+
// eslint-disable-next-line @sentry-internal/sdk/no-unsafe-random-apis
4950
startTime: Date.now(),
5051
});
5152
}

packages/core/src/scope.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import { isPlainObject } from './utils/is';
2222
import { merge } from './utils/merge';
2323
import { uuid4 } from './utils/misc';
2424
import { generateTraceId } from './utils/propagationContext';
25-
import { withRandomSafeContext } from './utils/randomSafeContext';
25+
import { safeMathRandom } from './utils/randomSafeContext';
2626
import { _getSpanForScope, _setSpanForScope } from './utils/spanOnScope';
2727
import { truncate } from './utils/string';
2828
import { dateTimestampInSeconds } from './utils/time';
@@ -168,8 +168,8 @@ export class Scope {
168168
this._contexts = {};
169169
this._sdkProcessingMetadata = {};
170170
this._propagationContext = {
171-
traceId: withRandomSafeContext(generateTraceId),
172-
sampleRand: withRandomSafeContext(() => Math.random()),
171+
traceId: generateTraceId(),
172+
sampleRand: safeMathRandom(),
173173
};
174174
}
175175

@@ -552,8 +552,8 @@ export class Scope {
552552
_setSpanForScope(this, undefined);
553553
this._attachments = [];
554554
this.setPropagationContext({
555-
traceId: withRandomSafeContext(generateTraceId),
556-
sampleRand: withRandomSafeContext(() => Math.random()),
555+
traceId: generateTraceId(),
556+
sampleRand: safeMathRandom(),
557557
});
558558

559559
this._notifyScopeListeners();

packages/core/src/tracing/sentrySpan.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import type { SpanStatus } from '../types-hoist/spanStatus';
2626
import type { TimedEvent } from '../types-hoist/timedEvent';
2727
import { debug } from '../utils/debug-logger';
2828
import { generateSpanId, generateTraceId } from '../utils/propagationContext';
29-
import { withRandomSafeContext } from '../utils/randomSafeContext';
3029
import {
3130
convertSpanLinksForEnvelope,
3231
getRootSpan,
@@ -77,9 +76,9 @@ export class SentrySpan implements Span {
7776
* @hidden
7877
*/
7978
public constructor(spanContext: SentrySpanArguments = {}) {
80-
this._traceId = spanContext.traceId || withRandomSafeContext(generateTraceId);
81-
this._spanId = spanContext.spanId || withRandomSafeContext(generateSpanId);
82-
this._startTime = spanContext.startTimestamp || withRandomSafeContext(timestampInSeconds);
79+
this._traceId = spanContext.traceId || generateTraceId();
80+
this._spanId = spanContext.spanId || generateSpanId();
81+
this._startTime = spanContext.startTimestamp || timestampInSeconds();
8382
this._links = spanContext.links;
8483

8584
this._attributes = {};

packages/core/src/tracing/trace.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { handleCallbackErrors } from '../utils/handleCallbackErrors';
1717
import { hasSpansEnabled } from '../utils/hasSpansEnabled';
1818
import { parseSampleRate } from '../utils/parseSampleRate';
1919
import { generateTraceId } from '../utils/propagationContext';
20+
import { safeMathRandom } from '../utils/randomSafeContext';
2021
import { _getSpanForScope, _setSpanForScope } from '../utils/spanOnScope';
2122
import { addChildSpanToSpan, getRootSpan, spanIsSampled, spanTimeInputToSeconds, spanToJSON } from '../utils/spanUtils';
2223
import { propagationContextFromHeaders, shouldContinueTrace } from '../utils/tracing';
@@ -293,7 +294,7 @@ export function startNewTrace<T>(callback: () => T): T {
293294
return withScope(scope => {
294295
scope.setPropagationContext({
295296
traceId: generateTraceId(),
296-
sampleRand: Math.random(),
297+
sampleRand: safeMathRandom(),
297298
});
298299
DEBUG_BUILD && debug.log(`Starting a new trace with id ${scope.getPropagationContext().traceId}`);
299300
return withActiveSpan(null, callback);

packages/core/src/utils/misc.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { Exception } from '../types-hoist/exception';
33
import type { Mechanism } from '../types-hoist/mechanism';
44
import type { StackFrame } from '../types-hoist/stackframe';
55
import { addNonEnumerableProperty } from './object';
6+
import { safeMathRandom, withRandomSafeContext } from './randomSafeContext';
67
import { snipLine } from './string';
78
import { GLOBAL_OBJ } from './worldwide';
89

@@ -24,7 +25,7 @@ function getCrypto(): CryptoInternal | undefined {
2425
let emptyUuid: string | undefined;
2526

2627
function getRandomByte(): number {
27-
return Math.random() * 16;
28+
return safeMathRandom() * 16;
2829
}
2930

3031
/**
@@ -35,7 +36,8 @@ function getRandomByte(): number {
3536
export function uuid4(crypto = getCrypto()): string {
3637
try {
3738
if (crypto?.randomUUID) {
38-
return crypto.randomUUID().replace(/-/g, '');
39+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
40+
return withRandomSafeContext(() => crypto.randomUUID!()).replace(/-/g, '');
3941
}
4042
} catch {
4143
// some runtimes can crash invoking crypto

packages/core/src/utils/randomSafeContext.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,19 @@ export function withRandomSafeContext<T>(cb: () => T): T {
2323

2424
return globalWithSymbol[sym](cb);
2525
}
26+
27+
/**
28+
* Identical to Math.random() but wrapped in withRandomSafeContext
29+
* to ensure safe random number generation in certain contexts (e.g., Next.js Cache Components).
30+
*/
31+
export function safeMathRandom(): number {
32+
return withRandomSafeContext(() => Math.random());
33+
}
34+
35+
/**
36+
* Identical to performance.now() but wrapped in withRandomSafeContext
37+
* to ensure safe time value generation in certain contexts (e.g., Next.js Cache Components).
38+
*/
39+
export function safeDateNow(): number {
40+
return withRandomSafeContext(() => Date.now());
41+
}

packages/core/src/utils/ratelimit.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { DataCategory } from '../types-hoist/datacategory';
22
import type { TransportMakeRequestResponse } from '../types-hoist/transport';
3+
import { safeDateNow } from './randomSafeContext';
34

45
// Intentionally keeping the key broad, as we don't know for sure what rate limit headers get returned from backend
56
export type RateLimits = Record<string, number>;
@@ -12,7 +13,7 @@ export const DEFAULT_RETRY_AFTER = 60 * 1000; // 60 seconds
1213
* @param now current unix timestamp
1314
*
1415
*/
15-
export function parseRetryAfterHeader(header: string, now: number = Date.now()): number {
16+
export function parseRetryAfterHeader(header: string, now: number = safeDateNow()): number {
1617
const headerDelay = parseInt(`${header}`, 10);
1718
if (!isNaN(headerDelay)) {
1819
return headerDelay * 1000;
@@ -40,7 +41,7 @@ export function disabledUntil(limits: RateLimits, dataCategory: DataCategory): n
4041
/**
4142
* Checks if a category is rate limited
4243
*/
43-
export function isRateLimited(limits: RateLimits, dataCategory: DataCategory, now: number = Date.now()): boolean {
44+
export function isRateLimited(limits: RateLimits, dataCategory: DataCategory, now: number = safeDateNow()): boolean {
4445
return disabledUntil(limits, dataCategory) > now;
4546
}
4647

@@ -52,7 +53,7 @@ export function isRateLimited(limits: RateLimits, dataCategory: DataCategory, no
5253
export function updateRateLimits(
5354
limits: RateLimits,
5455
{ statusCode, headers }: TransportMakeRequestResponse,
55-
now: number = Date.now(),
56+
now: number = safeDateNow(),
5657
): RateLimits {
5758
const updatedRateLimits: RateLimits = {
5859
...limits,

packages/core/src/utils/time.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { safeDateNow, withRandomSafeContext } from './randomSafeContext';
12
import { GLOBAL_OBJ } from './worldwide';
23

34
const ONE_SECOND_IN_MS = 1000;
@@ -21,7 +22,7 @@ interface Performance {
2122
* Returns a timestamp in seconds since the UNIX epoch using the Date API.
2223
*/
2324
export function dateTimestampInSeconds(): number {
24-
return Date.now() / ONE_SECOND_IN_MS;
25+
return safeDateNow() / ONE_SECOND_IN_MS;
2526
}
2627

2728
/**
@@ -50,7 +51,7 @@ function createUnixTimestampInSecondsFunc(): () => number {
5051
// See: https://github.com/mdn/content/issues/4713
5152
// See: https://dev.to/noamr/when-a-millisecond-is-not-a-millisecond-3h6
5253
return () => {
53-
return (timeOrigin + performance.now()) / ONE_SECOND_IN_MS;
54+
return (timeOrigin + withRandomSafeContext(() => performance.now())) / ONE_SECOND_IN_MS;
5455
};
5556
}
5657

@@ -92,8 +93,8 @@ function getBrowserTimeOrigin(): number | undefined {
9293
}
9394

9495
const threshold = 300_000; // 5 minutes in milliseconds
95-
const performanceNow = performance.now();
96-
const dateNow = Date.now();
96+
const performanceNow = withRandomSafeContext(() => performance.now());
97+
const dateNow = safeDateNow();
9798

9899
const timeOrigin = performance.timeOrigin;
99100
if (typeof timeOrigin === 'number') {

0 commit comments

Comments
 (0)