From 0a27a09341d96a264a5c71af2eac6680e679ee39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zdyba=C5=82?= Date: Mon, 5 Apr 2021 22:21:46 +0200 Subject: [PATCH 1/3] Skip FirefoxAccounts during Firefox CSV Import Firefox exports 'chrome://FirefoxAccounts' if Firefox Accouts are used in browser. It's quite hacky - password field in CSV is actually a JSON encoded data, not a password. Because it's not a useful record, it should be skipped during import. --- .../importers/firefoxCsvImporter.spec.ts | 86 +++++++++++++++++++ src/importers/firefoxCsvImporter.ts | 4 +- 2 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 spec/common/importers/firefoxCsvImporter.spec.ts diff --git a/spec/common/importers/firefoxCsvImporter.spec.ts b/spec/common/importers/firefoxCsvImporter.spec.ts new file mode 100644 index 000000000..8a07a18cc --- /dev/null +++ b/spec/common/importers/firefoxCsvImporter.spec.ts @@ -0,0 +1,86 @@ +import { FirefoxCsvImporter as Importer } from '../../../src/importers/firefoxCsvImporter'; + +import { CipherView } from '../../../src/models/view/cipherView'; +import { LoginView } from '../../../src/models/view/loginView'; +import { LoginUriView } from '../../../src/models/view/loginUriView'; + +import { Utils } from '../../../src/misc/utils'; + +import { FieldType } from '../../../src/enums'; + +if (Utils.isNode) { + // Polyfills + // tslint:disable-next-line + const jsdom: any = require('jsdom'); + (global as any).DOMParser = new jsdom.JSDOM().window.DOMParser; +} + +const CipherData = [ + { + title: 'should parse password', + csv: `"url","username","password","httpRealm","formActionOrigin","guid","timeCreated","timeLastUsed","timePasswordChanged" +"https://example.com","foo","bar",,"","{d61e37fa-2bc4-469a-bd66-41fd3b0005e0}","1612345678900","1612345678900","1612345678900" +`, + expected: Object.assign(new CipherView(), { + id: null, + organizationId: null, + folderId: null, + name: 'example.com', + login: Object.assign(new LoginView(), { + username: 'foo', + password: 'bar', + uris: [ + Object.assign(new LoginUriView(), { + uri: "https://example.com", + }), + ], + }), + notes: null, + type: 1, + }), + }, + { + title: 'should skip "chrome://FirefoxAccounts"', + csv: `"url","username","password","httpRealm","formActionOrigin","guid","timeCreated","timeLastUsed","timePasswordChanged" +"chrome://FirefoxAccounts","bla-bla-foo-bar","{""version"":1,""accountData"":{""kSync"":""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"",""kXCS"":""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"",""kExtSync"":""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"",""kExtKbHash"":""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"",""scopedKeys"":{""https://identity.mozilla.com/apps/oldsync"":{""kid"":""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",""k"":""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"",""kty"":""xxx""},""sync:addon_storage"":{""kid"":""xxxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"",""k"":""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"",""kty"":""xxx""}}}}","Firefox Accounts credentials",,"{d61e37fa-2bc4-469a-bd66-41fd3b0005e0}","1612345678900","1612345678900","1612345678900" +"https://example.com","foo","bar",,"","{d61e37fa-2bc4-469a-bd66-41fd3b0005e0}","1612345678900","1612345678900","1612345678900" +`, + expected: Object.assign(new CipherView(), { + id: null, + organizationId: null, + folderId: null, + name: 'example.com', + login: Object.assign(new LoginView(), { + username: 'foo', + password: 'bar', + uris: [ + Object.assign(new LoginUriView(), { + uri: "https://example.com", + }), + ], + }), + notes: null, + type: 1, + }), + }, +]; + +describe('Firefox 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 (data.expected.hasOwnProperty(property)) { + expect(cipher.hasOwnProperty(property)).toBe(true); + expect(cipher[property]).toEqual(data.expected[property]); + } + } + }); + }); +}); diff --git a/src/importers/firefoxCsvImporter.ts b/src/importers/firefoxCsvImporter.ts index fb41cc29a..9a9e1f3ad 100644 --- a/src/importers/firefoxCsvImporter.ts +++ b/src/importers/firefoxCsvImporter.ts @@ -12,7 +12,9 @@ export class FirefoxCsvImporter extends BaseImporter implements Importer { return Promise.resolve(result); } - results.forEach(value => { + results.filter(value => { + return value.url !== "chrome://FirefoxAccounts" + }).forEach(value => { const cipher = this.initLoginCipher(); const url = this.getValueOrDefault(value.url, this.getValueOrDefault(value.hostname)); cipher.name = this.getValueOrDefault(this.nameFromUrl(url), '--'); From ff8ab5e9e66577b2fd1209f603873f40974ca3b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zdyba=C5=82?= Date: Mon, 5 Apr 2021 23:36:59 +0200 Subject: [PATCH 2/3] Fix indentation --- .../importers/firefoxCsvImporter.spec.ts | 38 +++++++++---------- src/importers/firefoxCsvImporter.ts | 4 +- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/spec/common/importers/firefoxCsvImporter.spec.ts b/spec/common/importers/firefoxCsvImporter.spec.ts index 8a07a18cc..e7a840df9 100644 --- a/spec/common/importers/firefoxCsvImporter.spec.ts +++ b/spec/common/importers/firefoxCsvImporter.spec.ts @@ -26,21 +26,21 @@ const CipherData = [ organizationId: null, folderId: null, name: 'example.com', - login: Object.assign(new LoginView(), { - username: 'foo', - password: 'bar', - uris: [ - Object.assign(new LoginUriView(), { - uri: "https://example.com", - }), - ], - }), + login: Object.assign(new LoginView(), { + username: 'foo', + password: 'bar', + uris: [ + Object.assign(new LoginUriView(), { + uri: "https://example.com", + }), + ], + }), notes: null, type: 1, }), }, { - title: 'should skip "chrome://FirefoxAccounts"', + title: 'should skip "chrome://FirefoxAccounts"', csv: `"url","username","password","httpRealm","formActionOrigin","guid","timeCreated","timeLastUsed","timePasswordChanged" "chrome://FirefoxAccounts","bla-bla-foo-bar","{""version"":1,""accountData"":{""kSync"":""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"",""kXCS"":""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"",""kExtSync"":""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"",""kExtKbHash"":""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"",""scopedKeys"":{""https://identity.mozilla.com/apps/oldsync"":{""kid"":""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",""k"":""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"",""kty"":""xxx""},""sync:addon_storage"":{""kid"":""xxxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"",""k"":""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"",""kty"":""xxx""}}}}","Firefox Accounts credentials",,"{d61e37fa-2bc4-469a-bd66-41fd3b0005e0}","1612345678900","1612345678900","1612345678900" "https://example.com","foo","bar",,"","{d61e37fa-2bc4-469a-bd66-41fd3b0005e0}","1612345678900","1612345678900","1612345678900" @@ -50,15 +50,15 @@ const CipherData = [ organizationId: null, folderId: null, name: 'example.com', - login: Object.assign(new LoginView(), { - username: 'foo', - password: 'bar', - uris: [ - Object.assign(new LoginUriView(), { - uri: "https://example.com", - }), - ], - }), + login: Object.assign(new LoginView(), { + username: 'foo', + password: 'bar', + uris: [ + Object.assign(new LoginUriView(), { + uri: "https://example.com", + }), + ], + }), notes: null, type: 1, }), diff --git a/src/importers/firefoxCsvImporter.ts b/src/importers/firefoxCsvImporter.ts index 9a9e1f3ad..02573eae0 100644 --- a/src/importers/firefoxCsvImporter.ts +++ b/src/importers/firefoxCsvImporter.ts @@ -13,8 +13,8 @@ export class FirefoxCsvImporter extends BaseImporter implements Importer { } results.filter(value => { - return value.url !== "chrome://FirefoxAccounts" - }).forEach(value => { + return value.url !== "chrome://FirefoxAccounts" + }).forEach(value => { const cipher = this.initLoginCipher(); const url = this.getValueOrDefault(value.url, this.getValueOrDefault(value.hostname)); cipher.name = this.getValueOrDefault(this.nameFromUrl(url), '--'); From 51ed83bd9151c3972609f9d9e953ae45ade785e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zdyba=C5=82?= Date: Sun, 11 Apr 2021 20:53:50 +0200 Subject: [PATCH 3/3] Move test Firefox test data to files, fix linter errors --- .../importers/firefoxCsvImporter.spec.ts | 27 +++++-------------- .../firefoxCsv/firefoxAccountsData.csv.ts | 4 +++ .../firefoxCsv/simplePasswordData.csv.ts | 2 ++ src/importers/firefoxCsvImporter.ts | 2 +- 4 files changed, 14 insertions(+), 21 deletions(-) create mode 100644 spec/common/importers/testData/firefoxCsv/firefoxAccountsData.csv.ts create mode 100644 spec/common/importers/testData/firefoxCsv/simplePasswordData.csv.ts diff --git a/spec/common/importers/firefoxCsvImporter.spec.ts b/spec/common/importers/firefoxCsvImporter.spec.ts index e7a840df9..fde2408ee 100644 --- a/spec/common/importers/firefoxCsvImporter.spec.ts +++ b/spec/common/importers/firefoxCsvImporter.spec.ts @@ -1,26 +1,16 @@ import { FirefoxCsvImporter as Importer } from '../../../src/importers/firefoxCsvImporter'; import { CipherView } from '../../../src/models/view/cipherView'; -import { LoginView } from '../../../src/models/view/loginView'; import { LoginUriView } from '../../../src/models/view/loginUriView'; +import { LoginView } from '../../../src/models/view/loginView'; -import { Utils } from '../../../src/misc/utils'; - -import { FieldType } from '../../../src/enums'; - -if (Utils.isNode) { - // Polyfills - // tslint:disable-next-line - const jsdom: any = require('jsdom'); - (global as any).DOMParser = new jsdom.JSDOM().window.DOMParser; -} +import { data as firefoxAccountsData } from './testData/firefoxCsv/firefoxAccountsData.csv'; +import { data as simplePasswordData } from './testData/firefoxCsv/simplePasswordData.csv'; const CipherData = [ { title: 'should parse password', - csv: `"url","username","password","httpRealm","formActionOrigin","guid","timeCreated","timeLastUsed","timePasswordChanged" -"https://example.com","foo","bar",,"","{d61e37fa-2bc4-469a-bd66-41fd3b0005e0}","1612345678900","1612345678900","1612345678900" -`, + csv: simplePasswordData, expected: Object.assign(new CipherView(), { id: null, organizationId: null, @@ -31,7 +21,7 @@ const CipherData = [ password: 'bar', uris: [ Object.assign(new LoginUriView(), { - uri: "https://example.com", + uri: 'https://example.com', }), ], }), @@ -41,10 +31,7 @@ const CipherData = [ }, { title: 'should skip "chrome://FirefoxAccounts"', - csv: `"url","username","password","httpRealm","formActionOrigin","guid","timeCreated","timeLastUsed","timePasswordChanged" -"chrome://FirefoxAccounts","bla-bla-foo-bar","{""version"":1,""accountData"":{""kSync"":""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"",""kXCS"":""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"",""kExtSync"":""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"",""kExtKbHash"":""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"",""scopedKeys"":{""https://identity.mozilla.com/apps/oldsync"":{""kid"":""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",""k"":""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"",""kty"":""xxx""},""sync:addon_storage"":{""kid"":""xxxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"",""k"":""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"",""kty"":""xxx""}}}}","Firefox Accounts credentials",,"{d61e37fa-2bc4-469a-bd66-41fd3b0005e0}","1612345678900","1612345678900","1612345678900" -"https://example.com","foo","bar",,"","{d61e37fa-2bc4-469a-bd66-41fd3b0005e0}","1612345678900","1612345678900","1612345678900" -`, + csv: firefoxAccountsData, expected: Object.assign(new CipherView(), { id: null, organizationId: null, @@ -55,7 +42,7 @@ const CipherData = [ password: 'bar', uris: [ Object.assign(new LoginUriView(), { - uri: "https://example.com", + uri: 'https://example.com', }), ], }), diff --git a/spec/common/importers/testData/firefoxCsv/firefoxAccountsData.csv.ts b/spec/common/importers/testData/firefoxCsv/firefoxAccountsData.csv.ts new file mode 100644 index 000000000..2f2c1b55e --- /dev/null +++ b/spec/common/importers/testData/firefoxCsv/firefoxAccountsData.csv.ts @@ -0,0 +1,4 @@ +export const data = `"url","username","password","httpRealm","formActionOrigin","guid","timeCreated","timeLastUsed","timePasswordChanged" +"chrome://FirefoxAccounts","bla-bla-foo-bar","{""version"":1,""accountData"":{""kSync"":""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"",""kXCS"":""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"",""kExtSync"":""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"",""kExtKbHash"":""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"",""scopedKeys"":{""https://identity.mozilla.com/apps/oldsync"":{""kid"":""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",""k"":""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"",""kty"":""xxx""},""sync:addon_storage"":{""kid"":""xxxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"",""k"":""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"",""kty"":""xxx""}}}}","Firefox Accounts credentials",,"{d61e37fa-2bc4-469a-bd66-41fd3b0005e0}","1612345678900","1612345678900","1612345678900" +"https://example.com","foo","bar",,"","{d61e37fa-2bc4-469a-bd66-41fd3b0005e0}","1612345678900","1612345678900","1612345678900" +`; diff --git a/spec/common/importers/testData/firefoxCsv/simplePasswordData.csv.ts b/spec/common/importers/testData/firefoxCsv/simplePasswordData.csv.ts new file mode 100644 index 000000000..90e19f199 --- /dev/null +++ b/spec/common/importers/testData/firefoxCsv/simplePasswordData.csv.ts @@ -0,0 +1,2 @@ +export const data = `"url","username","password","httpRealm","formActionOrigin","guid","timeCreated","timeLastUsed","timePasswordChanged" +"https://example.com","foo","bar",,"","{d61e37fa-2bc4-469a-bd66-41fd3b0005e0}","1612345678900","1612345678900","1612345678900"`; diff --git a/src/importers/firefoxCsvImporter.ts b/src/importers/firefoxCsvImporter.ts index 02573eae0..df82cf973 100644 --- a/src/importers/firefoxCsvImporter.ts +++ b/src/importers/firefoxCsvImporter.ts @@ -13,7 +13,7 @@ export class FirefoxCsvImporter extends BaseImporter implements Importer { } results.filter(value => { - return value.url !== "chrome://FirefoxAccounts" + return value.url !== 'chrome://FirefoxAccounts'; }).forEach(value => { const cipher = this.initLoginCipher(); const url = this.getValueOrDefault(value.url, this.getValueOrDefault(value.hostname));