From 754ea1e09a207bc9ff4a632d04e752ec9ee42134 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rui=20Tom=C3=A9?= <108268980+r-tome@users.noreply.github.com> Date: Tue, 25 Oct 2022 09:49:57 +0100 Subject: [PATCH] [EC-417] Extracting the app name from the Android user item on CSV import (#3254) * [EC-417] Extracting the app name from the Android user item on CSV import * [EC-417] Updated android csv pattern to match starting with 'android://' * [EC-417] Added unit tests for ChromeCsvImporter --- .../spec/importers/chromeCsvImporter.spec.ts | 72 +++++++++++++++++++ .../testData/chromeCsv/androidData.csv.ts | 2 + .../chromeCsv/simplePasswordData.csv.ts | 2 + .../common/src/importers/chromeCsvImporter.ts | 8 ++- 4 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 libs/common/spec/importers/chromeCsvImporter.spec.ts create mode 100644 libs/common/spec/importers/testData/chromeCsv/androidData.csv.ts create mode 100644 libs/common/spec/importers/testData/chromeCsv/simplePasswordData.csv.ts diff --git a/libs/common/spec/importers/chromeCsvImporter.spec.ts b/libs/common/spec/importers/chromeCsvImporter.spec.ts new file mode 100644 index 0000000000..aba1329fd8 --- /dev/null +++ b/libs/common/spec/importers/chromeCsvImporter.spec.ts @@ -0,0 +1,72 @@ +import { ChromeCsvImporter as Importer } from "@bitwarden/common/importers/chromeCsvImporter"; +import { CipherView } from "@bitwarden/common/models/view/cipherView"; +import { LoginUriView } from "@bitwarden/common/models/view/loginUriView"; +import { LoginView } from "@bitwarden/common/models/view/loginView"; + +import { data as androidData } from "./testData/chromeCsv/androidData.csv"; +import { data as simplePasswordData } from "./testData/chromeCsv/simplePasswordData.csv"; + +const CipherData = [ + { + title: "should parse app name", + csv: androidData, + expected: Object.assign(new CipherView(), { + id: null, + organizationId: null, + folderId: null, + name: "com.xyz.example.app.android", + login: Object.assign(new LoginView(), { + username: "username@example.com", + password: "Qh6W4Wz55YGFNU", + uris: [ + Object.assign(new LoginUriView(), { + uri: "android://N2H9MndUUUt3JuQSWAKexOU9oJLJeHR4nyUGac5E1TXKppkY7xtdRl6l8vKo1hQWCqAEy4gsNLUBIbVxpdmhOP==@com.xyz.example.app.android/", + }), + ], + }), + notes: null, + type: 1, + }), + }, + { + title: "should parse password", + csv: simplePasswordData, + expected: Object.assign(new CipherView(), { + id: null, + organizationId: null, + folderId: null, + name: "www.example.com", + login: Object.assign(new LoginView(), { + username: "username@example.com", + password: "wpC9qFvsbWQK5Z", + uris: [ + Object.assign(new LoginUriView(), { + uri: "https://www.example.com/", + }), + ], + }), + notes: null, + type: 1, + }), + }, +]; + +describe("Chrome CSV Importer", () => { + CipherData.forEach((data) => { + it(data.title, async () => { + const importer = new Importer(); + const result = await importer.parse(data.csv); + expect(result != null).toBe(true); + expect(result.ciphers.length).toBeGreaterThan(0); + + const cipher = result.ciphers.shift(); + let property: keyof typeof data.expected; + for (property in data.expected) { + if (Object.prototype.hasOwnProperty.call(data.expected, property)) { + expect(Object.prototype.hasOwnProperty.call(cipher, property)).toBe(true); + expect(cipher[property]).toEqual(data.expected[property]); + } + } + }); + }); +}); diff --git a/libs/common/spec/importers/testData/chromeCsv/androidData.csv.ts b/libs/common/spec/importers/testData/chromeCsv/androidData.csv.ts new file mode 100644 index 0000000000..7b4b18094a --- /dev/null +++ b/libs/common/spec/importers/testData/chromeCsv/androidData.csv.ts @@ -0,0 +1,2 @@ +export const data = `name,url,username,password +,android://N2H9MndUUUt3JuQSWAKexOU9oJLJeHR4nyUGac5E1TXKppkY7xtdRl6l8vKo1hQWCqAEy4gsNLUBIbVxpdmhOP==@com.xyz.example.app.android/,username@example.com,Qh6W4Wz55YGFNU`; diff --git a/libs/common/spec/importers/testData/chromeCsv/simplePasswordData.csv.ts b/libs/common/spec/importers/testData/chromeCsv/simplePasswordData.csv.ts new file mode 100644 index 0000000000..9ae3e9ebe1 --- /dev/null +++ b/libs/common/spec/importers/testData/chromeCsv/simplePasswordData.csv.ts @@ -0,0 +1,2 @@ +export const data = `name,url,username,password +www.example.com,https://www.example.com/,username@example.com,wpC9qFvsbWQK5Z`; diff --git a/libs/common/src/importers/chromeCsvImporter.ts b/libs/common/src/importers/chromeCsvImporter.ts index 085b8cd28a..0208a29b73 100644 --- a/libs/common/src/importers/chromeCsvImporter.ts +++ b/libs/common/src/importers/chromeCsvImporter.ts @@ -4,6 +4,8 @@ import { BaseImporter } from "./baseImporter"; import { Importer } from "./importer"; export class ChromeCsvImporter extends BaseImporter implements Importer { + private androidPatternRegex = new RegExp("^android:\\/\\/.*(?<=@)(.*)(?=\\/)"); + parse(data: string): Promise { const result = new ImportResult(); const results = this.parseCsv(data, true); @@ -14,7 +16,11 @@ export class ChromeCsvImporter extends BaseImporter implements Importer { results.forEach((value) => { const cipher = this.initLoginCipher(); - cipher.name = this.getValueOrDefault(value.name, "--"); + let name = value.name; + if (!name && this.androidPatternRegex.test(value.url)) { + name = value.url.match(this.androidPatternRegex)[1]; + } + cipher.name = this.getValueOrDefault(name, "--"); cipher.login.username = this.getValueOrDefault(value.username); cipher.login.password = this.getValueOrDefault(value.password); cipher.login.uris = this.makeUriArray(value.url);