Dashlane Csv-Importer (#708)

* Move existing dashlane importer into dashlaneImporters

* Add testData for Dashlane CSV importer

* Add dashlane Csv importer and unit tests

* Fixed linting issues

* Moved dashlaneCsv types to own file

* Register DashlaneCsv importer

* Removed temp private method and use base impl

* rename spec imports

* Move scope of mapped columns

* Migrate folders into collection if imported via org
This commit is contained in:
Daniel James Smith 2022-03-07 19:21:23 +01:00 committed by GitHub
parent 6aae3beb76
commit 6e345bc4cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 749 additions and 11 deletions

View File

@ -7,6 +7,7 @@ export const featuredImportOptions = [
{ id: "bitwardenjson", name: "Bitwarden (json)" },
{ id: "bitwardencsv", name: "Bitwarden (csv)" },
{ id: "chromecsv", name: "Chrome (csv)" },
{ id: "dashlanecsv", name: "Dashlane (csv)" },
{ id: "dashlanejson", name: "Dashlane (json)" },
{ id: "firefoxcsv", name: "Firefox (csv)" },
{ id: "keepass2xml", name: "KeePass 2 (xml)" },

View File

@ -0,0 +1,271 @@
import { CipherType } from "../../enums/cipherType";
import { SecureNoteType } from "../../enums/secureNoteType";
import { ImportResult } from "../../models/domain/importResult";
import { CardView } from "../../models/view/cardView";
import { CipherView } from "../../models/view/cipherView";
import { IdentityView } from "../../models/view/identityView";
import { LoginView } from "../../models/view/loginView";
import { BaseImporter } from "../baseImporter";
import { Importer } from "../importer";
import {
CredentialsRecord,
IdRecord,
PaymentsRecord,
PersonalInformationRecord,
SecureNoteRecord,
} from "./types/dashlaneCsvTypes";
const _mappedCredentialsColums = new Set([
"title",
"note",
"username",
"password",
"url",
"otpSecret",
"category",
]);
const _mappedPersonalInfoAsIdentiyColumns = new Set([
"type",
"title",
"first_name",
"middle_name",
"last_name",
"login",
"email",
"phone_number",
"address",
"country",
"state",
"city",
"zip",
// Skip item_name as we already have set a combined name
"item_name",
]);
const _mappedSecureNoteColumns = new Set(["title", "note"]);
export class DashlaneCsvImporter extends BaseImporter implements Importer {
parse(data: string): Promise<ImportResult> {
const result = new ImportResult();
const results = this.parseCsv(data, true);
if (results == null) {
result.success = false;
return Promise.resolve(result);
}
if (results[0].type != null && results[0].title != null) {
const personalRecords = results as PersonalInformationRecord[];
// If personalRecords has only one "name" then create an Identity-Cipher
if (personalRecords.filter((x) => x.type === "name").length === 1) {
const cipher = this.initLoginCipher();
cipher.type = CipherType.Identity;
cipher.identity = new IdentityView();
results.forEach((row) => {
this.parsePersonalInformationRecordAsIdentity(cipher, row);
});
this.cleanupCipher(cipher);
result.ciphers.push(cipher);
result.success = true;
return Promise.resolve(result);
}
}
results.forEach((row) => {
const cipher = this.initLoginCipher();
const rowKeys = Object.keys(row);
if (rowKeys[0] === "username") {
this.processFolder(result, row.category);
this.parseCredentialsRecord(cipher, row);
}
if (rowKeys[0] === "type" && rowKeys[1] === "account_name") {
this.parsePaymentRecord(cipher, row);
}
if (rowKeys[0] === "type" && rowKeys[1] === "number") {
this.parseIdRecord(cipher, row);
}
if ((rowKeys[0] === "type") != null && rowKeys[1] === "title") {
this.parsePersonalInformationRecord(cipher, row);
}
if (rowKeys[0] === "title" && rowKeys[1] === "note") {
this.parseSecureNoteRecords(cipher, row);
}
this.convertToNoteIfNeeded(cipher);
this.cleanupCipher(cipher);
result.ciphers.push(cipher);
});
if (this.organization) {
this.moveFoldersToCollections(result);
}
result.success = true;
return Promise.resolve(result);
}
parseCredentialsRecord(cipher: CipherView, row: CredentialsRecord) {
cipher.type = CipherType.Login;
cipher.login = new LoginView();
cipher.name = row.title;
cipher.notes = row.note;
cipher.login.username = row.username;
cipher.login.password = row.password;
cipher.login.totp = row.otpSecret;
cipher.login.uris = this.makeUriArray(row.url);
this.importUnmappedFields(cipher, row, _mappedCredentialsColums);
}
parsePaymentRecord(cipher: CipherView, row: PaymentsRecord) {
cipher.type = CipherType.Card;
cipher.card = new CardView();
cipher.name = row.account_name;
let mappedValues: string[] = [];
switch (row.type) {
case "credit_card":
cipher.card.cardholderName = row.account_name;
cipher.card.number = row.cc_number;
cipher.card.brand = this.getCardBrand(cipher.card.number);
cipher.card.code = row.code;
cipher.card.expMonth = row.expiration_month;
cipher.card.expYear = row.expiration_year.substring(2, 4);
// If you add more mapped fields please extend this
mappedValues = [
"account_name",
"account_holder",
"cc_number",
"code",
"expiration_month",
"expiration_year",
];
break;
case "bank":
cipher.card.cardholderName = row.account_holder;
cipher.card.number = row.account_number;
// If you add more mapped fields please extend this
mappedValues = ["account_name", "account_holder", "account_number"];
break;
default:
break;
}
this.importUnmappedFields(cipher, row, new Set(mappedValues));
}
parseIdRecord(cipher: CipherView, row: IdRecord) {
cipher.type = CipherType.Identity;
cipher.identity = new IdentityView();
const mappedValues: string[] = ["name", "number"];
switch (row.type) {
case "card":
cipher.name = `${row.name} ${row.type}`;
this.processFullName(cipher, row.name);
cipher.identity.licenseNumber = row.number;
break;
case "passport":
cipher.name = `${row.name} ${row.type}`;
this.processFullName(cipher, row.name);
cipher.identity.passportNumber = row.number;
break;
case "license":
cipher.name = `${row.name} ${row.type}`;
this.processFullName(cipher, row.name);
cipher.identity.licenseNumber = row.number;
cipher.identity.state = row.state;
mappedValues.push("state");
break;
case "social_security":
cipher.name = `${row.name} ${row.type}`;
this.processFullName(cipher, row.name);
cipher.identity.ssn = row.number;
break;
case "tax_number":
cipher.name = row.type;
cipher.identity.licenseNumber = row.number;
break;
default:
break;
}
// If you add more mapped fields please extend this
this.importUnmappedFields(cipher, row, new Set(mappedValues));
}
parsePersonalInformationRecord(cipher: CipherView, row: PersonalInformationRecord) {
cipher.type = CipherType.SecureNote;
cipher.secureNote.type = SecureNoteType.Generic;
if (row.type === "name") {
cipher.name = `${row.title} ${row.first_name} ${row.middle_name} ${row.last_name}`
.replace(" ", " ")
.trim();
} else {
cipher.name = row.item_name;
}
const dataRow = row as any;
Object.keys(row).forEach((key) => {
this.processKvp(cipher, key, dataRow[key]);
});
}
parsePersonalInformationRecordAsIdentity(cipher: CipherView, row: PersonalInformationRecord) {
switch (row.type) {
case "name":
this.processFullName(cipher, `${row.first_name} ${row.middle_name} ${row.last_name}`);
cipher.identity.title = row.title;
cipher.name = cipher.identity.fullName;
cipher.identity.username = row.login;
break;
case "email":
cipher.identity.email = row.email;
break;
case "number":
cipher.identity.phone = row.phone_number;
break;
case "address":
cipher.identity.address1 = row.address;
cipher.identity.city = row.city;
cipher.identity.postalCode = row.zip;
cipher.identity.state = row.state;
cipher.identity.country = row.country;
break;
default:
break;
}
this.importUnmappedFields(cipher, row, _mappedPersonalInfoAsIdentiyColumns);
}
parseSecureNoteRecords(cipher: CipherView, row: SecureNoteRecord) {
cipher.type = CipherType.SecureNote;
cipher.secureNote.type = SecureNoteType.Generic;
cipher.name = row.title;
cipher.notes = row.note;
this.importUnmappedFields(cipher, row, _mappedSecureNoteColumns);
}
importUnmappedFields(cipher: CipherView, row: any, mappedValues: Set<string>) {
const unmappedFields = Object.keys(row).filter((x) => !mappedValues.has(x));
unmappedFields.forEach((key) => {
const item = row as any;
this.processKvp(cipher, key, item[key]);
});
}
}

View File

@ -1,13 +1,12 @@
import { CipherType } from "../enums/cipherType";
import { SecureNoteType } from "../enums/secureNoteType";
import { ImportResult } from "../models/domain/importResult";
import { CardView } from "../models/view/cardView";
import { CipherView } from "../models/view/cipherView";
import { IdentityView } from "../models/view/identityView";
import { SecureNoteView } from "../models/view/secureNoteView";
import { BaseImporter } from "./baseImporter";
import { Importer } from "./importer";
import { CipherType } from "../../enums/cipherType";
import { SecureNoteType } from "../../enums/secureNoteType";
import { ImportResult } from "../../models/domain/importResult";
import { CardView } from "../../models/view/cardView";
import { CipherView } from "../../models/view/cipherView";
import { IdentityView } from "../../models/view/identityView";
import { SecureNoteView } from "../../models/view/secureNoteView";
import { BaseImporter } from "../baseImporter";
import { Importer } from "../importer";
const HandledResults = new Set([
"ADDRESS",

View File

@ -0,0 +1,68 @@
// tslint:disable
export class CredentialsRecord {
username: string;
username2: string;
username3: string;
title: string;
password: string;
note: string;
url: string;
category: string;
otpSecret: string;
}
export class PaymentsRecord {
type: string;
account_name: string;
account_holder: string;
cc_number: string;
code: string;
expiration_month: string;
expiration_year: string;
routing_number: string;
account_number: string;
country: string;
issuing_bank: string;
}
export class IdRecord {
type: string;
number: string;
name: string;
issue_date: string;
expiration_date: string;
place_of_issue: string;
state: string;
}
export class PersonalInformationRecord {
type: string;
title: string;
first_name: string;
middle_name: string;
last_name: string;
login: string;
date_of_birth: string;
place_of_birth: string;
email: string;
email_type: string;
item_name: string;
phone_number: string;
address: string;
country: string;
state: string;
city: string;
zip: string;
address_recipient: string;
address_building: string;
address_apartment: string;
address_floor: string;
address_door_code: string;
job_title: string;
url: string;
}
export class SecureNoteRecord {
title: string;
note: string;
}

View File

@ -26,7 +26,8 @@ import { ButtercupCsvImporter } from "../importers/buttercupCsvImporter";
import { ChromeCsvImporter } from "../importers/chromeCsvImporter";
import { ClipperzHtmlImporter } from "../importers/clipperzHtmlImporter";
import { CodebookCsvImporter } from "../importers/codebookCsvImporter";
import { DashlaneJsonImporter } from "../importers/dashlaneJsonImporter";
import { DashlaneCsvImporter } from "../importers/dashlaneImporters/dashlaneCsvImporter";
import { DashlaneJsonImporter } from "../importers/dashlaneImporters/dashlaneJsonImporter";
import { EncryptrCsvImporter } from "../importers/encryptrCsvImporter";
import { EnpassCsvImporter } from "../importers/enpassCsvImporter";
import { EnpassJsonImporter } from "../importers/enpassJsonImporter";
@ -218,6 +219,8 @@ export class ImportService implements ImportServiceAbstraction {
return new EnpassJsonImporter();
case "pwsafexml":
return new PasswordSafeXmlImporter();
case "dashlanecsv":
return new DashlaneCsvImporter();
case "dashlanejson":
return new DashlaneJsonImporter();
case "msecurecsv":

3
package-lock.json generated
View File

@ -88,6 +88,7 @@
}
},
"common": {
"name": "@bitwarden/jslib-common",
"version": "0.0.0",
"license": "GPL-3.0",
"dependencies": {
@ -114,6 +115,7 @@
}
},
"electron": {
"name": "@bitwarden/jslib-electron",
"version": "0.0.0",
"license": "GPL-3.0",
"dependencies": {
@ -133,6 +135,7 @@
}
},
"node": {
"name": "@bitwarden/jslib-node",
"version": "0.0.0",
"license": "GPL-3.0",
"dependencies": {

View File

@ -0,0 +1,367 @@
import { CipherType } from "jslib-common/enums/cipherType";
import { DashlaneCsvImporter as Importer } from "jslib-common/importers/dashlaneImporters/dashlaneCsvImporter";
import { credentialsData } from "./testData/dashlaneCsv/credentials.csv";
import { identityData } from "./testData/dashlaneCsv/id.csv";
import { multiplePersonalInfoData } from "./testData/dashlaneCsv/multiplePersonalInfo.csv";
import { paymentsData } from "./testData/dashlaneCsv/payments.csv";
import { personalInfoData } from "./testData/dashlaneCsv/personalInfo.csv";
import { secureNoteData } from "./testData/dashlaneCsv/securenotes.csv";
describe("Dashlane CSV Importer", () => {
let importer: Importer;
beforeEach(() => {
importer = new Importer();
});
it("should parse login records", async () => {
const result = await importer.parse(credentialsData);
expect(result != null).toBe(true);
const cipher = result.ciphers.shift();
expect(cipher.name).toEqual("example.com");
expect(cipher.login.username).toEqual("jdoe");
expect(cipher.login.password).toEqual("somePassword");
expect(cipher.login.totp).toEqual("someTOTPSeed");
expect(cipher.login.uris.length).toEqual(1);
const uriView = cipher.login.uris.shift();
expect(uriView.uri).toEqual("https://www.example.com");
expect(cipher.notes).toEqual("some note for example.com");
});
it("should parse an item and create a folder", async () => {
const result = await importer.parse(credentialsData);
expect(result).not.toBeNull();
expect(result.success).toBe(true);
expect(result.folders.length).toBe(1);
expect(result.folders[0].name).toBe("Entertainment");
expect(result.folderRelationships[0]).toEqual([0, 0]);
});
it("should parse payment records", async () => {
const result = await importer.parse(paymentsData);
expect(result).not.toBeNull();
expect(result.success).toBe(true);
expect(result.ciphers.length).toBe(2);
// Account
const cipher = result.ciphers.shift();
expect(cipher.type).toBe(CipherType.Card);
expect(cipher.name).toBe("John's savings account");
expect(cipher.card.brand).toBeNull();
expect(cipher.card.cardholderName).toBe("John Doe");
expect(cipher.card.number).toBe("accountNumber");
expect(cipher.card.code).toBeNull();
expect(cipher.card.expMonth).toBeNull();
expect(cipher.card.expYear).toBeNull();
expect(cipher.fields.length).toBe(4);
expect(cipher.fields[0].name).toBe("type");
expect(cipher.fields[0].value).toBe("bank");
expect(cipher.fields[1].name).toBe("routing_number");
expect(cipher.fields[1].value).toBe("routingNumber");
expect(cipher.fields[2].name).toBe("country");
expect(cipher.fields[2].value).toBe("US");
expect(cipher.fields[3].name).toBe("issuing_bank");
expect(cipher.fields[3].value).toBe("US-ALLY");
// CreditCard
const cipher2 = result.ciphers.shift();
expect(cipher2.type).toBe(CipherType.Card);
expect(cipher2.name).toBe("John Doe");
expect(cipher2.card.brand).toBe("Visa");
expect(cipher2.card.cardholderName).toBe("John Doe");
expect(cipher2.card.number).toBe("41111111111111111");
expect(cipher2.card.code).toBe("123");
expect(cipher2.card.expMonth).toBe("01");
expect(cipher2.card.expYear).toBe("23");
expect(cipher2.fields.length).toBe(2);
expect(cipher2.fields[0].name).toBe("type");
expect(cipher2.fields[0].value).toBe("credit_card");
expect(cipher2.fields[1].name).toBe("country");
expect(cipher2.fields[1].value).toBe("US");
});
it("should parse ids records", async () => {
const result = await importer.parse(identityData);
expect(result).not.toBeNull();
expect(result.success).toBe(true);
// Type card
const cipher = result.ciphers.shift();
expect(cipher.type).toBe(CipherType.Identity);
expect(cipher.name).toBe("John Doe card");
expect(cipher.identity.fullName).toBe("John Doe");
expect(cipher.identity.firstName).toBe("John");
expect(cipher.identity.middleName).toBeNull();
expect(cipher.identity.lastName).toBe("Doe");
expect(cipher.identity.licenseNumber).toBe("123123123");
expect(cipher.fields.length).toBe(3);
expect(cipher.fields[0].name).toEqual("type");
expect(cipher.fields[0].value).toEqual("card");
expect(cipher.fields[1].name).toEqual("issue_date");
expect(cipher.fields[1].value).toEqual("2022-1-30");
expect(cipher.fields[2].name).toEqual("expiration_date");
expect(cipher.fields[2].value).toEqual("2032-1-30");
// Type passport
const cipher2 = result.ciphers.shift();
expect(cipher2.type).toBe(CipherType.Identity);
expect(cipher2.name).toBe("John Doe passport");
expect(cipher2.identity.fullName).toBe("John Doe");
expect(cipher2.identity.firstName).toBe("John");
expect(cipher2.identity.middleName).toBeNull();
expect(cipher2.identity.lastName).toBe("Doe");
expect(cipher2.identity.passportNumber).toBe("123123123");
expect(cipher2.fields.length).toBe(4);
expect(cipher2.fields[0].name).toEqual("type");
expect(cipher2.fields[0].value).toEqual("passport");
expect(cipher2.fields[1].name).toEqual("issue_date");
expect(cipher2.fields[1].value).toEqual("2022-1-30");
expect(cipher2.fields[2].name).toEqual("expiration_date");
expect(cipher2.fields[2].value).toEqual("2032-1-30");
expect(cipher2.fields[3].name).toEqual("place_of_issue");
expect(cipher2.fields[3].value).toEqual("somewhere in Germany");
// Type license
const cipher3 = result.ciphers.shift();
expect(cipher3.type).toBe(CipherType.Identity);
expect(cipher3.name).toBe("John Doe license");
expect(cipher3.identity.fullName).toBe("John Doe");
expect(cipher3.identity.firstName).toBe("John");
expect(cipher3.identity.middleName).toBeNull();
expect(cipher3.identity.lastName).toBe("Doe");
expect(cipher3.identity.licenseNumber).toBe("1234556");
expect(cipher3.identity.state).toBe("DC");
expect(cipher3.fields.length).toBe(3);
expect(cipher3.fields[0].name).toEqual("type");
expect(cipher3.fields[0].value).toEqual("license");
expect(cipher3.fields[1].name).toEqual("issue_date");
expect(cipher3.fields[1].value).toEqual("2022-8-10");
expect(cipher3.fields[2].name).toEqual("expiration_date");
expect(cipher3.fields[2].value).toEqual("2022-10-10");
// Type social_security
const cipher4 = result.ciphers.shift();
expect(cipher4.type).toBe(CipherType.Identity);
expect(cipher4.name).toBe("John Doe social_security");
expect(cipher4.identity.fullName).toBe("John Doe");
expect(cipher4.identity.firstName).toBe("John");
expect(cipher4.identity.middleName).toBeNull();
expect(cipher4.identity.lastName).toBe("Doe");
expect(cipher4.identity.ssn).toBe("123123123");
expect(cipher4.fields.length).toBe(1);
expect(cipher4.fields[0].name).toEqual("type");
expect(cipher4.fields[0].value).toEqual("social_security");
// Type tax_number
const cipher5 = result.ciphers.shift();
expect(cipher5.type).toBe(CipherType.Identity);
expect(cipher5.name).toBe("tax_number");
expect(cipher5.identity.licenseNumber).toBe("123123123");
expect(cipher5.fields.length).toBe(1);
expect(cipher5.fields[0].name).toEqual("type");
expect(cipher5.fields[0].value).toEqual("tax_number");
});
it("should parse secureNote records", async () => {
const result = await importer.parse(secureNoteData);
expect(result).not.toBeNull();
expect(result.success).toBe(true);
expect(result.ciphers.length).toBe(1);
const cipher = result.ciphers.shift();
expect(cipher.type).toBe(CipherType.SecureNote);
expect(cipher.name).toBe("01");
expect(cipher.notes).toBe("test");
});
it("should parse personal information records (multiple identities)", async () => {
const result = await importer.parse(multiplePersonalInfoData);
expect(result).not.toBeNull();
expect(result.success).toBe(true);
expect(result.ciphers.length).toBe(6);
// name
const cipher = result.ciphers.shift();
expect(cipher.type).toBe(CipherType.SecureNote);
expect(cipher.name).toBe("MR John Doe");
expect(cipher.fields.length).toBe(7);
expect(cipher.fields[0].name).toEqual("type");
expect(cipher.fields[0].value).toEqual("name");
expect(cipher.fields[1].name).toEqual("title");
expect(cipher.fields[1].value).toEqual("MR");
expect(cipher.fields[2].name).toEqual("first_name");
expect(cipher.fields[2].value).toEqual("John");
expect(cipher.fields[3].name).toEqual("last_name");
expect(cipher.fields[3].value).toEqual("Doe");
expect(cipher.fields[4].name).toEqual("login");
expect(cipher.fields[4].value).toEqual("jdoe");
expect(cipher.fields[5].name).toEqual("date_of_birth");
expect(cipher.fields[5].value).toEqual("2022-01-30");
expect(cipher.fields[6].name).toEqual("place_of_birth");
expect(cipher.fields[6].value).toEqual("world");
// email
const cipher2 = result.ciphers.shift();
expect(cipher2.type).toBe(CipherType.SecureNote);
expect(cipher2.name).toBe("Johns email");
expect(cipher2.fields.length).toBe(4);
expect(cipher2.fields[0].name).toEqual("type");
expect(cipher2.fields[0].value).toEqual("email");
expect(cipher2.fields[1].name).toEqual("email");
expect(cipher2.fields[1].value).toEqual("jdoe@example.com");
expect(cipher2.fields[2].name).toEqual("email_type");
expect(cipher2.fields[2].value).toEqual("personal");
expect(cipher2.fields[3].name).toEqual("item_name");
expect(cipher2.fields[3].value).toEqual("Johns email");
// number
const cipher3 = result.ciphers.shift();
expect(cipher3.type).toBe(CipherType.SecureNote);
expect(cipher3.name).toBe("John's number");
expect(cipher3.fields.length).toBe(3);
expect(cipher3.fields[0].name).toEqual("type");
expect(cipher3.fields[0].value).toEqual("number");
expect(cipher3.fields[1].name).toEqual("item_name");
expect(cipher3.fields[1].value).toEqual("John's number");
expect(cipher3.fields[2].name).toEqual("phone_number");
expect(cipher3.fields[2].value).toEqual("+49123123123");
// address
const cipher4 = result.ciphers.shift();
expect(cipher4.type).toBe(CipherType.SecureNote);
expect(cipher4.name).toBe("John's home address");
expect(cipher4.fields.length).toBe(12);
expect(cipher4.fields[0].name).toEqual("type");
expect(cipher4.fields[0].value).toEqual("address");
expect(cipher4.fields[1].name).toEqual("item_name");
expect(cipher4.fields[1].value).toEqual("John's home address");
expect(cipher4.fields[2].name).toEqual("address");
expect(cipher4.fields[2].value).toEqual("1 some street");
expect(cipher4.fields[3].name).toEqual("country");
expect(cipher4.fields[3].value).toEqual("de");
expect(cipher4.fields[4].name).toEqual("state");
expect(cipher4.fields[4].value).toEqual("DE-0-NW");
expect(cipher4.fields[5].name).toEqual("city");
expect(cipher4.fields[5].value).toEqual("some city");
expect(cipher4.fields[6].name).toEqual("zip");
expect(cipher4.fields[6].value).toEqual("123123");
expect(cipher4.fields[7].name).toEqual("address_recipient");
expect(cipher4.fields[7].value).toEqual("John");
expect(cipher4.fields[8].name).toEqual("address_building");
expect(cipher4.fields[8].value).toEqual("1");
expect(cipher4.fields[9].name).toEqual("address_apartment");
expect(cipher4.fields[9].value).toEqual("1");
expect(cipher4.fields[10].name).toEqual("address_floor");
expect(cipher4.fields[10].value).toEqual("1");
expect(cipher4.fields[11].name).toEqual("address_door_code");
expect(cipher4.fields[11].value).toEqual("123");
// website
const cipher5 = result.ciphers.shift();
expect(cipher5.type).toBe(CipherType.SecureNote);
expect(cipher5.name).toBe("Website");
expect(cipher5.fields.length).toBe(3);
expect(cipher5.fields[0].name).toEqual("type");
expect(cipher5.fields[0].value).toEqual("website");
expect(cipher5.fields[1].name).toEqual("item_name");
expect(cipher5.fields[1].value).toEqual("Website");
expect(cipher5.fields[2].name).toEqual("url");
expect(cipher5.fields[2].value).toEqual("website.com");
// 2nd name/identity
const cipher6 = result.ciphers.shift();
expect(cipher6.type).toBe(CipherType.SecureNote);
expect(cipher6.name).toBe("Mrs Jane Doe");
expect(cipher6.fields.length).toBe(7);
expect(cipher6.fields[0].name).toEqual("type");
expect(cipher6.fields[0].value).toEqual("name");
expect(cipher6.fields[1].name).toEqual("title");
expect(cipher6.fields[1].value).toEqual("Mrs");
expect(cipher6.fields[2].name).toEqual("first_name");
expect(cipher6.fields[2].value).toEqual("Jane");
expect(cipher6.fields[3].name).toEqual("last_name");
expect(cipher6.fields[3].value).toEqual("Doe");
expect(cipher6.fields[4].name).toEqual("login");
expect(cipher6.fields[4].value).toEqual("jdoe");
expect(cipher6.fields[5].name).toEqual("date_of_birth");
expect(cipher6.fields[5].value).toEqual("2022-01-30");
expect(cipher6.fields[6].name).toEqual("place_of_birth");
expect(cipher6.fields[6].value).toEqual("earth");
});
it("should combine personal information records to one identity if only one identity present", async () => {
const result = await importer.parse(personalInfoData);
expect(result).not.toBeNull();
expect(result.success).toBe(true);
const cipher = result.ciphers.shift();
expect(cipher.type).toBe(CipherType.Identity);
expect(cipher.name).toBe("MR John Doe");
expect(cipher.identity.fullName).toBe("MR John Doe");
expect(cipher.identity.title).toBe("MR");
expect(cipher.identity.firstName).toBe("John");
expect(cipher.identity.middleName).toBeNull();
expect(cipher.identity.lastName).toBe("Doe");
expect(cipher.identity.username).toBe("jdoe");
expect(cipher.identity.email).toBe("jdoe@example.com");
expect(cipher.identity.phone).toBe("+49123123123");
expect(cipher.fields.length).toBe(9);
expect(cipher.fields[0].name).toBe("date_of_birth");
expect(cipher.fields[0].value).toBe("2022-01-30");
expect(cipher.fields[1].name).toBe("place_of_birth");
expect(cipher.fields[1].value).toBe("world");
expect(cipher.fields[2].name).toBe("email_type");
expect(cipher.fields[2].value).toBe("personal");
expect(cipher.fields[3].name).toBe("address_recipient");
expect(cipher.fields[3].value).toBe("John");
expect(cipher.fields[4].name).toBe("address_building");
expect(cipher.fields[4].value).toBe("1");
expect(cipher.fields[5].name).toBe("address_apartment");
expect(cipher.fields[5].value).toBe("1");
expect(cipher.fields[6].name).toBe("address_floor");
expect(cipher.fields[6].value).toBe("1");
expect(cipher.fields[7].name).toBe("address_door_code");
expect(cipher.fields[7].value).toBe("123");
expect(cipher.fields[8].name).toBe("url");
expect(cipher.fields[8].value).toBe("website.com");
});
});

View File

@ -0,0 +1,2 @@
export const credentialsData = `username,username2,username3,title,password,note,url,category,otpSecret
jdoe,,,example.com,somePassword,some note for example.com,https://www.example.com,Entertainment,someTOTPSeed`;

View File

@ -0,0 +1,6 @@
export const identityData = `type,number,name,issue_date,expiration_date,place_of_issue,state
card,123123123,John Doe,2022-1-30,2032-1-30,,
passport,123123123,John Doe,2022-1-30,2032-1-30,somewhere in Germany,
license,1234556,John Doe,2022-8-10,2022-10-10,,DC
social_security,123123123,John Doe,,,,
tax_number,123123123,,,,,`;

View File

@ -0,0 +1,7 @@
export const multiplePersonalInfoData = `type,title,first_name,middle_name,last_name,login,date_of_birth,place_of_birth,email,email_type,item_name,phone_number,address,country,state,city,zip,address_recipient,address_building,address_apartment,address_floor,address_door_code,job_title,url
name,MR,John,,Doe,jdoe,2022-01-30,world,,,,,,,,,,,,,,,,
email,,,,,,,,jdoe@example.com,personal,Johns email,,,,,,,,,,,,,
number,,,,,,,,,,John's number,+49123123123,,,,,,,,,,,,
address,,,,,,,,,,John's home address,,1 some street,de,DE-0-NW,some city,123123,John,1,1,1,123,,
website,,,,,,,,,,Website,,,,,,,,,,,,,website.com
name,Mrs,Jane,,Doe,jdoe,2022-01-30,earth,,,,,,,,,,,,,,,,`;

View File

@ -0,0 +1,3 @@
export const paymentsData = `type,account_name,account_holder,cc_number,code,expiration_month,expiration_year,routing_number,account_number,country,issuing_bank
bank,John's savings account,John Doe,,,,,routingNumber,accountNumber,US,US-ALLY
credit_card,John Doe,,41111111111111111,123,01,2023,,,US,`;

View File

@ -0,0 +1,6 @@
export const personalInfoData = `type,title,first_name,middle_name,last_name,login,date_of_birth,place_of_birth,email,email_type,item_name,phone_number,address,country,state,city,zip,address_recipient,address_building,address_apartment,address_floor,address_door_code,job_title,url
name,MR,John,,Doe,jdoe,2022-01-30,world,,,,,,,,,,,,,,,,
email,,,,,,,,jdoe@example.com,personal,Johns email,,,,,,,,,,,,,
number,,,,,,,,,,John's number,+49123123123,,,,,,,,,,,,
address,,,,,,,,,,John's home address,,1 some street,de,DE-0-NW,some city,123123,John,1,1,1,123,,
website,,,,,,,,,,Website,,,,,,,,,,,,,website.com`;

View File

@ -0,0 +1,2 @@
export const secureNoteData = `title,note
01,test`;