Add options to mask texts (#540)
* feat: add options to mask texts * feat: add the default mask function * refactor: rename options to identify the difference between mask text and mask input * test: add tests about masking * doc: add options about masking * chore: bump up rrweb-snapshot version
This commit is contained in:
10
guide.md
10
guide.md
@@ -142,10 +142,13 @@ The parameter of `rrweb.record` accepts the following options.
|
||||
| checkoutEveryNms | - | take a full snapshot after every N ms<br />refer to the [checkout](#checkout) chapter |
|
||||
| blockClass | 'rr-block' | Use a string or RegExp to configure which elements should be blocked, refer to the [privacy](#privacy) chapter |
|
||||
| ignoreClass | 'rr-ignore' | Use a string or RegExp to configure which elements should be ignored, refer to the [privacy](#privacy) chapter |
|
||||
| blockSelector | null | Use a string or RegExp to configure which selector should be blocked, refer to the [privacy](#privacy) chapter |
|
||||
| maskTextClass | 'rr-mask' | Use a string or RegExp to configure which elements should be masked, refer to the [privacy](#privacy) chapter |
|
||||
| blockSelector | null | Use a string to configure which selector should be blocked, refer to the [privacy](#privacy) chapter |
|
||||
| maskTextSelector | null | Use a string to configure which selector should be masked, refer to the [privacy](#privacy) chapter |
|
||||
| maskAllInputs | false | mask all input content as \* |
|
||||
| maskInputOptions | {} | mask some kinds of input \*<br />refer to the [list](https://github.com/rrweb-io/rrweb-snapshot/blob/6728d12b3cddd96951c86d948578f99ada5749ff/src/types.ts#L72) |
|
||||
| maskInputFn | - | customize mask input content recording logic |
|
||||
| maskInputOptions | {} | mask some kinds of input \*<br />refer to the [list](https://github.com/rrweb-io/rrweb-snapshot/blob/6728d12b3cddd96951c86d948578f99ada5749ff/src/types.ts#L72) |
|
||||
| maskInputFn | - | customize mask input content recording logic |
|
||||
| maskTextFn | - | customize mask text content recording logic |
|
||||
| slimDOMOptions | {} | remove unnecessary parts of the DOM <br />refer to the [list](https://github.com/rrweb-io/rrweb-snapshot/blob/6728d12b3cddd96951c86d948578f99ada5749ff/src/types.ts#L91) |
|
||||
| inlineStylesheet | true | whether to inline the stylesheet in the events |
|
||||
| hooks | {} | hooks for events<br />refer to the [list](https://github.com/rrweb-io/rrweb/blob/9488deb6d54a5f04350c063d942da5e96ab74075/src/types.ts#L207) |
|
||||
@@ -163,6 +166,7 @@ You may find some contents on the webpage which are not willing to be recorded,
|
||||
- An element with the class name `.rr-ignore` will not record its input events.
|
||||
- `input[type="password"]` will be ignored as default.
|
||||
- Mask options to mask the content in input elements.
|
||||
- A text of elements with the class name `.rr-mask` and its children will be masked.
|
||||
|
||||
#### Checkout
|
||||
|
||||
|
||||
@@ -66,6 +66,6 @@
|
||||
"@xstate/fsm": "^1.4.0",
|
||||
"fflate": "^0.4.4",
|
||||
"mitt": "^1.1.3",
|
||||
"rrweb-snapshot": "^1.1.1"
|
||||
"rrweb-snapshot": "^1.1.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,11 +43,14 @@ function record<T = eventWithTime>(
|
||||
blockClass = 'rr-block',
|
||||
blockSelector = null,
|
||||
ignoreClass = 'rr-ignore',
|
||||
maskTextClass = 'rr-mask',
|
||||
maskTextSelector = null,
|
||||
inlineStylesheet = true,
|
||||
maskAllInputs,
|
||||
maskInputOptions: _maskInputOptions,
|
||||
slimDOMOptions: _slimDOMOptions,
|
||||
maskInputFn,
|
||||
maskTextFn,
|
||||
hooks,
|
||||
packFn,
|
||||
sampling = {},
|
||||
@@ -203,8 +206,11 @@ function record<T = eventWithTime>(
|
||||
bypassOptions: {
|
||||
blockClass,
|
||||
blockSelector,
|
||||
maskTextClass,
|
||||
maskTextSelector,
|
||||
inlineStylesheet,
|
||||
maskInputOptions,
|
||||
maskTextFn,
|
||||
recordCanvas,
|
||||
slimDOMOptions,
|
||||
iframeManager,
|
||||
@@ -228,8 +234,11 @@ function record<T = eventWithTime>(
|
||||
const [node, idNodeMap] = snapshot(document, {
|
||||
blockClass,
|
||||
blockSelector,
|
||||
maskTextClass,
|
||||
maskTextSelector,
|
||||
inlineStylesheet,
|
||||
maskAllInputs: maskInputOptions,
|
||||
maskTextFn,
|
||||
slimDOM: slimDOMOptions,
|
||||
recordCanvas,
|
||||
onSerialize: (n) => {
|
||||
@@ -396,6 +405,8 @@ function record<T = eventWithTime>(
|
||||
),
|
||||
blockClass,
|
||||
ignoreClass,
|
||||
maskTextClass,
|
||||
maskTextSelector,
|
||||
maskInputOptions,
|
||||
inlineStylesheet,
|
||||
sampling,
|
||||
@@ -403,6 +414,7 @@ function record<T = eventWithTime>(
|
||||
collectFonts,
|
||||
doc,
|
||||
maskInputFn,
|
||||
maskTextFn,
|
||||
logOptions,
|
||||
blockSelector,
|
||||
slimDOMOptions,
|
||||
|
||||
@@ -6,15 +6,18 @@ import {
|
||||
SlimDOMOptions,
|
||||
IGNORED_NODE,
|
||||
isShadowRoot,
|
||||
needMaskingText,
|
||||
} from 'rrweb-snapshot';
|
||||
import {
|
||||
mutationRecord,
|
||||
blockClass,
|
||||
maskTextClass,
|
||||
mutationCallBack,
|
||||
textCursor,
|
||||
attributeCursor,
|
||||
removedNodeMutation,
|
||||
addedNodeMutation,
|
||||
MaskTextFn,
|
||||
} from '../types';
|
||||
import {
|
||||
mirror,
|
||||
@@ -159,8 +162,11 @@ export default class MutationBuffer {
|
||||
private emissionCallback: mutationCallBack;
|
||||
private blockClass: blockClass;
|
||||
private blockSelector: string | null;
|
||||
private maskTextClass: maskTextClass;
|
||||
private maskTextSelector: string | null;
|
||||
private inlineStylesheet: boolean;
|
||||
private maskInputOptions: MaskInputOptions;
|
||||
private maskTextFn: MaskTextFn | undefined;
|
||||
private recordCanvas: boolean;
|
||||
private slimDOMOptions: SlimDOMOptions;
|
||||
private doc: Document;
|
||||
@@ -172,8 +178,11 @@ export default class MutationBuffer {
|
||||
cb: mutationCallBack,
|
||||
blockClass: blockClass,
|
||||
blockSelector: string | null,
|
||||
maskTextClass: maskTextClass,
|
||||
maskTextSelector: string | null,
|
||||
inlineStylesheet: boolean,
|
||||
maskInputOptions: MaskInputOptions,
|
||||
maskTextFn: MaskTextFn | undefined,
|
||||
recordCanvas: boolean,
|
||||
slimDOMOptions: SlimDOMOptions,
|
||||
doc: Document,
|
||||
@@ -182,8 +191,11 @@ export default class MutationBuffer {
|
||||
) {
|
||||
this.blockClass = blockClass;
|
||||
this.blockSelector = blockSelector;
|
||||
this.maskTextClass = maskTextClass;
|
||||
this.maskTextSelector = maskTextSelector;
|
||||
this.inlineStylesheet = inlineStylesheet;
|
||||
this.maskInputOptions = maskInputOptions;
|
||||
this.maskTextFn = maskTextFn;
|
||||
this.recordCanvas = recordCanvas;
|
||||
this.slimDOMOptions = slimDOMOptions;
|
||||
this.emissionCallback = cb;
|
||||
@@ -266,9 +278,12 @@ export default class MutationBuffer {
|
||||
map: mirror.map,
|
||||
blockClass: this.blockClass,
|
||||
blockSelector: this.blockSelector,
|
||||
maskTextClass: this.maskTextClass,
|
||||
maskTextSelector: this.maskTextSelector,
|
||||
skipChild: true,
|
||||
inlineStylesheet: this.inlineStylesheet,
|
||||
maskInputOptions: this.maskInputOptions,
|
||||
maskTextFn: this.maskTextFn,
|
||||
slimDOMOptions: this.slimDOMOptions,
|
||||
recordCanvas: this.recordCanvas,
|
||||
onSerialize: (currentN) => {
|
||||
@@ -409,7 +424,16 @@ export default class MutationBuffer {
|
||||
const value = m.target.textContent;
|
||||
if (!isBlocked(m.target, this.blockClass) && value !== m.oldValue) {
|
||||
this.texts.push({
|
||||
value,
|
||||
value:
|
||||
needMaskingText(
|
||||
m.target,
|
||||
this.maskTextClass,
|
||||
this.maskTextSelector,
|
||||
) && value
|
||||
? this.maskTextFn
|
||||
? this.maskTextFn(value)
|
||||
: value.replace(/[\S]/g, '*')
|
||||
: value,
|
||||
node: m.target,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
inputCallback,
|
||||
hookResetter,
|
||||
blockClass,
|
||||
maskTextClass,
|
||||
IncrementalSource,
|
||||
hooksParam,
|
||||
Arguments,
|
||||
@@ -36,6 +37,7 @@ import {
|
||||
fontCallback,
|
||||
fontParam,
|
||||
MaskInputFn,
|
||||
MaskTextFn,
|
||||
logCallback,
|
||||
LogRecordOptions,
|
||||
Logger,
|
||||
@@ -62,8 +64,11 @@ export function initMutationObserver(
|
||||
doc: Document,
|
||||
blockClass: blockClass,
|
||||
blockSelector: string | null,
|
||||
maskTextClass: maskTextClass,
|
||||
maskTextSelector: string | null,
|
||||
inlineStylesheet: boolean,
|
||||
maskInputOptions: MaskInputOptions,
|
||||
maskTextFn: MaskTextFn | undefined,
|
||||
recordCanvas: boolean,
|
||||
slimDOMOptions: SlimDOMOptions,
|
||||
iframeManager: IframeManager,
|
||||
@@ -77,8 +82,11 @@ export function initMutationObserver(
|
||||
cb,
|
||||
blockClass,
|
||||
blockSelector,
|
||||
maskTextClass,
|
||||
maskTextSelector,
|
||||
inlineStylesheet,
|
||||
maskInputOptions,
|
||||
maskTextFn,
|
||||
recordCanvas,
|
||||
slimDOMOptions,
|
||||
doc,
|
||||
@@ -777,8 +785,11 @@ export function initObservers(
|
||||
o.doc,
|
||||
o.blockClass,
|
||||
o.blockSelector,
|
||||
o.maskTextClass,
|
||||
o.maskTextSelector,
|
||||
o.inlineStylesheet,
|
||||
o.maskInputOptions,
|
||||
o.maskTextFn,
|
||||
o.recordCanvas,
|
||||
o.slimDOMOptions,
|
||||
o.iframeManager,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { mutationCallBack, blockClass } from '../types';
|
||||
import { mutationCallBack, blockClass, maskTextClass, MaskTextFn } from '../types';
|
||||
import { MaskInputOptions, SlimDOMOptions } from 'rrweb-snapshot';
|
||||
import { IframeManager } from './iframe-manager';
|
||||
import { initMutationObserver } from './observer';
|
||||
@@ -6,8 +6,11 @@ import { initMutationObserver } from './observer';
|
||||
type BypassOptions = {
|
||||
blockClass: blockClass;
|
||||
blockSelector: string | null;
|
||||
maskTextClass: maskTextClass;
|
||||
maskTextSelector: string | null;
|
||||
inlineStylesheet: boolean;
|
||||
maskInputOptions: MaskInputOptions;
|
||||
maskTextFn: MaskTextFn | undefined;
|
||||
recordCanvas: boolean;
|
||||
slimDOMOptions: SlimDOMOptions;
|
||||
iframeManager: IframeManager;
|
||||
@@ -31,8 +34,11 @@ export class ShadowDomManager {
|
||||
doc,
|
||||
this.bypassOptions.blockClass,
|
||||
this.bypassOptions.blockSelector,
|
||||
this.bypassOptions.maskTextClass,
|
||||
this.bypassOptions.maskTextSelector,
|
||||
this.bypassOptions.inlineStylesheet,
|
||||
this.bypassOptions.maskInputOptions,
|
||||
this.bypassOptions.maskTextFn,
|
||||
this.bypassOptions.recordCanvas,
|
||||
this.bypassOptions.slimDOMOptions,
|
||||
this.bypassOptions.iframeManager,
|
||||
|
||||
10
src/types.ts
10
src/types.ts
@@ -163,6 +163,8 @@ export type eventWithTime = event & {
|
||||
|
||||
export type blockClass = string | RegExp;
|
||||
|
||||
export type maskTextClass = string | RegExp;
|
||||
|
||||
export type SamplingStrategy = Partial<{
|
||||
/**
|
||||
* false means not to record mouse/touch move events
|
||||
@@ -196,9 +198,12 @@ export type recordOptions<T> = {
|
||||
blockClass?: blockClass;
|
||||
blockSelector?: string;
|
||||
ignoreClass?: string;
|
||||
maskTextClass?: maskTextClass;
|
||||
maskTextSelector?: string;
|
||||
maskAllInputs?: boolean;
|
||||
maskInputOptions?: MaskInputOptions;
|
||||
maskInputFn?: MaskInputFn;
|
||||
maskTextFn?: MaskTextFn;
|
||||
slimDOMOptions?: SlimDOMOptions | 'all' | true;
|
||||
inlineStylesheet?: boolean;
|
||||
hooks?: hooksParam;
|
||||
@@ -222,8 +227,11 @@ export type observerParam = {
|
||||
blockClass: blockClass;
|
||||
blockSelector: string | null;
|
||||
ignoreClass: string;
|
||||
maskTextClass: maskTextClass;
|
||||
maskTextSelector: string | null;
|
||||
maskInputOptions: MaskInputOptions;
|
||||
maskInputFn?: MaskInputFn;
|
||||
maskTextFn?: MaskTextFn;
|
||||
inlineStylesheet: boolean;
|
||||
styleSheetRuleCb: styleSheetRuleCallback;
|
||||
canvasMutationCb: canvasMutationCallback;
|
||||
@@ -583,6 +591,8 @@ export enum ReplayerEvents {
|
||||
|
||||
export type MaskInputFn = (text: string) => string;
|
||||
|
||||
export type MaskTextFn = (text: string) => string;
|
||||
|
||||
// store the state that would be changed during the process(unmount from dom and mount again)
|
||||
export type ElementState = {
|
||||
// [scrollLeft,scrollTop]
|
||||
|
||||
@@ -4111,6 +4111,746 @@ exports[`mask 1`] = `
|
||||
]"
|
||||
`;
|
||||
|
||||
exports[`mask-character-data 1`] = `
|
||||
"[
|
||||
{
|
||||
\\"type\\": 0,
|
||||
\\"data\\": {}
|
||||
},
|
||||
{
|
||||
\\"type\\": 1,
|
||||
\\"data\\": {}
|
||||
},
|
||||
{
|
||||
\\"type\\": 4,
|
||||
\\"data\\": {
|
||||
\\"href\\": \\"about:blank\\",
|
||||
\\"width\\": 1920,
|
||||
\\"height\\": 1080
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"data\\": {
|
||||
\\"node\\": {
|
||||
\\"type\\": 0,
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"html\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"head\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 3
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"body\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 5
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"p\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"mutation observer\\",
|
||||
\\"id\\": 7
|
||||
}
|
||||
],
|
||||
\\"id\\": 6
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 8
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"ul\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 10
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"li\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 11
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 12
|
||||
}
|
||||
],
|
||||
\\"id\\": 9
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n\\\\n \\",
|
||||
\\"id\\": 13
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"script\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"SCRIPT_PLACEHOLDER\\",
|
||||
\\"id\\": 15
|
||||
}
|
||||
],
|
||||
\\"id\\": 14
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\\\n \\",
|
||||
\\"id\\": 16
|
||||
}
|
||||
],
|
||||
\\"id\\": 4
|
||||
}
|
||||
],
|
||||
\\"id\\": 2
|
||||
}
|
||||
],
|
||||
\\"id\\": 1
|
||||
},
|
||||
\\"initialOffset\\": {
|
||||
\\"left\\": 0,
|
||||
\\"top\\": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 0,
|
||||
\\"texts\\": [],
|
||||
\\"attributes\\": [
|
||||
{
|
||||
\\"id\\": 6,
|
||||
\\"attributes\\": {
|
||||
\\"class\\": \\"rr-mask\\"
|
||||
}
|
||||
}
|
||||
],
|
||||
\\"removes\\": [
|
||||
{
|
||||
\\"parentId\\": 6,
|
||||
\\"id\\": 7
|
||||
}
|
||||
],
|
||||
\\"adds\\": [
|
||||
{
|
||||
\\"parentId\\": 9,
|
||||
\\"nextId\\": null,
|
||||
\\"node\\": {
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"li\\",
|
||||
\\"attributes\\": {
|
||||
\\"class\\": \\"rr-mask\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 17
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 17,
|
||||
\\"nextId\\": null,
|
||||
\\"node\\": {
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"*** **** ****\\",
|
||||
\\"id\\": 18
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 6,
|
||||
\\"nextId\\": null,
|
||||
\\"node\\": {
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"*******\\",
|
||||
\\"id\\": 19
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]"
|
||||
`;
|
||||
|
||||
exports[`mask-text 1`] = `
|
||||
"[
|
||||
{
|
||||
\\"type\\": 0,
|
||||
\\"data\\": {}
|
||||
},
|
||||
{
|
||||
\\"type\\": 1,
|
||||
\\"data\\": {}
|
||||
},
|
||||
{
|
||||
\\"type\\": 4,
|
||||
\\"data\\": {
|
||||
\\"href\\": \\"about:blank\\",
|
||||
\\"width\\": 1920,
|
||||
\\"height\\": 1080
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"data\\": {
|
||||
\\"node\\": {
|
||||
\\"type\\": 0,
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 1,
|
||||
\\"name\\": \\"html\\",
|
||||
\\"publicId\\": \\"\\",
|
||||
\\"systemId\\": \\"\\",
|
||||
\\"id\\": 2
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"html\\",
|
||||
\\"attributes\\": {
|
||||
\\"lang\\": \\"en\\"
|
||||
},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"head\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 5
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"meta\\",
|
||||
\\"attributes\\": {
|
||||
\\"charset\\": \\"UTF-8\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 6
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 7
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"meta\\",
|
||||
\\"attributes\\": {
|
||||
\\"name\\": \\"viewport\\",
|
||||
\\"content\\": \\"width=device-width, initial-scale=1.0\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 8
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 9
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"meta\\",
|
||||
\\"attributes\\": {
|
||||
\\"http-equiv\\": \\"X-UA-Compatible\\",
|
||||
\\"content\\": \\"ie=edge\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 10
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 11
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"title\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"Mask text\\",
|
||||
\\"id\\": 13
|
||||
}
|
||||
],
|
||||
\\"id\\": 12
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 14
|
||||
}
|
||||
],
|
||||
\\"id\\": 4
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 15
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"body\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 17
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"p\\",
|
||||
\\"attributes\\": {
|
||||
\\"class\\": \\"rr-mask\\"
|
||||
},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"*****\\",
|
||||
\\"id\\": 19
|
||||
}
|
||||
],
|
||||
\\"id\\": 18
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 20
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"div\\",
|
||||
\\"attributes\\": {
|
||||
\\"class\\": \\"rr-mask\\"
|
||||
},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 22
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"span\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"*****\\",
|
||||
\\"id\\": 24
|
||||
}
|
||||
],
|
||||
\\"id\\": 23
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 25
|
||||
}
|
||||
],
|
||||
\\"id\\": 21
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 26
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"div\\",
|
||||
\\"attributes\\": {
|
||||
\\"data-masking\\": \\"true\\"
|
||||
},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 28
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"div\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 30
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"div\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"*****\\",
|
||||
\\"id\\": 32
|
||||
}
|
||||
],
|
||||
\\"id\\": 31
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 33
|
||||
}
|
||||
],
|
||||
\\"id\\": 29
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 34
|
||||
}
|
||||
],
|
||||
\\"id\\": 27
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\\\n \\",
|
||||
\\"id\\": 35
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"script\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"SCRIPT_PLACEHOLDER\\",
|
||||
\\"id\\": 37
|
||||
}
|
||||
],
|
||||
\\"id\\": 36
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\\\n \\\\n\\\\n\\",
|
||||
\\"id\\": 38
|
||||
}
|
||||
],
|
||||
\\"id\\": 16
|
||||
}
|
||||
],
|
||||
\\"id\\": 3
|
||||
}
|
||||
],
|
||||
\\"id\\": 1
|
||||
},
|
||||
\\"initialOffset\\": {
|
||||
\\"left\\": 0,
|
||||
\\"top\\": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
]"
|
||||
`;
|
||||
|
||||
exports[`mask-text-fn 1`] = `
|
||||
"[
|
||||
{
|
||||
\\"type\\": 0,
|
||||
\\"data\\": {}
|
||||
},
|
||||
{
|
||||
\\"type\\": 1,
|
||||
\\"data\\": {}
|
||||
},
|
||||
{
|
||||
\\"type\\": 4,
|
||||
\\"data\\": {
|
||||
\\"href\\": \\"about:blank\\",
|
||||
\\"width\\": 1920,
|
||||
\\"height\\": 1080
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"data\\": {
|
||||
\\"node\\": {
|
||||
\\"type\\": 0,
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 1,
|
||||
\\"name\\": \\"html\\",
|
||||
\\"publicId\\": \\"\\",
|
||||
\\"systemId\\": \\"\\",
|
||||
\\"id\\": 2
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"html\\",
|
||||
\\"attributes\\": {
|
||||
\\"lang\\": \\"en\\"
|
||||
},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"head\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 5
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"meta\\",
|
||||
\\"attributes\\": {
|
||||
\\"charset\\": \\"UTF-8\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 6
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 7
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"meta\\",
|
||||
\\"attributes\\": {
|
||||
\\"name\\": \\"viewport\\",
|
||||
\\"content\\": \\"width=device-width, initial-scale=1.0\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 8
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 9
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"meta\\",
|
||||
\\"attributes\\": {
|
||||
\\"http-equiv\\": \\"X-UA-Compatible\\",
|
||||
\\"content\\": \\"ie=edge\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 10
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 11
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"title\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"Mask text\\",
|
||||
\\"id\\": 13
|
||||
}
|
||||
],
|
||||
\\"id\\": 12
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 14
|
||||
}
|
||||
],
|
||||
\\"id\\": 4
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 15
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"body\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 17
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"p\\",
|
||||
\\"attributes\\": {
|
||||
\\"class\\": \\"rr-mask\\"
|
||||
},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"****1\\",
|
||||
\\"id\\": 19
|
||||
}
|
||||
],
|
||||
\\"id\\": 18
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 20
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"div\\",
|
||||
\\"attributes\\": {
|
||||
\\"class\\": \\"rr-mask\\"
|
||||
},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 22
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"span\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"****2\\",
|
||||
\\"id\\": 24
|
||||
}
|
||||
],
|
||||
\\"id\\": 23
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 25
|
||||
}
|
||||
],
|
||||
\\"id\\": 21
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 26
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"div\\",
|
||||
\\"attributes\\": {
|
||||
\\"data-masking\\": \\"true\\"
|
||||
},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 28
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"div\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 30
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"div\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"****3\\",
|
||||
\\"id\\": 32
|
||||
}
|
||||
],
|
||||
\\"id\\": 31
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 33
|
||||
}
|
||||
],
|
||||
\\"id\\": 29
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 34
|
||||
}
|
||||
],
|
||||
\\"id\\": 27
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\\\n \\",
|
||||
\\"id\\": 35
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"script\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"SCRIPT_PLACEHOLDER\\",
|
||||
\\"id\\": 37
|
||||
}
|
||||
],
|
||||
\\"id\\": 36
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\\\n \\\\n\\\\n\\",
|
||||
\\"id\\": 38
|
||||
}
|
||||
],
|
||||
\\"id\\": 16
|
||||
}
|
||||
],
|
||||
\\"id\\": 3
|
||||
}
|
||||
],
|
||||
\\"id\\": 1
|
||||
},
|
||||
\\"initialOffset\\": {
|
||||
\\"left\\": 0,
|
||||
\\"top\\": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
]"
|
||||
`;
|
||||
|
||||
exports[`maskInputOptions 1`] = `
|
||||
"[
|
||||
{
|
||||
|
||||
20
test/html/mask-text.html
Normal file
20
test/html/mask-text.html
Normal file
@@ -0,0 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
|
||||
<title>Mask text</title>
|
||||
</head>
|
||||
<body>
|
||||
<p class="rr-mask">mask1</p>
|
||||
<div class="rr-mask">
|
||||
<span>mask2</span>
|
||||
</div>
|
||||
<div data-masking="true">
|
||||
<div>
|
||||
<div>mask3</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -72,8 +72,10 @@ describe('record integration tests', function (this: ISuite) {
|
||||
emit: event => {
|
||||
window.snapshots.push(event);
|
||||
},
|
||||
maskTextSelector: ${JSON.stringify(options.maskTextSelector)},
|
||||
maskAllInputs: ${options.maskAllInputs},
|
||||
maskInputOptions: ${JSON.stringify(options.maskAllInputs)},
|
||||
maskTextFn: ${options.maskTextFn},
|
||||
recordCanvas: ${options.recordCanvas},
|
||||
recordLog: ${options.recordLog},
|
||||
});
|
||||
@@ -456,4 +458,52 @@ describe('record integration tests', function (this: ISuite) {
|
||||
const snapshots = await page.evaluate('window.snapshots');
|
||||
assertSnapshot(snapshots, __filename, 'shadow-dom');
|
||||
});
|
||||
|
||||
it('should mask texts', async () => {
|
||||
const page: puppeteer.Page = await this.browser.newPage();
|
||||
await page.goto('about:blank');
|
||||
await page.setContent(
|
||||
getHtml.call(this, 'mask-text.html', {
|
||||
maskTextSelector: '[data-masking="true"]',
|
||||
}),
|
||||
);
|
||||
|
||||
const snapshots = await page.evaluate('window.snapshots');
|
||||
assertSnapshot(snapshots, __filename, 'mask-text');
|
||||
});
|
||||
|
||||
it('should mask texts using maskTextFn', async () => {
|
||||
const page: puppeteer.Page = await this.browser.newPage();
|
||||
await page.goto('about:blank');
|
||||
await page.setContent(
|
||||
getHtml.call(this, 'mask-text.html', {
|
||||
maskTextSelector: '[data-masking="true"]',
|
||||
maskTextFn: (t: string) => t.replace(/[a-z]/g, '*'),
|
||||
}),
|
||||
);
|
||||
|
||||
const snapshots = await page.evaluate('window.snapshots');
|
||||
assertSnapshot(snapshots, __filename, 'mask-text-fn');
|
||||
});
|
||||
|
||||
it('can mask character data mutations', async () => {
|
||||
const page: puppeteer.Page = await this.browser.newPage();
|
||||
await page.goto('about:blank');
|
||||
await page.setContent(getHtml.call(this, 'mutation-observer.html'));
|
||||
|
||||
await page.evaluate(() => {
|
||||
const li = document.createElement('li');
|
||||
const ul = document.querySelector('ul') as HTMLUListElement;
|
||||
const p = document.querySelector('p') as HTMLParagraphElement;
|
||||
[li, p].forEach((element) => {
|
||||
element.className = 'rr-mask';
|
||||
});
|
||||
ul.appendChild(li);
|
||||
li.innerText = 'new list item';
|
||||
p.innerText = 'mutated';
|
||||
});
|
||||
|
||||
const snapshots = await page.evaluate('window.snapshots');
|
||||
assertSnapshot(snapshots, __filename, 'mask-character-data');
|
||||
});
|
||||
});
|
||||
|
||||
8
typings/types.d.ts
vendored
8
typings/types.d.ts
vendored
@@ -3,6 +3,7 @@ import { serializedNodeWithId, idNodeMap, INode, MaskInputOptions, SlimDOMOption
|
||||
import { PackFn, UnpackFn } from './packer/base';
|
||||
import { FontFaceDescriptors } from 'css-font-loading-module';
|
||||
import { IframeManager } from './record/iframe-manager';
|
||||
import { MaskTextFn } from '../src/types';
|
||||
export declare enum EventType {
|
||||
DomContentLoaded = 0,
|
||||
Load = 1,
|
||||
@@ -109,6 +110,7 @@ export declare type eventWithTime = event & {
|
||||
delay?: number;
|
||||
};
|
||||
export declare type blockClass = string | RegExp;
|
||||
export declare type maskTextClass = string | RegExp;
|
||||
export declare type SamplingStrategy = Partial<{
|
||||
mousemove: boolean | number;
|
||||
mousemoveCallback: number;
|
||||
@@ -123,9 +125,12 @@ export declare type recordOptions<T> = {
|
||||
blockClass?: blockClass;
|
||||
blockSelector?: string;
|
||||
ignoreClass?: string;
|
||||
maskTextClass?: maskTextClass;
|
||||
maskTextSelector?: string | null;
|
||||
maskAllInputs?: boolean;
|
||||
maskInputOptions?: MaskInputOptions;
|
||||
maskInputFn?: MaskInputFn;
|
||||
maskTextFn?: MaskTextFn;
|
||||
slimDOMOptions?: SlimDOMOptions | 'all' | true;
|
||||
inlineStylesheet?: boolean;
|
||||
hooks?: hooksParam;
|
||||
@@ -146,9 +151,12 @@ export declare type observerParam = {
|
||||
mediaInteractionCb: mediaInteractionCallback;
|
||||
blockClass: blockClass;
|
||||
blockSelector: string | null;
|
||||
maskTextClass: maskTextClass;
|
||||
maskTextSelector: string | null;
|
||||
ignoreClass: string;
|
||||
maskInputOptions: MaskInputOptions;
|
||||
maskInputFn?: MaskInputFn;
|
||||
maskTextFn?: MaskTextFn;
|
||||
inlineStylesheet: boolean;
|
||||
styleSheetRuleCb: styleSheetRuleCallback;
|
||||
canvasMutationCb: canvasMutationCallback;
|
||||
|
||||
@@ -2767,10 +2767,10 @@ rollup@^2.3.3:
|
||||
optionalDependencies:
|
||||
fsevents "~2.1.2"
|
||||
|
||||
rrweb-snapshot@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/rrweb-snapshot/-/rrweb-snapshot-1.1.1.tgz#71da8792f43b8bd7017851edcd02e3d7c7cfef9f"
|
||||
integrity sha512-xRX7s2/MA/Ifnul4ImAquD1w/Nkz6WOACm3xdKDdQrCD/xKdgcu1yWoJ8eSIXyfVSuIt4VfrhxJdeHyhC1gmGQ==
|
||||
rrweb-snapshot@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/rrweb-snapshot/-/rrweb-snapshot-1.1.2.tgz#e9a5ce11f2dba8ff58e22f7f0b96954ef9133be2"
|
||||
integrity sha512-J/BCClbk1fs9ilU9Bn8J/vUdumUllvNsmC1rVSz7rxqVzo+MthpI83gll1S3rBQVcxmQcLGmJWpkyO7M2rGyTw==
|
||||
|
||||
run-async@^2.2.0:
|
||||
version "2.4.1"
|
||||
|
||||
Reference in New Issue
Block a user