diff --git a/spec/common/services/export.service.spec.ts b/spec/common/services/export.service.spec.ts index 3f2d5f7bc5..e931d29a53 100644 --- a/spec/common/services/export.service.spec.ts +++ b/spec/common/services/export.service.spec.ts @@ -2,6 +2,7 @@ import { Substitute, SubstituteOf } from '@fluffy-spoon/substitute'; import { ApiService } from '../../../src/abstractions/api.service'; import { CipherService } from '../../../src/abstractions/cipher.service'; +import { CryptoService } from '../../../src/abstractions/crypto.service'; import { FolderService } from '../../../src/abstractions/folder.service'; import { ExportService } from '../../../src/services/export.service'; @@ -74,16 +75,18 @@ describe('ExportService', () => { let apiService: SubstituteOf; let cipherService: SubstituteOf; let folderService: SubstituteOf; + let cryptoService: SubstituteOf; beforeEach(() => { apiService = Substitute.for(); cipherService = Substitute.for(); folderService = Substitute.for(); + cryptoService = Substitute.for(); folderService.getAllDecrypted().resolves([]); folderService.getAll().resolves([]); - exportService = new ExportService(folderService, cipherService, apiService); + exportService = new ExportService(folderService, cipherService, apiService, cryptoService); }); it('exports unecrypted user ciphers', async () => { diff --git a/src/importers/bitwardenJsonImporter.ts b/src/importers/bitwardenJsonImporter.ts index 3c37d2867d..13c26b6c0d 100644 --- a/src/importers/bitwardenJsonImporter.ts +++ b/src/importers/bitwardenJsonImporter.ts @@ -1,16 +1,24 @@ import { BaseImporter } from './baseImporter'; import { Importer } from './importer'; +import { EncString } from '../models/domain/encString'; import { ImportResult } from '../models/domain/importResult'; import { CipherWithIds } from '../models/export/cipherWithIds'; import { CollectionWithId } from '../models/export/collectionWithId'; import { FolderWithId } from '../models/export/folderWithId'; +import { CryptoService } from '../abstractions/crypto.service'; +import { I18nService } from '../abstractions/i18n.service'; + export class BitwardenJsonImporter extends BaseImporter implements Importer { private results: any; private result: ImportResult; + constructor(private cryptoService: CryptoService, private i18nService: I18nService) { + super(); + } + async parse(data: string): Promise { this.result = new ImportResult(); this.results = JSON.parse(data); @@ -25,11 +33,20 @@ export class BitwardenJsonImporter extends BaseImporter implements Importer { this.parseDecrypted(); } - this.result.success = true; return this.result; } private async parseEncrypted() { + if (this.results.encKeyValidation_DO_NOT_EDIT != null) { + const encKeyValidation = new EncString(this.results.encKeyValidation_DO_NOT_EDIT); + const encKeyValidationDecrypt = await this.cryptoService.decryptToUtf8(encKeyValidation); + if (encKeyValidationDecrypt === null) { + this.result.success = false; + this.result.errorMessage = this.i18nService.t('importEncKeyError'); + return; + } + } + const groupingsMap = new Map(); if (this.organization && this.results.collections != null) { @@ -82,6 +99,8 @@ export class BitwardenJsonImporter extends BaseImporter implements Importer { this.cleanupCipher(view); this.result.ciphers.push(view); } + + this.result.success = true; } private parseDecrypted() { @@ -133,5 +152,7 @@ export class BitwardenJsonImporter extends BaseImporter implements Importer { this.cleanupCipher(cipher); this.result.ciphers.push(cipher); }); + + this.result.success = true; } } diff --git a/src/services/export.service.ts b/src/services/export.service.ts index 78adb484a8..51aa72a300 100644 --- a/src/services/export.service.ts +++ b/src/services/export.service.ts @@ -4,6 +4,7 @@ import { CipherType } from '../enums/cipherType'; import { ApiService } from '../abstractions/api.service'; import { CipherService } from '../abstractions/cipher.service'; +import { CryptoService } from '../abstractions/crypto.service'; import { ExportService as ExportServiceAbstraction } from '../abstractions/export.service'; import { FolderService } from '../abstractions/folder.service'; @@ -23,9 +24,11 @@ import { CipherWithIds as CipherExport } from '../models/export/cipherWithIds'; import { CollectionWithId as CollectionExport } from '../models/export/collectionWithId'; import { FolderWithId as FolderExport } from '../models/export/folderWithId'; +import { Utils } from '../misc/utils'; + export class ExportService implements ExportServiceAbstraction { constructor(private folderService: FolderService, private cipherService: CipherService, - private apiService: ApiService) { } + private apiService: ApiService, private cryptoService: CryptoService) { } async getExport(format: 'csv' | 'json' | 'encrypted_json' = 'csv'): Promise { if (format === 'encrypted_json') { @@ -141,8 +144,11 @@ export class ExportService implements ExportServiceAbstraction { await Promise.all(promises); + const encKeyValidation = await this.cryptoService.encrypt(Utils.newGuid()); + const jsonDoc: any = { encrypted: true, + encKeyValidation_DO_NOT_EDIT: encKeyValidation.encryptedString, folders: [], items: [], }; diff --git a/src/services/import.service.ts b/src/services/import.service.ts index 4f4364eb19..a3fd970095 100644 --- a/src/services/import.service.ts +++ b/src/services/import.service.ts @@ -1,6 +1,7 @@ import { ApiService } from '../abstractions/api.service'; import { CipherService } from '../abstractions/cipher.service'; import { CollectionService } from '../abstractions/collection.service'; +import { CryptoService } from '../abstractions/crypto.service'; import { FolderService } from '../abstractions/folder.service'; import { I18nService } from '../abstractions/i18n.service'; import { @@ -143,7 +144,8 @@ export class ImportService implements ImportServiceAbstraction { constructor(private cipherService: CipherService, private folderService: FolderService, private apiService: ApiService, private i18nService: I18nService, - private collectionService: CollectionService, private platformUtilsService: PlatformUtilsService) { } + private collectionService: CollectionService, private platformUtilsService: PlatformUtilsService, + private cryptoService: CryptoService) { } getImportOptions(): ImportOption[] { return this.featuredImportOptions.concat(this.regularImportOptions); @@ -172,7 +174,11 @@ export class ImportService implements ImportServiceAbstraction { } return null; } else { - return new Error(this.i18nService.t('importFormatError')); + if (!Utils.isNullOrWhitespace(importResult.errorMessage)) { + return new Error(importResult.errorMessage); + } else { + return new Error(this.i18nService.t('importFormatError')); + } } } @@ -194,7 +200,7 @@ export class ImportService implements ImportServiceAbstraction { case 'bitwardencsv': return new BitwardenCsvImporter(); case 'bitwardenjson': - return new BitwardenJsonImporter(); + return new BitwardenJsonImporter(this.cryptoService, this.i18nService); case 'lastpasscsv': case 'passboltcsv': return new LastPassCsvImporter();