Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ export function pagesRouterInstrumentPageLoad(client: Client): void {
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'pageload',
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.pageload.nextjs.pages_router_instrumentation',
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: route ? 'route' : 'url',
...(params && client.getOptions().sendDefaultPii && { ...params }),
...(params && { ...params }),
},
},
{ sentryTrace, baggage },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,19 @@ export function addHeadersAsAttributes(
return {};
}

const client = getClient();
const dataCollection = client?.getDataCollectionOptions();

if (dataCollection?.httpHeaders.request === false) {
return {};
}

const headersDict: Record<string, string | string[] | undefined> =
headers instanceof Headers || (typeof headers === 'object' && 'get' in headers)
? winterCGHeadersToDict(headers as Headers)
: headers;

const headerAttributes = httpHeadersToSpanAttributes(headersDict, getClient()?.getOptions().sendDefaultPii ?? false);
const headerAttributes = httpHeadersToSpanAttributes(headersDict, dataCollection ?? false);

if (span) {
span.setAttributes(headerAttributes);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@ export function setUrlProcessingMetadata(event: Event): void {
return;
}

// Only add URL if sendDefaultPii is enabled, as URLs may contain PII
const client = getClient();
if (!client?.getOptions().sendDefaultPii) {
if (!client) {
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ async function withServerActionInstrumentationImplementation<A extends (...args:
callback: A,
): Promise<ReturnType<A>> {
return withIsolationScope(async isolationScope => {
const sendDefaultPii = getClient()?.getOptions().sendDefaultPii;
const shouldRecordResponse = getClient()?.getDataCollectionOptions().httpBodies.includes('outgoingResponse');

let sentryTraceHeader;
let baggageHeader;
Expand Down Expand Up @@ -138,7 +138,7 @@ async function withServerActionInstrumentationImplementation<A extends (...args:
}
});

if (options.recordResponse !== undefined ? options.recordResponse : sendDefaultPii) {
if (options.recordResponse !== undefined ? options.recordResponse : shouldRecordResponse) {
getIsolationScope().setExtra('server_action_result', result);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,9 @@ describe('pagesRouterInstrumentPageLoad', () => {

it.each([
[
'https://example.com/lforst/posts/1337?q=42',
'https://example.com/chargome/posts/1337?q=42',
'/[user]/posts/[id]',
{ user: 'lforst', id: '1337', q: '42' },
{ user: 'chargome', id: '1337', q: '42' },
{
pageProps: {
_sentryTraceData: 'c82b8554881b4d28ad977de04a4fb40a-a755953cd3394d5f-1',
Expand All @@ -128,6 +128,9 @@ describe('pagesRouterInstrumentPageLoad', () => {
'sentry.op': 'pageload',
'sentry.origin': 'auto.pageload.nextjs.pages_router_instrumentation',
'sentry.source': 'route',
user: 'chargome',
id: '1337',
q: '42',
},
},
],
Expand Down
54 changes: 54 additions & 0 deletions packages/nextjs/test/utils/addHeadersAsAttributes.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import * as SentryCore from '@sentry/core';
import { describe, expect, it, vi } from 'vitest';
import { addHeadersAsAttributes } from '../../src/common/utils/addHeadersAsAttributes';

describe('addHeadersAsAttributes', () => {
it('returns empty object when headers are undefined', () => {
expect(addHeadersAsAttributes(undefined)).toEqual({});
});

it('returns empty object when httpHeaders.request is false', () => {
vi.spyOn(SentryCore, 'getClient').mockReturnValue({
getDataCollectionOptions: () => ({
httpHeaders: { request: false, response: true },
}),
} as unknown as SentryCore.Client);

const result = addHeadersAsAttributes({ 'content-type': 'application/json' });
expect(result).toEqual({});
});

it('passes PII headers through when httpHeaders.request is true', () => {
vi.spyOn(SentryCore, 'getClient').mockReturnValue({
getDataCollectionOptions: () => ({
httpHeaders: { request: true, response: true },
}),
} as unknown as SentryCore.Client);

const result = addHeadersAsAttributes({
'content-type': 'application/json',
'x-forwarded-for': '127.0.0.1',
});

expect(result).toMatchObject({
'http.request.header.content_type': 'application/json',
'http.request.header.x_forwarded_for': '127.0.0.1',
});
});

it('filters denied headers when httpHeaders.request is a deny list', () => {
vi.spyOn(SentryCore, 'getClient').mockReturnValue({
getDataCollectionOptions: () => ({
httpHeaders: { request: { deny: ['forwarded'] }, response: true },
}),
} as unknown as SentryCore.Client);

const result = addHeadersAsAttributes({
'content-type': 'application/json',
'x-forwarded-for': '127.0.0.1',
});

expect(result['http.request.header.content_type']).toBe('application/json');
expect(result['http.request.header.x_forwarded_for']).toBe('[Filtered]');
});
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Feat PR lacks integration or E2E test coverage

Low Severity

This is a feat PR that migrates multiple code paths from sendDefaultPii to the dataCollection API, but the diff only includes unit tests (with heavy mocking). No integration or E2E test is present to validate the end-to-end behavior of these data-collection changes within an actual Next.js application. Per the review rules, feat PRs need at least one integration or E2E test. Flagging this because it was mentioned in the rules file.

Additional Locations (1)
Fix in Cursor Fix in Web

Triggered by project rule: PR Review Guidelines for Cursor Bot

Reviewed by Cursor Bugbot for commit 1ea8c62. Configure here.

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.

we got existing tests that cover this

58 changes: 58 additions & 0 deletions packages/nextjs/test/utils/setUrlProcessingMetadata.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import type { Event } from '@sentry/core';
import * as SentryCore from '@sentry/core';
import { describe, expect, it, vi } from 'vitest';
import { setUrlProcessingMetadata } from '../../src/common/utils/setUrlProcessingMetadata';

describe('setUrlProcessingMetadata', () => {
it('skips non-transaction events', () => {
const event: Event = { type: undefined };
setUrlProcessingMetadata(event);
});

it('adds URL without sendDefaultPii', () => {
vi.spyOn(SentryCore, 'getClient').mockReturnValue({
getOptions: () => ({ sendDefaultPii: false }),
} as unknown as SentryCore.Client);

const scopeData = {
sdkProcessingMetadata: {
normalizedRequest: {
headers: {
'x-forwarded-proto': 'https',
host: 'example.com',
},
},
},
};

const event: Event = {
type: 'transaction',
contexts: {
trace: {
op: 'http.server',
data: {
'next.route': '/api/users/[id]',
'http.target': '/api/users/123',
},
},
},
sdkProcessingMetadata: {
capturedSpanIsolationScope: { getScopeData: () => scopeData },
},
};

setUrlProcessingMetadata(event);
expect(scopeData.sdkProcessingMetadata.normalizedRequest.url).toBe('https://example.com/api/users/123');
});

it('skips when no client is available', () => {
vi.spyOn(SentryCore, 'getClient').mockReturnValue(undefined);

const event: Event = {
type: 'transaction',
contexts: { trace: { op: 'http.server', data: { 'next.route': '/test' } } },
};

setUrlProcessingMetadata(event);
});
});
Loading