From 48b8d6b6b73b349c70e6f2611e694f43a0e5ddf9 Mon Sep 17 00:00:00 2001 From: Martin Sonnberger Date: Thu, 29 Oct 2026 11:12:12 +0100 Subject: [PATCH] fix(core): Respect header collection for session user agents Co-Authored-By: OpenAI Codex --- packages/core/src/exports.ts | 10 +++++--- packages/core/test/lib/session.test.ts | 35 +++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/packages/core/src/exports.ts b/packages/core/src/exports.ts index 19e9509ea3a0..ce531805a997 100644 --- a/packages/core/src/exports.ts +++ b/packages/core/src/exports.ts @@ -10,6 +10,7 @@ import type { Primitive } from './types/misc'; import type { Session, SessionContext } from './types/session'; import type { SeverityLevel } from './types/severity'; import type { User } from './types/user'; +import { filterKeyValueData } from './utils/data-collection/filterKeyValueData'; import { debug } from './utils/debug-logger'; import type { ExclusiveEventHintOrCaptureContext } from './utils/prepareEvent'; import { parseEventHintOrCaptureContext } from './utils/prepareEvent'; @@ -252,12 +253,15 @@ export function startSession(context?: SessionContext): Session { const { user } = getCombinedScopeData(isolationScope, getCurrentScope()); - // Will fetch userAgent if called from browser sdk - const { userAgent } = GLOBAL_OBJ.navigator || {}; + let userAgent = GLOBAL_OBJ.navigator?.userAgent; + if (userAgent) { + const headerCollection = getClient()?.getDataCollectionOptions().httpHeaders.request ?? true; + userAgent = filterKeyValueData({ 'User-Agent': userAgent }, headerCollection)['User-Agent']; + } const session = makeSession({ user, - ...(userAgent && { userAgent }), + userAgent, ...context, }); diff --git a/packages/core/test/lib/session.test.ts b/packages/core/test/lib/session.test.ts index dfe3a764af16..f23c89e5231d 100644 --- a/packages/core/test/lib/session.test.ts +++ b/packages/core/test/lib/session.test.ts @@ -1,7 +1,40 @@ -import { describe, expect, it, test } from 'vitest'; +import { afterEach, beforeEach, describe, expect, it, test, vi } from 'vitest'; +import { getCurrentScope } from '../../src/currentScopes'; +import { startSession } from '../../src/exports'; import { closeSession, makeSession, updateSession } from '../../src/session'; import type { SessionContext } from '../../src/types/session'; import { timestampInSeconds } from '../../src/utils/time'; +import { getDefaultTestClientOptions, TestClient } from '../mocks/client'; +import { resetGlobals } from '../testutils'; + +describe('startSession', () => { + const userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/130.0.0.0 Safari/537.36'; + + beforeEach(() => { + resetGlobals(); + vi.stubGlobal('navigator', { userAgent }); + getCurrentScope().setClient( + new TestClient(getDefaultTestClientOptions({ dataCollection: { httpHeaders: { request: false } } })), + ); + }); + + afterEach(() => { + vi.unstubAllGlobals(); + resetGlobals(); + }); + + it('omits the automatic user agent when request header collection is disabled', () => { + const session = startSession(); + + expect(session.toJSON().attrs?.user_agent).toBeUndefined(); + }); + + it('preserves an explicit user agent when request header collection is disabled', () => { + const session = startSession({ userAgent: 'custom-user-agent' }); + + expect(session.toJSON().attrs?.user_agent).toBe('custom-user-agent'); + }); +}); describe('Session', () => { it('initializes with the proper defaults', () => {