From e580dfd94a5197447dcb0b1d313c1a4bbfc8cdf8 Mon Sep 17 00:00:00 2001 From: Spencer Date: Wed, 22 Nov 2023 15:56:38 -0500 Subject: [PATCH 1/5] Improve default privacy mode --- packages/rrweb-snapshot/src/snapshot.ts | 6 ++-- packages/rrweb-snapshot/src/types.ts | 42 ------------------------- packages/rrweb-snapshot/src/utils.ts | 35 +++++++-------------- packages/rrweb/src/record/mutation.ts | 7 ++--- packages/rrweb/src/record/observer.ts | 15 ++------- 5 files changed, 20 insertions(+), 85 deletions(-) diff --git a/packages/rrweb-snapshot/src/snapshot.ts b/packages/rrweb-snapshot/src/snapshot.ts index 57b002fe..5465cf87 100644 --- a/packages/rrweb-snapshot/src/snapshot.ts +++ b/packages/rrweb-snapshot/src/snapshot.ts @@ -614,10 +614,12 @@ function serializeTextNode( /* Start of Highlight */ // Randomizes the text content to a string of the same length. const enableStrictPrivacy = privacySetting === 'strict'; + const highlightOverwriteRecord = n.parentElement?.getAttribute("data-hl-record"); const obfuscateDefaultPrivacy = privacySetting === 'default' && shouldObfuscateTextByDefault(textContent); if ( (enableStrictPrivacy || obfuscateDefaultPrivacy) && + !highlightOverwriteRecord && !textContentHandled && parentTagName ) { @@ -744,9 +746,7 @@ function serializeElementNode( type, tagName, value, - inputId: (n as HTMLInputElement).id, - inputName: (n as HTMLInputElement).name, - autocomplete: (n as HTMLInputElement).autocomplete, + overwriteRecord: n.getAttribute("data-hl-record"), maskInputOptions, maskInputFn, }); diff --git a/packages/rrweb-snapshot/src/types.ts b/packages/rrweb-snapshot/src/types.ts index 11bbe6c2..dbe1df77 100644 --- a/packages/rrweb-snapshot/src/types.ts +++ b/packages/rrweb-snapshot/src/types.ts @@ -135,48 +135,6 @@ export type MaskInputOptions = Partial<{ textarea: boolean; select: boolean; password: boolean; - // Highlight: added additonal fields to obfuscate - name: boolean; - 'given-name': boolean; - 'family-name': boolean; - 'additional-name': boolean; - 'one-time-code': boolean; - 'street-address': boolean; - address: boolean; - 'address-line1': boolean; - 'address-line2': boolean; - 'address-line3': boolean; - 'address-level4': boolean; - 'address-level3': boolean; - 'address-level2': boolean; - 'address-level1': boolean; - city: boolean; - state: boolean; - country: boolean; - zip: boolean; - 'country-name': boolean; - 'postal-code': boolean; - 'cc-name': boolean; - 'cc-given-name': boolean; - 'cc-additional-name': boolean; - 'cc-family-name': boolean; - 'cc-number': boolean; - 'cc-exp': boolean; - 'cc-exp-month': boolean; - 'cc-exp-year': boolean; - 'cc-csc': boolean; - 'cc-type': boolean; - bday: boolean; - 'bday-day': boolean; - 'bday-month': boolean; - 'bday-year': boolean; - sex: boolean; - 'tel-country-code': boolean; - 'tel-national': boolean; - 'tel-area-code': boolean; - 'tel-local': boolean; - 'tel-extension': boolean; - ssn: boolean; }>; export type SlimDOMOptions = Partial<{ diff --git a/packages/rrweb-snapshot/src/utils.ts b/packages/rrweb-snapshot/src/utils.ts index 9ba55531..deef233e 100644 --- a/packages/rrweb-snapshot/src/utils.ts +++ b/packages/rrweb-snapshot/src/utils.ts @@ -270,11 +270,11 @@ const EMAIL_REGEX = new RegExp( const LONG_NUMBER_REGEX = new RegExp('[0-9]{9,16}'); // unformatted ssn, phone numbers, or credit card numbers const SSN_REGEX = new RegExp('[0-9]{3}-?[0-9]{2}-?[0-9]{4}'); const PHONE_NUMBER_REGEX = new RegExp( - '[+]?[(]?[0-9]{3}[)]?[-s.]?[0-9]{3}[-s.]?[0-9]{4,6}', + '[+]?[(]?[0-9]{3}[)]?[-\s.]?[0-9]{3}[-\s.]?[0-9]{4,6}', ); const CREDIT_CARD_REGEX = new RegExp('[0-9]{4}-?[0-9]{4}-?[0-9]{4}-?[0-9]{4}'); const ADDRESS_REGEX = new RegExp( - '[0-9]{1,5}.?[0-9]{0,3}s[a-zA-Z]{2,30}s[a-zA-Z]{2,15}', + '[0-9]{1,5}.?[0-9]{0,3}\s[a-zA-Z]{2,30}\s[a-zA-Z]{2,15}', ); const IP_REGEX = new RegExp('(?:[0-9]{1,3}.){3}[0-9]{1,3}'); @@ -298,27 +298,20 @@ export const maskedInputType = ({ maskInputOptions, tagName, type, - inputId, - inputName, - autocomplete, + overwriteRecord, }: { maskInputOptions: MaskInputOptions; tagName: string; type: string | null; - inputId: string | null; - inputName: string | null; - autocomplete: boolean | string | null; + overwriteRecord: string | null; }): boolean => { const actualType = type && type.toLowerCase(); return ( - maskInputOptions[tagName.toLowerCase() as keyof MaskInputOptions] || - (actualType && maskInputOptions[actualType as keyof MaskInputOptions]) || - (inputId && maskInputOptions[inputId as keyof MaskInputOptions]) || - (inputName && maskInputOptions[inputName as keyof MaskInputOptions]) || - (!!autocomplete && - typeof autocomplete === 'string' && - !!maskInputOptions[autocomplete as keyof MaskInputOptions]) + overwriteRecord !== 'true' && ( + !!maskInputOptions[tagName.toLowerCase() as keyof MaskInputOptions] || + !!(actualType && maskInputOptions[actualType as keyof MaskInputOptions]) + ) ); }; @@ -327,19 +320,15 @@ export function maskInputValue({ maskInputOptions, tagName, type, - inputId, - inputName, - autocomplete, value, + overwriteRecord, maskInputFn, }: { maskInputOptions: MaskInputOptions; tagName: string; type: string | null; - inputId: string | null; - inputName: string | null; - autocomplete: boolean | string | null; value: string | null; + overwriteRecord: string | null; maskInputFn?: MaskInputFn; }): string { let text = value || ''; @@ -349,9 +338,7 @@ export function maskInputValue({ maskInputOptions, tagName, type, - inputId, - inputName, - autocomplete, + overwriteRecord, }) ) { if (maskInputFn) { diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts index f42a5cb8..ebd7eb35 100644 --- a/packages/rrweb/src/record/mutation.ts +++ b/packages/rrweb/src/record/mutation.ts @@ -432,7 +432,8 @@ export default class MutationBuffer { const obfuscateDefaultPrivacy = this.privacySetting === 'default' && shouldObfuscateTextByDefault(value); - if ((enableStrictPrivacy || obfuscateDefaultPrivacy) && value) { + const highlightOverwriteRecord = text.node?.parentElement?.getAttribute("data-hl-record"); + if ((enableStrictPrivacy || obfuscateDefaultPrivacy) && highlightOverwriteRecord && value) { value = obfuscateText(value); } return { @@ -515,9 +516,7 @@ export default class MutationBuffer { tagName: target.tagName, type, value, - inputId: target.id, - inputName: target.getAttribute('name'), - autocomplete: target.getAttribute('autocomplete'), + overwriteRecord: target.getAttribute('data-hl-record'), maskInputFn: this.maskInputFn, }); } diff --git a/packages/rrweb/src/record/observer.ts b/packages/rrweb/src/record/observer.ts index 2038c96b..f3487d1e 100644 --- a/packages/rrweb/src/record/observer.ts +++ b/packages/rrweb/src/record/observer.ts @@ -394,12 +394,7 @@ function initInputObserver({ let text = (target as HTMLInputElement).value; let isChecked = false; const type: Lowercase = getInputType(target) || ''; - - const { - id: inputId, - name: inputName, - autocomplete, - } = target as HTMLInputElement; + const overwriteRecord = target.getAttribute('data-hl-record') if (type === 'radio' || type === 'checkbox') { isChecked = (target as HTMLInputElement).checked; @@ -408,9 +403,7 @@ function initInputObserver({ maskInputOptions, type, tagName, - inputId, - inputName, - autocomplete, + overwriteRecord, }) ) { text = maskInputValue({ @@ -418,9 +411,7 @@ function initInputObserver({ tagName, type, value: text, - inputId, - inputName, - autocomplete, + overwriteRecord, maskInputFn, }); } From edef573fe72f0e2ce31d8ad784d1ae55652de4ef Mon Sep 17 00:00:00 2001 From: SpennyNDaJets Date: Wed, 22 Nov 2023 20:58:21 +0000 Subject: [PATCH 2/5] Apply formatting changes --- packages/rrweb-snapshot/src/snapshot.ts | 5 +++-- packages/rrweb-snapshot/src/utils.ts | 11 +++++------ packages/rrweb/src/record/mutation.ts | 9 +++++++-- packages/rrweb/src/record/observer.ts | 2 +- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/packages/rrweb-snapshot/src/snapshot.ts b/packages/rrweb-snapshot/src/snapshot.ts index 5465cf87..26eab059 100644 --- a/packages/rrweb-snapshot/src/snapshot.ts +++ b/packages/rrweb-snapshot/src/snapshot.ts @@ -614,7 +614,8 @@ function serializeTextNode( /* Start of Highlight */ // Randomizes the text content to a string of the same length. const enableStrictPrivacy = privacySetting === 'strict'; - const highlightOverwriteRecord = n.parentElement?.getAttribute("data-hl-record"); + const highlightOverwriteRecord = + n.parentElement?.getAttribute('data-hl-record'); const obfuscateDefaultPrivacy = privacySetting === 'default' && shouldObfuscateTextByDefault(textContent); if ( @@ -746,7 +747,7 @@ function serializeElementNode( type, tagName, value, - overwriteRecord: n.getAttribute("data-hl-record"), + overwriteRecord: n.getAttribute('data-hl-record'), maskInputOptions, maskInputFn, }); diff --git a/packages/rrweb-snapshot/src/utils.ts b/packages/rrweb-snapshot/src/utils.ts index deef233e..e2bf994d 100644 --- a/packages/rrweb-snapshot/src/utils.ts +++ b/packages/rrweb-snapshot/src/utils.ts @@ -270,11 +270,11 @@ const EMAIL_REGEX = new RegExp( const LONG_NUMBER_REGEX = new RegExp('[0-9]{9,16}'); // unformatted ssn, phone numbers, or credit card numbers const SSN_REGEX = new RegExp('[0-9]{3}-?[0-9]{2}-?[0-9]{4}'); const PHONE_NUMBER_REGEX = new RegExp( - '[+]?[(]?[0-9]{3}[)]?[-\s.]?[0-9]{3}[-\s.]?[0-9]{4,6}', + '[+]?[(]?[0-9]{3}[)]?[-s.]?[0-9]{3}[-s.]?[0-9]{4,6}', ); const CREDIT_CARD_REGEX = new RegExp('[0-9]{4}-?[0-9]{4}-?[0-9]{4}-?[0-9]{4}'); const ADDRESS_REGEX = new RegExp( - '[0-9]{1,5}.?[0-9]{0,3}\s[a-zA-Z]{2,30}\s[a-zA-Z]{2,15}', + '[0-9]{1,5}.?[0-9]{0,3}s[a-zA-Z]{2,30}s[a-zA-Z]{2,15}', ); const IP_REGEX = new RegExp('(?:[0-9]{1,3}.){3}[0-9]{1,3}'); @@ -308,10 +308,9 @@ export const maskedInputType = ({ const actualType = type && type.toLowerCase(); return ( - overwriteRecord !== 'true' && ( - !!maskInputOptions[tagName.toLowerCase() as keyof MaskInputOptions] || - !!(actualType && maskInputOptions[actualType as keyof MaskInputOptions]) - ) + overwriteRecord !== 'true' && + (!!maskInputOptions[tagName.toLowerCase() as keyof MaskInputOptions] || + !!(actualType && maskInputOptions[actualType as keyof MaskInputOptions])) ); }; diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts index ebd7eb35..c1986e93 100644 --- a/packages/rrweb/src/record/mutation.ts +++ b/packages/rrweb/src/record/mutation.ts @@ -432,8 +432,13 @@ export default class MutationBuffer { const obfuscateDefaultPrivacy = this.privacySetting === 'default' && shouldObfuscateTextByDefault(value); - const highlightOverwriteRecord = text.node?.parentElement?.getAttribute("data-hl-record"); - if ((enableStrictPrivacy || obfuscateDefaultPrivacy) && highlightOverwriteRecord && value) { + const highlightOverwriteRecord = + text.node?.parentElement?.getAttribute('data-hl-record'); + if ( + (enableStrictPrivacy || obfuscateDefaultPrivacy) && + highlightOverwriteRecord && + value + ) { value = obfuscateText(value); } return { diff --git a/packages/rrweb/src/record/observer.ts b/packages/rrweb/src/record/observer.ts index f3487d1e..215249fe 100644 --- a/packages/rrweb/src/record/observer.ts +++ b/packages/rrweb/src/record/observer.ts @@ -394,7 +394,7 @@ function initInputObserver({ let text = (target as HTMLInputElement).value; let isChecked = false; const type: Lowercase = getInputType(target) || ''; - const overwriteRecord = target.getAttribute('data-hl-record') + const overwriteRecord = target.getAttribute('data-hl-record'); if (type === 'radio' || type === 'checkbox') { isChecked = (target as HTMLInputElement).checked; From d1ea5d60c22ba1f1e63d221ae85cc98ecf9cbc00 Mon Sep 17 00:00:00 2001 From: Spencer Date: Wed, 22 Nov 2023 16:34:15 -0500 Subject: [PATCH 3/5] Fix address and phone number regex --- packages/rrweb-snapshot/src/utils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/rrweb-snapshot/src/utils.ts b/packages/rrweb-snapshot/src/utils.ts index e2bf994d..72aeb3b3 100644 --- a/packages/rrweb-snapshot/src/utils.ts +++ b/packages/rrweb-snapshot/src/utils.ts @@ -270,11 +270,11 @@ const EMAIL_REGEX = new RegExp( const LONG_NUMBER_REGEX = new RegExp('[0-9]{9,16}'); // unformatted ssn, phone numbers, or credit card numbers const SSN_REGEX = new RegExp('[0-9]{3}-?[0-9]{2}-?[0-9]{4}'); const PHONE_NUMBER_REGEX = new RegExp( - '[+]?[(]?[0-9]{3}[)]?[-s.]?[0-9]{3}[-s.]?[0-9]{4,6}', + '[+]?[(]?[0-9]{3}[)]?[-\s.]?[0-9]{3}[-\s.]?[0-9]{4,6}', ); const CREDIT_CARD_REGEX = new RegExp('[0-9]{4}-?[0-9]{4}-?[0-9]{4}-?[0-9]{4}'); const ADDRESS_REGEX = new RegExp( - '[0-9]{1,5}.?[0-9]{0,3}s[a-zA-Z]{2,30}s[a-zA-Z]{2,15}', + '[0-9]{1,5}.?[0-9]{0,3}\s[a-zA-Z]{2,30}\s[a-zA-Z]{2,15}', ); const IP_REGEX = new RegExp('(?:[0-9]{1,3}.){3}[0-9]{1,3}'); From de0769ac330dcaa7022b373c35c4fb9001a212ab Mon Sep 17 00:00:00 2001 From: SpennyNDaJets Date: Wed, 22 Nov 2023 21:35:53 +0000 Subject: [PATCH 4/5] Apply formatting changes --- packages/rrweb-snapshot/src/utils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/rrweb-snapshot/src/utils.ts b/packages/rrweb-snapshot/src/utils.ts index 72aeb3b3..e2bf994d 100644 --- a/packages/rrweb-snapshot/src/utils.ts +++ b/packages/rrweb-snapshot/src/utils.ts @@ -270,11 +270,11 @@ const EMAIL_REGEX = new RegExp( const LONG_NUMBER_REGEX = new RegExp('[0-9]{9,16}'); // unformatted ssn, phone numbers, or credit card numbers const SSN_REGEX = new RegExp('[0-9]{3}-?[0-9]{2}-?[0-9]{4}'); const PHONE_NUMBER_REGEX = new RegExp( - '[+]?[(]?[0-9]{3}[)]?[-\s.]?[0-9]{3}[-\s.]?[0-9]{4,6}', + '[+]?[(]?[0-9]{3}[)]?[-s.]?[0-9]{3}[-s.]?[0-9]{4,6}', ); const CREDIT_CARD_REGEX = new RegExp('[0-9]{4}-?[0-9]{4}-?[0-9]{4}-?[0-9]{4}'); const ADDRESS_REGEX = new RegExp( - '[0-9]{1,5}.?[0-9]{0,3}\s[a-zA-Z]{2,30}\s[a-zA-Z]{2,15}', + '[0-9]{1,5}.?[0-9]{0,3}s[a-zA-Z]{2,30}s[a-zA-Z]{2,15}', ); const IP_REGEX = new RegExp('(?:[0-9]{1,3}.){3}[0-9]{1,3}'); From b3a8b697ca957e1d43fed4105e195a11c75124c0 Mon Sep 17 00:00:00 2001 From: Spencer Date: Wed, 22 Nov 2023 16:45:20 -0500 Subject: [PATCH 5/5] Update regex with prettier ignore --- packages/rrweb-snapshot/src/utils.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/rrweb-snapshot/src/utils.ts b/packages/rrweb-snapshot/src/utils.ts index e2bf994d..e1ecce65 100644 --- a/packages/rrweb-snapshot/src/utils.ts +++ b/packages/rrweb-snapshot/src/utils.ts @@ -269,12 +269,14 @@ const EMAIL_REGEX = new RegExp( ); const LONG_NUMBER_REGEX = new RegExp('[0-9]{9,16}'); // unformatted ssn, phone numbers, or credit card numbers const SSN_REGEX = new RegExp('[0-9]{3}-?[0-9]{2}-?[0-9]{4}'); +// prettier-ignore const PHONE_NUMBER_REGEX = new RegExp( - '[+]?[(]?[0-9]{3}[)]?[-s.]?[0-9]{3}[-s.]?[0-9]{4,6}', + '[+]?[(]?[0-9]{3}[)]?[-\s.]?[0-9]{3}[-\s.]?[0-9]{4,6}', ); const CREDIT_CARD_REGEX = new RegExp('[0-9]{4}-?[0-9]{4}-?[0-9]{4}-?[0-9]{4}'); +// prettier-ignore const ADDRESS_REGEX = new RegExp( - '[0-9]{1,5}.?[0-9]{0,3}s[a-zA-Z]{2,30}s[a-zA-Z]{2,15}', + '[0-9]{1,5}.?[0-9]{0,3}\s[a-zA-Z]{2,30}\s[a-zA-Z]{2,15}', ); const IP_REGEX = new RegExp('(?:[0-9]{1,3}.){3}[0-9]{1,3}');