From dae4316e527a5adc318a63756c98a1246d1ca3b5 Mon Sep 17 00:00:00 2001 From: John Pham Date: Tue, 23 Mar 2021 20:14:43 -0400 Subject: [PATCH 1/7] Adds text randomization --- src/snapshot/snapshot.ts | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/src/snapshot/snapshot.ts b/src/snapshot/snapshot.ts index fcfb9f56..461f7330 100644 --- a/src/snapshot/snapshot.ts +++ b/src/snapshot/snapshot.ts @@ -215,6 +215,8 @@ function serializeNode( maskInputOptions = {}, recordCanvas, } = options; + const isStrictPrivacyMode = true; + switch (n.nodeType) { case n.DOCUMENT_NODE: return { @@ -229,7 +231,7 @@ function serializeNode( systemId: (n as DocumentType).systemId, }; case n.ELEMENT_NODE: - const needBlock = _isBlockedElement( + let needBlock = _isBlockedElement( n as HTMLElement, blockClass, blockSelector, @@ -318,13 +320,14 @@ function serializeNode( if ((n as HTMLElement).scrollTop) { attributes.rr_scrollTop = (n as HTMLElement).scrollTop; } - if (needBlock) { + if (needBlock || (tagName === 'img' && isStrictPrivacyMode)) { const { width, height } = (n as HTMLElement).getBoundingClientRect(); attributes = { class: attributes.class, rr_width: `${width}px`, rr_height: `${height}px`, }; + needBlock = true; } return { type: NodeType.Element, @@ -341,11 +344,35 @@ function serializeNode( n.parentNode && (n.parentNode as HTMLElement).tagName; let textContent = (n as Text).textContent; const isStyle = parentTagName === 'STYLE' ? true : undefined; + /** Determines if this node has been handled already. */ + let textContentHandled = false; if (isStyle && textContent) { textContent = absoluteToStylesheet(textContent, getHref()); + textContentHandled = true; } if (parentTagName === 'SCRIPT') { textContent = 'SCRIPT_PLACEHOLDER'; + textContentHandled = true; + } + + // Randomizes the text content to a string of the same length. + if (isStrictPrivacyMode && !textContentHandled && parentTagName) { + const IGNORE_TAG_NAMES = new Set([ + 'HEAD', + 'TITLE', + 'STYLE', + 'SCRIPT', + 'HTML', + 'BODY', + 'NOSCRIPT', + ]); + if (!IGNORE_TAG_NAMES.has(parentTagName)) { + textContent = + textContent + ?.split(' ') + .map((word) => Math.random().toString(20).substr(2, word.length)) + .join(' ') || ''; + } } return { type: NodeType.Text, From 028df9a12fd2eb1791e0f2f8e14c5978979ebaa1 Mon Sep 17 00:00:00 2001 From: John Pham Date: Wed, 24 Mar 2021 14:00:14 -0400 Subject: [PATCH 2/7] Update blocked styles --- src/snapshot/rebuild.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/snapshot/rebuild.ts b/src/snapshot/rebuild.ts index b09d4234..66d5d58e 100644 --- a/src/snapshot/rebuild.ts +++ b/src/snapshot/rebuild.ts @@ -165,6 +165,13 @@ function buildNode( if (name === 'rr_height') { (node as HTMLElement).style.height = value; } + /** Highlight Code Start */ + // rr_width and rr_height are only set if the node is blocked/censored. + if (name === 'rr_width' || name === 'rr_height') { + (node as HTMLElement).style.backgroundColor = '#000000'; + (node as HTMLElement).style.borderRadius = '5px'; + } + /** Highlight Code End */ if (name === 'rr_mediaState') { switch (value) { case 'played': From 0012708fa1c8550c4faef62d57f636f296dcdc8b Mon Sep 17 00:00:00 2001 From: John Pham Date: Wed, 24 Mar 2021 14:32:02 -0400 Subject: [PATCH 3/7] Add privacy mode --- src/record/index.ts | 3 +++ src/record/mutation.ts | 4 ++++ src/record/observer.ts | 3 +++ src/snapshot/snapshot.ts | 14 +++++++++++--- src/types.ts | 6 ++++++ typings/record/mutation.d.ts | 3 ++- typings/replay/index.d.ts | 2 +- typings/snapshot/rebuild.d.ts | 2 -- typings/snapshot/snapshot.d.ts | 11 ++--------- typings/types.d.ts | 4 ++++ 10 files changed, 36 insertions(+), 16 deletions(-) diff --git a/src/record/index.ts b/src/record/index.ts index 38a00ee0..1eed434a 100644 --- a/src/record/index.ts +++ b/src/record/index.ts @@ -49,6 +49,7 @@ function record( collectFonts = false, recordLog = false, debug, + isStrictPrivacy = false, } = options; // runtime checks for user options if (!emit) { @@ -191,6 +192,7 @@ function record( maskAllInputs: maskInputOptions, slimDOM: slimDOMOptions, recordCanvas, + isStrictPrivacy, }); if (!node) { @@ -366,6 +368,7 @@ function record( collectFonts, slimDOMOptions, logOptions, + isStrictPrivacy, }, hooks, ), diff --git a/src/record/mutation.ts b/src/record/mutation.ts index 1fc13d1f..7ef559c6 100644 --- a/src/record/mutation.ts +++ b/src/record/mutation.ts @@ -147,6 +147,7 @@ export default class MutationBuffer { private inlineStylesheet: boolean; private maskInputOptions: MaskInputOptions; private recordCanvas: boolean; + private isStrictPrivacy: boolean; private slimDOMOptions: SlimDOMOptions; public init( @@ -157,6 +158,7 @@ export default class MutationBuffer { maskInputOptions: MaskInputOptions, recordCanvas: boolean, slimDOMOptions: SlimDOMOptions, + isStrictPrivacy: boolean, ) { this.blockClass = blockClass; this.blockSelector = blockSelector; @@ -164,6 +166,7 @@ export default class MutationBuffer { this.maskInputOptions = maskInputOptions; this.recordCanvas = recordCanvas; this.slimDOMOptions = slimDOMOptions; + this.isStrictPrivacy = isStrictPrivacy; this.emissionCallback = cb; } @@ -228,6 +231,7 @@ export default class MutationBuffer { maskInputOptions: this.maskInputOptions, slimDOMOptions: this.slimDOMOptions, recordCanvas: this.recordCanvas, + isStrictPrivacy: this.isStrictPrivacy, }); if (sn) { adds.push({ diff --git a/src/record/observer.ts b/src/record/observer.ts index ba67c4ae..608d171c 100644 --- a/src/record/observer.ts +++ b/src/record/observer.ts @@ -60,6 +60,7 @@ function initMutationObserver( maskInputOptions: MaskInputOptions, recordCanvas: boolean, slimDOMOptions: SlimDOMOptions, + isStrictPrivacy: boolean, ): MutationObserver { // see mutation.ts for details mutationBuffer.init( @@ -70,6 +71,7 @@ function initMutationObserver( maskInputOptions, recordCanvas, slimDOMOptions, + isStrictPrivacy, ); let mutationBufferCtor = window.MutationObserver; const angularZoneSymbol = (window as WindowWithAngularZone)?.Zone?.__symbol__?.( @@ -720,6 +722,7 @@ export function initObservers( o.maskInputOptions, o.recordCanvas, o.slimDOMOptions, + o.isStrictPrivacy, ); const mousemoveHandler = initMoveObserver(o.mousemoveCb, o.sampling); const mouseInteractionHandler = initMouseInteractionObserver( diff --git a/src/snapshot/snapshot.ts b/src/snapshot/snapshot.ts index 461f7330..b346fa68 100644 --- a/src/snapshot/snapshot.ts +++ b/src/snapshot/snapshot.ts @@ -205,6 +205,7 @@ function serializeNode( inlineStylesheet: boolean; maskInputOptions: MaskInputOptions; recordCanvas: boolean; + isStrictPrivacy: boolean; }, ): serializedNode | false { const { @@ -214,8 +215,8 @@ function serializeNode( inlineStylesheet, maskInputOptions = {}, recordCanvas, + isStrictPrivacy, } = options; - const isStrictPrivacyMode = true; switch (n.nodeType) { case n.DOCUMENT_NODE: @@ -320,7 +321,7 @@ function serializeNode( if ((n as HTMLElement).scrollTop) { attributes.rr_scrollTop = (n as HTMLElement).scrollTop; } - if (needBlock || (tagName === 'img' && isStrictPrivacyMode)) { + if (needBlock || (tagName === 'img' && isStrictPrivacy)) { const { width, height } = (n as HTMLElement).getBoundingClientRect(); attributes = { class: attributes.class, @@ -356,7 +357,7 @@ function serializeNode( } // Randomizes the text content to a string of the same length. - if (isStrictPrivacyMode && !textContentHandled && parentTagName) { + if (isStrictPrivacy && !textContentHandled && parentTagName) { const IGNORE_TAG_NAMES = new Set([ 'HEAD', 'TITLE', @@ -499,6 +500,7 @@ export function serializeNodeWithId( slimDOMOptions: SlimDOMOptions; recordCanvas?: boolean; preserveWhiteSpace?: boolean; + isStrictPrivacy: boolean; }, ): serializedNodeWithId | null { const { @@ -511,6 +513,7 @@ export function serializeNodeWithId( maskInputOptions = {}, slimDOMOptions, recordCanvas = false, + isStrictPrivacy, } = options; let { preserveWhiteSpace = true } = options; const _serializedNode = serializeNode(n, { @@ -520,6 +523,7 @@ export function serializeNodeWithId( inlineStylesheet, maskInputOptions, recordCanvas, + isStrictPrivacy, }); if (!_serializedNode) { // TODO: dev only @@ -579,6 +583,7 @@ export function serializeNodeWithId( slimDOMOptions, recordCanvas, preserveWhiteSpace, + isStrictPrivacy, }); if (serializedChildNode) { serializedNode.childNodes.push(serializedChildNode); @@ -597,6 +602,7 @@ function snapshot( slimDOM?: boolean | SlimDOMOptions; recordCanvas?: boolean; blockSelector?: string | null; + isStrictPrivacy: boolean; }, ): [serializedNodeWithId | null, idNodeMap] { const { @@ -606,6 +612,7 @@ function snapshot( blockSelector = null, maskAllInputs = false, slimDOM = false, + isStrictPrivacy = false, } = options || {}; const idNodeMap: idNodeMap = {}; const maskInputOptions: MaskInputOptions = @@ -659,6 +666,7 @@ function snapshot( maskInputOptions, slimDOMOptions, recordCanvas, + isStrictPrivacy, }), idNodeMap, ]; diff --git a/src/types.ts b/src/types.ts index b3c8d916..365d4cd1 100644 --- a/src/types.ts +++ b/src/types.ts @@ -207,6 +207,11 @@ export type recordOptions = { mousemoveWait?: number; recordLog?: boolean | LogRecordOptions; debug?: boolean; + /** + * Enabling this will disable recording of text data on the page. This is useful if you do not want to record personally identifiable information. + * Text will be randomized. Instead of seeing "Hello World" in a recording, you will see "1fds1 j59a0". + */ + isStrictPrivacy?: boolean; }; export type observerParam = { @@ -232,6 +237,7 @@ export type observerParam = { recordCanvas: boolean; collectFonts: boolean; slimDOMOptions: SlimDOMOptions; + isStrictPrivacy: boolean; }; export type hooksParam = { diff --git a/typings/record/mutation.d.ts b/typings/record/mutation.d.ts index 14245a0a..32ed3653 100644 --- a/typings/record/mutation.d.ts +++ b/typings/record/mutation.d.ts @@ -16,8 +16,9 @@ export default class MutationBuffer { private inlineStylesheet; private maskInputOptions; private recordCanvas; + private isStrictPrivacy; private slimDOMOptions; - init(cb: mutationCallBack, blockClass: blockClass, blockSelector: string | null, inlineStylesheet: boolean, maskInputOptions: MaskInputOptions, recordCanvas: boolean, slimDOMOptions: SlimDOMOptions): void; + init(cb: mutationCallBack, blockClass: blockClass, blockSelector: string | null, inlineStylesheet: boolean, maskInputOptions: MaskInputOptions, recordCanvas: boolean, slimDOMOptions: SlimDOMOptions, isStrictPrivacy: boolean): void; freeze(): void; unfreeze(): void; isFrozen(): boolean; diff --git a/typings/replay/index.d.ts b/typings/replay/index.d.ts index ee6d02fa..696cadfd 100644 --- a/typings/replay/index.d.ts +++ b/typings/replay/index.d.ts @@ -13,13 +13,13 @@ export declare class Replayer { private mouseTail; private tailPositions; private emitter; + private activityIntervals; private inactiveEndTimestamp; private legacy_missingNodeRetryMap; private treeIndex; private fragmentParentMap; private elementStateMap; private imageMap; - private activityIntervals; constructor(events: Array, config?: Partial); on(event: string, handler: Handler): this; setConfig(config: Partial): void; diff --git a/typings/snapshot/rebuild.d.ts b/typings/snapshot/rebuild.d.ts index 6ab9f04b..35eb6020 100644 --- a/typings/snapshot/rebuild.d.ts +++ b/typings/snapshot/rebuild.d.ts @@ -5,12 +5,10 @@ export declare function buildNodeWithSN(n: serializedNodeWithId, options: { map: idNodeMap; skipChild?: boolean; hackCss: boolean; - afterAppend?: (n: INode) => unknown; }): INode | null; declare function rebuild(n: serializedNodeWithId, options: { doc: Document; onVisit?: (node: INode) => unknown; hackCss?: boolean; - afterAppend?: (n: INode) => unknown; }): [Node | null, idNodeMap]; export default rebuild; diff --git a/typings/snapshot/snapshot.d.ts b/typings/snapshot/snapshot.d.ts index 62626737..300706dd 100644 --- a/typings/snapshot/snapshot.d.ts +++ b/typings/snapshot/snapshot.d.ts @@ -15,10 +15,7 @@ export declare function serializeNodeWithId(n: Node | INode, options: { slimDOMOptions: SlimDOMOptions; recordCanvas?: boolean; preserveWhiteSpace?: boolean; - onSerialize?: (n: INode) => unknown; - onIframeLoad?: (iframeINode: INode, node: serializedNodeWithId) => unknown; - iframeLoadTimeout?: number; - debug?: boolean; + isStrictPrivacy: boolean; }): serializedNodeWithId | null; declare function snapshot(n: Document, options?: { blockClass?: string | RegExp; @@ -27,11 +24,7 @@ declare function snapshot(n: Document, options?: { slimDOM?: boolean | SlimDOMOptions; recordCanvas?: boolean; blockSelector?: string | null; - preserveWhiteSpace?: boolean; - onSerialize?: (n: INode) => unknown; - onIframeLoad?: (iframeINode: INode, node: serializedNodeWithId) => unknown; - iframeLoadTimeout?: number; - debug?: boolean; + isStrictPrivacy: boolean; }): [serializedNodeWithId | null, idNodeMap]; export declare function visitSnapshot(node: serializedNodeWithId, onVisit: (node: serializedNodeWithId) => unknown): void; export declare function cleanupSnapshot(): void; diff --git a/typings/types.d.ts b/typings/types.d.ts index 199e684a..1173e5e8 100644 --- a/typings/types.d.ts +++ b/typings/types.d.ts @@ -140,6 +140,7 @@ export declare type recordOptions = { mousemoveWait?: number; recordLog?: boolean | LogRecordOptions; debug?: boolean; + isStrictPrivacy?: boolean; }; export declare type observerParam = { mutationCb: mutationCallBack; @@ -164,6 +165,7 @@ export declare type observerParam = { recordCanvas: boolean; collectFonts: boolean; slimDOMOptions: SlimDOMOptions; + isStrictPrivacy: boolean; }; export declare type hooksParam = { mutation?: mutationCallBack; @@ -367,6 +369,8 @@ export declare type playerConfig = { unpackFn?: UnpackFn; logConfig: LogReplayConfig; inactiveThreshold: number; + inactiveSkipTime: number; + maxSkipSpeed: number; }; export declare type LogReplayConfig = { level?: Array | undefined; From cf56b80771413a6ee669b5cfd6c19575ed603736 Mon Sep 17 00:00:00 2001 From: John Pham Date: Wed, 24 Mar 2021 15:40:39 -0400 Subject: [PATCH 4/7] Don't record images --- src/snapshot/snapshot.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/snapshot/snapshot.ts b/src/snapshot/snapshot.ts index b346fa68..3675bdbb 100644 --- a/src/snapshot/snapshot.ts +++ b/src/snapshot/snapshot.ts @@ -555,6 +555,16 @@ export function serializeNodeWithId( let recordChild = !skipChild; if (serializedNode.type === NodeType.Element) { recordChild = recordChild && !serializedNode.needBlock; + + /** Highlight Code Begin */ + // Remove the image's src if isStrictPrivacy. + if (serializedNode.needBlock && serializedNode.tagName === 'img') { + const clone = n.cloneNode(); + ((clone as unknown) as HTMLImageElement).src = ''; + map[id] = clone as INode; + } + /** Highlight Code End */ + // this property was not needed in replay side delete serializedNode.needBlock; } From 2a17f3f28726a45f1ca9ade600440bb04dd3180e Mon Sep 17 00:00:00 2001 From: John Pham Date: Wed, 24 Mar 2021 15:41:09 -0400 Subject: [PATCH 5/7] v0.10.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9b9aecfb..c4a5a9af 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@highlight-run/rrweb", - "version": "0.9.29", + "version": "0.10.0", "description": "record and replay the web", "scripts": { "test": "npm run bundle:browser && cross-env TS_NODE_CACHE=false TS_NODE_FILES=true mocha -r ts-node/register test/**/*.test.ts", From f5e37e13cbfbff7608bc426b768f0dc605389776 Mon Sep 17 00:00:00 2001 From: John Pham Date: Wed, 24 Mar 2021 21:45:12 -0400 Subject: [PATCH 6/7] Rename flag to enableStrictPrivacy --- src/record/index.ts | 6 +++--- src/record/mutation.ts | 8 ++++---- src/record/observer.ts | 6 +++--- src/snapshot/snapshot.ts | 24 ++++++++++++------------ src/types.ts | 4 ++-- typings/record/mutation.d.ts | 4 ++-- typings/snapshot/snapshot.d.ts | 4 ++-- typings/types.d.ts | 4 ++-- 8 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/record/index.ts b/src/record/index.ts index 1eed434a..bf256ec2 100644 --- a/src/record/index.ts +++ b/src/record/index.ts @@ -49,7 +49,7 @@ function record( collectFonts = false, recordLog = false, debug, - isStrictPrivacy = false, + enableStrictPrivacy = false, } = options; // runtime checks for user options if (!emit) { @@ -192,7 +192,7 @@ function record( maskAllInputs: maskInputOptions, slimDOM: slimDOMOptions, recordCanvas, - isStrictPrivacy, + enableStrictPrivacy, }); if (!node) { @@ -368,7 +368,7 @@ function record( collectFonts, slimDOMOptions, logOptions, - isStrictPrivacy, + enableStrictPrivacy, }, hooks, ), diff --git a/src/record/mutation.ts b/src/record/mutation.ts index 7ef559c6..b35c0ce5 100644 --- a/src/record/mutation.ts +++ b/src/record/mutation.ts @@ -147,7 +147,7 @@ export default class MutationBuffer { private inlineStylesheet: boolean; private maskInputOptions: MaskInputOptions; private recordCanvas: boolean; - private isStrictPrivacy: boolean; + private enableStrictPrivacy: boolean; private slimDOMOptions: SlimDOMOptions; public init( @@ -158,7 +158,7 @@ export default class MutationBuffer { maskInputOptions: MaskInputOptions, recordCanvas: boolean, slimDOMOptions: SlimDOMOptions, - isStrictPrivacy: boolean, + enableStrictPrivacy: boolean, ) { this.blockClass = blockClass; this.blockSelector = blockSelector; @@ -166,7 +166,7 @@ export default class MutationBuffer { this.maskInputOptions = maskInputOptions; this.recordCanvas = recordCanvas; this.slimDOMOptions = slimDOMOptions; - this.isStrictPrivacy = isStrictPrivacy; + this.enableStrictPrivacy = enableStrictPrivacy; this.emissionCallback = cb; } @@ -231,7 +231,7 @@ export default class MutationBuffer { maskInputOptions: this.maskInputOptions, slimDOMOptions: this.slimDOMOptions, recordCanvas: this.recordCanvas, - isStrictPrivacy: this.isStrictPrivacy, + enableStrictPrivacy: this.enableStrictPrivacy, }); if (sn) { adds.push({ diff --git a/src/record/observer.ts b/src/record/observer.ts index 608d171c..317beef0 100644 --- a/src/record/observer.ts +++ b/src/record/observer.ts @@ -60,7 +60,7 @@ function initMutationObserver( maskInputOptions: MaskInputOptions, recordCanvas: boolean, slimDOMOptions: SlimDOMOptions, - isStrictPrivacy: boolean, + enableStrictPrivacy: boolean, ): MutationObserver { // see mutation.ts for details mutationBuffer.init( @@ -71,7 +71,7 @@ function initMutationObserver( maskInputOptions, recordCanvas, slimDOMOptions, - isStrictPrivacy, + enableStrictPrivacy, ); let mutationBufferCtor = window.MutationObserver; const angularZoneSymbol = (window as WindowWithAngularZone)?.Zone?.__symbol__?.( @@ -722,7 +722,7 @@ export function initObservers( o.maskInputOptions, o.recordCanvas, o.slimDOMOptions, - o.isStrictPrivacy, + o.enableStrictPrivacy, ); const mousemoveHandler = initMoveObserver(o.mousemoveCb, o.sampling); const mouseInteractionHandler = initMouseInteractionObserver( diff --git a/src/snapshot/snapshot.ts b/src/snapshot/snapshot.ts index 3675bdbb..2bed2ea5 100644 --- a/src/snapshot/snapshot.ts +++ b/src/snapshot/snapshot.ts @@ -205,7 +205,7 @@ function serializeNode( inlineStylesheet: boolean; maskInputOptions: MaskInputOptions; recordCanvas: boolean; - isStrictPrivacy: boolean; + enableStrictPrivacy: boolean; }, ): serializedNode | false { const { @@ -215,7 +215,7 @@ function serializeNode( inlineStylesheet, maskInputOptions = {}, recordCanvas, - isStrictPrivacy, + enableStrictPrivacy, } = options; switch (n.nodeType) { @@ -321,7 +321,7 @@ function serializeNode( if ((n as HTMLElement).scrollTop) { attributes.rr_scrollTop = (n as HTMLElement).scrollTop; } - if (needBlock || (tagName === 'img' && isStrictPrivacy)) { + if (needBlock || (tagName === 'img' && enableStrictPrivacy)) { const { width, height } = (n as HTMLElement).getBoundingClientRect(); attributes = { class: attributes.class, @@ -357,7 +357,7 @@ function serializeNode( } // Randomizes the text content to a string of the same length. - if (isStrictPrivacy && !textContentHandled && parentTagName) { + if (enableStrictPrivacy && !textContentHandled && parentTagName) { const IGNORE_TAG_NAMES = new Set([ 'HEAD', 'TITLE', @@ -500,7 +500,7 @@ export function serializeNodeWithId( slimDOMOptions: SlimDOMOptions; recordCanvas?: boolean; preserveWhiteSpace?: boolean; - isStrictPrivacy: boolean; + enableStrictPrivacy: boolean; }, ): serializedNodeWithId | null { const { @@ -513,7 +513,7 @@ export function serializeNodeWithId( maskInputOptions = {}, slimDOMOptions, recordCanvas = false, - isStrictPrivacy, + enableStrictPrivacy, } = options; let { preserveWhiteSpace = true } = options; const _serializedNode = serializeNode(n, { @@ -523,7 +523,7 @@ export function serializeNodeWithId( inlineStylesheet, maskInputOptions, recordCanvas, - isStrictPrivacy, + enableStrictPrivacy, }); if (!_serializedNode) { // TODO: dev only @@ -557,7 +557,7 @@ export function serializeNodeWithId( recordChild = recordChild && !serializedNode.needBlock; /** Highlight Code Begin */ - // Remove the image's src if isStrictPrivacy. + // Remove the image's src if enableStrictPrivacy. if (serializedNode.needBlock && serializedNode.tagName === 'img') { const clone = n.cloneNode(); ((clone as unknown) as HTMLImageElement).src = ''; @@ -593,7 +593,7 @@ export function serializeNodeWithId( slimDOMOptions, recordCanvas, preserveWhiteSpace, - isStrictPrivacy, + enableStrictPrivacy, }); if (serializedChildNode) { serializedNode.childNodes.push(serializedChildNode); @@ -612,7 +612,7 @@ function snapshot( slimDOM?: boolean | SlimDOMOptions; recordCanvas?: boolean; blockSelector?: string | null; - isStrictPrivacy: boolean; + enableStrictPrivacy: boolean; }, ): [serializedNodeWithId | null, idNodeMap] { const { @@ -622,7 +622,7 @@ function snapshot( blockSelector = null, maskAllInputs = false, slimDOM = false, - isStrictPrivacy = false, + enableStrictPrivacy = false, } = options || {}; const idNodeMap: idNodeMap = {}; const maskInputOptions: MaskInputOptions = @@ -676,7 +676,7 @@ function snapshot( maskInputOptions, slimDOMOptions, recordCanvas, - isStrictPrivacy, + enableStrictPrivacy, }), idNodeMap, ]; diff --git a/src/types.ts b/src/types.ts index 365d4cd1..4615b953 100644 --- a/src/types.ts +++ b/src/types.ts @@ -211,7 +211,7 @@ export type recordOptions = { * Enabling this will disable recording of text data on the page. This is useful if you do not want to record personally identifiable information. * Text will be randomized. Instead of seeing "Hello World" in a recording, you will see "1fds1 j59a0". */ - isStrictPrivacy?: boolean; + enableStrictPrivacy?: boolean; }; export type observerParam = { @@ -237,7 +237,7 @@ export type observerParam = { recordCanvas: boolean; collectFonts: boolean; slimDOMOptions: SlimDOMOptions; - isStrictPrivacy: boolean; + enableStrictPrivacy: boolean; }; export type hooksParam = { diff --git a/typings/record/mutation.d.ts b/typings/record/mutation.d.ts index 32ed3653..3584a0ff 100644 --- a/typings/record/mutation.d.ts +++ b/typings/record/mutation.d.ts @@ -16,9 +16,9 @@ export default class MutationBuffer { private inlineStylesheet; private maskInputOptions; private recordCanvas; - private isStrictPrivacy; + private enableStrictPrivacy; private slimDOMOptions; - init(cb: mutationCallBack, blockClass: blockClass, blockSelector: string | null, inlineStylesheet: boolean, maskInputOptions: MaskInputOptions, recordCanvas: boolean, slimDOMOptions: SlimDOMOptions, isStrictPrivacy: boolean): void; + init(cb: mutationCallBack, blockClass: blockClass, blockSelector: string | null, inlineStylesheet: boolean, maskInputOptions: MaskInputOptions, recordCanvas: boolean, slimDOMOptions: SlimDOMOptions, enableStrictPrivacy: boolean): void; freeze(): void; unfreeze(): void; isFrozen(): boolean; diff --git a/typings/snapshot/snapshot.d.ts b/typings/snapshot/snapshot.d.ts index 300706dd..9d12b13f 100644 --- a/typings/snapshot/snapshot.d.ts +++ b/typings/snapshot/snapshot.d.ts @@ -15,7 +15,7 @@ export declare function serializeNodeWithId(n: Node | INode, options: { slimDOMOptions: SlimDOMOptions; recordCanvas?: boolean; preserveWhiteSpace?: boolean; - isStrictPrivacy: boolean; + enableStrictPrivacy: boolean; }): serializedNodeWithId | null; declare function snapshot(n: Document, options?: { blockClass?: string | RegExp; @@ -24,7 +24,7 @@ declare function snapshot(n: Document, options?: { slimDOM?: boolean | SlimDOMOptions; recordCanvas?: boolean; blockSelector?: string | null; - isStrictPrivacy: boolean; + enableStrictPrivacy: boolean; }): [serializedNodeWithId | null, idNodeMap]; export declare function visitSnapshot(node: serializedNodeWithId, onVisit: (node: serializedNodeWithId) => unknown): void; export declare function cleanupSnapshot(): void; diff --git a/typings/types.d.ts b/typings/types.d.ts index 1173e5e8..4a0f4cc8 100644 --- a/typings/types.d.ts +++ b/typings/types.d.ts @@ -140,7 +140,7 @@ export declare type recordOptions = { mousemoveWait?: number; recordLog?: boolean | LogRecordOptions; debug?: boolean; - isStrictPrivacy?: boolean; + enableStrictPrivacy?: boolean; }; export declare type observerParam = { mutationCb: mutationCallBack; @@ -165,7 +165,7 @@ export declare type observerParam = { recordCanvas: boolean; collectFonts: boolean; slimDOMOptions: SlimDOMOptions; - isStrictPrivacy: boolean; + enableStrictPrivacy: boolean; }; export declare type hooksParam = { mutation?: mutationCallBack; From 9ab9619d93129b4660d3ac600b314745c2bf8c29 Mon Sep 17 00:00:00 2001 From: John Pham Date: Thu, 25 Mar 2021 11:08:10 -0700 Subject: [PATCH 7/7] Set redacted style on replayer side --- src/record/index.ts | 4 ++-- src/replay/index.ts | 3 ++- src/replay/styles/inject-style.ts | 2 ++ src/snapshot/rebuild.ts | 7 ------- src/snapshot/snapshot.ts | 5 ++++- test/html/block.html | 2 +- 6 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/record/index.ts b/src/record/index.ts index bf256ec2..95396d20 100644 --- a/src/record/index.ts +++ b/src/record/index.ts @@ -33,9 +33,9 @@ function record( emit, checkoutEveryNms, checkoutEveryNth, - blockClass = 'rr-block', + blockClass = 'highlight-block', blockSelector = null, - ignoreClass = 'rr-ignore', + ignoreClass = 'highlight-ignore', inlineStylesheet = true, maskAllInputs, maskInputOptions: _maskInputOptions, diff --git a/src/replay/index.ts b/src/replay/index.ts index 55d952ce..ac8ddca3 100644 --- a/src/replay/index.ts +++ b/src/replay/index.ts @@ -127,7 +127,7 @@ export class Replayer { skipInactive: false, showWarning: true, showDebug: false, - blockClass: 'rr-block', + blockClass: 'highlight-block', liveMode: false, insertStyleRules: [], triggerFocus: true, @@ -641,6 +641,7 @@ export class Replayer { const styleEl = document.createElement('style'); const { documentElement, head } = this.iframe.contentDocument; documentElement!.insertBefore(styleEl, head); + console.log(this.config.blockClass); const injectStylesRules = getInjectStyleRules( this.config.blockClass, ).concat(this.config.insertStyleRules); diff --git a/src/replay/styles/inject-style.ts b/src/replay/styles/inject-style.ts index a0ac939c..01981354 100644 --- a/src/replay/styles/inject-style.ts +++ b/src/replay/styles/inject-style.ts @@ -1,6 +1,8 @@ const rules: (blockClass: string) => string[] = (blockClass: string) => [ `iframe, .${blockClass} { background: #ccc }`, 'noscript { display: none !important; }', + `.${blockClass} { background: black; border-radius: 5px; }`, + `.${blockClass}:hover::after {content: 'Redacted'; color: white; text-align: center; width: 100%; display: block;}`, ]; export default rules; diff --git a/src/snapshot/rebuild.ts b/src/snapshot/rebuild.ts index 66d5d58e..b09d4234 100644 --- a/src/snapshot/rebuild.ts +++ b/src/snapshot/rebuild.ts @@ -165,13 +165,6 @@ function buildNode( if (name === 'rr_height') { (node as HTMLElement).style.height = value; } - /** Highlight Code Start */ - // rr_width and rr_height are only set if the node is blocked/censored. - if (name === 'rr_width' || name === 'rr_height') { - (node as HTMLElement).style.backgroundColor = '#000000'; - (node as HTMLElement).style.borderRadius = '5px'; - } - /** Highlight Code End */ if (name === 'rr_mediaState') { switch (value) { case 'played': diff --git a/src/snapshot/snapshot.ts b/src/snapshot/snapshot.ts index 2bed2ea5..d9555254 100644 --- a/src/snapshot/snapshot.ts +++ b/src/snapshot/snapshot.ts @@ -354,6 +354,9 @@ function serializeNode( if (parentTagName === 'SCRIPT') { textContent = 'SCRIPT_PLACEHOLDER'; textContentHandled = true; + } else if (parentTagName === 'NOSCRIPT') { + textContent = ''; + textContentHandled = true; } // Randomizes the text content to a string of the same length. @@ -616,7 +619,7 @@ function snapshot( }, ): [serializedNodeWithId | null, idNodeMap] { const { - blockClass = 'rr-block', + blockClass = 'highlight-block', inlineStylesheet = true, recordCanvas = false, blockSelector = null, diff --git a/test/html/block.html b/test/html/block.html index 6fee77f7..a2c5b614 100644 --- a/test/html/block.html +++ b/test/html/block.html @@ -7,7 +7,7 @@ Block record -
+