Skip to content

Commit

Permalink
feat: Ensure password inputs are masked when switching type
Browse files Browse the repository at this point in the history
Apply formatting changes

use data- attribute


ref: Ensure type is always lowercased


add changeset
  • Loading branch information
mydea committed Mar 20, 2023
1 parent a82a3b4 commit bdfa260
Show file tree
Hide file tree
Showing 8 changed files with 548 additions and 424 deletions.
6 changes: 6 additions & 0 deletions .changeset/new-snakes-call.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'rrweb-snapshot': minor
'rrweb': minor
---

feat: Ensure password inputs remain masked when switching input type
7 changes: 6 additions & 1 deletion packages/rrweb-snapshot/src/snapshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -674,8 +674,13 @@ function serializeElementNode(
attributes.type !== 'button' &&
value
) {
const type: string | null = n.hasAttribute('data-rr-is-password')
? 'password'
: typeof attributes.type === 'string'
? attributes.type.toLowerCase()
: null;
attributes.value = maskInputValue({
type: attributes.type,
type,
tagName,
value,
maskInputOptions,
Expand Down
6 changes: 4 additions & 2 deletions packages/rrweb-snapshot/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,14 +162,16 @@ export function maskInputValue({
}: {
maskInputOptions: MaskInputOptions;
tagName: string;
type: string | number | boolean | null;
type: string | null;
value: string | null;
maskInputFn?: MaskInputFn;
}): string {
let text = value || '';
const actualType = type && type.toLowerCase();

if (
maskInputOptions[tagName.toLowerCase() as keyof MaskInputOptions] ||
maskInputOptions[type as keyof MaskInputOptions]
(actualType && maskInputOptions[actualType as keyof MaskInputOptions])
) {
if (maskInputFn) {
text = maskInputFn(text);
Expand Down
19 changes: 17 additions & 2 deletions packages/rrweb/src/record/mutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -488,11 +488,15 @@ export default class MutationBuffer {
const target = m.target as HTMLElement;
let attributeName = m.attributeName as string;
let value = (m.target as HTMLElement).getAttribute(attributeName);
const type = target.hasAttribute('data-rr-is-password')
? 'password'
: target.getAttribute('type') || null;

if (attributeName === 'value') {
value = maskInputValue({
maskInputOptions: this.maskInputOptions,
tagName: (m.target as HTMLElement).tagName,
type: (m.target as HTMLElement).getAttribute('type'),
tagName: target.tagName,
type,
value,
maskInputFn: this.maskInputFn,
});
Expand Down Expand Up @@ -527,6 +531,17 @@ export default class MutationBuffer {
};
this.attributes.push(item);
}

// Keep this property on inputs that used to be password inputs
// This is used to ensure we do not unmask value when using e.g. a "Show password" type button
if (
m.attributeName === 'type' &&
(m.target as HTMLElement).tagName === 'INPUT' &&
(m.oldValue || '').toLowerCase() === 'password'
) {
(m.target as HTMLElement).setAttribute('data-rr-is-password', 'true');
}

if (attributeName === 'style') {
const old = this.doc.createElement('span');
if (m.oldValue) {
Expand Down
27 changes: 16 additions & 11 deletions packages/rrweb/src/record/observer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -338,39 +338,44 @@ function initInputObserver({
userTriggeredOnInput,
}: observerParam): listenerHandler {
function eventHandler(event: Event) {
let target = getEventTarget(event);
let target = getEventTarget(event) as HTMLElement | null;
const userTriggered = event.isTrusted;
const tagName = target && target.tagName;

/**
* If a site changes the value 'selected' of an option element, the value of its parent element, usually a select element, will be changed as well.
* We can treat this change as a value change of the select element the current target belongs to.
*/
if (target && (target as Element).tagName === 'OPTION')
target = (target as Element).parentElement;
if (target && tagName === 'OPTION') {
target = target.parentElement;
}
if (
!target ||
!(target as Element).tagName ||
INPUT_TAGS.indexOf((target as Element).tagName) < 0 ||
!tagName ||
INPUT_TAGS.indexOf(tagName) < 0 ||
isBlocked(target as Node, blockClass, blockSelector, true)
) {
return;
}
const type: string | undefined = (target as HTMLInputElement).type;
if ((target as HTMLElement).classList.contains(ignoreClass)) {

if (target.classList.contains(ignoreClass)) {
return;
}
let text = (target as HTMLInputElement).value;
let isChecked = false;
const type: string = target.hasAttribute('data-rr-is-password')
? 'password'
: ((target as HTMLInputElement).type || '').toLowerCase();

if (type === 'radio' || type === 'checkbox') {
isChecked = (target as HTMLInputElement).checked;
} else if (
maskInputOptions[
(target as Element).tagName.toLowerCase() as keyof MaskInputOptions
] ||
maskInputOptions[tagName.toLowerCase() as keyof MaskInputOptions] ||
maskInputOptions[type as keyof MaskInputOptions]
) {
text = maskInputValue({
maskInputOptions,
tagName: (target as HTMLElement).tagName,
tagName,
type,
value: text,
maskInputFn,
Expand Down
Loading

0 comments on commit bdfa260

Please sign in to comment.