diff --git a/packages/cloudflare/src/integrations/httpServer.ts b/packages/cloudflare/src/integrations/httpServer.ts index 31747e11a4d1..0beb462087cd 100644 --- a/packages/cloudflare/src/integrations/httpServer.ts +++ b/packages/cloudflare/src/integrations/httpServer.ts @@ -14,10 +14,11 @@ export interface HttpServerIntegrationOptions { * Available options: * - `'none'`: No request bodies will be attached * - `'small'`: Request bodies up to 1,000 bytes will be attached - * - `'medium'`: Request bodies up to 10,000 bytes will be attached (default) + * - `'medium'`: Request bodies up to 10,000 bytes will be attached * - `'always'`: Request bodies will always be attached (up to 1MB limit) * - * @default 'medium' + * When not set, falls back to `dataCollection.httpBodies`: if `'incomingRequest'` + * is listed, bodies are captured at `'medium'` size; otherwise no bodies are captured. */ maxRequestBodySize?: MaxRequestBodySize; @@ -42,14 +43,14 @@ export interface HttpServerIntegrationOptions { interface HttpServerIntegrationInstance { name: string; - maxRequestBodySize: MaxRequestBodySize; + maxRequestBodySize: MaxRequestBodySize | undefined; ignoreRequestBody?: (url: string, request: Request) => boolean; } const _httpServerIntegration = ((options: HttpServerIntegrationOptions = {}): HttpServerIntegrationInstance => { return { name: INTEGRATION_NAME, - maxRequestBodySize: options.maxRequestBodySize ?? 'medium', + maxRequestBodySize: options.maxRequestBodySize, ignoreRequestBody: options.ignoreRequestBody, }; }) satisfies IntegrationFn; @@ -85,7 +86,10 @@ export async function captureIncomingRequestBody(client: Client, request: Reques return; } - const maxRequestBodySize = integration.maxRequestBodySize; + // Integration-level option takes precedence; fall back to dataCollection.httpBodies + const maxRequestBodySize = + integration.maxRequestBodySize ?? + (client.getDataCollectionOptions().httpBodies.includes('incomingRequest') ? 'medium' : 'none'); if (maxRequestBodySize === 'none') { return; diff --git a/packages/cloudflare/src/request.ts b/packages/cloudflare/src/request.ts index a4c42ccbd7e8..40bd27a7b1e8 100644 --- a/packages/cloudflare/src/request.ts +++ b/packages/cloudflare/src/request.ts @@ -74,7 +74,7 @@ export function wrapRequestHandler( attributes, httpHeadersToSpanAttributes( winterCGHeadersToDict(request.headers), - getClient()?.getOptions().sendDefaultPii ?? false, + getClient()?.getDataCollectionOptions() ?? false, ), ); diff --git a/packages/cloudflare/src/sdk.ts b/packages/cloudflare/src/sdk.ts index b957fabe1e70..a85584b382c9 100644 --- a/packages/cloudflare/src/sdk.ts +++ b/packages/cloudflare/src/sdk.ts @@ -24,7 +24,6 @@ import { defaultStackParser } from './vendor/stacktrace'; /** Get the default integrations for the Cloudflare SDK. */ export function getDefaultIntegrations(options: CloudflareOptions): Integration[] { - const sendDefaultPii = options.sendDefaultPii ?? false; return [ // The Dedupe integration should not be used in workflows because we want to // capture all step failures, even if they are the same error. @@ -38,8 +37,7 @@ export function getDefaultIntegrations(options: CloudflareOptions): Integration[ fetchIntegration(), honoIntegration(), httpServerIntegration(), - // TODO(v11): the `include` object should be defined directly in the integration based on `sendDefaultPii` - requestDataIntegration(sendDefaultPii ? undefined : { include: { cookies: false } }), + requestDataIntegration(), consoleIntegration(), ]; } diff --git a/packages/cloudflare/test/request.test.ts b/packages/cloudflare/test/request.test.ts index 2164989833b3..307bba57f98a 100644 --- a/packages/cloudflare/test/request.test.ts +++ b/packages/cloudflare/test/request.test.ts @@ -205,7 +205,7 @@ describe('withSentry', () => { expect(sentryEvent.contexts?.culture).toEqual({ timezone: 'UTC' }); }); - test('captures request body with default integration (medium size)', async () => { + test('does not capture request body by default (dataCollection.httpBodies is empty)', async () => { let sentryEvent: Event = {}; const context = createMockExecutionContext(); @@ -213,7 +213,36 @@ describe('withSentry', () => { { options: { ...MOCK_OPTIONS, - // Default integrations include httpServerIntegration with 'medium' default + beforeSend(event) { + sentryEvent = event; + return null; + }, + }, + request: new Request('https://example.com', { + method: 'POST', + headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ username: 'test', data: 'value' }), + }), + context, + }, + () => { + SentryCore.captureMessage('request body'); + return new Response('test'); + }, + ); + + expect(sentryEvent.sdkProcessingMetadata?.normalizedRequest?.data).toBeUndefined(); + }); + + test('captures request body when dataCollection.httpBodies includes incomingRequest', async () => { + let sentryEvent: Event = {}; + const context = createMockExecutionContext(); + + await wrapRequestHandler( + { + options: { + ...MOCK_OPTIONS, + dataCollection: { httpBodies: ['incomingRequest'] }, beforeSend(event) { sentryEvent = event; return null;