From 966e8d3fb8a02b72ba2d404f06fc76356cfd24d6 Mon Sep 17 00:00:00 2001 From: Jonathan Prusik Date: Tue, 7 Jan 2025 13:48:18 -0500 Subject: [PATCH] [PM-16667] Followup clarifying work (#12665) * clean up readability * fix ts-strict violations * fix consistency with uncertain cases in isCardExpired --- libs/common/src/autofill/utils.spec.ts | 2 +- libs/common/src/autofill/utils.ts | 60 ++++++++++++++++---------- 2 files changed, 38 insertions(+), 24 deletions(-) diff --git a/libs/common/src/autofill/utils.spec.ts b/libs/common/src/autofill/utils.spec.ts index 516a09e03d1..4dd36ba7d89 100644 --- a/libs/common/src/autofill/utils.spec.ts +++ b/libs/common/src/autofill/utils.spec.ts @@ -93,7 +93,7 @@ function getCardExpiryDateValues() { [undefined, undefined, false], // no month, no year, invalid values ["", "", false], // no month, no year, invalid values ["12", "agdredg42grg35grrr. ea3534@#^145345ag$%^ -_#$rdg ", false], // invalid values - ["0", `${currentYear}`, true], // invalid month + ["0", `${currentYear}`, false], // invalid month ["0", `${currentYear - 1}`, true], // invalid 0 month ["00", `${currentYear + 1}`, false], // invalid 0 month [`${currentMonth}`, "0000", true], // current month, in the year 2000 diff --git a/libs/common/src/autofill/utils.ts b/libs/common/src/autofill/utils.ts index d9276cdbc8b..6bee5e1a198 100644 --- a/libs/common/src/autofill/utils.ts +++ b/libs/common/src/autofill/utils.ts @@ -1,5 +1,3 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { DelimiterPatternExpression, ExpiryFullYearPattern, @@ -25,11 +23,11 @@ export function normalizeExpiryYearFormat(yearInput: string | number): Year | nu let expirationYear = yearInputIsEmpty ? null : `${yearInput}`; // Exit early if year is already formatted correctly or empty - if (yearInputIsEmpty || /^[1-9]{1}\d{3}$/.test(expirationYear)) { + if (yearInputIsEmpty || (expirationYear && /^[1-9]{1}\d{3}$/.test(expirationYear))) { return expirationYear as Year; } - expirationYear = expirationYear + expirationYear = (expirationYear || "") // For safety, because even input[type="number"] will allow decimals .replace(/[^\d]/g, "") // remove any leading zero padding (leave the last leading zero if it ends the string) @@ -53,7 +51,7 @@ export function normalizeExpiryYearFormat(yearInput: string | number): Year | nu /** * Takes a cipher card view and returns "true" if the month and year affirmativey indicate - * the card is expired. + * the card is expired. Uncertain cases return "false". * * @param {CardView} cipherCard * @return {*} {boolean} @@ -62,30 +60,34 @@ export function isCardExpired(cipherCard: CardView): boolean { if (cipherCard) { const { expMonth = null, expYear = null } = cipherCard; + if (!expYear) { + return false; + } + const now = new Date(); const normalizedYear = normalizeExpiryYearFormat(expYear); - const parsedYear = parseInt(normalizedYear, 10); + const parsedYear = normalizedYear ? parseInt(normalizedYear, 10) : NaN; - const expiryYearIsBeforeThisYear = parsedYear < now.getFullYear(); - const expiryYearIsAfterThisYear = parsedYear > now.getFullYear(); + const expiryYearIsBeforeCurrentYear = parsedYear < now.getFullYear(); + const expiryYearIsAfterCurrentYear = parsedYear > now.getFullYear(); // If the expiry year is before the current year, skip checking the month, since it must be expired - if (normalizedYear && expiryYearIsBeforeThisYear) { + if (normalizedYear && expiryYearIsBeforeCurrentYear) { return true; } // If the expiry year is after the current year, skip checking the month, since it cannot be expired - if (normalizedYear && expiryYearIsAfterThisYear) { + if (normalizedYear && expiryYearIsAfterCurrentYear) { return false; } if (normalizedYear && expMonth) { const parsedMonthInteger = parseInt(expMonth, 10); - const parsedMonthIsInvalid = !parsedMonthInteger || isNaN(parsedMonthInteger); + const parsedMonthIsValid = parsedMonthInteger && !isNaN(parsedMonthInteger); - // If the parsed month value is 0, we don't know when the expiry passes this year, so treat it as expired - if (parsedMonthIsInvalid) { - return true; + // If the parsed month value is 0, we don't know when the expiry passes this year, so do not treat it as expired + if (!parsedMonthIsValid) { + return false; } // `Date` months are zero-indexed @@ -257,13 +259,18 @@ function parseNonDelimitedYearMonthExpiry(dateInput: string): [string | null, st parsedMonth = dateInput.slice(-1); const currentYear = new Date().getFullYear(); - const normalizedParsedYear = parseInt(normalizeExpiryYearFormat(parsedYear), 10); - const normalizedParsedYearAlternative = parseInt( - normalizeExpiryYearFormat(dateInput.slice(-2)), - 10, - ); + const normalizedYearFormat = normalizeExpiryYearFormat(parsedYear); + const normalizedParsedYear = normalizedYearFormat && parseInt(normalizedYearFormat, 10); + const normalizedExpiryYearFormat = normalizeExpiryYearFormat(dateInput.slice(-2)); + const normalizedParsedYearAlternative = + normalizedExpiryYearFormat && parseInt(normalizedExpiryYearFormat, 10); - if (normalizedParsedYear < currentYear && normalizedParsedYearAlternative >= currentYear) { + if ( + normalizedParsedYear && + normalizedParsedYear < currentYear && + normalizedParsedYearAlternative && + normalizedParsedYearAlternative >= currentYear + ) { parsedYear = dateInput.slice(-2); parsedMonth = dateInput.slice(0, 1); } @@ -295,17 +302,24 @@ export function parseYearMonthExpiry(combinedExpiryValue: string): [Year | null, // If there is only one date part, no delimiter was found in the passed value if (dateParts.length === 1) { - [parsedYear, parsedMonth] = parseNonDelimitedYearMonthExpiry(sanitizedFirstPart); + const [parsedNonDelimitedYear, parsedNonDelimitedMonth] = + parseNonDelimitedYearMonthExpiry(sanitizedFirstPart); + + parsedYear = parsedNonDelimitedYear; + parsedMonth = parsedNonDelimitedMonth; } // There are multiple date parts else { - [parsedYear, parsedMonth] = parseDelimitedYearMonthExpiry([ + const [parsedDelimitedYear, parsedDelimitedMonth] = parseDelimitedYearMonthExpiry([ sanitizedFirstPart, sanitizedSecondPart, ]); + + parsedYear = parsedDelimitedYear; + parsedMonth = parsedDelimitedMonth; } - const normalizedParsedYear = normalizeExpiryYearFormat(parsedYear); + const normalizedParsedYear = parsedYear ? normalizeExpiryYearFormat(parsedYear) : null; const normalizedParsedMonth = parsedMonth?.replace(/^0+/, "").slice(0, 2); // Set "empty" values to null