From a04be9d08657ca7082634b11dc7b424256aae009 Mon Sep 17 00:00:00 2001 From: cy-moi Date: Mon, 15 Apr 2024 15:41:56 +0200 Subject: [PATCH 01/32] Add enablePrivacyForActionName in config; implement action name mask accordingly --- packages/{rum => rum-core}/src/constants.ts | 0 .../action/getActionNameFromElement.spec.ts | 86 +++++++++++++++++++ .../domain/action/getActionNameFromElement.ts | 18 +++- .../domain/action/trackClickActions.spec.ts | 49 ++++++++++- .../src/domain/action/trackClickActions.ts | 34 ++++++-- packages/rum-core/src/domain/configuration.ts | 3 + .../src/domain}/privacy.spec.ts | 2 +- .../record => rum-core/src/domain}/privacy.ts | 4 +- packages/rum-core/src/index.ts | 2 + .../serialization/htmlAst.specHelper.ts | 2 +- .../serialization/serialization.types.ts | 2 +- .../serialization/serializationUtils.spec.ts | 2 +- .../serialization/serializationUtils.ts | 6 +- .../serialization/serializeAttribute.spec.ts | 2 +- .../serialization/serializeAttribute.ts | 7 +- .../serialization/serializeAttributes.ts | 4 +- .../serialization/serializeNode.spec.ts | 2 +- .../record/serialization/serializeNode.ts | 4 +- .../domain/record/trackers/trackInput.spec.ts | 2 +- .../src/domain/record/trackers/trackInput.ts | 4 +- .../record/trackers/trackMediaInteraction.ts | 3 +- .../record/trackers/trackMouseInteraction.ts | 3 +- .../record/trackers/trackMutation.spec.ts | 4 +- .../domain/record/trackers/trackMutation.ts | 8 +- .../src/domain/record/trackers/trackScroll.ts | 4 +- .../record/trackers/trackers.specHelper.ts | 2 +- packages/rum/src/entries/internal.ts | 7 +- 27 files changed, 219 insertions(+), 47 deletions(-) rename packages/{rum => rum-core}/src/constants.ts (100%) rename packages/{rum/src/domain/record => rum-core/src/domain}/privacy.spec.ts (99%) rename packages/{rum/src/domain/record => rum-core/src/domain}/privacy.ts (98%) diff --git a/packages/rum/src/constants.ts b/packages/rum-core/src/constants.ts similarity index 100% rename from packages/rum/src/constants.ts rename to packages/rum-core/src/constants.ts diff --git a/packages/rum-core/src/domain/action/getActionNameFromElement.spec.ts b/packages/rum-core/src/domain/action/getActionNameFromElement.spec.ts index 3ef7567603..a7357b3b84 100644 --- a/packages/rum-core/src/domain/action/getActionNameFromElement.spec.ts +++ b/packages/rum-core/src/domain/action/getActionNameFromElement.spec.ts @@ -345,4 +345,90 @@ describe('getActionNameFromElement', () => { ).toBe('Foo') }) }) + + describe('with privacyEnabledForActionName', () => { + it('extracts attribute text when privacyEnabledActionName is false', () => { + expect( + getActionNameFromElement( + appendElement(` +
+ ignored +
+ `), + undefined, + false + ) + ).toBe('foo') + }) + + it('extracts user defined attribute text when privacyEnabledActionName is false', () => { + expect( + getActionNameFromElement( + appendElement(` +
+ ignored +
+ `), + 'data-test-id', + false + ) + ).toBe('foo') + }) + + it('extracts inner text when privacyEnabledActionName is false and attribute is empty', () => { + expect( + getActionNameFromElement( + appendElement(` +
+ foo +
+ `), + 'data-test-id', + false + ) + ).toBe('foo') + }) + + it('returns placeholder when privacyEnabledActionName is true and attribute is empty', () => { + expect( + getActionNameFromElement( + appendElement(` +
+ foo +
+ `), + 'data-test-id', + true + ) + ).toBe('Masked Element') + }) + + it('extracts default attribute text when privacyEnabledActionName is true', () => { + expect( + getActionNameFromElement( + appendElement(` +
+ ignored +
+ `), + undefined, + true + ) + ).toBe('foo') + }) + + it('extracts user defined attribute text when privacyEnabledActionName is true', () => { + expect( + getActionNameFromElement( + appendElement(` +
+ ignored +
+ `), + 'data-test-id', + true + ) + ).toBe('foo') + }) + }) }) diff --git a/packages/rum-core/src/domain/action/getActionNameFromElement.ts b/packages/rum-core/src/domain/action/getActionNameFromElement.ts index 2d3f060303..6b102a4ef3 100644 --- a/packages/rum-core/src/domain/action/getActionNameFromElement.ts +++ b/packages/rum-core/src/domain/action/getActionNameFromElement.ts @@ -6,8 +6,22 @@ import { getParentElement } from '../../browser/polyfills' * It can also be retrieved from a user defined attribute. */ export const DEFAULT_PROGRAMMATIC_ACTION_NAME_ATTRIBUTE = 'data-dd-action-name' - -export function getActionNameFromElement(element: Element, userProgrammaticAttribute?: string): string { +export const ACTION_NAME_PLACEHOLDER = 'Masked Element' +export function getActionNameFromElement( + element: Element, + userProgrammaticAttribute?: string, + privacyEnabledForActionName?: boolean +): string { + // Only get the defined action name if + // * privacy is enabled for action name + // * and privacy is enabled for the Node or globally + if (privacyEnabledForActionName) { + return ( + getActionNameFromElementProgrammatically(element, DEFAULT_PROGRAMMATIC_ACTION_NAME_ATTRIBUTE) || + (userProgrammaticAttribute && getActionNameFromElementProgrammatically(element, userProgrammaticAttribute)) || + ACTION_NAME_PLACEHOLDER + ) + } // Proceed to get the action name in two steps: // * first, get the name programmatically, explicitly defined by the user. // * then, use strategies that are known to return good results. Those strategies will be used on diff --git a/packages/rum-core/src/domain/action/trackClickActions.spec.ts b/packages/rum-core/src/domain/action/trackClickActions.spec.ts index ded25f2369..3ab535a9ad 100644 --- a/packages/rum-core/src/domain/action/trackClickActions.spec.ts +++ b/packages/rum-core/src/domain/action/trackClickActions.spec.ts @@ -1,5 +1,5 @@ import type { Context, Duration } from '@datadog/browser-core' -import { addDuration, clocksNow, timeStampNow, relativeNow } from '@datadog/browser-core' +import { addDuration, clocksNow, timeStampNow, relativeNow, DefaultPrivacyLevel } from '@datadog/browser-core' import { createNewEvent } from '@datadog/browser-core/test' import type { TestSetupBuilder } from '../../../test' import { setup, createFakeClick } from '../../../test' @@ -232,6 +232,53 @@ describe('trackClickActions', () => { expect(events.length).toBe(1) }) + fdescribe('with enablePrivacyForActionName false', () => { + it('extracts action name when default privacy level is mask', () => { + setupBuilder.withConfiguration({ + defaultPrivacyLevel: DefaultPrivacyLevel.MASK, + enablePrivacyForActionName: false, + }) + const { clock } = setupBuilder.build() + emulateClick({ activity: {} }) + expect(findActionId()).not.toBeUndefined() + clock.tick(EXPIRE_DELAY) + + expect(events.length).toBe(1) + expect(events[0].name).toBe('Click me') + }) + }) + + fdescribe('with enablePrivacyForActionName true', () => { + it('get placeholder when defaultPrivacyLevel is mask without programmatically declared action name', () => { + setupBuilder.withConfiguration({ + defaultPrivacyLevel: DefaultPrivacyLevel.MASK, + enablePrivacyForActionName: true, + }) + const { clock } = setupBuilder.build() + emulateClick({ activity: {} }) + expect(findActionId()).not.toBeUndefined() + clock.tick(EXPIRE_DELAY) + + expect(events.length).toBe(1) + expect(events[0].name).toBe('Masked Element') + }) + + it('get placeholder when html override is hidden', () => { + input.setAttribute('data-dd-privacy', 'hidden') + setupBuilder.withConfiguration({ + defaultPrivacyLevel: DefaultPrivacyLevel.MASK, + enablePrivacyForActionName: true, + }) + const { clock } = setupBuilder.build() + emulateClick({ activity: {} }) + expect(findActionId()).not.toBeUndefined() + clock.tick(EXPIRE_DELAY) + + expect(events.length).toBe(1) + expect(events[0].name).toBe('Masked Element') + }) + }) + describe('rage clicks', () => { it('considers a chain of three clicks or more as a single action with "rage" frustration type', () => { const { clock } = setupBuilder.build() diff --git a/packages/rum-core/src/domain/action/trackClickActions.ts b/packages/rum-core/src/domain/action/trackClickActions.ts index b587bae9f2..142100c916 100644 --- a/packages/rum-core/src/domain/action/trackClickActions.ts +++ b/packages/rum-core/src/domain/action/trackClickActions.ts @@ -11,18 +11,21 @@ import { clocksNow, ONE_SECOND, elapsed, + DefaultPrivacyLevel, } from '@datadog/browser-core' import type { FrustrationType } from '../../rawRumEvent.types' import { ActionType } from '../../rawRumEvent.types' +import { NodePrivacyLevel } from '../../constants' import type { RumConfiguration } from '../configuration' import type { LifeCycle } from '../lifeCycle' import { LifeCycleEventType } from '../lifeCycle' import { trackEventCounts } from '../trackEventCounts' import { PAGE_ACTIVITY_VALIDATION_DELAY, waitPageActivityEnd } from '../waitPageActivityEnd' import { getSelectorFromElement } from '../getSelectorFromElement' +import { getNodePrivacyLevel } from '../privacy' import type { ClickChain } from './clickChain' import { createClickChain } from './clickChain' -import { getActionNameFromElement } from './getActionNameFromElement' +import { getActionNameFromElement, ACTION_NAME_PLACEHOLDER } from './getActionNameFromElement' import type { MouseEventOnElement, UserActivity } from './listenActionEvents' import { listenActionEvents } from './listenActionEvents' import { computeFrustration } from './computeFrustration' @@ -42,6 +45,7 @@ export interface ClickAction { selector_with_stable_attributes?: string width: number height: number + masked?: boolean } position?: { x: number; y: number } startClocks: ClocksState @@ -133,7 +137,7 @@ function processPointerDown( domMutationObservable: Observable, pointerDownEvent: MouseEventOnElement ) { - const clickActionBase = computeClickActionBase(pointerDownEvent, configuration.actionNameAttribute) + const clickActionBase = computeClickActionBase(pointerDownEvent, configuration) let hadActivityOnPointerDown = false @@ -209,21 +213,33 @@ function startClickAction( type ClickActionBase = Pick -function computeClickActionBase(event: MouseEventOnElement, actionNameAttribute?: string): ClickActionBase { +function computeClickActionBase(event: MouseEventOnElement, configuration: RumConfiguration): ClickActionBase { const rect = event.target.getBoundingClientRect() + const privacyEnabledForActionName = + (getNodePrivacyLevel(event.target, configuration.defaultPrivacyLevel) === NodePrivacyLevel.HIDDEN || + configuration.defaultPrivacyLevel === DefaultPrivacyLevel.MASK) && + configuration.enablePrivacyForActionName + const actionName = getActionNameFromElement( + event.target, + configuration.actionNameAttribute, + privacyEnabledForActionName + ) + const target = { + width: Math.round(rect.width), + height: Math.round(rect.height), + selector: getSelectorFromElement(event.target, configuration.actionNameAttribute), + } + return { type: ActionType.CLICK, - target: { - width: Math.round(rect.width), - height: Math.round(rect.height), - selector: getSelectorFromElement(event.target, actionNameAttribute), - }, + target: + actionName === ACTION_NAME_PLACEHOLDER && privacyEnabledForActionName ? assign({ masked: true }, target) : target, position: { // Use clientX and Y because for SVG element offsetX and Y are relatives to the element x: Math.round(event.clientX - rect.left), y: Math.round(event.clientY - rect.top), }, - name: getActionNameFromElement(event.target, actionNameAttribute), + name: actionName, } } diff --git a/packages/rum-core/src/domain/configuration.ts b/packages/rum-core/src/domain/configuration.ts index 491ac2f77f..c8b6d29c23 100644 --- a/packages/rum-core/src/domain/configuration.ts +++ b/packages/rum-core/src/domain/configuration.ts @@ -39,6 +39,7 @@ export interface RumInitConfiguration extends InitConfiguration { startSessionReplayRecordingManually?: boolean | undefined // action options + enablePrivacyForActionName?: boolean | undefined trackUserInteractions?: boolean | undefined actionNameAttribute?: string | undefined @@ -61,6 +62,7 @@ export interface RumConfiguration extends Configuration { compressIntakeRequests: boolean applicationId: string defaultPrivacyLevel: DefaultPrivacyLevel + enablePrivacyForActionName: boolean sessionReplaySampleRate: number startSessionReplayRecordingManually: boolean trackUserInteractions: boolean @@ -129,6 +131,7 @@ export function validateAndBuildRumConfiguration( defaultPrivacyLevel: objectHasValue(DefaultPrivacyLevel, initConfiguration.defaultPrivacyLevel) ? initConfiguration.defaultPrivacyLevel : DefaultPrivacyLevel.MASK, + enablePrivacyForActionName: initConfiguration.enablePrivacyForActionName ?? false, customerDataTelemetrySampleRate: 1, traceContextInjection: objectHasValue(TraceContextInjection, initConfiguration.traceContextInjection) ? initConfiguration.traceContextInjection diff --git a/packages/rum/src/domain/record/privacy.spec.ts b/packages/rum-core/src/domain/privacy.spec.ts similarity index 99% rename from packages/rum/src/domain/record/privacy.spec.ts rename to packages/rum-core/src/domain/privacy.spec.ts index 1d360deb5d..37dcbd5067 100644 --- a/packages/rum/src/domain/record/privacy.spec.ts +++ b/packages/rum-core/src/domain/privacy.spec.ts @@ -5,7 +5,7 @@ import { PRIVACY_ATTR_VALUE_HIDDEN, PRIVACY_ATTR_VALUE_MASK, PRIVACY_ATTR_VALUE_MASK_USER_INPUT, -} from '../../constants' +} from '../constants' import { getNodeSelfPrivacyLevel, reducePrivacyLevel, getNodePrivacyLevel, shouldMaskNode } from './privacy' describe('getNodePrivacyLevel', () => { diff --git a/packages/rum/src/domain/record/privacy.ts b/packages/rum-core/src/domain/privacy.ts similarity index 98% rename from packages/rum/src/domain/record/privacy.ts rename to packages/rum-core/src/domain/privacy.ts index 28cabc2c49..c09bafe514 100644 --- a/packages/rum/src/domain/record/privacy.ts +++ b/packages/rum-core/src/domain/privacy.ts @@ -1,4 +1,4 @@ -import { isElementNode, getParentNode, isTextNode } from '@datadog/browser-rum-core' +import { isElementNode, getParentNode, isTextNode } from '../browser/htmlDomUtils' import { NodePrivacyLevel, PRIVACY_ATTR_NAME, @@ -12,7 +12,7 @@ import { PRIVACY_CLASS_HIDDEN, FORM_PRIVATE_TAG_NAMES, CENSORED_STRING_MARK, -} from '../../constants' +} from '../constants' const TEXT_MASKING_CHAR = 'x' diff --git a/packages/rum-core/src/index.ts b/packages/rum-core/src/index.ts index f65c4de15d..ace48acb03 100644 --- a/packages/rum-core/src/index.ts +++ b/packages/rum-core/src/index.ts @@ -36,3 +36,5 @@ export * from './browser/htmlDomUtils' export * from './browser/polyfills' export { getSessionReplayUrl } from './domain/getSessionReplayUrl' export { isLongDataUrl, sanitizeDataUrl, MAX_ATTRIBUTE_VALUE_CHAR_LENGTH } from './domain/resource/resourceUtils' +export * from './constants' +export { getNodePrivacyLevel, shouldMaskNode, getTextContent, NodePrivacyLevelCache } from './domain/privacy' diff --git a/packages/rum/src/domain/record/serialization/htmlAst.specHelper.ts b/packages/rum/src/domain/record/serialization/htmlAst.specHelper.ts index 3817d8d783..d8372ebf66 100644 --- a/packages/rum/src/domain/record/serialization/htmlAst.specHelper.ts +++ b/packages/rum/src/domain/record/serialization/htmlAst.specHelper.ts @@ -2,7 +2,7 @@ import type { RumConfiguration } from '@datadog/browser-rum-core' import { display, noop, objectValues } from '@datadog/browser-core' import type { SerializedNodeWithId } from '../../../types' import { serializeNodeWithId, SerializationContextStatus, createElementsScrollPositions } from '..' -import { NodePrivacyLevel, PRIVACY_ATTR_NAME } from '../../../constants' +import { NodePrivacyLevel, PRIVACY_ATTR_NAME } from '../../../../../rum-core/src/constants' export const makeHtmlDoc = (htmlContent: string, privacyTag: string) => { try { diff --git a/packages/rum/src/domain/record/serialization/serialization.types.ts b/packages/rum/src/domain/record/serialization/serialization.types.ts index bc5a35207c..e8242c2d9f 100644 --- a/packages/rum/src/domain/record/serialization/serialization.types.ts +++ b/packages/rum/src/domain/record/serialization/serialization.types.ts @@ -1,5 +1,5 @@ import type { RumConfiguration } from '@datadog/browser-rum-core' -import type { NodePrivacyLevel } from '../../../constants' +import type { NodePrivacyLevel } from '../../../../../rum-core/src/constants' import type { ElementsScrollPositions } from '../elementsScrollPositions' import type { ShadowRootsController } from '../shadowRootsController' diff --git a/packages/rum/src/domain/record/serialization/serializationUtils.spec.ts b/packages/rum/src/domain/record/serialization/serializationUtils.spec.ts index 18cac235b4..c3b7463ae2 100644 --- a/packages/rum/src/domain/record/serialization/serializationUtils.spec.ts +++ b/packages/rum/src/domain/record/serialization/serializationUtils.spec.ts @@ -1,5 +1,5 @@ import { isIE } from '@datadog/browser-core' -import { NodePrivacyLevel } from '../../../constants' +import { NodePrivacyLevel } from '../../../../../rum-core/src/constants' import { getSerializedNodeId, hasSerializedNode, diff --git a/packages/rum/src/domain/record/serialization/serializationUtils.ts b/packages/rum/src/domain/record/serialization/serializationUtils.ts index 218aa564b2..1cc2471129 100644 --- a/packages/rum/src/domain/record/serialization/serializationUtils.ts +++ b/packages/rum/src/domain/record/serialization/serializationUtils.ts @@ -1,8 +1,8 @@ import { buildUrl } from '@datadog/browser-core' import { getParentNode, isNodeShadowRoot } from '@datadog/browser-rum-core' -import type { NodePrivacyLevel } from '../../../constants' -import { CENSORED_STRING_MARK } from '../../../constants' -import { shouldMaskNode } from '../privacy' +import type { NodePrivacyLevel } from '../../../../../rum-core/src/constants' +import { CENSORED_STRING_MARK } from '../../../../../rum-core/src/constants' +import { shouldMaskNode } from '../../../../../rum-core/src/domain/privacy' import type { NodeWithSerializedNode } from './serialization.types' const serializedNodeIds = new WeakMap() diff --git a/packages/rum/src/domain/record/serialization/serializeAttribute.spec.ts b/packages/rum/src/domain/record/serialization/serializeAttribute.spec.ts index 9f446afc60..342bcdb9ed 100644 --- a/packages/rum/src/domain/record/serialization/serializeAttribute.spec.ts +++ b/packages/rum/src/domain/record/serialization/serializeAttribute.spec.ts @@ -6,7 +6,7 @@ import { DEFAULT_PROGRAMMATIC_ACTION_NAME_ATTRIBUTE, MAX_ATTRIBUTE_VALUE_CHAR_LENGTH, } from '@datadog/browser-rum-core' -import { NodePrivacyLevel, PRIVACY_ATTR_NAME } from '../../../constants' +import { NodePrivacyLevel, PRIVACY_ATTR_NAME } from '../../../../../rum-core/src/constants' import { serializeAttribute } from './serializeAttribute' const DEFAULT_CONFIGURATION = {} as RumConfiguration diff --git a/packages/rum/src/domain/record/serialization/serializeAttribute.ts b/packages/rum/src/domain/record/serialization/serializeAttribute.ts index c16a276350..ea745989f1 100644 --- a/packages/rum/src/domain/record/serialization/serializeAttribute.ts +++ b/packages/rum/src/domain/record/serialization/serializeAttribute.ts @@ -1,7 +1,12 @@ import { startsWith } from '@datadog/browser-core' import { STABLE_ATTRIBUTES, isLongDataUrl, sanitizeDataUrl } from '@datadog/browser-rum-core' import type { RumConfiguration } from '@datadog/browser-rum-core' -import { NodePrivacyLevel, PRIVACY_ATTR_NAME, CENSORED_STRING_MARK, CENSORED_IMG_MARK } from '../../../constants' +import { + NodePrivacyLevel, + PRIVACY_ATTR_NAME, + CENSORED_STRING_MARK, + CENSORED_IMG_MARK, +} from '../../../../../rum-core/src/constants' import { censoredImageForSize } from './serializationUtils' export function serializeAttribute( diff --git a/packages/rum/src/domain/record/serialization/serializeAttributes.ts b/packages/rum/src/domain/record/serialization/serializeAttributes.ts index 39de074d79..54f4406bfa 100644 --- a/packages/rum/src/domain/record/serialization/serializeAttributes.ts +++ b/packages/rum/src/domain/record/serialization/serializeAttributes.ts @@ -1,7 +1,7 @@ import { ExperimentalFeature, isExperimentalFeatureEnabled, isSafari } from '@datadog/browser-core' -import { NodePrivacyLevel } from '../../../constants' -import { shouldMaskNode } from '../privacy' +import { NodePrivacyLevel } from '../../../../../rum-core/src/constants' +import { shouldMaskNode } from '../../../../../rum-core/src/domain/privacy' import { getElementInputValue, switchToAbsoluteUrl, getValidTagName } from './serializationUtils' import type { SerializeOptions } from './serialization.types' import { SerializationContextStatus } from './serialization.types' diff --git a/packages/rum/src/domain/record/serialization/serializeNode.spec.ts b/packages/rum/src/domain/record/serialization/serializeNode.spec.ts index 461b6457da..3058a40179 100644 --- a/packages/rum/src/domain/record/serialization/serializeNode.spec.ts +++ b/packages/rum/src/domain/record/serialization/serializeNode.spec.ts @@ -15,7 +15,7 @@ import { PRIVACY_ATTR_VALUE_HIDDEN, PRIVACY_ATTR_VALUE_MASK, PRIVACY_ATTR_VALUE_MASK_USER_INPUT, -} from '../../../constants' +} from '../../../../../rum-core/src/constants' import type { ElementNode, SerializedNodeWithId } from '../../../types' import { NodeType } from '../../../types' import { appendElement } from '../../../../../rum-core/test' diff --git a/packages/rum/src/domain/record/serialization/serializeNode.ts b/packages/rum/src/domain/record/serialization/serializeNode.ts index cfde247201..36c7344d5e 100644 --- a/packages/rum/src/domain/record/serialization/serializeNode.ts +++ b/packages/rum/src/domain/record/serialization/serializeNode.ts @@ -11,8 +11,8 @@ import type { TextNode, } from '../../../types' import { NodeType } from '../../../types' -import { NodePrivacyLevel, PRIVACY_ATTR_NAME, PRIVACY_ATTR_VALUE_HIDDEN } from '../../../constants' -import { reducePrivacyLevel, getNodeSelfPrivacyLevel, getTextContent } from '../privacy' +import { NodePrivacyLevel, PRIVACY_ATTR_NAME, PRIVACY_ATTR_VALUE_HIDDEN } from '../../../../../rum-core/src/constants' +import { reducePrivacyLevel, getNodeSelfPrivacyLevel, getTextContent } from '../../../../../rum-core/src/domain/privacy' import { getSerializedNodeId, getValidTagName, setSerializedNodeId } from './serializationUtils' import type { SerializeOptions } from './serialization.types' import { serializeStyleSheets } from './serializeStyleSheets' diff --git a/packages/rum/src/domain/record/trackers/trackInput.spec.ts b/packages/rum/src/domain/record/trackers/trackInput.spec.ts index f7986d59b6..02e7ae01c6 100644 --- a/packages/rum/src/domain/record/trackers/trackInput.spec.ts +++ b/packages/rum/src/domain/record/trackers/trackInput.spec.ts @@ -3,7 +3,7 @@ import type { Clock } from '@datadog/browser-core/test' import { createNewEvent, mockClock } from '@datadog/browser-core/test' import type { RumConfiguration } from '@datadog/browser-rum-core' import { appendElement } from '../../../../../rum-core/test' -import { PRIVACY_ATTR_NAME, PRIVACY_ATTR_VALUE_MASK_USER_INPUT } from '../../../constants' +import { PRIVACY_ATTR_NAME, PRIVACY_ATTR_VALUE_MASK_USER_INPUT } from '../../../../../rum-core/src/constants' import { serializeDocument, SerializationContextStatus } from '../serialization' import { createElementsScrollPositions } from '../elementsScrollPositions' import { IncrementalSource, RecordType } from '../../../types' diff --git a/packages/rum/src/domain/record/trackers/trackInput.ts b/packages/rum/src/domain/record/trackers/trackInput.ts index 2e5232eb11..c1334e069b 100644 --- a/packages/rum/src/domain/record/trackers/trackInput.ts +++ b/packages/rum/src/domain/record/trackers/trackInput.ts @@ -1,11 +1,9 @@ import { instrumentSetter, assign, DOM_EVENT, addEventListeners, forEach, noop } from '@datadog/browser-core' -import { cssEscape } from '@datadog/browser-rum-core' +import { NodePrivacyLevel, getNodePrivacyLevel, shouldMaskNode, cssEscape } from '@datadog/browser-rum-core' import type { RumConfiguration } from '@datadog/browser-rum-core' -import { NodePrivacyLevel } from '../../../constants' import { IncrementalSource } from '../../../types' import type { BrowserIncrementalSnapshotRecord, InputData, InputState } from '../../../types' import { getEventTarget } from '../eventsUtils' -import { getNodePrivacyLevel, shouldMaskNode } from '../privacy' import { getElementInputValue, getSerializedNodeId, hasSerializedNode } from '../serialization' import { assembleIncrementalSnapshot } from '../assembly' import type { Tracker } from './types' diff --git a/packages/rum/src/domain/record/trackers/trackMediaInteraction.ts b/packages/rum/src/domain/record/trackers/trackMediaInteraction.ts index 899d443e81..13afc94fac 100644 --- a/packages/rum/src/domain/record/trackers/trackMediaInteraction.ts +++ b/packages/rum/src/domain/record/trackers/trackMediaInteraction.ts @@ -1,10 +1,9 @@ import { DOM_EVENT, addEventListeners } from '@datadog/browser-core' import type { RumConfiguration } from '@datadog/browser-rum-core' -import { NodePrivacyLevel } from '../../../constants' +import { NodePrivacyLevel, getNodePrivacyLevel } from '@datadog/browser-rum-core' import type { BrowserIncrementalSnapshotRecord, MediaInteractionData } from '../../../types' import { IncrementalSource, MediaInteractionType } from '../../../types' import { getEventTarget } from '../eventsUtils' -import { getNodePrivacyLevel } from '../privacy' import { getSerializedNodeId, hasSerializedNode } from '../serialization' import { assembleIncrementalSnapshot } from '../assembly' import type { Tracker } from './types' diff --git a/packages/rum/src/domain/record/trackers/trackMouseInteraction.ts b/packages/rum/src/domain/record/trackers/trackMouseInteraction.ts index 5cfe033b98..b5ad0895cd 100644 --- a/packages/rum/src/domain/record/trackers/trackMouseInteraction.ts +++ b/packages/rum/src/domain/record/trackers/trackMouseInteraction.ts @@ -1,11 +1,10 @@ import { assign, addEventListeners, DOM_EVENT } from '@datadog/browser-core' +import { getNodePrivacyLevel, NodePrivacyLevel } from '@datadog/browser-rum-core' import type { RumConfiguration } from '@datadog/browser-rum-core' -import { NodePrivacyLevel } from '../../../constants' import type { MouseInteraction, MouseInteractionData, BrowserIncrementalSnapshotRecord } from '../../../types' import { IncrementalSource, MouseInteractionType } from '../../../types' import { assembleIncrementalSnapshot } from '../assembly' import { getEventTarget } from '../eventsUtils' -import { getNodePrivacyLevel } from '../privacy' import { getSerializedNodeId, hasSerializedNode } from '../serialization' import type { RecordIds } from '../recordIds' import { tryToComputeCoordinates } from './trackMove' diff --git a/packages/rum/src/domain/record/trackers/trackMutation.spec.ts b/packages/rum/src/domain/record/trackers/trackMutation.spec.ts index 312cdbbcc9..bd3286556b 100644 --- a/packages/rum/src/domain/record/trackers/trackMutation.spec.ts +++ b/packages/rum/src/domain/record/trackers/trackMutation.spec.ts @@ -1,14 +1,14 @@ import { DefaultPrivacyLevel, isIE } from '@datadog/browser-core' import type { RumConfiguration } from '@datadog/browser-rum-core' import { collectAsyncCalls } from '@datadog/browser-core/test' -import { createMutationPayloadValidator } from '../../../../test' import { NodePrivacyLevel, PRIVACY_ATTR_NAME, PRIVACY_ATTR_VALUE_ALLOW, PRIVACY_ATTR_VALUE_MASK, PRIVACY_ATTR_VALUE_MASK_USER_INPUT, -} from '../../../constants' +} from '@datadog/browser-rum-core' +import { createMutationPayloadValidator } from '../../../../test' import type { AttributeMutation, Attributes, BrowserMutationPayload } from '../../../types' import { NodeType } from '../../../types' import { serializeDocument, SerializationContextStatus } from '../serialization' diff --git a/packages/rum/src/domain/record/trackers/trackMutation.ts b/packages/rum/src/domain/record/trackers/trackMutation.ts index 2463478917..5b705b5836 100644 --- a/packages/rum/src/domain/record/trackers/trackMutation.ts +++ b/packages/rum/src/domain/record/trackers/trackMutation.ts @@ -1,12 +1,14 @@ import { monitor, noop } from '@datadog/browser-core' -import type { RumConfiguration } from '@datadog/browser-rum-core' +import type { RumConfiguration, NodePrivacyLevelCache } from '@datadog/browser-rum-core' import { isNodeShadowHost, getMutationObserverConstructor, getParentNode, forEachChildNodes, + getNodePrivacyLevel, + getTextContent, + NodePrivacyLevel, } from '@datadog/browser-rum-core' -import { NodePrivacyLevel } from '../../../constants' import { IncrementalSource } from '../../../types' import type { BrowserMutationData, @@ -16,8 +18,6 @@ import type { TextMutation, BrowserIncrementalSnapshotRecord, } from '../../../types' -import type { NodePrivacyLevelCache } from '../privacy' -import { getNodePrivacyLevel, getTextContent } from '../privacy' import type { NodeWithSerializedNode } from '../serialization' import { getElementInputValue, diff --git a/packages/rum/src/domain/record/trackers/trackScroll.ts b/packages/rum/src/domain/record/trackers/trackScroll.ts index d06ae8c3a0..df00846722 100644 --- a/packages/rum/src/domain/record/trackers/trackScroll.ts +++ b/packages/rum/src/domain/record/trackers/trackScroll.ts @@ -1,13 +1,11 @@ import { DOM_EVENT, throttle, addEventListener } from '@datadog/browser-core' import type { RumConfiguration } from '@datadog/browser-rum-core' -import { getScrollX, getScrollY } from '@datadog/browser-rum-core' +import { getScrollX, getScrollY, getNodePrivacyLevel, NodePrivacyLevel } from '@datadog/browser-rum-core' import type { ElementsScrollPositions } from '../elementsScrollPositions' import { getEventTarget } from '../eventsUtils' -import { getNodePrivacyLevel } from '../privacy' import { getSerializedNodeId, hasSerializedNode } from '../serialization' import { IncrementalSource } from '../../../types' import type { BrowserIncrementalSnapshotRecord, ScrollData } from '../../../types' -import { NodePrivacyLevel } from '../../../constants' import { assembleIncrementalSnapshot } from '../assembly' import type { Tracker } from './types' diff --git a/packages/rum/src/domain/record/trackers/trackers.specHelper.ts b/packages/rum/src/domain/record/trackers/trackers.specHelper.ts index c51f934bf7..08fd5de530 100644 --- a/packages/rum/src/domain/record/trackers/trackers.specHelper.ts +++ b/packages/rum/src/domain/record/trackers/trackers.specHelper.ts @@ -1,7 +1,7 @@ import { noop } from '@datadog/browser-core' import type { RumConfiguration } from '@datadog/browser-rum-core' import type { ShadowRootsController } from '../shadowRootsController' -import { NodePrivacyLevel } from '../../../constants' +import { NodePrivacyLevel } from '../../../../../rum-core/src/constants' export const DEFAULT_SHADOW_ROOT_CONTROLLER: ShadowRootsController = { flush: noop, diff --git a/packages/rum/src/entries/internal.ts b/packages/rum/src/entries/internal.ts index 8b78b62997..984dc8fde6 100644 --- a/packages/rum/src/entries/internal.ts +++ b/packages/rum/src/entries/internal.ts @@ -6,7 +6,12 @@ * changes. */ export type { TimeStamp } from '@datadog/browser-core' -export { PRIVACY_ATTR_NAME, PRIVACY_ATTR_VALUE_HIDDEN, PRIVACY_CLASS_HIDDEN, NodePrivacyLevel } from '../constants' +export { + PRIVACY_ATTR_NAME, + PRIVACY_ATTR_VALUE_HIDDEN, + PRIVACY_CLASS_HIDDEN, + NodePrivacyLevel, +} from '../../../rum-core/src/constants' export * from '../types' From 4406479608975967610920d87395141c9cd1a7fb Mon Sep 17 00:00:00 2001 From: cy-moi Date: Mon, 15 Apr 2024 16:00:09 +0200 Subject: [PATCH 02/32] Fix import from browser-rum-core --- packages/rum-core/src/index.ts | 2 +- .../record/serialization/htmlAst.specHelper.ts | 2 +- .../record/serialization/serialization.types.ts | 3 +-- .../serialization/serializationUtils.spec.ts | 2 +- .../record/serialization/serializationUtils.ts | 6 ++---- .../serialization/serializeAttribute.spec.ts | 3 ++- .../record/serialization/serializeAttribute.ts | 8 +++++--- .../record/serialization/serializeAttributes.ts | 3 +-- .../record/serialization/serializeNode.spec.ts | 2 +- .../domain/record/serialization/serializeNode.ts | 14 +++++++++++--- .../src/domain/record/trackers/trackInput.spec.ts | 2 +- .../domain/record/trackers/trackers.specHelper.ts | 2 +- packages/rum/src/entries/internal.ts | 2 +- 13 files changed, 29 insertions(+), 22 deletions(-) diff --git a/packages/rum-core/src/index.ts b/packages/rum-core/src/index.ts index ace48acb03..7345b8f41a 100644 --- a/packages/rum-core/src/index.ts +++ b/packages/rum-core/src/index.ts @@ -37,4 +37,4 @@ export * from './browser/polyfills' export { getSessionReplayUrl } from './domain/getSessionReplayUrl' export { isLongDataUrl, sanitizeDataUrl, MAX_ATTRIBUTE_VALUE_CHAR_LENGTH } from './domain/resource/resourceUtils' export * from './constants' -export { getNodePrivacyLevel, shouldMaskNode, getTextContent, NodePrivacyLevelCache } from './domain/privacy' +export * from './domain/privacy' diff --git a/packages/rum/src/domain/record/serialization/htmlAst.specHelper.ts b/packages/rum/src/domain/record/serialization/htmlAst.specHelper.ts index d8372ebf66..c50cc71ca9 100644 --- a/packages/rum/src/domain/record/serialization/htmlAst.specHelper.ts +++ b/packages/rum/src/domain/record/serialization/htmlAst.specHelper.ts @@ -1,8 +1,8 @@ import type { RumConfiguration } from '@datadog/browser-rum-core' +import { NodePrivacyLevel, PRIVACY_ATTR_NAME } from '@datadog/browser-rum-core' import { display, noop, objectValues } from '@datadog/browser-core' import type { SerializedNodeWithId } from '../../../types' import { serializeNodeWithId, SerializationContextStatus, createElementsScrollPositions } from '..' -import { NodePrivacyLevel, PRIVACY_ATTR_NAME } from '../../../../../rum-core/src/constants' export const makeHtmlDoc = (htmlContent: string, privacyTag: string) => { try { diff --git a/packages/rum/src/domain/record/serialization/serialization.types.ts b/packages/rum/src/domain/record/serialization/serialization.types.ts index e8242c2d9f..452aee1a03 100644 --- a/packages/rum/src/domain/record/serialization/serialization.types.ts +++ b/packages/rum/src/domain/record/serialization/serialization.types.ts @@ -1,5 +1,4 @@ -import type { RumConfiguration } from '@datadog/browser-rum-core' -import type { NodePrivacyLevel } from '../../../../../rum-core/src/constants' +import type { RumConfiguration, NodePrivacyLevel } from '@datadog/browser-rum-core' import type { ElementsScrollPositions } from '../elementsScrollPositions' import type { ShadowRootsController } from '../shadowRootsController' diff --git a/packages/rum/src/domain/record/serialization/serializationUtils.spec.ts b/packages/rum/src/domain/record/serialization/serializationUtils.spec.ts index c3b7463ae2..6ee0f8ced8 100644 --- a/packages/rum/src/domain/record/serialization/serializationUtils.spec.ts +++ b/packages/rum/src/domain/record/serialization/serializationUtils.spec.ts @@ -1,5 +1,5 @@ import { isIE } from '@datadog/browser-core' -import { NodePrivacyLevel } from '../../../../../rum-core/src/constants' +import { NodePrivacyLevel } from '@datadog/browser-rum-core' import { getSerializedNodeId, hasSerializedNode, diff --git a/packages/rum/src/domain/record/serialization/serializationUtils.ts b/packages/rum/src/domain/record/serialization/serializationUtils.ts index 1cc2471129..57f6ac6dba 100644 --- a/packages/rum/src/domain/record/serialization/serializationUtils.ts +++ b/packages/rum/src/domain/record/serialization/serializationUtils.ts @@ -1,8 +1,6 @@ import { buildUrl } from '@datadog/browser-core' -import { getParentNode, isNodeShadowRoot } from '@datadog/browser-rum-core' -import type { NodePrivacyLevel } from '../../../../../rum-core/src/constants' -import { CENSORED_STRING_MARK } from '../../../../../rum-core/src/constants' -import { shouldMaskNode } from '../../../../../rum-core/src/domain/privacy' +import { getParentNode, isNodeShadowRoot, CENSORED_STRING_MARK, shouldMaskNode } from '@datadog/browser-rum-core' +import type { NodePrivacyLevel } from '@datadog/browser-rum-core' import type { NodeWithSerializedNode } from './serialization.types' const serializedNodeIds = new WeakMap() diff --git a/packages/rum/src/domain/record/serialization/serializeAttribute.spec.ts b/packages/rum/src/domain/record/serialization/serializeAttribute.spec.ts index 342bcdb9ed..6034ca5199 100644 --- a/packages/rum/src/domain/record/serialization/serializeAttribute.spec.ts +++ b/packages/rum/src/domain/record/serialization/serializeAttribute.spec.ts @@ -5,8 +5,9 @@ import { STABLE_ATTRIBUTES, DEFAULT_PROGRAMMATIC_ACTION_NAME_ATTRIBUTE, MAX_ATTRIBUTE_VALUE_CHAR_LENGTH, + NodePrivacyLevel, + PRIVACY_ATTR_NAME, } from '@datadog/browser-rum-core' -import { NodePrivacyLevel, PRIVACY_ATTR_NAME } from '../../../../../rum-core/src/constants' import { serializeAttribute } from './serializeAttribute' const DEFAULT_CONFIGURATION = {} as RumConfiguration diff --git a/packages/rum/src/domain/record/serialization/serializeAttribute.ts b/packages/rum/src/domain/record/serialization/serializeAttribute.ts index ea745989f1..4c6991c5e1 100644 --- a/packages/rum/src/domain/record/serialization/serializeAttribute.ts +++ b/packages/rum/src/domain/record/serialization/serializeAttribute.ts @@ -1,12 +1,14 @@ import { startsWith } from '@datadog/browser-core' -import { STABLE_ATTRIBUTES, isLongDataUrl, sanitizeDataUrl } from '@datadog/browser-rum-core' -import type { RumConfiguration } from '@datadog/browser-rum-core' import { NodePrivacyLevel, PRIVACY_ATTR_NAME, CENSORED_STRING_MARK, CENSORED_IMG_MARK, -} from '../../../../../rum-core/src/constants' + STABLE_ATTRIBUTES, + isLongDataUrl, + sanitizeDataUrl, +} from '@datadog/browser-rum-core' +import type { RumConfiguration } from '@datadog/browser-rum-core' import { censoredImageForSize } from './serializationUtils' export function serializeAttribute( diff --git a/packages/rum/src/domain/record/serialization/serializeAttributes.ts b/packages/rum/src/domain/record/serialization/serializeAttributes.ts index 54f4406bfa..409c3085e3 100644 --- a/packages/rum/src/domain/record/serialization/serializeAttributes.ts +++ b/packages/rum/src/domain/record/serialization/serializeAttributes.ts @@ -1,7 +1,6 @@ import { ExperimentalFeature, isExperimentalFeatureEnabled, isSafari } from '@datadog/browser-core' -import { NodePrivacyLevel } from '../../../../../rum-core/src/constants' -import { shouldMaskNode } from '../../../../../rum-core/src/domain/privacy' +import { NodePrivacyLevel, shouldMaskNode } from '@datadog/browser-rum-core' import { getElementInputValue, switchToAbsoluteUrl, getValidTagName } from './serializationUtils' import type { SerializeOptions } from './serialization.types' import { SerializationContextStatus } from './serialization.types' diff --git a/packages/rum/src/domain/record/serialization/serializeNode.spec.ts b/packages/rum/src/domain/record/serialization/serializeNode.spec.ts index 3058a40179..5077220af8 100644 --- a/packages/rum/src/domain/record/serialization/serializeNode.spec.ts +++ b/packages/rum/src/domain/record/serialization/serializeNode.spec.ts @@ -15,7 +15,7 @@ import { PRIVACY_ATTR_VALUE_HIDDEN, PRIVACY_ATTR_VALUE_MASK, PRIVACY_ATTR_VALUE_MASK_USER_INPUT, -} from '../../../../../rum-core/src/constants' +} from '@datadog/browser-rum-core' import type { ElementNode, SerializedNodeWithId } from '../../../types' import { NodeType } from '../../../types' import { appendElement } from '../../../../../rum-core/test' diff --git a/packages/rum/src/domain/record/serialization/serializeNode.ts b/packages/rum/src/domain/record/serialization/serializeNode.ts index 36c7344d5e..84726c55bc 100644 --- a/packages/rum/src/domain/record/serialization/serializeNode.ts +++ b/packages/rum/src/domain/record/serialization/serializeNode.ts @@ -1,4 +1,14 @@ -import { isNodeShadowRoot, hasChildNodes, forEachChildNodes } from '@datadog/browser-rum-core' +import { + reducePrivacyLevel, + getNodeSelfPrivacyLevel, + getTextContent, + isNodeShadowRoot, + hasChildNodes, + forEachChildNodes, + NodePrivacyLevel, + PRIVACY_ATTR_NAME, + PRIVACY_ATTR_VALUE_HIDDEN, +} from '@datadog/browser-rum-core' import { assign } from '@datadog/browser-core' import type { DocumentFragmentNode, @@ -11,8 +21,6 @@ import type { TextNode, } from '../../../types' import { NodeType } from '../../../types' -import { NodePrivacyLevel, PRIVACY_ATTR_NAME, PRIVACY_ATTR_VALUE_HIDDEN } from '../../../../../rum-core/src/constants' -import { reducePrivacyLevel, getNodeSelfPrivacyLevel, getTextContent } from '../../../../../rum-core/src/domain/privacy' import { getSerializedNodeId, getValidTagName, setSerializedNodeId } from './serializationUtils' import type { SerializeOptions } from './serialization.types' import { serializeStyleSheets } from './serializeStyleSheets' diff --git a/packages/rum/src/domain/record/trackers/trackInput.spec.ts b/packages/rum/src/domain/record/trackers/trackInput.spec.ts index 02e7ae01c6..394fe11d12 100644 --- a/packages/rum/src/domain/record/trackers/trackInput.spec.ts +++ b/packages/rum/src/domain/record/trackers/trackInput.spec.ts @@ -2,8 +2,8 @@ import { DefaultPrivacyLevel, isIE } from '@datadog/browser-core' import type { Clock } from '@datadog/browser-core/test' import { createNewEvent, mockClock } from '@datadog/browser-core/test' import type { RumConfiguration } from '@datadog/browser-rum-core' +import { PRIVACY_ATTR_NAME, PRIVACY_ATTR_VALUE_MASK_USER_INPUT } from '@datadog/browser-rum-core' import { appendElement } from '../../../../../rum-core/test' -import { PRIVACY_ATTR_NAME, PRIVACY_ATTR_VALUE_MASK_USER_INPUT } from '../../../../../rum-core/src/constants' import { serializeDocument, SerializationContextStatus } from '../serialization' import { createElementsScrollPositions } from '../elementsScrollPositions' import { IncrementalSource, RecordType } from '../../../types' diff --git a/packages/rum/src/domain/record/trackers/trackers.specHelper.ts b/packages/rum/src/domain/record/trackers/trackers.specHelper.ts index 08fd5de530..3c4c188319 100644 --- a/packages/rum/src/domain/record/trackers/trackers.specHelper.ts +++ b/packages/rum/src/domain/record/trackers/trackers.specHelper.ts @@ -1,7 +1,7 @@ import { noop } from '@datadog/browser-core' +import { NodePrivacyLevel } from '@datadog/browser-rum-core' import type { RumConfiguration } from '@datadog/browser-rum-core' import type { ShadowRootsController } from '../shadowRootsController' -import { NodePrivacyLevel } from '../../../../../rum-core/src/constants' export const DEFAULT_SHADOW_ROOT_CONTROLLER: ShadowRootsController = { flush: noop, diff --git a/packages/rum/src/entries/internal.ts b/packages/rum/src/entries/internal.ts index 984dc8fde6..b93d97641c 100644 --- a/packages/rum/src/entries/internal.ts +++ b/packages/rum/src/entries/internal.ts @@ -11,7 +11,7 @@ export { PRIVACY_ATTR_VALUE_HIDDEN, PRIVACY_CLASS_HIDDEN, NodePrivacyLevel, -} from '../../../rum-core/src/constants' +} from '@datadog/browser-rum-core' export * from '../types' From 1357c7e8afcd6bbd31f9c989295a69b415b79f3d Mon Sep 17 00:00:00 2001 From: cy-moi Date: Mon, 15 Apr 2024 16:47:58 +0200 Subject: [PATCH 03/32] Add enable_privacy_for_action_name into configuration --- packages/rum-core/src/domain/configuration.spec.ts | 1 + packages/rum-core/src/domain/configuration.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/rum-core/src/domain/configuration.spec.ts b/packages/rum-core/src/domain/configuration.spec.ts index d47fd8a5be..515a093dd5 100644 --- a/packages/rum-core/src/domain/configuration.spec.ts +++ b/packages/rum-core/src/domain/configuration.spec.ts @@ -468,6 +468,7 @@ describe('serializeRumConfiguration', () => { start_session_replay_recording_manually: true, action_name_attribute: 'test-id', default_privacy_level: 'allow', + enable_privacy_for_action_name: false, track_resources: true, track_long_task: true, use_worker_url: true, diff --git a/packages/rum-core/src/domain/configuration.ts b/packages/rum-core/src/domain/configuration.ts index c8b6d29c23..8bae3fcfc1 100644 --- a/packages/rum-core/src/domain/configuration.ts +++ b/packages/rum-core/src/domain/configuration.ts @@ -209,6 +209,7 @@ export function serializeRumConfiguration(configuration: RumInitConfiguration) { Array.isArray(configuration.allowedTracingUrls) && configuration.allowedTracingUrls.length > 0, selected_tracing_propagators: getSelectedTracingPropagators(configuration), default_privacy_level: configuration.defaultPrivacyLevel, + enable_privacy_for_action_name: configuration.enablePrivacyForActionName, use_excluded_activity_urls: Array.isArray(configuration.excludedActivityUrls) && configuration.excludedActivityUrls.length > 0, use_worker_url: !!configuration.workerUrl, From 42fc2595896ae778b1a4f4adf0c4ed6c4e03b705 Mon Sep 17 00:00:00 2001 From: cy-moi Date: Wed, 17 Apr 2024 11:03:59 +0200 Subject: [PATCH 04/32] Sync types with rum-events-format --- .../core/src/domain/telemetry/telemetryEvent.types.ts | 9 ++++++--- packages/rum-core/src/domain/configuration.spec.ts | 1 + packages/rum-core/src/rumEvent.types.ts | 4 ++++ rum-events-format | 2 +- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/core/src/domain/telemetry/telemetryEvent.types.ts b/packages/core/src/domain/telemetry/telemetryEvent.types.ts index a2b415be2c..cb0fc2e50e 100644 --- a/packages/core/src/domain/telemetry/telemetryEvent.types.ts +++ b/packages/core/src/domain/telemetry/telemetryEvent.types.ts @@ -193,6 +193,10 @@ export type TelemetryConfigurationEvent = CommonTelemetryProperties & { * Session replay default privacy level */ default_privacy_level?: string + /** + * Privacy control for action name + */ + enable_privacy_for_action_name?: boolean /** * Whether the request origins list to ignore when computing the page activity is used */ @@ -302,7 +306,7 @@ export type TelemetryConfigurationEvent = CommonTelemetryProperties & { */ batch_upload_frequency?: number /** - * Maximum number of batches processed sequencially without a delay + * Maximum number of batches processed sequentially without a delay */ batch_processing_level?: number /** @@ -329,9 +333,8 @@ export type TelemetryConfigurationEvent = CommonTelemetryProperties & { * The threshold used for iOS App Hangs monitoring (in milliseconds) */ app_hang_threshold?: number - /** - * Either forward logs to the PCI compliant intake or not + * Whether logs are sent to the PCI-compliant intake */ use_pci_intake?: boolean /** diff --git a/packages/rum-core/src/domain/configuration.spec.ts b/packages/rum-core/src/domain/configuration.spec.ts index 515a093dd5..00707834ba 100644 --- a/packages/rum-core/src/domain/configuration.spec.ts +++ b/packages/rum-core/src/domain/configuration.spec.ts @@ -429,6 +429,7 @@ describe('serializeRumConfiguration', () => { traceSampleRate: 50, traceContextInjection: TraceContextInjection.ALL, defaultPrivacyLevel: 'allow', + enablePrivacyForActionName: false, subdomain: 'foo', sessionReplaySampleRate: 60, startSessionReplayRecordingManually: true, diff --git a/packages/rum-core/src/rumEvent.types.ts b/packages/rum-core/src/rumEvent.types.ts index a6d2170d40..fb1bd2e87d 100644 --- a/packages/rum-core/src/rumEvent.types.ts +++ b/packages/rum-core/src/rumEvent.types.ts @@ -395,6 +395,10 @@ export type RumErrorEvent = CommonProperties & readonly disposition?: 'enforce' | 'report' [k: string]: unknown } + /** + * Time since application start when error happened (in milliseconds) + */ + readonly time_since_app_start?: number [k: string]: unknown } /** diff --git a/rum-events-format b/rum-events-format index be033e3251..844e2f49a4 160000 --- a/rum-events-format +++ b/rum-events-format @@ -1 +1 @@ -Subproject commit be033e3251da4a20891a774f9843c489a693c80d +Subproject commit 844e2f49a4aa7779245eda1bbfa537bf8c6dd399 From e6d225cae2d70ce0c20dbf03ef20fe1549957d65 Mon Sep 17 00:00:00 2001 From: cy-moi Date: Wed, 17 Apr 2024 12:01:11 +0200 Subject: [PATCH 05/32] =?UTF-8?q?=F0=9F=90=9B=20Fix=20bugs=20found=20in=20?= =?UTF-8?q?comments?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../action/getActionNameFromElement.spec.ts | 6 +- .../domain/action/getActionNameFromElement.ts | 17 +++--- .../domain/action/trackClickActions.spec.ts | 4 +- .../src/domain/action/trackClickActions.ts | 57 +++++++++++-------- 4 files changed, 48 insertions(+), 36 deletions(-) diff --git a/packages/rum-core/src/domain/action/getActionNameFromElement.spec.ts b/packages/rum-core/src/domain/action/getActionNameFromElement.spec.ts index a7357b3b84..5928dd74a8 100644 --- a/packages/rum-core/src/domain/action/getActionNameFromElement.spec.ts +++ b/packages/rum-core/src/domain/action/getActionNameFromElement.spec.ts @@ -400,7 +400,7 @@ describe('getActionNameFromElement', () => { 'data-test-id', true ) - ).toBe('Masked Element') + ).toEqual({ name: 'Masked Element', masked: true }) }) it('extracts default attribute text when privacyEnabledActionName is true', () => { @@ -414,7 +414,7 @@ describe('getActionNameFromElement', () => { undefined, true ) - ).toBe('foo') + ).toEqual({ name: 'foo', masked: false }) }) it('extracts user defined attribute text when privacyEnabledActionName is true', () => { @@ -428,7 +428,7 @@ describe('getActionNameFromElement', () => { 'data-test-id', true ) - ).toBe('foo') + ).toEqual({ name: 'foo', masked: false }) }) }) }) diff --git a/packages/rum-core/src/domain/action/getActionNameFromElement.ts b/packages/rum-core/src/domain/action/getActionNameFromElement.ts index 6b102a4ef3..bfd3b6303b 100644 --- a/packages/rum-core/src/domain/action/getActionNameFromElement.ts +++ b/packages/rum-core/src/domain/action/getActionNameFromElement.ts @@ -11,16 +11,18 @@ export function getActionNameFromElement( element: Element, userProgrammaticAttribute?: string, privacyEnabledForActionName?: boolean -): string { +): { name: string; masked?: boolean } | string { + const definedActionNameFromElement = + getActionNameFromElementProgrammatically(element, DEFAULT_PROGRAMMATIC_ACTION_NAME_ATTRIBUTE) || + (userProgrammaticAttribute && getActionNameFromElementProgrammatically(element, userProgrammaticAttribute)) // Only get the defined action name if // * privacy is enabled for action name // * and privacy is enabled for the Node or globally if (privacyEnabledForActionName) { - return ( - getActionNameFromElementProgrammatically(element, DEFAULT_PROGRAMMATIC_ACTION_NAME_ATTRIBUTE) || - (userProgrammaticAttribute && getActionNameFromElementProgrammatically(element, userProgrammaticAttribute)) || - ACTION_NAME_PLACEHOLDER - ) + return { + name: definedActionNameFromElement || ACTION_NAME_PLACEHOLDER, + masked: definedActionNameFromElement ? false : true, + } } // Proceed to get the action name in two steps: // * first, get the name programmatically, explicitly defined by the user. @@ -29,8 +31,7 @@ export function getActionNameFromElement( // * if no name is found this way, use strategies returning less accurate names as a fallback. // Those are much likely to succeed. return ( - getActionNameFromElementProgrammatically(element, DEFAULT_PROGRAMMATIC_ACTION_NAME_ATTRIBUTE) || - (userProgrammaticAttribute && getActionNameFromElementProgrammatically(element, userProgrammaticAttribute)) || + definedActionNameFromElement || getActionNameFromElementForStrategies(element, userProgrammaticAttribute, priorityStrategies) || getActionNameFromElementForStrategies(element, userProgrammaticAttribute, fallbackStrategies) || '' diff --git a/packages/rum-core/src/domain/action/trackClickActions.spec.ts b/packages/rum-core/src/domain/action/trackClickActions.spec.ts index 3ab535a9ad..fe4bea6734 100644 --- a/packages/rum-core/src/domain/action/trackClickActions.spec.ts +++ b/packages/rum-core/src/domain/action/trackClickActions.spec.ts @@ -232,7 +232,7 @@ describe('trackClickActions', () => { expect(events.length).toBe(1) }) - fdescribe('with enablePrivacyForActionName false', () => { + describe('with enablePrivacyForActionName false', () => { it('extracts action name when default privacy level is mask', () => { setupBuilder.withConfiguration({ defaultPrivacyLevel: DefaultPrivacyLevel.MASK, @@ -248,7 +248,7 @@ describe('trackClickActions', () => { }) }) - fdescribe('with enablePrivacyForActionName true', () => { + describe('with enablePrivacyForActionName true', () => { it('get placeholder when defaultPrivacyLevel is mask without programmatically declared action name', () => { setupBuilder.withConfiguration({ defaultPrivacyLevel: DefaultPrivacyLevel.MASK, diff --git a/packages/rum-core/src/domain/action/trackClickActions.ts b/packages/rum-core/src/domain/action/trackClickActions.ts index 142100c916..a2b5c22589 100644 --- a/packages/rum-core/src/domain/action/trackClickActions.ts +++ b/packages/rum-core/src/domain/action/trackClickActions.ts @@ -25,7 +25,7 @@ import { getSelectorFromElement } from '../getSelectorFromElement' import { getNodePrivacyLevel } from '../privacy' import type { ClickChain } from './clickChain' import { createClickChain } from './clickChain' -import { getActionNameFromElement, ACTION_NAME_PLACEHOLDER } from './getActionNameFromElement' +import { getActionNameFromElement } from './getActionNameFromElement' import type { MouseEventOnElement, UserActivity } from './listenActionEvents' import { listenActionEvents } from './listenActionEvents' import { computeFrustration } from './computeFrustration' @@ -82,24 +82,27 @@ export function trackClickActions( lifeCycle.subscribe(LifeCycleEventType.VIEW_ENDED, stopClickChain) const { stop: stopActionEventsListener } = listenActionEvents<{ - clickActionBase: ClickActionBase + clickActionBase: ClickActionBase | undefined hadActivityOnPointerDown: () => boolean }>(configuration, { onPointerDown: (pointerDownEvent) => processPointerDown(configuration, lifeCycle, domMutationObservable, pointerDownEvent), - onPointerUp: ({ clickActionBase, hadActivityOnPointerDown }, startEvent, getUserActivity) => - startClickAction( - configuration, - lifeCycle, - domMutationObservable, - history, - stopObservable, - appendClickToClickChain, - clickActionBase, - startEvent, - getUserActivity, - hadActivityOnPointerDown - ), + onPointerUp: ({ clickActionBase, hadActivityOnPointerDown }, startEvent, getUserActivity) => { + if (clickActionBase) { + startClickAction( + configuration, + lifeCycle, + domMutationObservable, + history, + stopObservable, + appendClickToClickChain, + clickActionBase, + startEvent, + getUserActivity, + hadActivityOnPointerDown + ) + } + }, }) const actionContexts: ActionContexts = { @@ -213,17 +216,26 @@ function startClickAction( type ClickActionBase = Pick -function computeClickActionBase(event: MouseEventOnElement, configuration: RumConfiguration): ClickActionBase { +function computeClickActionBase( + event: MouseEventOnElement, + configuration: RumConfiguration +): ClickActionBase | undefined { const rect = event.target.getBoundingClientRect() + const privacyLevel = getNodePrivacyLevel(event.target, configuration.defaultPrivacyLevel) + + // When the node is set to hidden, we do not track click action + if (privacyLevel === NodePrivacyLevel.HIDDEN && configuration.enablePrivacyForActionName) { + return + } + const privacyEnabledForActionName = - (getNodePrivacyLevel(event.target, configuration.defaultPrivacyLevel) === NodePrivacyLevel.HIDDEN || - configuration.defaultPrivacyLevel === DefaultPrivacyLevel.MASK) && - configuration.enablePrivacyForActionName - const actionName = getActionNameFromElement( + privacyLevel === DefaultPrivacyLevel.MASK && configuration.enablePrivacyForActionName + const actionNameResult = getActionNameFromElement( event.target, configuration.actionNameAttribute, privacyEnabledForActionName ) + const target = { width: Math.round(rect.width), height: Math.round(rect.height), @@ -232,14 +244,13 @@ function computeClickActionBase(event: MouseEventOnElement, configuration: RumCo return { type: ActionType.CLICK, - target: - actionName === ACTION_NAME_PLACEHOLDER && privacyEnabledForActionName ? assign({ masked: true }, target) : target, + target: typeof actionNameResult === 'string' ? target : assign({ masked: actionNameResult.masked }, target), position: { // Use clientX and Y because for SVG element offsetX and Y are relatives to the element x: Math.round(event.clientX - rect.left), y: Math.round(event.clientY - rect.top), }, - name: actionName, + name: typeof actionNameResult === 'string' ? actionNameResult : actionNameResult.name, } } From 78a5adf0b56d272457ee03fe6be0c3aba2efe372 Mon Sep 17 00:00:00 2001 From: cy-moi Date: Wed, 17 Apr 2024 19:17:56 +0200 Subject: [PATCH 06/32] Fix hidden unit test --- .../src/domain/action/trackClickActions.spec.ts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/packages/rum-core/src/domain/action/trackClickActions.spec.ts b/packages/rum-core/src/domain/action/trackClickActions.spec.ts index fe4bea6734..97c0cc532e 100644 --- a/packages/rum-core/src/domain/action/trackClickActions.spec.ts +++ b/packages/rum-core/src/domain/action/trackClickActions.spec.ts @@ -249,22 +249,19 @@ describe('trackClickActions', () => { }) describe('with enablePrivacyForActionName true', () => { - it('get placeholder when defaultPrivacyLevel is mask without programmatically declared action name', () => { + it('does not track click actions when html override set hidden', () => { setupBuilder.withConfiguration({ - defaultPrivacyLevel: DefaultPrivacyLevel.MASK, enablePrivacyForActionName: true, }) + button.setAttribute('data-dd-privacy', 'hidden') + const { clock } = setupBuilder.build() emulateClick({ activity: {} }) - expect(findActionId()).not.toBeUndefined() clock.tick(EXPIRE_DELAY) - expect(events.length).toBe(1) - expect(events[0].name).toBe('Masked Element') + expect(events.length).toBe(0) }) - - it('get placeholder when html override is hidden', () => { - input.setAttribute('data-dd-privacy', 'hidden') + it('get placeholder when defaultPrivacyLevel is mask without programmatically declared action name', () => { setupBuilder.withConfiguration({ defaultPrivacyLevel: DefaultPrivacyLevel.MASK, enablePrivacyForActionName: true, From f4e1a286384ee98324192fce29c11a1a0c90e248 Mon Sep 17 00:00:00 2001 From: cy-moi Date: Mon, 22 Apr 2024 14:00:57 +0200 Subject: [PATCH 07/32] Fix getActionName return type --- .../action/getActionNameFromElement.spec.ts | 516 +++++++++--------- .../domain/action/getActionNameFromElement.ts | 15 +- .../src/domain/action/trackClickActions.ts | 7 +- 3 files changed, 266 insertions(+), 272 deletions(-) diff --git a/packages/rum-core/src/domain/action/getActionNameFromElement.spec.ts b/packages/rum-core/src/domain/action/getActionNameFromElement.spec.ts index 5928dd74a8..cf73a24e3f 100644 --- a/packages/rum-core/src/domain/action/getActionNameFromElement.spec.ts +++ b/packages/rum-core/src/domain/action/getActionNameFromElement.spec.ts @@ -3,390 +3,382 @@ import { getActionNameFromElement } from './getActionNameFromElement' describe('getActionNameFromElement', () => { it('extracts the textual content of an element', () => { - expect(getActionNameFromElement(appendElement('
Foo
bar
'))).toBe('Foo bar') + const { name } = getActionNameFromElement(appendElement('
Foo
bar
')) + expect(name).toBe('Foo bar') }) it('extracts the text of an input button', () => { - expect(getActionNameFromElement(appendElement(''))).toBe('Click') + const { name } = getActionNameFromElement(appendElement('')) + expect(name).toBe('Click') }) it('extracts the alt text of an image', () => { - expect(getActionNameFromElement(appendElement('bar'))).toBe('bar') + const { name } = getActionNameFromElement(appendElement('bar')) + expect(name).toBe('bar') }) it('extracts the title text of an image', () => { - expect(getActionNameFromElement(appendElement(''))).toBe('foo') + const { name } = getActionNameFromElement(appendElement('')) + expect(name).toBe('foo') }) it('extracts the text of an aria-label attribute', () => { - expect(getActionNameFromElement(appendElement(''))).toBe('Foo') + const { name } = getActionNameFromElement(appendElement('')) + expect(name).toBe('Foo') }) it('gets the parent element textual content if everything else fails', () => { - expect(getActionNameFromElement(appendElement('
Foo
'))).toBe('Foo') + const { name } = getActionNameFromElement(appendElement('
Foo
')) + expect(name).toBe('Foo') }) it("doesn't get the value of a text input", () => { - expect(getActionNameFromElement(appendElement(''))).toBe('') + const { name } = getActionNameFromElement(appendElement('')) + expect(name).toBe('') }) it("doesn't get the value of a password input", () => { - expect(getActionNameFromElement(appendElement(''))).toBe('') + const { name } = getActionNameFromElement(appendElement('')) + expect(name).toBe('') }) it('limits the name length to a reasonable size', () => { - expect( - getActionNameFromElement( - appendElement( - '
Foooooooooooooooooo baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaar baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaz
' - ) + const { name } = getActionNameFromElement( + appendElement( + '
Foooooooooooooooooo baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaar baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaz
' ) - ).toBe('Foooooooooooooooooo baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa [...]') + ) + expect(name).toBe( + 'Foooooooooooooooooo baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa [...]' + ) }) it('normalize white spaces', () => { - expect(getActionNameFromElement(appendElement('
foo\tbar\n\n baz
'))).toBe('foo bar baz') + const { name } = getActionNameFromElement(appendElement('
foo\tbar\n\n baz
')) + expect(name).toBe('foo bar baz') }) it('ignores the inline script textual content', () => { - expect(getActionNameFromElement(appendElement("
b
"))).toBe('b') + const { name } = getActionNameFromElement(appendElement("
b
")) + expect(name).toBe('b') }) it('extracts text from SVG elements', () => { - expect(getActionNameFromElement(appendElement('foo bar'))).toBe('foo bar') + const { name } = getActionNameFromElement(appendElement('foo bar')) + expect(name).toBe('foo bar') }) it('extracts text from an associated label', () => { - expect( - getActionNameFromElement( - appendElement(` -
- -
ignored
- -
- `) - ) - ).toBe('label text') + const { name } = getActionNameFromElement( + appendElement(` +
+ +
ignored
+ +
+ `) + ) + expect(name).toBe('label text') }) it('extracts text from a parent label', () => { - expect( - getActionNameFromElement( - appendElement(` - - `) - ) - ).toBe('foo bar') + const { name } = getActionNameFromElement( + appendElement(` + + `) + ) + expect(name).toBe('foo bar') }) it('extracts text from the first OPTION element when clicking on a SELECT', () => { - expect( - getActionNameFromElement( - appendElement(` - - `) - ) - ).toBe('foo') + const { name } = getActionNameFromElement( + appendElement(` + + `) + ) + expect(name).toBe('foo') }) it('extracts text from a aria-labelledby associated element', () => { - expect( - getActionNameFromElement( - appendElement(` -
- -
ignored
- -
- `) - ) - ).toBe('label text') + const { name } = getActionNameFromElement( + appendElement(` +
+ +
ignored
+ +
+ `) + ) + expect(name).toBe('label text') }) it('extracts text from multiple aria-labelledby associated elements', () => { - expect( - getActionNameFromElement( - appendElement(` -
- -
ignored
- -
ignored
- -
- `) - ) - ).toBe('label text') + const { name } = getActionNameFromElement( + appendElement(` +
+ +
ignored
+ +
ignored
+ +
+ `) + ) + expect(name).toBe('label text') }) it('extracts text from a BUTTON element', () => { - expect( - getActionNameFromElement( - appendElement(` -
-
ignored
- -
- `) - ) - ).toBe('foo') + const { name } = getActionNameFromElement( + appendElement(` +
+
ignored
+ +
+ `) + ) + expect(name).toBe('foo') }) it('extracts text from a role=button element', () => { - expect( - getActionNameFromElement( - appendElement(` -
-
ignored
-
foo
-
- `) - ) - ).toBe('foo') + const { name } = getActionNameFromElement( + appendElement(` +
+
ignored
+
foo
+
+ `) + ) + expect(name).toBe('foo') }) it('limits the recursion to the 10th parent', () => { - expect( - getActionNameFromElement( - appendElement(` -
-
ignored
- - - -
- `) - ) - ).toBe('') + const { name } = getActionNameFromElement( + appendElement(` +
+
ignored
+ + + +
+ `) + ) + expect(name).toBe('') }) it('limits the recursion to the BODY element', () => { - expect( - getActionNameFromElement( - appendElement(` -
ignored
- - `) - ) - ).toBe('') + const { name } = getActionNameFromElement( + appendElement(` +
ignored
+ + `) + ) + expect(name).toBe('') }) it('limits the recursion to a FORM element', () => { - expect( - getActionNameFromElement( - appendElement(` -
-
ignored
-
- -
-
- `) - ) - ).toBe('') + const { name } = getActionNameFromElement( + appendElement(` +
+
ignored
+
+ +
+
+ `) + ) + expect(name).toBe('') }) it('extracts the name from a parent FORM element', () => { - expect( - getActionNameFromElement( - appendElement(` -
-
ignored
-
- -
-
- `) - ) - ).toBe('foo') + const { name } = getActionNameFromElement( + appendElement(` +
+
ignored
+
+ +
+
+ `) + ) + expect(name).toBe('foo') }) it('extracts the whole textual content of a button', () => { - expect( - getActionNameFromElement( - appendElement(` - - `) - ) - ).toBe('foo bar') + const { name } = getActionNameFromElement( + appendElement(` + + `) + ) + expect(name).toBe('foo bar') }) it('ignores the textual content of contenteditable elements', () => { - expect( - getActionNameFromElement( - appendElement(` -
- ignored - ignored -
- `) - ) - ).toBe('') + const { name } = getActionNameFromElement( + appendElement(` +
+ ignored + ignored +
+ `) + ) + expect(name).toBe('') }) it('extracts the name from attributes of contenteditable elements', () => { - expect( - getActionNameFromElement( - appendElement(` -
- ignored - ignored -
- `) - ) - ).toBe('foo') + const { name } = getActionNameFromElement( + appendElement(` +
+ ignored + ignored +
+ `) + ) + expect(name).toBe('foo') }) it('computes an action name on SVG elements (IE does not support parentElement property on them)', () => { - expect( - getActionNameFromElement( - appendElement(` - - `) + `), + defaultConfiguration ) expect(name).toBe('foo') }) @@ -159,7 +175,8 @@ describe('getActionNameFromElement', () => {
ignored
foo
- `) + `), + defaultConfiguration ) expect(name).toBe('foo') }) @@ -173,7 +190,8 @@ describe('getActionNameFromElement', () => { - `) + `), + defaultConfiguration ) expect(name).toBe('') }) @@ -183,7 +201,8 @@ describe('getActionNameFromElement', () => { appendElement(`
ignored
- `) + `), + defaultConfiguration ) expect(name).toBe('') }) @@ -197,7 +216,8 @@ describe('getActionNameFromElement', () => { - `) + `), + defaultConfiguration ) expect(name).toBe('') }) @@ -211,7 +231,8 @@ describe('getActionNameFromElement', () => { - `) + `), + defaultConfiguration ) expect(name).toBe('foo') }) @@ -223,7 +244,8 @@ describe('getActionNameFromElement', () => { foo bar - `) + `), + defaultConfiguration ) expect(name).toBe('foo bar') }) @@ -235,7 +257,8 @@ describe('getActionNameFromElement', () => { ignored ignored - `) + `), + defaultConfiguration ) expect(name).toBe('') }) @@ -247,7 +270,8 @@ describe('getActionNameFromElement', () => { ignored ignored - `) + `), + defaultConfiguration ) expect(name).toBe('foo') }) @@ -258,7 +282,8 @@ describe('getActionNameFromElement', () => {