diff --git a/libs/common/spec/importers/fsecure-fsk-importer.spec.ts b/libs/common/spec/importers/fsecure-fsk-importer.spec.ts deleted file mode 100644 index a49fdcc47a..0000000000 --- a/libs/common/spec/importers/fsecure-fsk-importer.spec.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { FSecureFskImporter as Importer } from "@bitwarden/common/importers/fsecure-fsk-importer"; - -const TestDataWithStyleSetToWebsite: string = JSON.stringify({ - data: { - "8d58b5cf252dd06fbd98f5289e918ab1": { - color: "#00baff", - reatedDate: 1609302913, - creditCvv: "", - creditExpiry: "", - creditNumber: "", - favorite: 0, - modifiedDate: 1609302913, - notes: "note", - password: "word", - passwordList: [], - passwordModifiedDate: 1609302913, - rev: 1, - service: "My first pass", - style: "website", - type: 1, - url: "https://bitwarden.com", - username: "pass", - }, - }, -}); - -const TestDataWithStyleSetToGlobe: string = JSON.stringify({ - data: { - "8d58b5cf252dd06fbd98f5289e918ab1": { - color: "#00baff", - reatedDate: 1609302913, - creditCvv: "", - creditExpiry: "", - creditNumber: "", - favorite: 0, - modifiedDate: 1609302913, - notes: "note", - password: "word", - passwordList: [], - passwordModifiedDate: 1609302913, - rev: 1, - service: "My first pass", - style: "globe", - type: 1, - url: "https://bitwarden.com", - username: "pass", - }, - }, -}); - -describe("FSecure FSK Importer", () => { - it("should parse data with style set to website", async () => { - const importer = new Importer(); - const result = await importer.parse(TestDataWithStyleSetToWebsite); - expect(result != null).toBe(true); - - const cipher = result.ciphers.shift(); - expect(cipher.login.username).toEqual("pass"); - expect(cipher.login.password).toEqual("word"); - expect(cipher.login.uris.length).toEqual(1); - const uriView = cipher.login.uris.shift(); - expect(uriView.uri).toEqual("https://bitwarden.com"); - }); - - it("should parse data with style set to globe", async () => { - const importer = new Importer(); - const result = await importer.parse(TestDataWithStyleSetToGlobe); - expect(result != null).toBe(true); - - const cipher = result.ciphers.shift(); - expect(cipher.login.username).toEqual("pass"); - expect(cipher.login.password).toEqual("word"); - expect(cipher.login.uris.length).toEqual(1); - const uriView = cipher.login.uris.shift(); - expect(uriView.uri).toEqual("https://bitwarden.com"); - }); -}); diff --git a/libs/common/src/importers/fsecure-fsk-importer.ts b/libs/common/src/importers/fsecure-fsk-importer.ts deleted file mode 100644 index 26cca341ac..0000000000 --- a/libs/common/src/importers/fsecure-fsk-importer.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { CipherType } from "../enums/cipherType"; -import { ImportResult } from "../models/domain/import-result"; -import { CardView } from "../models/view/card.view"; - -import { BaseImporter } from "./base-importer"; -import { Importer } from "./importer"; - -export class FSecureFskImporter extends BaseImporter implements Importer { - parse(data: string): Promise { - const result = new ImportResult(); - const results = JSON.parse(data); - if (results == null || results.data == null) { - result.success = false; - return Promise.resolve(result); - } - - for (const key in results.data) { - // eslint-disable-next-line - if (!results.data.hasOwnProperty(key)) { - continue; - } - - const value = results.data[key]; - const cipher = this.initLoginCipher(); - cipher.name = this.getValueOrDefault(value.service); - cipher.notes = this.getValueOrDefault(value.notes); - - if (value.style === "website" || value.style === "globe") { - cipher.login.username = this.getValueOrDefault(value.username); - cipher.login.password = this.getValueOrDefault(value.password); - cipher.login.uris = this.makeUriArray(value.url); - } else if (value.style === "creditcard") { - cipher.type = CipherType.Card; - cipher.card = new CardView(); - cipher.card.cardholderName = this.getValueOrDefault(value.username); - cipher.card.number = this.getValueOrDefault(value.creditNumber); - cipher.card.brand = this.getCardBrand(cipher.card.number); - cipher.card.code = this.getValueOrDefault(value.creditCvv); - if (!this.isNullOrWhitespace(value.creditExpiry)) { - if (!this.setCardExpiration(cipher, value.creditExpiry)) { - this.processKvp(cipher, "Expiration", value.creditExpiry); - } - } - if (!this.isNullOrWhitespace(value.password)) { - this.processKvp(cipher, "PIN", value.password); - } - } else { - continue; - } - - this.convertToNoteIfNeeded(cipher); - this.cleanupCipher(cipher); - result.ciphers.push(cipher); - } - - result.success = true; - return Promise.resolve(result); - } -} diff --git a/libs/common/src/importers/fsecure/fsecure-fsk-importer.spec.ts b/libs/common/src/importers/fsecure/fsecure-fsk-importer.spec.ts new file mode 100644 index 0000000000..d8fab657ce --- /dev/null +++ b/libs/common/src/importers/fsecure/fsecure-fsk-importer.spec.ts @@ -0,0 +1,52 @@ +import { CipherType } from "../../enums/cipherType"; + +import { FSecureFskImporter as Importer } from "./fsecure-fsk-importer"; +import { CreditCardTestEntry, LoginTestEntry } from "./fsk-test-data"; + +describe("FSecure FSK Importer", () => { + it("should import data of type login", async () => { + const importer = new Importer(); + const LoginTestEntryStringified = JSON.stringify(LoginTestEntry); + const result = await importer.parse(LoginTestEntryStringified); + expect(result != null).toBe(true); + + const cipher = result.ciphers.shift(); + + expect(cipher.name).toEqual("example.com"); + expect(cipher.favorite).toBe(true); + expect(cipher.notes).toEqual("some note for example.com"); + + expect(cipher.type).toBe(CipherType.Login); + expect(cipher.login.username).toEqual("jdoe"); + expect(cipher.login.password).toEqual("somePassword"); + + expect(cipher.login.uris.length).toEqual(1); + const uriView = cipher.login.uris.shift(); + expect(uriView.uri).toEqual("https://www.example.com"); + }); + + it("should import data of type creditCard", async () => { + const importer = new Importer(); + const CreditCardTestEntryStringified = JSON.stringify(CreditCardTestEntry); + const result = await importer.parse(CreditCardTestEntryStringified); + expect(result != null).toBe(true); + + const cipher = result.ciphers.shift(); + + expect(cipher.name).toEqual("My credit card"); + expect(cipher.favorite).toBe(false); + expect(cipher.notes).toEqual("some notes to my card"); + + expect(cipher.type).toBe(CipherType.Card); + expect(cipher.card.cardholderName).toEqual("John Doe"); + expect(cipher.card.number).toEqual("4242424242424242"); + expect(cipher.card.code).toEqual("123"); + + expect(cipher.fields.length).toBe(2); + expect(cipher.fields[0].name).toEqual("Expiration"); + expect(cipher.fields[0].value).toEqual("22.10.2026"); + + expect(cipher.fields[1].name).toEqual("PIN"); + expect(cipher.fields[1].value).toEqual("1234"); + }); +}); diff --git a/libs/common/src/importers/fsecure/fsecure-fsk-importer.ts b/libs/common/src/importers/fsecure/fsecure-fsk-importer.ts new file mode 100644 index 0000000000..c86c43640a --- /dev/null +++ b/libs/common/src/importers/fsecure/fsecure-fsk-importer.ts @@ -0,0 +1,79 @@ +import { CipherType } from "../../enums/cipherType"; +import { ImportResult } from "../../models/domain/import-result"; +import { CardView } from "../../models/view/card.view"; +import { CipherView } from "../../models/view/cipher.view"; +import { BaseImporter } from "../base-importer"; +import { Importer } from "../importer"; + +import { FskEntry, FskEntryTypesEnum, FskFile } from "./fsecure-fsk-types"; + +export class FSecureFskImporter extends BaseImporter implements Importer { + parse(data: string): Promise { + const result = new ImportResult(); + const results: FskFile = JSON.parse(data); + if (results == null || results.data == null) { + result.success = false; + return Promise.resolve(result); + } + + for (const key in results.data) { + // eslint-disable-next-line + if (!results.data.hasOwnProperty(key)) { + continue; + } + + const value = results.data[key]; + const cipher = this.parseEntry(value); + result.ciphers.push(cipher); + } + + result.success = true; + return Promise.resolve(result); + } + + private parseEntry(entry: FskEntry): CipherView { + const cipher = this.initLoginCipher(); + cipher.name = this.getValueOrDefault(entry.service); + cipher.notes = this.getValueOrDefault(entry.notes); + cipher.favorite = entry.favorite > 0; + + switch (entry.type) { + case FskEntryTypesEnum.Login: + this.handleLoginEntry(entry, cipher); + break; + case FskEntryTypesEnum.CreditCard: + this.handleCreditCardEntry(entry, cipher); + break; + default: + return; + break; + } + + this.convertToNoteIfNeeded(cipher); + this.cleanupCipher(cipher); + return cipher; + } + + private handleLoginEntry(entry: FskEntry, cipher: CipherView) { + cipher.login.username = this.getValueOrDefault(entry.username); + cipher.login.password = this.getValueOrDefault(entry.password); + cipher.login.uris = this.makeUriArray(entry.url); + } + + private handleCreditCardEntry(entry: FskEntry, cipher: CipherView) { + cipher.type = CipherType.Card; + cipher.card = new CardView(); + cipher.card.cardholderName = this.getValueOrDefault(entry.username); + cipher.card.number = this.getValueOrDefault(entry.creditNumber); + cipher.card.brand = this.getCardBrand(cipher.card.number); + cipher.card.code = this.getValueOrDefault(entry.creditCvv); + if (!this.isNullOrWhitespace(entry.creditExpiry)) { + if (!this.setCardExpiration(cipher, entry.creditExpiry)) { + this.processKvp(cipher, "Expiration", entry.creditExpiry); + } + } + if (!this.isNullOrWhitespace(entry.password)) { + this.processKvp(cipher, "PIN", entry.password); + } + } +} diff --git a/libs/common/src/importers/fsecure/fsecure-fsk-types.ts b/libs/common/src/importers/fsecure/fsecure-fsk-types.ts new file mode 100644 index 0000000000..71797a0f8c --- /dev/null +++ b/libs/common/src/importers/fsecure/fsecure-fsk-types.ts @@ -0,0 +1,37 @@ +export interface FskFile { + data: Data; +} + +export interface Data { + [key: string]: FskEntry; +} + +export enum FskEntryTypesEnum { + Login = 1, + CreditCard = 2, +} + +export interface FskEntry { + color: string; + creditCvv: string; + creditExpiry: string; + creditNumber: string; + favorite: number; // UNIX timestamp + notes: string; + password: string; + passwordList: PasswordList[]; + passwordModifiedDate: number; // UNIX timestamp + rev: string | number; + service: string; + style: string; + type: FskEntryTypesEnum; + url: string; + username: string; + createdDate: number; // UNIX timestamp + modifiedDate: number; // UNIX timestamp +} + +export interface PasswordList { + changedate: string; + password: string; +} diff --git a/libs/common/src/importers/fsecure/fsk-test-data.ts b/libs/common/src/importers/fsecure/fsk-test-data.ts new file mode 100644 index 0000000000..c088dd40b4 --- /dev/null +++ b/libs/common/src/importers/fsecure/fsk-test-data.ts @@ -0,0 +1,49 @@ +import { FskFile } from "./fsecure-fsk-types"; + +export const LoginTestEntry: FskFile = { + data: { + "1c3a2e31dcaa8459edd70a9d895ce298": { + color: "#00A34D", + createdDate: 0, + creditCvv: "", + creditExpiry: "", + creditNumber: "", + favorite: 1666440874, + modifiedDate: 0, + notes: "some note for example.com", + password: "somePassword", + passwordList: [], + passwordModifiedDate: 0, + rev: 1, + service: "example.com", + style: "website", + type: 1, + url: "https://www.example.com", + username: "jdoe", + }, + }, +}; + +export const CreditCardTestEntry: FskFile = { + data: { + "156498a46a3254f16035cbbbd09c2b8f": { + color: "#00baff", + createdDate: 1666438977, + creditCvv: "123", + creditExpiry: "22.10.2026", + creditNumber: "4242424242424242", + favorite: 0, + modifiedDate: 1666438977, + notes: "some notes to my card", + password: "1234", + passwordList: [], + passwordModifiedDate: 1666438977, + rev: 1, + service: "My credit card", + style: "creditcard", + type: 2, + url: "mybank", + username: "John Doe", + }, + }, +}; diff --git a/libs/common/src/services/import.service.ts b/libs/common/src/services/import.service.ts index 7543ec2bfd..c04b3626fd 100644 --- a/libs/common/src/services/import.service.ts +++ b/libs/common/src/services/import.service.ts @@ -31,7 +31,7 @@ import { EncryptrCsvImporter } from "../importers/encryptr-csv-importer"; import { EnpassCsvImporter } from "../importers/enpass-csv-importer"; import { EnpassJsonImporter } from "../importers/enpass-json-importer"; import { FirefoxCsvImporter } from "../importers/firefox-csv-importer"; -import { FSecureFskImporter } from "../importers/fsecure-fsk-importer"; +import { FSecureFskImporter } from "../importers/fsecure/fsecure-fsk-importer"; import { GnomeJsonImporter } from "../importers/gnome-json-importer"; import { ImportError } from "../importers/import-error"; import { Importer } from "../importers/importer";