Merge pull request #524 from openreplay/hide-containers-rule

feat(ui): add option to mask entire HTML/SVG containers and their children tree
This commit is contained in:
Alex K 2022-06-22 12:26:15 +02:00 committed by GitHub
commit e56fee3134
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 37 additions and 11 deletions

View file

@ -1,5 +1,5 @@
import {
RemoveNodeAttribute,
import {
RemoveNodeAttribute,
SetNodeAttribute,
SetNodeAttributeURLBased,
SetCSSDataURLBased,
@ -220,9 +220,10 @@ export default abstract class Observer {
}
const parent = node.parentNode;
let parentID: number | undefined;
// Disable parent check for the upper context HTMLHtmlElement, because it is root there... (before)
// TODO: get rid of "special" cases (there is an issue with CreateDocument altered behaviour though)
// TODO: Clean the logic (though now it workd fine)
// TODO: Clean the logic (though now it workd fine)
if (!isInstance(node, HTMLHtmlElement) || !this.isTopContext) {
if (parent === null) {
this.unbindNode(node);
@ -238,6 +239,9 @@ export default abstract class Observer {
return false;
}
this.app.sanitizer.handleNode(id, parentID, node);
if (this.app.sanitizer.isMaskedContainer(parentID)) {
return false;
}
}
let sibling = node.previousSibling;
while (sibling !== null) {
@ -259,20 +263,29 @@ export default abstract class Observer {
}
if (isNew === true) {
if (isInstance(node, Element)) {
let el: Element = node
if (parentID !== undefined) {
this.app.send(new
if (this.app.sanitizer.isMaskedContainer(id)) {
const width = el.clientWidth;
const height = el.clientHeight;
el = node.cloneNode() as Element;
(el as HTMLElement | SVGElement).style.width = width + 'px';
(el as HTMLElement | SVGElement).style.height = height + 'px';
}
this.app.send(new
CreateElementNode(
id,
parentID,
index,
node.tagName,
el.tagName,
isSVGElement(node),
),
);
}
for (let i = 0; i < node.attributes.length; i++) {
const attr = node.attributes[i];
this.sendNodeAttribute(id, node, attr.nodeName, attr.value);
for (let i = 0; i < el.attributes.length; i++) {
const attr = el.attributes[i];
this.sendNodeAttribute(id, el, attr.nodeName, attr.value);
}
} else if (isInstance(node, Text)) {
// for text node id != 0, hence parentID !== undefined and parent is Element

View file

@ -9,6 +9,7 @@ export interface Options {
export default class Sanitizer {
private readonly masked: Set<number> = new Set();
private readonly maskedContainers: Set<number> = new Set();
private readonly options: Options;
constructor(private readonly app: App, options: Partial<Options>) {
@ -21,10 +22,18 @@ export default class Sanitizer {
handleNode(id: number, parentID: number, node: Node) {
if (
this.masked.has(parentID) ||
(isElementNode(node) && hasOpenreplayAttribute(node, 'masked'))
(isElementNode(node) &&
hasOpenreplayAttribute(node, 'masked'))
) {
this.masked.add(id);
}
if (
this.maskedContainers.has(parentID) ||
(isElementNode(node) &&
hasOpenreplayAttribute(node, 'htmlmasked'))
) {
this.maskedContainers.add(id);
}
}
sanitize(id: number, data: string): string {
@ -34,7 +43,7 @@ export default class Sanitizer {
/[^\f\n\r\t\v\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]/g,
'█',
);
}
}
if (this.options.obscureTextNumbers) {
data = data.replace(/\d/g, '0');
}
@ -51,6 +60,9 @@ export default class Sanitizer {
isMasked(id: number): boolean {
return this.masked.has(id);
}
isMaskedContainer(id: number) {
return this.maskedContainers.has(id);
}
getInnerTextSecure(el: HTMLElement): string {
const id = this.app.nodes.getID(el)
@ -61,6 +73,7 @@ export default class Sanitizer {
clear(): void {
this.masked.clear();
this.maskedContainers.clear();
}
}
}