From 5a35431eaf3fd92b5866389489877b50aba373c2 Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Fri, 29 May 2026 14:46:58 +0200 Subject: [PATCH] migrate --- packages/remix/src/server/errors.ts | 8 +- packages/remix/src/server/instrumentServer.ts | 2 +- .../src/server/integrations/opentelemetry.ts | 4 +- packages/remix/test/server/errors.test.ts | 77 +++++++++++++++++++ 4 files changed, 87 insertions(+), 4 deletions(-) create mode 100644 packages/remix/test/server/errors.test.ts diff --git a/packages/remix/src/server/errors.ts b/packages/remix/src/server/errors.ts index 0c078c6c6230..10d8b3a3222b 100644 --- a/packages/remix/src/server/errors.ts +++ b/packages/remix/src/server/errors.ts @@ -93,9 +93,13 @@ export async function errorHandleDataFunction( return handleCallbackErrors( async () => { if (name === 'action' && span) { - const options = getClient()?.getOptions() as RemixOptions | undefined; + const client = getClient(); + const options = client?.getOptions() as RemixOptions | undefined; - if (options?.sendDefaultPii && options.captureActionFormDataKeys) { + if ( + client?.getDataCollectionOptions().httpBodies.includes('incomingRequest') && + options?.captureActionFormDataKeys + ) { await storeFormDataKeys(args, span, options.captureActionFormDataKeys); } } diff --git a/packages/remix/src/server/instrumentServer.ts b/packages/remix/src/server/instrumentServer.ts index f4b19926f802..a9003c925803 100644 --- a/packages/remix/src/server/instrumentServer.ts +++ b/packages/remix/src/server/instrumentServer.ts @@ -378,7 +378,7 @@ function wrapRequestHandler ServerBuild | Promise method: request.method, ...httpHeadersToSpanAttributes( winterCGHeadersToDict(request.headers), - clientOptions.sendDefaultPii ?? false, + getClient()?.getDataCollectionOptions(), ), }, }, diff --git a/packages/remix/src/server/integrations/opentelemetry.ts b/packages/remix/src/server/integrations/opentelemetry.ts index b4ad1d28bb6a..a3d37f7586cb 100644 --- a/packages/remix/src/server/integrations/opentelemetry.ts +++ b/packages/remix/src/server/integrations/opentelemetry.ts @@ -22,7 +22,9 @@ const _remixIntegration = (() => { const options = client?.getOptions() as RemixOptions | undefined; instrumentRemix({ - actionFormDataAttributes: options?.sendDefaultPii ? options?.captureActionFormDataKeys : undefined, + actionFormDataAttributes: client?.getDataCollectionOptions().httpBodies.includes('incomingRequest') + ? options?.captureActionFormDataKeys + : undefined, }); }, diff --git a/packages/remix/test/server/errors.test.ts b/packages/remix/test/server/errors.test.ts new file mode 100644 index 000000000000..a3faf2dfdfdc --- /dev/null +++ b/packages/remix/test/server/errors.test.ts @@ -0,0 +1,77 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import type { Client } from '@sentry/core'; +import * as core from '@sentry/core'; + +vi.mock('../../src/utils/utils', () => ({ + storeFormDataKeys: vi.fn(), +})); + +import { storeFormDataKeys } from '../../src/utils/utils'; +import { errorHandleDataFunction } from '../../src/server/errors'; + +function createMockClient(httpBodies: string[] = []): Client { + return { + getDataCollectionOptions: () => ({ + userInfo: false, + cookies: true, + httpHeaders: { request: true, response: true }, + httpBodies, + queryParams: true, + genAI: { inputs: true, outputs: true }, + stackFrameVariables: true, + frameContextLines: 5, + }), + getOptions: () => ({ + captureActionFormDataKeys: { username: true }, + }), + } as unknown as Client; +} + +describe('errorHandleDataFunction', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + it('captures form data when httpBodies includes incomingRequest', async () => { + vi.spyOn(core, 'getClient').mockReturnValue(createMockClient(['incomingRequest'])); + vi.spyOn(core, 'handleCallbackErrors').mockImplementation(async fn => fn()); + + const mockSpan = { setAttribute: vi.fn() } as any; + const mockArgs = { request: new Request('http://localhost', { method: 'POST' }) } as any; + const origFn = vi.fn().mockResolvedValue(new Response()); + + await errorHandleDataFunction.call(null, origFn, 'action', mockArgs, mockSpan); + + expect(storeFormDataKeys).toHaveBeenCalledWith(mockArgs, mockSpan, { username: true }); + }); + + it('does NOT capture form data when httpBodies is empty', async () => { + vi.spyOn(core, 'getClient').mockReturnValue(createMockClient([])); + vi.spyOn(core, 'handleCallbackErrors').mockImplementation(async fn => fn()); + + const mockSpan = { setAttribute: vi.fn() } as any; + const mockArgs = { request: new Request('http://localhost', { method: 'POST' }) } as any; + const origFn = vi.fn().mockResolvedValue(new Response()); + + await errorHandleDataFunction.call(null, origFn, 'action', mockArgs, mockSpan); + + expect(storeFormDataKeys).not.toHaveBeenCalled(); + }); + + it('does NOT capture form data for loader functions', async () => { + vi.spyOn(core, 'getClient').mockReturnValue(createMockClient(['incomingRequest'])); + vi.spyOn(core, 'handleCallbackErrors').mockImplementation(async fn => fn()); + + const mockSpan = { setAttribute: vi.fn() } as any; + const mockArgs = { request: new Request('http://localhost') } as any; + const origFn = vi.fn().mockResolvedValue(new Response()); + + await errorHandleDataFunction.call(null, origFn, 'loader', mockArgs, mockSpan); + + expect(storeFormDataKeys).not.toHaveBeenCalled(); + }); +});