From 64191328d65ad07021ceee5bd6150ade2cf3a987 Mon Sep 17 00:00:00 2001 From: Eoghan Murray Date: Fri, 29 Nov 2024 16:45:02 +0000 Subject: [PATCH 1/3] Fix for #1596 - we missed a path where masking could be skipped --- .changeset/textarea-inner-html.md | 5 + packages/rrweb/src/record/mutation.ts | 10 +- .../__snapshots__/integration.test.ts.snap | 133 +++++++++++++++++- packages/rrweb/test/integration.test.ts | 37 +++++ 4 files changed, 180 insertions(+), 5 deletions(-) create mode 100644 .changeset/textarea-inner-html.md diff --git a/.changeset/textarea-inner-html.md b/.changeset/textarea-inner-html.md new file mode 100644 index 0000000000..eb7a005451 --- /dev/null +++ b/.changeset/textarea-inner-html.md @@ -0,0 +1,5 @@ +--- +'rrweb': patch +--- + +#1596 Add masking for innerText mutations on textarea elements diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts index 42170b4940..49e9bf5a5b 100644 --- a/packages/rrweb/src/record/mutation.ts +++ b/packages/rrweb/src/record/mutation.ts @@ -533,10 +533,18 @@ export default class MutationBuffer { this.attributes.push(item); this.attributeMap.set(textarea, item); } - item.attributes.value = Array.from( + let value = Array.from( dom.childNodes(textarea), (cn) => dom.textContent(cn) || '', ).join(''); + item.attributes.value = maskInputValue({ + element: textarea, + maskInputOptions: this.maskInputOptions, + tagName: textarea.tagName, + type: getInputType(textarea), + value, + maskInputFn: this.maskInputFn, + }); }; private processMutation = (m: mutationRecord) => { diff --git a/packages/rrweb/test/__snapshots__/integration.test.ts.snap b/packages/rrweb/test/__snapshots__/integration.test.ts.snap index 4fa6c2f35b..738f2fe8e2 100644 --- a/packages/rrweb/test/__snapshots__/integration.test.ts.snap +++ b/packages/rrweb/test/__snapshots__/integration.test.ts.snap @@ -9964,6 +9964,21 @@ exports[`record integration tests > should not record input values if dynamicall { \\"parentId\\": 14, \\"nextId\\": 16, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"textarea\\", + \\"attributes\\": { + \\"id\\": \\"textarea\\", + \\"size\\": \\"50\\", + \\"value\\": \\"*************************\\" + }, + \\"childNodes\\": [], + \\"id\\": 21 + } + }, + { + \\"parentId\\": 14, + \\"nextId\\": 21, \\"node\\": { \\"type\\": 2, \\"tagName\\": \\"input\\", @@ -9973,7 +9988,7 @@ exports[`record integration tests > should not record input values if dynamicall \\"value\\": \\"**********************\\" }, \\"childNodes\\": [], - \\"id\\": 21 + \\"id\\": 22 } } ] @@ -9985,6 +10000,15 @@ exports[`record integration tests > should not record input values if dynamicall \\"source\\": 5, \\"text\\": \\"**********************\\", \\"isChecked\\": false, + \\"id\\": 22 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"*************************\\", + \\"isChecked\\": false, \\"id\\": 21 } }, @@ -9993,7 +10017,7 @@ exports[`record integration tests > should not record input values if dynamicall \\"data\\": { \\"source\\": 2, \\"type\\": 5, - \\"id\\": 21 + \\"id\\": 22 } }, { @@ -10002,7 +10026,7 @@ exports[`record integration tests > should not record input values if dynamicall \\"source\\": 5, \\"text\\": \\"***********************\\", \\"isChecked\\": false, - \\"id\\": 21 + \\"id\\": 22 } }, { @@ -10011,7 +10035,7 @@ exports[`record integration tests > should not record input values if dynamicall \\"source\\": 5, \\"text\\": \\"************************\\", \\"isChecked\\": false, - \\"id\\": 21 + \\"id\\": 22 } }, { @@ -10020,8 +10044,109 @@ exports[`record integration tests > should not record input values if dynamicall \\"source\\": 5, \\"text\\": \\"*************************\\", \\"isChecked\\": false, + \\"id\\": 22 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 6, + \\"id\\": 22 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 5, + \\"id\\": 21 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"**************************\\", + \\"isChecked\\": false, + \\"id\\": 21 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"***************************\\", + \\"isChecked\\": false, \\"id\\": 21 } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"****************************\\", + \\"isChecked\\": false, + \\"id\\": 21 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"**********************************************\\", + \\"isChecked\\": false, + \\"id\\": 22 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"*************************************************\\", + \\"isChecked\\": false, + \\"id\\": 21 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [ + { + \\"id\\": 22, + \\"attributes\\": { + \\"value\\": \\"**********************************************************************************************\\" + } + }, + { + \\"id\\": 21, + \\"attributes\\": { + \\"value\\": \\"*************************************************************************************************\\" + } + } + ], + \\"removes\\": [], + \\"adds\\": [] + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [ + { + \\"id\\": 21, + \\"attributes\\": { + \\"value\\": \\"****************************************************************\\" + } + } + ], + \\"removes\\": [], + \\"adds\\": [] + } } ]" `; diff --git a/packages/rrweb/test/integration.test.ts b/packages/rrweb/test/integration.test.ts index bc244620e0..cc32916060 100644 --- a/packages/rrweb/test/integration.test.ts +++ b/packages/rrweb/test/integration.test.ts @@ -783,9 +783,46 @@ describe('record integration tests', function (this: ISuite) { const nextElement = document.querySelector('#one')!; nextElement.parentNode!.insertBefore(el, nextElement); + + const ta = document.createElement('textarea'); + ta.size = 50; + ta.id = 'textarea'; + ta.setAttribute('size', '50'); + ta.value = 'textarea should be masked'; + + nextElement.parentNode!.insertBefore(ta, nextElement); }); await page.type('#input', 'moo'); + await page.type('#textarea', 'boo'); + + await page.evaluate(() => { + const el = document.querySelector('input'); + el.value = 'input attribute mutation should also be masked'; + + const ta = document.querySelector('textarea'); + ta.value = 'textarea attribute mutation should also be masked'; + }); + + await page.evaluate(() => { + const el = document.querySelector('input'); + el.setAttribute( + 'value', + "input attribute mutation should also be masked (even though the new value doesn't take effect)", + ); + + const ta = document.querySelector('textarea'); + ta.setAttribute( + 'value', + "textarea attribute mutation should also be masked (even though the new value doesn't take effect)", + ); + }); + + await page.evaluate(() => { + const ta = document.querySelector('textarea'); + ta.innerText = + 'textarea attribute mutation via innerText should also be masked '; + }); await assertSnapshot(page); }); From 96b381befbd2299292f9e49951c116d388270cf2 Mon Sep 17 00:00:00 2001 From: eoghanmurray Date: Fri, 29 Nov 2024 16:57:37 +0000 Subject: [PATCH 2/3] Apply formatting changes --- .changeset/textarea-inner-html.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/textarea-inner-html.md b/.changeset/textarea-inner-html.md index eb7a005451..c7f0c9df9e 100644 --- a/.changeset/textarea-inner-html.md +++ b/.changeset/textarea-inner-html.md @@ -1,5 +1,5 @@ --- -'rrweb': patch +"rrweb": patch --- #1596 Add masking for innerText mutations on textarea elements From 228bbaf4985cf116200c7197fb4a8eb420c40d68 Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Thu, 5 Dec 2024 10:15:57 +0100 Subject: [PATCH 3/3] Update packages/rrweb/src/record/mutation.ts --- packages/rrweb/src/record/mutation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts index 49e9bf5a5b..08e927a98f 100644 --- a/packages/rrweb/src/record/mutation.ts +++ b/packages/rrweb/src/record/mutation.ts @@ -533,7 +533,7 @@ export default class MutationBuffer { this.attributes.push(item); this.attributeMap.set(textarea, item); } - let value = Array.from( + const value = Array.from( dom.childNodes(textarea), (cn) => dom.textContent(cn) || '', ).join('');