diff --git a/.changeset/textarea-inner-html.md b/.changeset/textarea-inner-html.md new file mode 100644 index 00000000..c7f0c9df --- /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 42170b49..08e927a9 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( + const 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 4fa6c2f3..738f2fe8 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,9 +10000,61 @@ 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 } }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 5, + \\"id\\": 22 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"***********************\\", + \\"isChecked\\": false, + \\"id\\": 22 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"************************\\", + \\"isChecked\\": false, + \\"id\\": 22 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"*************************\\", + \\"isChecked\\": false, + \\"id\\": 22 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 6, + \\"id\\": 22 + } + }, { \\"type\\": 3, \\"data\\": { @@ -10000,7 +10067,7 @@ exports[`record integration tests > should not record input values if dynamicall \\"type\\": 3, \\"data\\": { \\"source\\": 5, - \\"text\\": \\"***********************\\", + \\"text\\": \\"**************************\\", \\"isChecked\\": false, \\"id\\": 21 } @@ -10009,7 +10076,7 @@ exports[`record integration tests > should not record input values if dynamicall \\"type\\": 3, \\"data\\": { \\"source\\": 5, - \\"text\\": \\"************************\\", + \\"text\\": \\"***************************\\", \\"isChecked\\": false, \\"id\\": 21 } @@ -10018,10 +10085,68 @@ exports[`record integration tests > should not record input values if dynamicall \\"type\\": 3, \\"data\\": { \\"source\\": 5, - \\"text\\": \\"*************************\\", + \\"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 f50c98e6..89534c99 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); });