diff --git a/src/__tests__/extensions/replay/config.test.ts b/src/__tests__/extensions/replay/config.test.ts index eb42e718a..4892c6356 100644 --- a/src/__tests__/extensions/replay/config.test.ts +++ b/src/__tests__/extensions/replay/config.test.ts @@ -4,8 +4,32 @@ import { buildNetworkRequestOptions } from '../../../extensions/replay/config' describe('config', () => { describe('network request options', () => { describe('maskRequestFn', () => { + it('can enable header recording remotely', () => { + const networkOptions = buildNetworkRequestOptions(defaultConfig(), { recordHeaders: true }) + expect(networkOptions.recordHeaders).toBe(true) + expect(networkOptions.recordBody).toBe(undefined) + }) + + it('can enable body recording remotely', () => { + const networkOptions = buildNetworkRequestOptions(defaultConfig(), { recordBody: true }) + expect(networkOptions.recordHeaders).toBe(undefined) + expect(networkOptions.recordBody).toBe(true) + }) + + it('client can force disable recording', () => { + const posthogConfig = defaultConfig() + posthogConfig.session_recording.recordHeaders = false + posthogConfig.session_recording.recordBody = false + const networkOptions = buildNetworkRequestOptions(posthogConfig, { + recordHeaders: true, + recordBody: true, + }) + expect(networkOptions.recordHeaders).toBe(false) + expect(networkOptions.recordBody).toBe(false) + }) + it('should remove the Authorization header from requests even if no other config is set', () => { - const networkOptions = buildNetworkRequestOptions(defaultConfig()) + const networkOptions = buildNetworkRequestOptions(defaultConfig(), {}) const cleaned = networkOptions.maskRequestFn!({ url: 'something', requestHeaders: { @@ -19,7 +43,7 @@ describe('config', () => { }) it('should cope with no headers when even if no other config is set', () => { - const networkOptions = buildNetworkRequestOptions(defaultConfig()) + const networkOptions = buildNetworkRequestOptions(defaultConfig(), {}) const cleaned = networkOptions.maskRequestFn!({ url: 'something', requestHeaders: undefined, @@ -38,7 +62,7 @@ describe('config', () => { }, } } - const networkOptions = buildNetworkRequestOptions(posthogConfig) + const networkOptions = buildNetworkRequestOptions(posthogConfig, {}) const cleaned = networkOptions.maskRequestFn!({ url: 'something', diff --git a/src/extensions/replay/config.ts b/src/extensions/replay/config.ts index ea37818f4..a435b4343 100644 --- a/src/extensions/replay/config.ts +++ b/src/extensions/replay/config.ts @@ -42,8 +42,15 @@ const removeAuthorizationHeader = (data: NetworkRequest): NetworkRequest => { * we _never_ want to record that header by accident * if someone complains then we'll add an opt-in to let them override it */ -export const buildNetworkRequestOptions = (instanceConfig: PostHogConfig): NetworkRecordOptions => { +export const buildNetworkRequestOptions = ( + instanceConfig: PostHogConfig, + remoteNetworkOptions: Pick +): NetworkRecordOptions => { const config = instanceConfig.session_recording as NetworkRecordOptions + // client can always disable despite remote options + const canRecordHeaders = config.recordHeaders === false ? false : remoteNetworkOptions.recordHeaders + const canRecordBody = config.recordBody === false ? false : remoteNetworkOptions.recordBody + config.maskRequestFn = _isFunction(instanceConfig.session_recording.maskNetworkRequestFn) ? (data) => { const cleanedRequest = removeAuthorizationHeader(data) @@ -58,5 +65,7 @@ export const buildNetworkRequestOptions = (instanceConfig: PostHogConfig): Netwo return { ...defaultNetworkOptions, ...config, + recordHeaders: canRecordHeaders, + recordBody: canRecordBody, } } diff --git a/src/extensions/replay/sessionrecording.ts b/src/extensions/replay/sessionrecording.ts index 07a3ac138..2da197873 100644 --- a/src/extensions/replay/sessionrecording.ts +++ b/src/extensions/replay/sessionrecording.ts @@ -14,7 +14,7 @@ import { truncateLargeConsoleLogs, } from './sessionrecording-utils' import { PostHog } from '../../posthog-core' -import { DecideResponse, NetworkRequest, Properties } from '../../types' +import { DecideResponse, NetworkRecordOptions, NetworkRequest, Properties } from '../../types' import { EventType, type eventWithTime, type listenerHandler } from '@rrweb/types' import Config from '../../config' import { _timestamp, loadScript } from '../../utils' @@ -92,7 +92,7 @@ export class SessionRecording { private receivedDecide: boolean private rrwebRecord: rrwebRecord | undefined private isIdle = false - private _captureNetworkPayloads: boolean | undefined + private _captureNetworkPayloads: Pick | undefined = undefined private _linkedFlagSeen: boolean = false private _lastActivityTimestamp: number = Date.now() @@ -255,8 +255,7 @@ export class SessionRecording { }) } - this._captureNetworkPayloads = - response.capturePerformance && response.sessionRecording?.networkPayloadCaptureEnabled + this._captureNetworkPayloads = response.sessionRecording?.networkPayloadCaptureEnabled const receivedSampleRate = response.sessionRecording?.sampleRate this._sampleRate = @@ -475,7 +474,11 @@ export class SessionRecording { } if (this._captureNetworkPayloads) { if (_isFunction((window as any).getRecordNetworkPlugin)) { - plugins.push((window as any).getRecordNetworkPlugin(buildNetworkRequestOptions(this.instance.config))) + plugins.push( + (window as any).getRecordNetworkPlugin( + buildNetworkRequestOptions(this.instance.config, this._captureNetworkPayloads) + ) + ) } } diff --git a/src/types.ts b/src/types.ts index 15c070a39..cad1c93d9 100644 --- a/src/types.ts +++ b/src/types.ts @@ -254,7 +254,7 @@ export interface DecideResponse { sampleRate?: string | null minimumDurationMilliseconds?: number linkedFlag?: string | null - networkPayloadCaptureEnabled?: boolean + networkPayloadCaptureEnabled?: Pick } surveys?: boolean toolbarParams: ToolbarParams