diff --git a/tracker/tracker/CHANGELOG.md b/tracker/tracker/CHANGELOG.md index 80f804cdc..c28cbb080 100644 --- a/tracker/tracker/CHANGELOG.md +++ b/tracker/tracker/CHANGELOG.md @@ -1,3 +1,7 @@ +## 16.1.0 + +- new `privateMode` option to hide all possible data from tracking + ## 16.0.3 - better handling for local svg spritemaps diff --git a/tracker/tracker/package.json b/tracker/tracker/package.json index f40314129..9b2ca79f4 100644 --- a/tracker/tracker/package.json +++ b/tracker/tracker/package.json @@ -1,7 +1,7 @@ { "name": "@openreplay/tracker", "description": "The OpenReplay tracker main package", - "version": "16.0.3", + "version": "16.1.0", "keywords": [ "logging", "replay" diff --git a/tracker/tracker/src/main/app/observer/observer.ts b/tracker/tracker/src/main/app/observer/observer.ts index 664c6edc7..f8adc6231 100644 --- a/tracker/tracker/src/main/app/observer/observer.ts +++ b/tracker/tracker/src/main/app/observer/observer.ts @@ -357,6 +357,9 @@ export default abstract class Observer { if (name === 'href' || value.length > 1e5) { value = '' } + if (['alt', 'placeholder'].includes(name) && this.app.sanitizer.privateMode) { + value = value.replaceAll(/./g, '*') + } this.app.attributeSender.sendSetAttribute(id, name, value) } diff --git a/tracker/tracker/src/main/app/sanitizer.ts b/tracker/tracker/src/main/app/sanitizer.ts index edf611e1e..b72dae7d8 100644 --- a/tracker/tracker/src/main/app/sanitizer.ts +++ b/tracker/tracker/src/main/app/sanitizer.ts @@ -41,12 +41,13 @@ export interface Options { export const stringWiper = (input: string) => input .trim() - .replace(/[^\f\n\r\t\v\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]/g, '█') + .replace(/[^\f\n\r\t\v\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]/g, '*') export default class Sanitizer { private readonly obscured: Set = new Set() private readonly hidden: Set = new Set() private readonly options: Options + public readonly privateMode: boolean private readonly app: App constructor(params: { app: App; options?: Partial }) { @@ -57,16 +58,17 @@ export default class Sanitizer { privateMode: false, domSanitizer: undefined, } + this.privateMode = params.options?.privateMode ?? false this.options = Object.assign(defaultOptions, params.options) } handleNode(id: number, parentID: number, node: Node) { if (this.options.privateMode) { if (isElementNode(node) && !hasOpenreplayAttribute(node, 'unmask')) { - this.obscured.add(id) + return this.obscured.add(id) } if (isTextNode(node) && !hasOpenreplayAttribute(node.parentNode as Element, 'unmask')) { - this.obscured.add(id) + return this.obscured.add(id) } } diff --git a/tracker/tracker/src/main/modules/console.ts b/tracker/tracker/src/main/modules/console.ts index efdd3be88..1a30ae5e8 100644 --- a/tracker/tracker/src/main/modules/console.ts +++ b/tracker/tracker/src/main/modules/console.ts @@ -108,9 +108,13 @@ export default function (app: App, opts: Partial): void { return } - const sendConsoleLog = app.safe((level: string, args: unknown[]): void => - app.send(ConsoleLog(level, printf(args))), - ) + const sendConsoleLog = app.safe((level: string, args: unknown[]): void => { + let logMsg = printf(args) + if (app.sanitizer.privateMode) { + logMsg = logMsg.replaceAll(/./g, '*') + } + app.send(ConsoleLog(level, logMsg)) + }) let n = 0 const reset = (): void => { diff --git a/tracker/tracker/src/main/modules/input.ts b/tracker/tracker/src/main/modules/input.ts index e8297f2b8..7fc3626ba 100644 --- a/tracker/tracker/src/main/modules/input.ts +++ b/tracker/tracker/src/main/modules/input.ts @@ -205,7 +205,10 @@ export default function (app: App, opts: Partial): void { inputTime: number, ) { const { value, mask } = getInputValue(id, node) - const label = getInputLabel(node) + let label = getInputLabel(node) + if (app.sanitizer.privateMode) { + label = label.replaceAll(/./g, '*') + } app.send(InputChange(id, value, mask !== 0, label, hesitationTime, inputTime)) } diff --git a/tracker/tracker/src/main/modules/mouse.ts b/tracker/tracker/src/main/modules/mouse.ts index 262005757..ab15a2303 100644 --- a/tracker/tracker/src/main/modules/mouse.ts +++ b/tracker/tracker/src/main/modules/mouse.ts @@ -230,11 +230,12 @@ export default function (app: App, options?: MouseHandlerOptions): void { const normalizedY = roundNumber(clickY / contentHeight) sendMouseMove() + const label = getTargetLabel(target) app.send( MouseClick( id, mouseTarget === target ? Math.round(performance.now() - mouseTargetTime) : 0, - getTargetLabel(target), + app.sanitizer.privateMode ? label.replaceAll(/./g, '*') : label, isClickable(target) && !disableClickmaps ? getSelector(id, target, options) : '', normalizedX, normalizedY, diff --git a/tracker/tracker/src/main/modules/network.ts b/tracker/tracker/src/main/modules/network.ts index 135a14a75..453406dcf 100644 --- a/tracker/tracker/src/main/modules/network.ts +++ b/tracker/tracker/src/main/modules/network.ts @@ -101,7 +101,7 @@ export default function (app: App, opts: Partial = {}) { } function sanitize(reqResInfo: RequestResponseData) { - if (!options.capturePayload) { + if (!options.capturePayload || app.sanitizer.privateMode) { // @ts-ignore delete reqResInfo.request.body delete reqResInfo.response.body @@ -136,18 +136,19 @@ export default function (app: App, opts: Partial = {}) { if (options.useProxy) { return createNetworkProxy( context, - options.ignoreHeaders, + app.sanitizer.privateMode ? true : options.ignoreHeaders, setSessionTokenHeader, sanitize, (message) => { if (options.failuresOnly && message.status < 400) { return } + const url = app.sanitizer.privateMode ? '************' : message.url app.send( NetworkRequest( message.requestType, message.method, - message.url, + url, message.request, message.response, message.status, diff --git a/tracker/tracker/src/main/modules/timing.ts b/tracker/tracker/src/main/modules/timing.ts index f8e7d63cf..ff3e4cfae 100644 --- a/tracker/tracker/src/main/modules/timing.ts +++ b/tracker/tracker/src/main/modules/timing.ts @@ -147,7 +147,7 @@ export default function (app: App, opts: Partial): void { entry.transferSize > entry.encodedBodySize ? entry.transferSize - entry.encodedBodySize : 0, entry.encodedBodySize || 0, entry.decodedBodySize || 0, - entry.name, + app.sanitizer.privateMode ? entry.name.replaceAll(/./g, '*') : entry.name, entry.initiatorType, entry.transferSize, // @ts-ignore