Skip to content

Mask placeholder attributes when input masking is enabled - #1912

Open
carlosdanieltt wants to merge 1 commit into
rrweb-io:mainfrom
mixpanel:fix/mask-placeholder-attributes-upstream
Open

Mask placeholder attributes when input masking is enabled#1912
carlosdanieltt wants to merge 1 commit into
rrweb-io:mainfrom
mixpanel:fix/mask-placeholder-attributes-upstream

Conversation

@carlosdanieltt

Copy link
Copy Markdown

Summary

  • When maskAllInputs or maskInputOptions is enabled, placeholder attributes on <input> and <textarea> elements are now masked through the same maskInputValue() path as input values
  • Previously, placeholders were always recorded in plain text even with input masking on, which could leak PII if apps set placeholder text dynamically with user data (e.g., "Enter email: user@example.com")
  • Masking respects maskInputOptions granularity (per input type) and custom maskInputFn callbacks

Test plan

  • New integration test confirms placeholders are replaced with * characters when maskAllInputs: true
  • Existing maskInputOptions test confirms non-masked input types still show plain placeholders
  • Existing maskInputFn test confirms custom masking function applies to placeholders (textarea with data-unmask-example keeps its placeholder unmasked)
  • All 51 integration tests pass

🤖 Generated with Claude Code

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>
Copilot AI review requested due to automatic review settings July 30, 2026 16:08
@changeset-bot

changeset-bot Bot commented Jul 30, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: d147abb

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends rrweb’s input-masking behavior to also mask placeholder attributes on <input> and <textarea> elements, preventing potential PII leakage when placeholders contain user-derived text.

Changes:

  • Mask placeholder during full snapshot serialization via the existing maskInputValue() path.
  • Mask placeholder attribute mutations in the recorder’s MutationBuffer attribute handling.
  • Add/adjust integration test coverage and fixtures/snapshots to validate placeholder masking behavior under maskAllInputs, maskInputOptions, and maskInputFn.

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-snapshot/src/snapshot.ts Masks placeholder values during element serialization using maskInputValue().
packages/rrweb/src/record/mutation.ts Extends attribute-mutation masking to include placeholder alongside value.
packages/rrweb/test/integration.test.ts Adds an integration test asserting placeholders are masked when maskAllInputs is enabled.
packages/rrweb/test/html/form.html Adds placeholder text to <input> and <textarea> fixtures used by integration tests.
packages/rrweb/test/snapshots/integration.test.ts.snap Updates snapshots to include placeholder attributes and their masked/unmasked forms depending on configuration.
Comments suppressed due to low confidence (1)

packages/rrweb/src/record/mutation.ts:588

  • When masking placeholder (and value) attribute mutations, maskInputValue() converts a null attribute value into an empty string. This means removeAttribute('placeholder') would be recorded as placeholder: "" instead of removing the attribute, which can change replay semantics (attribute absence vs empty string). Preserve null by only masking when the attribute value is non-null.
        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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants