diff --git a/tracker/tracker/src/main/app/observer/observer.ts b/tracker/tracker/src/main/app/observer/observer.ts index dcbba6365..ba6e46895 100644 --- a/tracker/tracker/src/main/app/observer/observer.ts +++ b/tracker/tracker/src/main/app/observer/observer.ts @@ -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 diff --git a/tracker/tracker/src/main/app/sanitizer.ts b/tracker/tracker/src/main/app/sanitizer.ts index 3e2e275ac..d870cfaca 100644 --- a/tracker/tracker/src/main/app/sanitizer.ts +++ b/tracker/tracker/src/main/app/sanitizer.ts @@ -9,6 +9,7 @@ export interface Options { export default class Sanitizer { private readonly masked: Set = new Set(); + private readonly maskedContainers: Set = new Set(); private readonly options: Options; constructor(private readonly app: App, options: Partial) { @@ -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(); } -} \ No newline at end of file +}