Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/cloudflare/src/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export function withSentry<
QueueHandlerMessage,
CfHostMetadata
>,
>(optionsCallback: (env: Env) => CloudflareOptions, handler: T): T {
>(optionsCallback: (env: Env) => CloudflareOptions | undefined, handler: T): T {
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: I made this return undefined as it was also working before theoretically. I adapted the types to also allow undefined just to have this case also handled

setAsyncLocalStorageAsyncContextStrategy();

try {
Expand Down
16 changes: 14 additions & 2 deletions packages/cloudflare/src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ function isVersionMetadata(value: unknown): value is CfVersionMetadata {
return typeof value === 'object' && value !== null && 'id' in value && typeof value.id === 'string';
}

function getEnvVar<T extends Record<string, unknown>>(env: unknown, varName: keyof T): string | undefined {
return typeof env === 'object' && env !== null && varName in env && typeof (env as T)[varName] === 'string' ? (env as T)[varName] as string : undefined;
}

/**
* Merges the options passed in from the user with the options we read from
* the Cloudflare `env` environment variable object.
Expand All @@ -31,7 +35,7 @@ function isVersionMetadata(value: unknown): value is CfVersionMetadata {
*
* @returns The final options.
*/
export function getFinalOptions(userOptions: CloudflareOptions, env: unknown): CloudflareOptions {
export function getFinalOptions(userOptions: CloudflareOptions = {}, env: unknown): CloudflareOptions {
if (typeof env !== 'object' || env === null) {
return userOptions;
}
Expand All @@ -44,5 +48,13 @@ export function getFinalOptions(userOptions: CloudflareOptions, env: unknown): C
? env.CF_VERSION_METADATA.id
: undefined;

return { release, ...userOptions };
const tracesSampleRate = userOptions.tracesSampleRate ?? parseFloat(getEnvVar(env, 'SENTRY_TRACE_SAMPLE_RATE') ?? '');
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Env variable name typo: missing 'S' in TRACES

High Severity

The code reads SENTRY_TRACE_SAMPLE_RATE (missing the 'S' in "TRACES"), but the standard Sentry env variable name is SENTRY_TRACES_SAMPLE_RATE. The tests also use SENTRY_TRACES_SAMPLE_RATE. This mismatch means the traces sample rate env variable will never be read, making the feature non-functional. This also means the related tests will fail.

Fix in Cursor Fix in Web


return {
dsn: userOptions.dsn ?? getEnvVar(env, 'SENTRY_DSN'),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

l: can we also add SENTRY_DEBUG? You could use envToBool for that from node-core, maybe lift that up into @sentry/core?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great idea. Added.

I also added tunnel to it

environment: userOptions.environment ?? getEnvVar(env, 'SENTRY_ENVIRONMENT'),
tracesSampleRate: isFinite(tracesSampleRate) ? tracesSampleRate : undefined,
release,
...userOptions,
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spread operator overrides env-derived fallback values

Medium Severity

The ...userOptions spread at the end of the return object overrides the env-derived fallback values for dsn, environment, and tracesSampleRate whenever those keys exist in userOptions — even if their values are undefined. This means if a user passes { dsn: undefined }, the SENTRY_DSN env fallback is silently ignored. The ?? fallback logic and the isFinite guard are both bypassed by the spread.

Fix in Cursor Fix in Web

}
54 changes: 54 additions & 0 deletions packages/cloudflare/test/options.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,60 @@ describe('getFinalOptions', () => {
expect(result).toEqual(userOptions);
});

it('uses SENTRY_DSN from env when user dsn is undefined', () => {
const userOptions = { release: 'user-release' };
const env = { SENTRY_DSN: 'https://key@ingest.sentry.io/1' };

const result = getFinalOptions(userOptions, env);

expect(result.dsn).toBe('https://key@ingest.sentry.io/1');
expect(result.release).toBe('user-release');
});

it('uses SENTRY_ENVIRONMENT from env when user environment is undefined', () => {
const userOptions = { dsn: 'test-dsn' };
const env = { SENTRY_ENVIRONMENT: 'staging' };

const result = getFinalOptions(userOptions, env);

expect(result.environment).toBe('staging');
});

it('uses SENTRY_TRACES_SAMPLE_RATE from env when user tracesSampleRate is undefined', () => {
const userOptions = { dsn: 'test-dsn' };
const env = { SENTRY_TRACES_SAMPLE_RATE: '0.5' };

const result = getFinalOptions(userOptions, env);

expect(result.tracesSampleRate).toBe(0.5);
});

it('does not use SENTRY_TRACES_SAMPLE_RATE from env when it is gibberish', () => {
const env = { SENTRY_TRACES_SAMPLE_RATE: 'ʕっ•ᴥ•ʔっ' };

const result = getFinalOptions(undefined, env);

expect(result.tracesSampleRate).toBeUndefined();
});

it('prefers user dsn over env SENTRY_DSN', () => {
const userOptions = { dsn: 'user-dsn', release: 'user-release' };
const env = { SENTRY_DSN: 'https://env@ingest.sentry.io/1' };

const result = getFinalOptions(userOptions, env);

expect(result.dsn).toBe('user-dsn');
});

it('ignores SENTRY_DSN when not a string', () => {
const userOptions = { dsn: undefined };
const env = { SENTRY_DSN: 123 };

const result = getFinalOptions(userOptions, env);

expect(result.dsn).toBeUndefined();
});

describe('CF_VERSION_METADATA', () => {
it('uses CF_VERSION_METADATA.id as release when no other release is set', () => {
const userOptions = { dsn: 'test-dsn' };
Expand Down
Loading