Mask placeholder attributes when input masking is enabled - #1911
Mask placeholder attributes when input masking is enabled#1911carlosdanieltt wants to merge 45 commits into
Conversation
Mixpanel-ify rrweb packages
Co-authored-by: Jakub Grzegorzewski <25271819+jakewski@users.noreply.github.com>
… into drv-sr-memory-leaks
SRFE-8790 Address Iframe memory leaks
…into mixpanel-release
SR-478 Add cross origin support
SRFE-8790 cleanup child nodes recursively
Ignore media interactions if not a supported media element during replay SR-490
MULTI-656 fix ci
MULTI-656 add null check
When maskAllInputs or maskInputOptions is enabled, only input values were masked while placeholder attributes were left in plain text. This could leak PII if apps set placeholder text dynamically with user data. Now placeholder attributes on input and textarea elements are masked through the same maskInputValue() path, respecting maskInputOptions granularity and custom maskInputFn callbacks. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
There was a problem hiding this comment.
Pull request overview
This PR closes a masking gap by ensuring placeholder attributes on <input> and <textarea> are masked whenever input masking is enabled, preventing potential PII leakage via dynamically generated placeholder text.
Changes:
- Mask
placeholdervalues during full snapshot serialization (rrweb-snapshot) using the existingmaskInputValue()path. - Mask
placeholderattribute mutations during incremental recording (rrweb) alongside existingvaluemasking. - Add an integration test for masked placeholders and update the shared form fixture + related snapshots.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| packages/rrweb/test/integration.test.ts | Adds integration coverage asserting placeholders are masked under maskAllInputs. |
| packages/rrweb/test/html/form.html | Adds placeholder attributes to fixture inputs used across integration tests. |
| packages/rrweb/test/snapshots/integration.test.ts.snap | Updates snapshots to reflect placeholder attributes and masking behavior. |
| packages/rrweb/src/record/mutation.ts | Extends attribute-mutation masking to include placeholder. |
| packages/rrweb-snapshot/src/snapshot.ts | Masks serialized placeholder attributes for inputs/textareas using maskInputValue(). |
Comments suppressed due to low confidence (1)
packages/rrweb/src/record/mutation.ts:588
- When masking
value/placeholderattribute mutations, the dedup guard compares the masked new value against the unmaskedm.oldValue. This can cause redundant attribute-mutation records when the underlying value changes but masks to the same output (e.g., same-length placeholders, or a maskInputFn that returns a constant), increasing payload size and replay churn. Consider maskingm.oldValuewith the same inputs before comparing, so the comparison is done on the recorded representation.
if (attributeName === 'value' || attributeName === 'placeholder') {
const type = getInputType(target);
value = maskInputValue({
element: target,
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…utes Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Required by mixpanel org policy. All actions now reference commit SHAs with version comments for readability. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Hey Carlos, this looks sound enough; did you come up with an alternative fix? |
|
Closed in favor of a more cleaned up version here: #1912 |
Summary
maskAllInputsormaskInputOptionsis enabled, placeholder attributes on<input>and<textarea>elements are now masked through the samemaskInputValue()path as input valuesmaskInputOptionsgranularity (per input type) and custommaskInputFncallbacksTest plan
*characters whenmaskAllInputs: truemaskInputOptionstest confirms non-masked input types still show plain placeholdersmaskInputFntest confirms custom masking function applies to placeholders (textarea withdata-unmask-examplekeeps its placeholder unmasked)🤖 Generated with Claude Code