From 4c7e29f3365529ced1fea4c17fd75ed7a5132c34 Mon Sep 17 00:00:00 2001 From: John Gracey Date: Tue, 29 Jun 2021 14:47:58 +0200 Subject: [PATCH 1/5] =?UTF-8?q?=E2=9C=A8[REPLAY-336]=20Privacy=20by=20Defa?= =?UTF-8?q?ult:=20MVP=20scaffold?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/core/src/domain/configuration.ts | 4 ++++ packages/rum-core/src/boot/rumPublicApi.ts | 2 ++ .../rum-recorder/src/boot/startRecording.ts | 8 +++++++ packages/rum-recorder/src/constants.ts | 22 ++++++++++++++++++ .../rum-recorder/src/domain/record/privacy.ts | 4 ++++ .../src/domain/record/serializationUtils.ts | 23 +++++++++++++++++-- 6 files changed, 61 insertions(+), 2 deletions(-) diff --git a/packages/core/src/domain/configuration.ts b/packages/core/src/domain/configuration.ts index 33aebe4ddc..c3e6a357aa 100644 --- a/packages/core/src/domain/configuration.ts +++ b/packages/core/src/domain/configuration.ts @@ -2,6 +2,7 @@ import { BuildEnv } from '../boot/init' import { CookieOptions, getCurrentSite } from '../browser/cookie' import { catchUserErrors } from '../tools/catchUserErrors' import { includes, ONE_KILO_BYTE, ONE_SECOND } from '../tools/utils' +import { CensorshipLevel } from '../../../../packages/rum-recorder/src/constants' import { computeTransportConfiguration, Datacenter } from './transportConfiguration' export const DEFAULT_CONFIGURATION = { @@ -13,6 +14,7 @@ export const DEFAULT_CONFIGURATION = { silentMultipleInit: false, trackInteractions: false, trackViewsManually: false, + censorshipLevel: CensorshipLevel.PRIVATE, /** * arbitrary value, byte precision not needed @@ -54,6 +56,7 @@ export interface UserConfiguration { trackViewsManually?: boolean proxyHost?: string beforeSend?: BeforeSendCallback + censorshipLevel?: CensorshipLevel service?: string env?: string @@ -81,6 +84,7 @@ export type Configuration = typeof DEFAULT_CONFIGURATION & service?: string beforeSend?: BeforeSendCallback + censorshipLevel?: CensorshipLevel isEnabled: (feature: string) => boolean } diff --git a/packages/rum-core/src/boot/rumPublicApi.ts b/packages/rum-core/src/boot/rumPublicApi.ts index 1f8fab8793..b2d9137be5 100644 --- a/packages/rum-core/src/boot/rumPublicApi.ts +++ b/packages/rum-core/src/boot/rumPublicApi.ts @@ -24,12 +24,14 @@ import { ProvidedSource } from '../domain/rumEventsCollection/error/errorCollect import { RumEventDomainContext } from '../domainContext.types' import { CommonContext, User, ActionType } from '../rawRumEvent.types' import { RumEvent } from '../rumEvent.types' +import { CensorshipLevel } from '../../../../packages/rum-recorder/src/constants' import { buildEnv } from './buildEnv' import { startRum } from './startRum' export interface RumUserConfiguration extends UserConfiguration { applicationId: string beforeSend?: (event: RumEvent, context: RumEventDomainContext) => void | boolean + censorshipLevel?: CensorshipLevel.PRIVATE } export type RumPublicApi = ReturnType diff --git a/packages/rum-recorder/src/boot/startRecording.ts b/packages/rum-recorder/src/boot/startRecording.ts index 5f580cc291..46e3d03c1c 100644 --- a/packages/rum-recorder/src/boot/startRecording.ts +++ b/packages/rum-recorder/src/boot/startRecording.ts @@ -6,6 +6,8 @@ import { startSegmentCollection } from '../domain/segmentCollection' import { send } from '../transport/send' import { RawRecord, RecordType } from '../types' +let configurationProviderRef: Configuration | undefined + export function startRecording( lifeCycle: LifeCycle, applicationId: string, @@ -13,6 +15,7 @@ export function startRecording( session: RumSession, parentContexts: ParentContexts ) { + configurationProviderRef = configuration const { addRecord, stop: stopSegmentCollection } = startSegmentCollection( lifeCycle, applicationId, @@ -36,6 +39,7 @@ export function startRecording( stop: () => { stopRecording() stopSegmentCollection() + configurationProviderRef = undefined }, } } @@ -47,3 +51,7 @@ export function trackViewEndRecord(lifeCycle: LifeCycle, addRawRecord: (record: }) }) } + +export function getRumRecorderConfig(): Configuration | undefined { + return configurationProviderRef +} diff --git a/packages/rum-recorder/src/constants.ts b/packages/rum-recorder/src/constants.ts index 1c79c3222f..a148322151 100644 --- a/packages/rum-recorder/src/constants.ts +++ b/packages/rum-recorder/src/constants.ts @@ -1,3 +1,9 @@ +export const enum CensorshipLevel { + PRIVATE = 'PRIVATE', + FORMS = 'FORMS', + PUBLIC = 'PUBLIC', +} + export const enum InputPrivacyMode { NONE = 1, IGNORED, @@ -12,3 +18,19 @@ export const PRIVACY_ATTR_VALUE_INPUT_MASKED = 'input-masked' export const PRIVACY_CLASS_HIDDEN = 'dd-privacy-hidden' export const PRIVACY_CLASS_INPUT_IGNORED = 'dd-privacy-input-ignored' export const PRIVACY_CLASS_INPUT_MASKED = 'dd-privacy-input-masked' + +export const PRIVACY_INPUT_MASK = '*****' + +export const FORM_PRIVATE_TAG_NAMES: { [tagName: string]: true } = { + INPUT: true, + LABEL: true, + SELECT: true, + TEXTAREA: true, + BUTTON: true, + FIELDSET: true, + DATALIST: true, + OUPUT: true, + OPTION: true, + OPTGROUP: true, + LEGEND: true, +} diff --git a/packages/rum-recorder/src/domain/record/privacy.ts b/packages/rum-recorder/src/domain/record/privacy.ts index 6fcb14b0bf..d27d09b2df 100644 --- a/packages/rum-recorder/src/domain/record/privacy.ts +++ b/packages/rum-recorder/src/domain/record/privacy.ts @@ -15,6 +15,8 @@ import { // to obfuscate. const PRIVACY_INPUT_TYPES_TO_IGNORE = ['email', 'password', 'tel'] +const MASKING_CHAR = '᙮' + // Returns true if the given DOM node should be hidden. Ancestors // are not checked. export function nodeShouldBeHidden(node: Node): boolean { @@ -89,3 +91,5 @@ function isElement(node: Node): node is Element { function isInputElement(elem: Element): elem is HTMLInputElement { return elem.tagName === 'INPUT' } + +export const censorText = (text: string) => text.replace(/[^\s]/g, MASKING_CHAR) diff --git a/packages/rum-recorder/src/domain/record/serializationUtils.ts b/packages/rum-recorder/src/domain/record/serializationUtils.ts index 648c2160ba..49febd7dcc 100644 --- a/packages/rum-recorder/src/domain/record/serializationUtils.ts +++ b/packages/rum-recorder/src/domain/record/serializationUtils.ts @@ -1,5 +1,6 @@ import { buildUrl } from '@datadog/browser-core' -import { InputPrivacyMode } from '../../constants' +import { CensorshipLevel, InputPrivacyMode, PRIVACY_INPUT_MASK } from '../../constants' +import { getRumRecorderConfig } from '../../boot/startRecording' import { getNodeInputPrivacyMode, getNodeOrAncestorsInputPrivacyMode } from './privacy' import { SerializedNodeWithId } from './types' @@ -125,5 +126,23 @@ export function getElementInputValue(element: Element, ancestorInputPrivacyMode? } export function maskValue(value: string) { - return value.replace(/./g, '*') + return value.replace(/.+/, PRIVACY_INPUT_MASK) +} + +export function isFlagEnabled(feature: string): boolean { + const configuration = getRumRecorderConfig() + if (!configuration) { + return false + } + return configuration.isEnabled(feature) +} + +export function getCensorshipLevel(): CensorshipLevel { + const configuration = getRumRecorderConfig() + if (!configuration) { + // Should never happen. Default to private + return CensorshipLevel.PRIVATE + } + const level: CensorshipLevel = configuration.censorshipLevel + return level } From 516c1242b3acfdfd75f548a8579383debcbeb035 Mon Sep 17 00:00:00 2001 From: John Gracey Date: Tue, 29 Jun 2021 15:01:48 +0200 Subject: [PATCH 2/5] revert input masking, now behind FF --- .../rum-recorder/src/domain/record/serializationUtils.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/rum-recorder/src/domain/record/serializationUtils.ts b/packages/rum-recorder/src/domain/record/serializationUtils.ts index 49febd7dcc..4a339556e7 100644 --- a/packages/rum-recorder/src/domain/record/serializationUtils.ts +++ b/packages/rum-recorder/src/domain/record/serializationUtils.ts @@ -126,7 +126,10 @@ export function getElementInputValue(element: Element, ancestorInputPrivacyMode? } export function maskValue(value: string) { - return value.replace(/.+/, PRIVACY_INPUT_MASK) + if (isFlagEnabled('privacy-by-default-poc')) { + return value.replace(/.+/, PRIVACY_INPUT_MASK) + } + return value.replace(/./g, '*') } export function isFlagEnabled(feature: string): boolean { From dbbcd5725c9196e3303b6e337759f63638bc4924 Mon Sep 17 00:00:00 2001 From: John Gracey Date: Tue, 29 Jun 2021 16:09:18 +0200 Subject: [PATCH 3/5] build fix --- packages/core/src/domain/configuration.ts | 7 +++---- packages/rum-core/src/boot/rumPublicApi.ts | 3 +-- .../rum-recorder/src/domain/record/serializationUtils.ts | 3 ++- rum-events-format | 2 +- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/core/src/domain/configuration.ts b/packages/core/src/domain/configuration.ts index c3e6a357aa..93efe2cc3f 100644 --- a/packages/core/src/domain/configuration.ts +++ b/packages/core/src/domain/configuration.ts @@ -2,7 +2,6 @@ import { BuildEnv } from '../boot/init' import { CookieOptions, getCurrentSite } from '../browser/cookie' import { catchUserErrors } from '../tools/catchUserErrors' import { includes, ONE_KILO_BYTE, ONE_SECOND } from '../tools/utils' -import { CensorshipLevel } from '../../../../packages/rum-recorder/src/constants' import { computeTransportConfiguration, Datacenter } from './transportConfiguration' export const DEFAULT_CONFIGURATION = { @@ -14,7 +13,7 @@ export const DEFAULT_CONFIGURATION = { silentMultipleInit: false, trackInteractions: false, trackViewsManually: false, - censorshipLevel: CensorshipLevel.PRIVATE, + censorshipLevel: 'PRIVATE', /** * arbitrary value, byte precision not needed @@ -56,7 +55,7 @@ export interface UserConfiguration { trackViewsManually?: boolean proxyHost?: string beforeSend?: BeforeSendCallback - censorshipLevel?: CensorshipLevel + censorshipLevel?: string service?: string env?: string @@ -84,7 +83,7 @@ export type Configuration = typeof DEFAULT_CONFIGURATION & service?: string beforeSend?: BeforeSendCallback - censorshipLevel?: CensorshipLevel + censorshipLevel?: string isEnabled: (feature: string) => boolean } diff --git a/packages/rum-core/src/boot/rumPublicApi.ts b/packages/rum-core/src/boot/rumPublicApi.ts index b2d9137be5..9b24c848c3 100644 --- a/packages/rum-core/src/boot/rumPublicApi.ts +++ b/packages/rum-core/src/boot/rumPublicApi.ts @@ -24,14 +24,13 @@ import { ProvidedSource } from '../domain/rumEventsCollection/error/errorCollect import { RumEventDomainContext } from '../domainContext.types' import { CommonContext, User, ActionType } from '../rawRumEvent.types' import { RumEvent } from '../rumEvent.types' -import { CensorshipLevel } from '../../../../packages/rum-recorder/src/constants' import { buildEnv } from './buildEnv' import { startRum } from './startRum' export interface RumUserConfiguration extends UserConfiguration { applicationId: string beforeSend?: (event: RumEvent, context: RumEventDomainContext) => void | boolean - censorshipLevel?: CensorshipLevel.PRIVATE + censorshipLevel?: string } export type RumPublicApi = ReturnType diff --git a/packages/rum-recorder/src/domain/record/serializationUtils.ts b/packages/rum-recorder/src/domain/record/serializationUtils.ts index 4a339556e7..3aa9b39f96 100644 --- a/packages/rum-recorder/src/domain/record/serializationUtils.ts +++ b/packages/rum-recorder/src/domain/record/serializationUtils.ts @@ -146,6 +146,7 @@ export function getCensorshipLevel(): CensorshipLevel { // Should never happen. Default to private return CensorshipLevel.PRIVATE } - const level: CensorshipLevel = configuration.censorshipLevel + // PENDING review from core package, core defines `censorshipLevel` as any string. + const level: CensorshipLevel = configuration.censorshipLevel as CensorshipLevel return level } diff --git a/rum-events-format b/rum-events-format index fcd5662c40..2ea84b56a4 160000 --- a/rum-events-format +++ b/rum-events-format @@ -1 +1 @@ -Subproject commit fcd5662c4007a3225ce9abb443bbb898b513b8b1 +Subproject commit 2ea84b56a4e0670b2d6e3e0c6a5fd27774ce4a3d From 76713d9ef8507d4172099b6491cdee0a0e6b700b Mon Sep 17 00:00:00 2001 From: John Gracey Date: Tue, 29 Jun 2021 17:07:23 +0200 Subject: [PATCH 4/5] lint fix hack --- .../src/domain/segmentCollection/segmentCollection.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rum-recorder/src/domain/segmentCollection/segmentCollection.ts b/packages/rum-recorder/src/domain/segmentCollection/segmentCollection.ts index 4475928977..36f30aa981 100644 --- a/packages/rum-recorder/src/domain/segmentCollection/segmentCollection.ts +++ b/packages/rum-recorder/src/domain/segmentCollection/segmentCollection.ts @@ -153,7 +153,7 @@ export function doStartSegmentCollection( state = { status: SegmentCollectionStatus.SegmentPending, segment, - expirationTimeoutId: setTimeout( + expirationTimeoutId: window.setTimeout( monitor(() => { flushSegment('max_duration') }), From 3ad2e855132a4994ccaef7e675b6bb4ea47b64d6 Mon Sep 17 00:00:00 2001 From: John Gracey Date: Wed, 30 Jun 2021 13:45:12 +0200 Subject: [PATCH 5/5] change to default public --- packages/core/src/domain/configuration.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/core/src/domain/configuration.ts b/packages/core/src/domain/configuration.ts index 93efe2cc3f..21fc709d7e 100644 --- a/packages/core/src/domain/configuration.ts +++ b/packages/core/src/domain/configuration.ts @@ -13,7 +13,7 @@ export const DEFAULT_CONFIGURATION = { silentMultipleInit: false, trackInteractions: false, trackViewsManually: false, - censorshipLevel: 'PRIVATE', + censorshipLevel: 'PUBLIC', /** * arbitrary value, byte precision not needed @@ -142,6 +142,10 @@ export function buildConfiguration(userConfiguration: UserConfiguration, buildEn configuration.trackViewsManually = !!userConfiguration.trackViewsManually } + if ('censorshipLevel' in userConfiguration) { + configuration.censorshipLevel = userConfiguration.censorshipLevel ?? configuration.censorshipLevel + } + return configuration }