1
0
mirror of https://github.com/bitwarden/browser.git synced 2025-02-22 02:21:34 +01:00

[PM-2256] Fix importer parsing credit card expiry year (#5444)

* Fix importer parsing credit card expiry year

When importing a credit card from Enpass it was found that with a 4
digit expiry year was prefixed with '20', stored at 11/202025 instead of
11/2025.

Fixed typo that checked length of month instead of year which
incorrectly added prefix.

* Refactor setCardExpiration to use RegExp
This commit is contained in:
Calum Lind 2023-05-31 09:08:39 +01:00 committed by GitHub
parent 2d9fdd68da
commit 3f35b78b40
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 128 additions and 24 deletions

View File

@ -0,0 +1,107 @@
import { CipherType } from "@bitwarden/common/vault/enums/cipher-type";
import { CardView } from "@bitwarden/common/vault/models/view/card.view";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { BaseImporter } from "../src/importers/base-importer";
class FakeBaseImporter extends BaseImporter {
initLoginCipher(): CipherView {
return super.initLoginCipher();
}
setCardExpiration(cipher: CipherView, expiration: string): boolean {
return super.setCardExpiration(cipher, expiration);
}
}
describe("BaseImporter class", () => {
const importer = new FakeBaseImporter();
let cipher: CipherView;
describe("setCardExpiration method", () => {
beforeEach(() => {
cipher = importer.initLoginCipher();
cipher.card = new CardView();
cipher.type = CipherType.Card;
});
it.each([
["01/2025", "1", "2025"],
["5/21", "5", "2021"],
["10/2100", "10", "2100"],
])(
"sets ciper card expYear & expMonth and returns true",
(expiration, expectedMonth, expectedYear) => {
const result = importer.setCardExpiration(cipher, expiration);
expect(cipher.card.expMonth).toBe(expectedMonth);
expect(cipher.card.expYear).toBe(expectedYear);
expect(result).toBe(true);
}
);
it.each([
["01/2032", "1"],
["09/2032", "9"],
["10/2032", "10"],
])("removes leading zero from month", (expiration, expectedMonth) => {
const result = importer.setCardExpiration(cipher, expiration);
expect(cipher.card.expMonth).toBe(expectedMonth);
expect(cipher.card.expYear).toBe("2032");
expect(result).toBe(true);
});
it.each([
["12/00", "2000"],
["12/99", "2099"],
["12/32", "2032"],
["12/2042", "2042"],
])("prefixes '20' to year if only two digits long", (expiration, expectedYear) => {
const result = importer.setCardExpiration(cipher, expiration);
expect(cipher.card.expYear).toHaveLength(4);
expect(cipher.card.expYear).toBe(expectedYear);
expect(result).toBe(true);
});
it.each([["01 / 2025"], ["01 / 2025"], [" 01/2025 "], [" 01/2025 "]])(
"removes any whitespace in expiration string",
(expiration) => {
const result = importer.setCardExpiration(cipher, expiration);
expect(cipher.card.expMonth).toBe("1");
expect(cipher.card.expYear).toBe("2025");
expect(result).toBe(true);
}
);
it.each([[""], [" "], [null]])(
"returns false if expiration is null or empty ",
(expiration) => {
const result = importer.setCardExpiration(cipher, expiration);
expect(result).toBe(false);
}
);
it.each([["0123"], ["01/03/23"]])(
"returns false if invalid card expiration string",
(expiration) => {
const result = importer.setCardExpiration(cipher, expiration);
expect(result).toBe(false);
}
);
it.each([["5/"], ["03/231"], ["12/1"], ["2/20221"]])(
"returns false if year is not 2 or 4 digits long",
(expiration) => {
const result = importer.setCardExpiration(cipher, expiration);
expect(result).toBe(false);
}
);
it.each([["/2023"], ["003/2023"], ["111/32"]])(
"returns false if month is not 1 or 2 digits long",
(expiration) => {
const result = importer.setCardExpiration(cipher, expiration);
expect(result).toBe(false);
}
);
});
});

View File

@ -100,7 +100,7 @@ describe("Enpass JSON Importer", () => {
expect(cipher.card.brand).toEqual("Amex");
expect(cipher.card.code).toEqual("1234");
expect(cipher.card.expMonth).toEqual("3");
expect(cipher.card.expYear).toEqual("23");
expect(cipher.card.expYear).toEqual("2023");
// remaining fields as custom fields
expect(cipher.fields.length).toEqual(9);

View File

@ -77,7 +77,7 @@ function expectCreditCard(cipher: CipherView) {
expect(cipher.card.number).toBe("4024007103939509");
expect(cipher.card.code).toBe("123");
expect(cipher.card.expMonth).toBe("1");
expect(cipher.card.expYear).toBe("22");
expect(cipher.card.expYear).toBe("2022");
}
function expectIdentity(cipher: CipherView) {

View File

@ -305,29 +305,26 @@ export abstract class BaseImporter {
}
protected setCardExpiration(cipher: CipherView, expiration: string): boolean {
if (!this.isNullOrWhitespace(expiration)) {
expiration = expiration.replace(/\s/g, "");
const parts = expiration.split("/");
if (parts.length === 2) {
let month: string = null;
let year: string = null;
if (parts[0].length === 1 || parts[0].length === 2) {
month = parts[0];
if (month.length === 2 && month[0] === "0") {
month = month.substr(1, 1);
}
}
if (parts[1].length === 2 || parts[1].length === 4) {
year = month.length === 2 ? "20" + parts[1] : parts[1];
}
if (month != null && year != null) {
cipher.card.expMonth = month;
cipher.card.expYear = year;
return true;
}
}
if (this.isNullOrWhitespace(expiration)) {
return false;
}
return false;
expiration = expiration.replace(/\s/g, "");
const monthRegex = "0?(?<month>[1-9]|1[0-2])";
const yearRegex = "(?<year>(?:[1-2][0-9])?[0-9]{2})";
const expiryRegex = new RegExp(`^${monthRegex}/${yearRegex}$`);
const expiryMatch = expiration.match(expiryRegex);
if (!expiryMatch) {
return false;
}
cipher.card.expMonth = expiryMatch.groups.month;
const year: string = expiryMatch.groups.year;
cipher.card.expYear = year.length === 2 ? "20" + year : year;
return true;
}
protected moveFoldersToCollections(result: ImportResult) {