diff --git a/libs/importer/src/components/import.component.html b/libs/importer/src/components/import.component.html
index 59ab6739c06..f6d6603dca9 100644
--- a/libs/importer/src/components/import.component.html
+++ b/libs/importer/src/components/import.component.html
@@ -435,6 +435,12 @@
and select Export items → Enter your Master Password and select Continue. → Save
the CSV file on your device.
+
+ On the desktop application, go to Tools → Export → Enter your master password
+ → Select XML Format (*.xml) as Export format → Click on next → Choose which
+ entries should be included in the export → Click on next to export into the location
+ previously chosen.
+
{
+ it("should return error with invalid export version", async () => {
+ const importer = new PasswordDepot17XmlImporter();
+ const result = await importer.parse(MacOS_WrongVersion);
+ expect(result.errorMessage).toBe(
+ "Unsupported export version detected - (only 17.0 is supported)",
+ );
+ });
+
+ it("should not create a folder/collection if the group fingerprint is null", async () => {
+ const importer = new PasswordDepot17XmlImporter();
+ const result = await importer.parse(MacOS_PasswordDepotXmlFile);
+ expect(result.folders.length).toBe(0);
+ });
+
+ it("should create folders and with correct assignments", async () => {
+ const importer = new PasswordDepot17XmlImporter();
+ const result = await importer.parse(MacOS_MultipleFolders);
+
+ // Expect 10 ciphers, 5 without a folder and 3 within 'folder macos' and 2 with 'folder 2'
+ expect(result.ciphers.length).toBe(10);
+
+ expect(result.folders.length).toBe(2);
+ expect(result.folders[0].name).toBe("folder macos");
+ expect(result.folders[1].name).toBe("folder 2");
+
+ // 3 items within 'folder macos'
+ expect(result.folderRelationships[0]).toEqual([5, 0]);
+ expect(result.folderRelationships[1]).toEqual([6, 0]);
+ expect(result.folderRelationships[2]).toEqual([7, 0]);
+
+ //2 items with 'folder 2'
+ expect(result.folderRelationships[3]).toEqual([8, 1]);
+ expect(result.folderRelationships[4]).toEqual([9, 1]);
+ });
+
+ it("should parse custom fields from a MacOS exported file", async () => {
+ const importer = new PasswordDepot17XmlImporter();
+ const result = await importer.parse(MacOS_PasswordDepotXmlFile);
+
+ const cipher = result.ciphers.shift();
+ expect(cipher.name).toBe("card 1");
+ expect(cipher.notes).toBe("comment");
+
+ expect(cipher.card).not.toBeNull();
+
+ expect(cipher.card.cardholderName).toBe("some CC holder");
+ expect(cipher.card.number).toBe("4242424242424242");
+ expect(cipher.card.brand).toBe("Visa");
+ expect(cipher.card.expMonth).toBe("8");
+ expect(cipher.card.expYear).toBe("2028");
+ expect(cipher.card.code).toBe("125");
+ });
+});
diff --git a/libs/importer/src/importers/password-depot/password-depot-17-xml-importer.spec.ts b/libs/importer/src/importers/password-depot/password-depot-17-xml-importer.spec.ts
new file mode 100644
index 00000000000..ea84603aef4
--- /dev/null
+++ b/libs/importer/src/importers/password-depot/password-depot-17-xml-importer.spec.ts
@@ -0,0 +1,496 @@
+// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop.
+// eslint-disable-next-line no-restricted-imports
+import { CollectionView } from "@bitwarden/admin-console/common";
+import { FieldType, SecureNoteType } from "@bitwarden/common/vault/enums";
+import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
+import { CipherType } from "@bitwarden/sdk-internal";
+
+import {
+ EncryptedFileData,
+ InvalidRootNodeData,
+ InvalidVersionData,
+ CreditCardTestData,
+ MissingPasswordsNodeData,
+ PasswordTestData,
+ IdentityTestData,
+ RDPTestData,
+ SoftwareLicenseTestData,
+ TeamViewerTestData,
+ PuttyTestData,
+ BankingTestData,
+ InformationTestData,
+ CertificateTestData,
+ EncryptedFileTestData,
+ DocumentTestData,
+} from "../spec-data/password-depot-xml";
+
+import { PasswordDepot17XmlImporter } from "./password-depot-17-xml-importer";
+
+describe("Password Depot 17 Xml Importer", () => {
+ it("should return error with missing root tag", async () => {
+ const importer = new PasswordDepot17XmlImporter();
+ const result = await importer.parse(InvalidRootNodeData);
+ expect(result.errorMessage).toBe("Missing `passwordfile` node.");
+ });
+
+ it("should return error with invalid export version", async () => {
+ const importer = new PasswordDepot17XmlImporter();
+ const result = await importer.parse(InvalidVersionData);
+ expect(result.errorMessage).toBe(
+ "Unsupported export version detected - (only 17.0 is supported)",
+ );
+ });
+
+ it("should return error if file is marked as encrypted", async () => {
+ const importer = new PasswordDepot17XmlImporter();
+ const result = await importer.parse(EncryptedFileData);
+ expect(result.errorMessage).toBe("Encrypted Password Depot files are not supported.");
+ });
+
+ it("should return error with missing passwords node tag", async () => {
+ const importer = new PasswordDepot17XmlImporter();
+ const result = await importer.parse(MissingPasswordsNodeData);
+ expect(result.success).toBe(false);
+ expect(result.errorMessage).toBe("Missing `passwordfile > passwords` node.");
+ });
+
+ it("should parse groups nodes into folders", async () => {
+ const importer = new PasswordDepot17XmlImporter();
+ const folder = new FolderView();
+ folder.name = "tempDB";
+ const actual = [folder];
+
+ const result = await importer.parse(PasswordTestData);
+ expect(result.folders).toEqual(actual);
+ });
+
+ it("should parse password type into logins", async () => {
+ const importer = new PasswordDepot17XmlImporter();
+ const result = await importer.parse(PasswordTestData);
+
+ const cipher = result.ciphers.shift();
+
+ expect(cipher.type).toEqual(CipherType.Login);
+ expect(cipher.name).toBe("password type");
+ expect(cipher.notes).toBe("someComment");
+
+ expect(cipher.login).not.toBeNull();
+ expect(cipher.login.username).toBe("someUser");
+ expect(cipher.login.password).toBe("p6J<]fmjv!:H&iJ7/Mwt@3i8");
+ expect(cipher.login.uri).toBe("http://example.com");
+ });
+
+ it("should parse any unmapped fields as custom fields", async () => {
+ const importer = new PasswordDepot17XmlImporter();
+ const result = await importer.parse(PasswordTestData);
+
+ const cipher = result.ciphers.shift();
+
+ expect(cipher.type).toBe(CipherType.Login);
+ expect(cipher.name).toBe("password type");
+
+ expect(cipher.fields).not.toBeNull();
+
+ expect(cipher.fields[0].name).toBe("lastmodified");
+ expect(cipher.fields[0].value).toBe("07.05.2025 13:37:56");
+ expect(cipher.fields[0].type).toBe(FieldType.Text);
+
+ expect(cipher.fields[1].name).toBe("expirydate");
+ expect(cipher.fields[1].value).toBe("07.05.2025");
+ expect(cipher.fields[0].type).toBe(FieldType.Text);
+
+ expect(cipher.fields[2].name).toBe("importance");
+ expect(cipher.fields[2].value).toBe("0");
+
+ let customField = cipher.fields.find((f) => f.name === "passwort");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual("password");
+ expect(customField.type).toEqual(FieldType.Hidden);
+
+ customField = cipher.fields.find((f) => f.name === "memo");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual("memo");
+ expect(customField.type).toEqual(FieldType.Text);
+
+ customField = cipher.fields.find((f) => f.name === "datum");
+ expect(customField).toBeDefined();
+ const expectedDate = new Date("2025-05-13T00:00:00Z");
+ expect(customField.value).toEqual(expectedDate.toLocaleDateString());
+ expect(customField.type).toEqual(FieldType.Text);
+
+ customField = cipher.fields.find((f) => f.name === "nummer");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual("1");
+ expect(customField.type).toEqual(FieldType.Text);
+
+ customField = cipher.fields.find((f) => f.name === "boolean");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual("1");
+ expect(customField.type).toEqual(FieldType.Boolean);
+
+ customField = cipher.fields.find((f) => f.name === "decimal");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual("1,01");
+ expect(customField.type).toEqual(FieldType.Text);
+
+ customField = cipher.fields.find((f) => f.name === "email");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual("who@cares.com");
+ expect(customField.type).toEqual(FieldType.Text);
+
+ customField = cipher.fields.find((f) => f.name === "url");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual("example.com");
+ expect(customField.type).toEqual(FieldType.Text);
+ });
+
+ it("should parse credit cards", async () => {
+ const importer = new PasswordDepot17XmlImporter();
+ const result = await importer.parse(CreditCardTestData);
+
+ const cipher = result.ciphers.shift();
+ expect(cipher.name).toBe("some CreditCard");
+ expect(cipher.notes).toBe("someComment");
+
+ expect(cipher.card).not.toBeNull();
+
+ expect(cipher.card.cardholderName).toBe("some CC holder");
+ expect(cipher.card.number).toBe("4222422242224222");
+ expect(cipher.card.brand).toBe("Visa");
+ expect(cipher.card.expMonth).toBe("5");
+ expect(cipher.card.expYear).toBe("2026");
+ expect(cipher.card.code).toBe("123");
+ });
+
+ it("should parse identity type", async () => {
+ const importer = new PasswordDepot17XmlImporter();
+ const result = await importer.parse(IdentityTestData);
+
+ const cipher = result.ciphers.shift();
+ expect(cipher.name).toBe("identity type");
+ expect(cipher.notes).toBe("someNote");
+
+ expect(cipher.identity).not.toBeNull();
+
+ expect(cipher.identity.firstName).toBe("firstName");
+ expect(cipher.identity.lastName).toBe("surName");
+ expect(cipher.identity.email).toBe("email");
+ expect(cipher.identity.company).toBe("someCompany");
+ expect(cipher.identity.address1).toBe("someStreet");
+ expect(cipher.identity.address2).toBe("address 2");
+ expect(cipher.identity.city).toBe("town");
+ expect(cipher.identity.state).toBe("state");
+ expect(cipher.identity.postalCode).toBe("zipCode");
+ expect(cipher.identity.country).toBe("country");
+ expect(cipher.identity.phone).toBe("phoneNumber");
+ });
+
+ it("should parse RDP type", async () => {
+ const importer = new PasswordDepot17XmlImporter();
+ const result = await importer.parse(RDPTestData);
+
+ const cipher = result.ciphers.shift();
+ expect(cipher.type).toEqual(CipherType.Login);
+ expect(cipher.name).toBe("rdp type");
+ expect(cipher.notes).toBe("someNote");
+
+ expect(cipher.login).not.toBeNull();
+ expect(cipher.login.username).toBe("someUser");
+ expect(cipher.login.password).toBe("somePassword");
+ expect(cipher.login.uri).toBe("ms-rd:subscribe?url=https://contoso.com");
+ });
+
+ it("should parse software license into secure notes", async () => {
+ const importer = new PasswordDepot17XmlImporter();
+ const result = await importer.parse(SoftwareLicenseTestData);
+
+ const cipher = result.ciphers.shift();
+
+ expect(cipher.type).toEqual(CipherType.SecureNote);
+ expect(cipher.name).toBe("software-license type");
+ expect(cipher.notes).toBe("someComment");
+
+ expect(cipher.secureNote).not.toBeNull();
+ expect(cipher.secureNote.type).toBe(SecureNoteType.Generic);
+
+ let customField = cipher.fields.find((f) => f.name === "IDS_LicenseProduct");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual("someProduct");
+
+ customField = cipher.fields.find((f) => f.name === "IDS_LicenseVersion");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual("someVersion");
+
+ customField = cipher.fields.find((f) => f.name === "IDS_LicenseName");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual("some User");
+
+ customField = cipher.fields.find((f) => f.name === "IDS_LicenseKey");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual("license-key");
+
+ customField = cipher.fields.find((f) => f.name === "IDS_LicenseAdditionalKey");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual("additional-license-key");
+
+ customField = cipher.fields.find((f) => f.name === "IDS_LicenseURL");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual("example.com");
+
+ customField = cipher.fields.find((f) => f.name === "IDS_LicenseProtected");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual("1");
+
+ customField = cipher.fields.find((f) => f.name === "IDS_LicenseUserName");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual("someUserName");
+
+ customField = cipher.fields.find((f) => f.name === "IDS_LicensePassword");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual("somePassword");
+
+ customField = cipher.fields.find((f) => f.name === "IDS_LicensePurchaseDate");
+ expect(customField).toBeDefined();
+ const expectedDate = new Date("2025-05-12T00:00:00Z");
+ expect(customField.value).toEqual(expectedDate.toLocaleDateString());
+
+ customField = cipher.fields.find((f) => f.name === "IDS_LicenseOrderNumber");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual("order number");
+
+ customField = cipher.fields.find((f) => f.name === "IDS_LicenseEmail");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual("someEmail");
+
+ customField = cipher.fields.find((f) => f.name === "IDS_LicenseExpires");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual("Nie");
+ });
+
+ it("should parse team viewer into login type", async () => {
+ const importer = new PasswordDepot17XmlImporter();
+ const result = await importer.parse(TeamViewerTestData);
+
+ const cipher = result.ciphers.shift();
+
+ expect(cipher.type).toEqual(CipherType.Login);
+ expect(cipher.name).toBe("TeamViewer type");
+ expect(cipher.notes).toBe("someNote");
+
+ expect(cipher.login).not.toBeNull();
+ expect(cipher.login.password).toBe("somePassword");
+ expect(cipher.login.username).toBe("");
+ expect(cipher.login.uri).toBe("partnerId");
+
+ const customField = cipher.fields.find((f) => f.name === "IDS_TeamViewerMode");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual("0");
+ });
+
+ it("should parse putty into login type", async () => {
+ const importer = new PasswordDepot17XmlImporter();
+ const result = await importer.parse(PuttyTestData);
+
+ const cipher = result.ciphers.shift();
+
+ expect(cipher.type).toEqual(CipherType.Login);
+ expect(cipher.name).toBe("Putty type");
+ expect(cipher.notes).toBe("someNote");
+
+ expect(cipher.login).not.toBeNull();
+ expect(cipher.login.password).toBe("somePassword");
+ expect(cipher.login.username).toBe("someUser");
+ expect(cipher.login.uri).toBe("localhost");
+
+ let customField = cipher.fields.find((f) => f.name === "IDS_PuTTyProtocol");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual("0");
+
+ customField = cipher.fields.find((f) => f.name === "IDS_PuTTyKeyFile");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual("pathToKeyFile");
+
+ customField = cipher.fields.find((f) => f.name === "IDS_PuTTyKeyPassword");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual("passwordForKeyFile");
+
+ customField = cipher.fields.find((f) => f.name === "IDS_PuTTyPort");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual("8080");
+ });
+
+ it("should parse banking item type into login type", async () => {
+ const importer = new PasswordDepot17XmlImporter();
+ const result = await importer.parse(BankingTestData);
+
+ const cipher = result.ciphers.shift();
+
+ expect(cipher.type).toEqual(CipherType.Login);
+ expect(cipher.name).toBe("banking type");
+ expect(cipher.notes).toBe("someNote");
+
+ expect(cipher.login).not.toBeNull();
+ expect(cipher.login.password).toBe("somePassword");
+ expect(cipher.login.username).toBe("someUser");
+ expect(cipher.login.uri).toBe("http://some-bank.com");
+
+ let customField = cipher.fields.find((f) => f.name === "IDS_ECHolder");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual("account holder");
+
+ customField = cipher.fields.find((f) => f.name === "IDS_ECAccountNumber");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual("1234567890");
+
+ customField = cipher.fields.find((f) => f.name === "IDS_ECBLZ");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual("12345678");
+
+ customField = cipher.fields.find((f) => f.name === "IDS_ECBankName");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual("someBank");
+
+ customField = cipher.fields.find((f) => f.name === "IDS_ECBIC");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual("bic");
+
+ customField = cipher.fields.find((f) => f.name === "IDS_ECIBAN");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual("iban");
+
+ customField = cipher.fields.find((f) => f.name === "IDS_ECCardNumber");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual("12345678");
+
+ customField = cipher.fields.find((f) => f.name === "IDS_ECPhone");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual("0049");
+
+ customField = cipher.fields.find((f) => f.name === "IDS_ECLegitimacyID");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual("1234");
+
+ customField = cipher.fields.find((f) => f.name === "IDS_ECPIN");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual("123");
+
+ customField = cipher.fields.find((f) => f.name === "tan_1_value");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual("1234");
+
+ customField = cipher.fields.find((f) => f.name === "tan_1_used");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual("12.05.2025 15:10:54");
+
+ // TAN entries
+ customField = cipher.fields.find((f) => f.name === "tan_1_amount");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual(" 100,00");
+
+ customField = cipher.fields.find((f) => f.name === "tan_1_comment");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual("some TAN note");
+
+ customField = cipher.fields.find((f) => f.name === "tan_1_ccode");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual("123");
+
+ customField = cipher.fields.find((f) => f.name === "tan_2_value");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual("4321");
+
+ customField = cipher.fields.find((f) => f.name === "tan_2_amount");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual(" 0,00");
+ });
+
+ it("should parse information into secure note type", async () => {
+ const importer = new PasswordDepot17XmlImporter();
+ const result = await importer.parse(InformationTestData);
+
+ const cipher = result.ciphers.shift();
+
+ expect(cipher.type).toEqual(CipherType.SecureNote);
+ expect(cipher.name).toBe("information type");
+ expect(cipher.notes).toBe("some note content");
+ });
+
+ it("should parse certificate into login type", async () => {
+ const importer = new PasswordDepot17XmlImporter();
+ const result = await importer.parse(CertificateTestData);
+
+ const cipher = result.ciphers.shift();
+
+ expect(cipher.type).toEqual(CipherType.Login);
+ expect(cipher.name).toBe("certificate type");
+ expect(cipher.notes).toBe("someNote");
+
+ expect(cipher.login).not.toBeNull();
+ expect(cipher.login.password).toBe("somePassword");
+ });
+
+ it("should parse encrypted file into login type", async () => {
+ const importer = new PasswordDepot17XmlImporter();
+ const result = await importer.parse(EncryptedFileTestData);
+
+ const cipher = result.ciphers.shift();
+
+ expect(cipher.type).toEqual(CipherType.Login);
+ expect(cipher.name).toBe("encrypted file type");
+ expect(cipher.notes).toBe("some comment");
+
+ expect(cipher.login).not.toBeNull();
+ expect(cipher.login.password).toBe("somePassword");
+ });
+
+ it("should parse document type into secure note", async () => {
+ const importer = new PasswordDepot17XmlImporter();
+ const result = await importer.parse(DocumentTestData);
+
+ const cipher = result.ciphers.shift();
+
+ expect(cipher.type).toEqual(CipherType.SecureNote);
+ expect(cipher.name).toBe("document type");
+ expect(cipher.notes).toBe("document comment");
+
+ let customField = cipher.fields.find((f) => f.name === "IDS_DocumentSize");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual("27071");
+
+ customField = cipher.fields.find((f) => f.name === "IDS_DocumentFolder");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual("C:\\Users\\DJSMI\\Downloads\\");
+
+ customField = cipher.fields.find((f) => f.name === "IDS_DocumentFile");
+ expect(customField).toBeDefined();
+ expect(customField.value).toEqual("C:\\Users\\DJSMI\\Downloads\\some.pdf");
+ });
+
+ it("should parse favourites and set them on the target item", async () => {
+ const importer = new PasswordDepot17XmlImporter();
+ const result = await importer.parse(PasswordTestData);
+
+ let cipher = result.ciphers.shift();
+ expect(cipher.name).toBe("password type");
+ expect(cipher.favorite).toBe(false);
+
+ cipher = result.ciphers.shift();
+ expect(cipher.name).toBe("password type (2)");
+ expect(cipher.favorite).toBe(true);
+
+ cipher = result.ciphers.shift();
+ expect(cipher.name).toBe("password type (3)");
+ expect(cipher.favorite).toBe(true);
+ });
+
+ it("should parse groups nodes into collections when importing into an organization", async () => {
+ const importer = new PasswordDepot17XmlImporter();
+ importer.organizationId = "someOrgId";
+ const collection = new CollectionView();
+ collection.name = "tempDB";
+ const actual = [collection];
+
+ const result = await importer.parse(PasswordTestData);
+ expect(result.collections).toEqual(actual);
+ });
+});
diff --git a/libs/importer/src/importers/password-depot/password-depot-17-xml-importer.ts b/libs/importer/src/importers/password-depot/password-depot-17-xml-importer.ts
new file mode 100644
index 00000000000..ab1f6b4689f
--- /dev/null
+++ b/libs/importer/src/importers/password-depot/password-depot-17-xml-importer.ts
@@ -0,0 +1,500 @@
+// FIXME: Update this file to be type safe and remove this and next line
+// @ts-strict-ignore
+import { CipherType, FieldType, SecureNoteType } from "@bitwarden/common/vault/enums";
+import { CardView } from "@bitwarden/common/vault/models/view/card.view";
+import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
+import { FieldView } from "@bitwarden/common/vault/models/view/field.view";
+import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
+import { IdentityView } from "@bitwarden/common/vault/models/view/identity.view";
+import { LoginView } from "@bitwarden/common/vault/models/view/login.view";
+import { SecureNoteView } from "@bitwarden/common/vault/models/view/secure-note.view";
+
+import { ImportResult } from "../../models/import-result";
+import { BaseImporter } from "../base-importer";
+import { Importer } from "../importer";
+
+import { PasswordDepotItemType, PasswordDepotCustomFieldType } from "./types";
+
+/**
+ * Importer for Password Depot 17 xml files.
+ * @see https://www.password-depot.de/
+ * It provides methods to parse the XML data, extract relevant information, and create cipher objects
+ */
+export class PasswordDepot17XmlImporter extends BaseImporter implements Importer {
+ result = new ImportResult();
+
+ _favouritesLookupTable = new Set();
+
+ // Parse the XML data from the Password Depot export file and extracts the relevant information
+ parse(data: string): Promise {
+ const doc: XMLDocument = this.parseXml(data);
+ if (doc == null) {
+ this.result.success = false;
+ return Promise.resolve(this.result);
+ }
+
+ // Check if the root node is present
+ const rootNode = doc.querySelector("passwordfile");
+ if (rootNode == null) {
+ this.result.errorMessage = "Missing `passwordfile` node.";
+ this.result.success = false;
+ return Promise.resolve(this.result);
+ }
+
+ // Check if the version is supported
+ const headerNode = this.querySelectorDirectChild(rootNode, "header");
+ if (headerNode == null) {
+ this.result.success = false;
+ return Promise.resolve(this.result);
+ }
+
+ let versionNode = this.querySelectorDirectChild(headerNode, "version");
+ if (versionNode == null) {
+ // Adding a fallback for MacOS Password Depot 17.0 export files
+ // These files do not have a version node, but a dataformat node instead
+ versionNode = this.querySelectorDirectChild(headerNode, "dataformat");
+ if (versionNode == null) {
+ this.result.success = false;
+ return Promise.resolve(this.result);
+ }
+ }
+
+ const version = versionNode.textContent;
+ if (!version.startsWith("17")) {
+ this.result.errorMessage = "Unsupported export version detected - (only 17.0 is supported)";
+ this.result.success = false;
+ return Promise.resolve(this.result);
+ }
+
+ // Abort import if the file is encrypted
+ const encryptedNode = this.querySelectorDirectChild(headerNode, "encrypted");
+ if (encryptedNode != null && encryptedNode.textContent == "True") {
+ this.result.errorMessage = "Encrypted Password Depot files are not supported.";
+ this.result.success = false;
+ return Promise.resolve(this.result);
+ }
+
+ // Check if the passwords node is present
+ // This node contains all the password entries
+ const passwordsNode = rootNode.querySelector("passwords");
+ if (passwordsNode == null) {
+ this.result.errorMessage = "Missing `passwordfile > passwords` node.";
+ this.result.success = false;
+ return Promise.resolve(this.result);
+ }
+
+ this.buildFavouritesLookupTable(rootNode);
+
+ this.querySelectorAllDirectChild(passwordsNode, "group").forEach((group) => {
+ this.traverse(group, "");
+ });
+
+ if (this.organization) {
+ this.moveFoldersToCollections(this.result);
+ }
+
+ this.result.success = true;
+ return Promise.resolve(this.result);
+ }
+
+ // Traverses the XML tree and processes each node
+ // It starts from the root node and goes through each group and item
+ // This method is recursive and handles nested groups
+ private traverse(node: Element, groupPrefixName: string) {
+ const folderIndex = this.result.folders.length;
+ let groupName = groupPrefixName;
+
+ if (groupName !== "") {
+ groupName += "/";
+ }
+
+ // Check if the group has a fingerprint attribute (GUID of a folder)
+ const groupFingerprint = node.attributes.getNamedItem("fingerprint");
+ if (groupFingerprint?.textContent != "" && groupFingerprint.textContent != "null") {
+ const nameEl = node.attributes.getNamedItem("name");
+ groupName += nameEl == null ? "-" : nameEl.textContent;
+ const folder = new FolderView();
+ folder.name = groupName;
+ this.result.folders.push(folder);
+ }
+
+ this.querySelectorAllDirectChild(node, "item").forEach((entry) => {
+ const cipherIndex = this.result.ciphers.length;
+
+ const cipher = this.initLoginCipher();
+ //Set default item type similar how we default to Login in other importers
+ let sourceType: PasswordDepotItemType = PasswordDepotItemType.Password;
+
+ const entryFields = entry.children;
+ for (let i = 0; i < entryFields.length; i++) {
+ const entryField = entryFields[i];
+
+ // Skip processing historical entries
+ if (entryField.tagName === "hitems") {
+ continue;
+ }
+
+ if (entryField.tagName === "description") {
+ cipher.name = entryField.textContent;
+ continue;
+ }
+
+ if (entryField.tagName === "comment") {
+ cipher.notes = entryField.textContent;
+ continue;
+ }
+
+ if (entryField.tagName === "type") {
+ sourceType = entryField.textContent as PasswordDepotItemType;
+ switch (sourceType) {
+ case PasswordDepotItemType.Password:
+ case PasswordDepotItemType.RDP:
+ case PasswordDepotItemType.Putty:
+ case PasswordDepotItemType.TeamViewer:
+ case PasswordDepotItemType.Banking:
+ case PasswordDepotItemType.Certificate:
+ case PasswordDepotItemType.EncryptedFile:
+ cipher.type = CipherType.Login;
+ cipher.login = new LoginView();
+ break;
+ case PasswordDepotItemType.CreditCard:
+ cipher.type = CipherType.Card;
+ cipher.card = new CardView();
+ break;
+ case PasswordDepotItemType.SoftwareLicense:
+ case PasswordDepotItemType.Information:
+ case PasswordDepotItemType.Document:
+ cipher.type = CipherType.SecureNote;
+ cipher.secureNote = new SecureNoteView();
+ cipher.secureNote.type = SecureNoteType.Generic;
+ break;
+ case PasswordDepotItemType.Identity:
+ cipher.type = CipherType.Identity;
+ cipher.identity = new IdentityView();
+ break;
+ }
+ continue;
+ }
+
+ if (
+ sourceType === PasswordDepotItemType.Password ||
+ sourceType === PasswordDepotItemType.RDP ||
+ sourceType === PasswordDepotItemType.Putty ||
+ sourceType === PasswordDepotItemType.TeamViewer ||
+ sourceType === PasswordDepotItemType.Banking ||
+ sourceType === PasswordDepotItemType.Certificate ||
+ sourceType === PasswordDepotItemType.EncryptedFile
+ ) {
+ if (this.parseLoginFields(entryField, cipher)) {
+ continue;
+ }
+ }
+
+ // fingerprint is the GUID of the entry
+ // Base on the previously parsed favourites, we can identify an entry and set the favorite flag accordingly
+ if (entryField.tagName === "fingerprint") {
+ if (this._favouritesLookupTable.has(entryField.textContent)) {
+ cipher.favorite = true;
+ }
+ }
+
+ if (entryField.tagName === "customfields") {
+ this.parseCustomFields(entryField, sourceType, cipher);
+ continue;
+ }
+
+ if (sourceType === PasswordDepotItemType.Banking && entryField.tagName === "tans") {
+ this.querySelectorAllDirectChild(entryField, "tan").forEach((tanEntry) => {
+ this.parseBankingTANs(tanEntry, cipher);
+ });
+ continue;
+ }
+
+ this.processKvp(cipher, entryField.tagName, entryField.textContent, FieldType.Text);
+ }
+
+ this.cleanupCipher(cipher);
+ this.result.ciphers.push(cipher);
+
+ if (groupName !== "") {
+ this.result.folderRelationships.push([cipherIndex, folderIndex]);
+ }
+ });
+
+ this.querySelectorAllDirectChild(node, "group").forEach((group) => {
+ this.traverse(group, groupName);
+ });
+ }
+
+ // Parses custom fields and adds them to the cipher
+ // It iterates through all the custom fields and adds them to the cipher
+ private parseCustomFields(
+ entryField: Element,
+ sourceType: PasswordDepotItemType,
+ cipher: CipherView,
+ ) {
+ this.querySelectorAllDirectChild(entryField, "field").forEach((customField) => {
+ const customFieldObject = this.parseCustomField(customField);
+ if (customFieldObject == null) {
+ return;
+ }
+
+ switch (sourceType) {
+ case PasswordDepotItemType.CreditCard:
+ if (this.parseCreditCardCustomFields(customFieldObject, cipher)) {
+ return;
+ }
+ break;
+ case PasswordDepotItemType.Identity:
+ if (this.parseIdentityCustomFields(customFieldObject, cipher)) {
+ return;
+ }
+ break;
+ case PasswordDepotItemType.Information:
+ if (this.parseInformationCustomFields(customFieldObject, cipher)) {
+ return;
+ }
+ break;
+ default:
+ // For other types, we will process the custom field as a regular key-value pair
+ break;
+ }
+
+ this.processKvp(
+ cipher,
+ customFieldObject.name,
+ customFieldObject.value,
+ customFieldObject.type,
+ );
+ });
+ }
+
+ // Parses login fields and adds them to the cipher
+ private parseLoginFields(entryField: Element, cipher: CipherView): boolean {
+ if (entryField.tagName === "username") {
+ cipher.login.username = entryField.textContent;
+ return true;
+ }
+
+ if (entryField.tagName === "password") {
+ cipher.login.password = entryField.textContent;
+ return true;
+ }
+
+ if (entryField.tagName === "url") {
+ cipher.login.uris = this.makeUriArray(entryField.textContent);
+ return true;
+ }
+
+ return false;
+ }
+
+ // Parses a custom field and adds it to the cipher
+ private parseCustomField(customField: Element): FieldView | null {
+ let key: string = undefined;
+ let value: string = undefined;
+ let sourceFieldType: PasswordDepotCustomFieldType = PasswordDepotCustomFieldType.Memo;
+ let visible: string = undefined;
+ // A custom field is represented by a element
+ // On exports from the Windows clients: It contains a , , and optionally a and element
+ // On exports from the MacOs clients the key-values are defined as xml attributes instead of child nodes
+ if (customField.hasAttributes()) {
+ key = customField.getAttribute("name");
+ if (key == null) {
+ return null;
+ }
+
+ value = customField.getAttribute("value");
+
+ const typeAttr = customField.getAttribute("type");
+ sourceFieldType =
+ typeAttr != null
+ ? (typeAttr as PasswordDepotCustomFieldType)
+ : PasswordDepotCustomFieldType.Memo;
+
+ visible = customField.getAttribute("visible");
+ } else {
+ const keyEl = this.querySelectorDirectChild(customField, "name");
+ key = keyEl != null ? keyEl.textContent : null;
+
+ if (key == null) {
+ return null;
+ }
+
+ const valueEl = this.querySelectorDirectChild(customField, "value");
+ value = valueEl != null ? valueEl.textContent : null;
+
+ const typeEl = this.querySelectorDirectChild(customField, "type");
+ sourceFieldType =
+ typeEl != null
+ ? (typeEl.textContent as PasswordDepotCustomFieldType)
+ : PasswordDepotCustomFieldType.Memo;
+
+ const visibleEl = this.querySelectorDirectChild(customField, "visible");
+ visible = visibleEl != null ? visibleEl.textContent : null;
+ }
+
+ if (sourceFieldType === PasswordDepotCustomFieldType.Date) {
+ if (!isNaN(value as unknown as number)) {
+ // Convert excel date format to JavaScript date
+ const numericValue = parseInt(value);
+ const secondsInDay = 86400;
+ const missingLeapYearDays = secondsInDay * 1000;
+ value = new Date((numericValue - (25567 + 2)) * missingLeapYearDays).toLocaleDateString();
+ }
+ }
+
+ if (sourceFieldType === PasswordDepotCustomFieldType.Password) {
+ return { name: key, value: value, type: FieldType.Hidden, linkedId: null } as FieldView;
+ }
+
+ if (sourceFieldType === PasswordDepotCustomFieldType.Boolean) {
+ return { name: key, value: value, type: FieldType.Boolean, linkedId: null } as FieldView;
+ }
+
+ if (visible == "0") {
+ return { name: key, value: value, type: FieldType.Hidden, linkedId: null } as FieldView;
+ }
+
+ return { name: key, value: value, type: FieldType.Text, linkedId: null } as FieldView;
+ }
+
+ // Parses credit card fields and adds them to the cipher
+ private parseCreditCardCustomFields(customField: FieldView, cipher: CipherView): boolean {
+ if (customField.name === "IDS_CardHolder") {
+ cipher.card.cardholderName = customField.value;
+ return true;
+ }
+
+ if (customField.name === "IDS_CardNumber") {
+ cipher.card.number = customField.value;
+ cipher.card.brand = CardView.getCardBrandByPatterns(cipher.card.number);
+ return true;
+ }
+
+ if (customField.name === "IDS_CardExpires") {
+ this.setCardExpiration(cipher, customField.value);
+ return true;
+ }
+
+ if (customField.name === "IDS_CardCode") {
+ cipher.card.code = customField.value;
+ return true;
+ }
+
+ return false;
+ }
+
+ // Parses identity fields and adds them to the cipher
+ private parseIdentityCustomFields(customField: FieldView, cipher: CipherView): boolean {
+ if (customField.name === "IDS_IdentityName") {
+ this.processFullName(cipher, customField.value);
+ return true;
+ }
+
+ if (customField.name === "IDS_IdentityEmail") {
+ cipher.identity.email = customField.value;
+ return true;
+ }
+
+ if (customField.name === "IDS_IdentityFirstName") {
+ cipher.identity.firstName = customField.value;
+ return true;
+ }
+
+ if (customField.name === "IDS_IdentityLastName") {
+ cipher.identity.lastName = customField.value;
+ return true;
+ }
+
+ if (customField.name === "IDS_IdentityCompany") {
+ cipher.identity.company = customField.value;
+ return true;
+ }
+
+ if (customField.name === "IDS_IdentityAddress1") {
+ cipher.identity.address1 = customField.value;
+ return true;
+ }
+
+ if (customField.name === "IDS_IdentityAddress2") {
+ cipher.identity.address2 = customField.value;
+ return true;
+ }
+
+ if (customField.name === "IDS_IdentityCity") {
+ cipher.identity.city = customField.value;
+ return true;
+ }
+
+ if (customField.name === "IDS_IdentityState") {
+ cipher.identity.state = customField.value;
+ return true;
+ }
+
+ if (customField.name === "IDS_IdentityZIP") {
+ cipher.identity.postalCode = customField.value;
+ return true;
+ }
+
+ if (customField.name === "IDS_IdentityCountry") {
+ cipher.identity.country = customField.value;
+ return true;
+ }
+
+ if (customField.name === "IDS_IdentityPhone") {
+ cipher.identity.phone = customField.value;
+ return true;
+ }
+
+ return false;
+ }
+
+ // Parses information custom fields and adds them to the cipher
+ private parseInformationCustomFields(customField: FieldView, cipher: CipherView): boolean {
+ if (customField.name === "IDS_InformationText") {
+ cipher.notes = customField.value;
+ return true;
+ }
+
+ return false;
+ }
+
+ // Parses TAN objects and adds them to the cipher
+ // It iterates through all the TAN fields and adds them to the cipher
+ private parseBankingTANs(TANsField: Element, cipher: CipherView) {
+ let tanNumber = "0";
+ const entryFields = TANsField.children;
+ for (let i = 0; i < entryFields.length; i++) {
+ const entryField = entryFields[i];
+
+ if (entryField.tagName === "number") {
+ tanNumber = entryField.textContent;
+ continue;
+ }
+
+ this.processKvp(cipher, `tan_${tanNumber}_${entryField.tagName}`, entryField.textContent);
+ }
+ }
+
+ // Parses the favourites-node from the XML file, which contains a base64 encoded string
+ // The string contains the fingerprints/GUIDs of the favourited entries, separated by new lines
+ private buildFavouritesLookupTable(rootNode: Element): void {
+ const favouritesNode = this.querySelectorDirectChild(rootNode, "favorites");
+ if (favouritesNode == null) {
+ return;
+ }
+
+ const decodedBase64String = atob(favouritesNode.textContent);
+ if (decodedBase64String.indexOf("\r\n") > 0) {
+ decodedBase64String.split("\r\n").forEach((line) => {
+ this._favouritesLookupTable.add(line);
+ });
+ return;
+ }
+
+ decodedBase64String.split("\n").forEach((line) => {
+ this._favouritesLookupTable.add(line);
+ });
+ }
+}
diff --git a/libs/importer/src/importers/password-depot/types/index.ts b/libs/importer/src/importers/password-depot/types/index.ts
new file mode 100644
index 00000000000..709e5992ae8
--- /dev/null
+++ b/libs/importer/src/importers/password-depot/types/index.ts
@@ -0,0 +1,2 @@
+export * from "./password-depot-item-type";
+export * from "./password-depot-custom-field-type";
diff --git a/libs/importer/src/importers/password-depot/types/password-depot-custom-field-type.ts b/libs/importer/src/importers/password-depot/types/password-depot-custom-field-type.ts
new file mode 100644
index 00000000000..166378b38b4
--- /dev/null
+++ b/libs/importer/src/importers/password-depot/types/password-depot-custom-field-type.ts
@@ -0,0 +1,15 @@
+/** This object represents the different custom field types in Password Depot */
+export const PasswordDepotCustomFieldType = Object.freeze({
+ Password: "1",
+ Memo: "2",
+ Date: "3",
+ Number: "4",
+ Boolean: "5",
+ Decimal: "6",
+ Email: "7",
+ URL: "8",
+} as const);
+
+/** This type represents the different custom field types in Password Depot */
+export type PasswordDepotCustomFieldType =
+ (typeof PasswordDepotCustomFieldType)[keyof typeof PasswordDepotCustomFieldType];
diff --git a/libs/importer/src/importers/password-depot/types/password-depot-item-type.ts b/libs/importer/src/importers/password-depot/types/password-depot-item-type.ts
new file mode 100644
index 00000000000..04ec3a48c85
--- /dev/null
+++ b/libs/importer/src/importers/password-depot/types/password-depot-item-type.ts
@@ -0,0 +1,19 @@
+/** This object represents the different item types in Password Depot */
+export const PasswordDepotItemType = Object.freeze({
+ Password: "0",
+ CreditCard: "1",
+ SoftwareLicense: "2",
+ Identity: "3",
+ Information: "4",
+ Banking: "5",
+ EncryptedFile: "6",
+ Document: "7",
+ RDP: "8",
+ Putty: "9",
+ TeamViewer: "10",
+ Certificate: "11",
+} as const);
+
+/** This type represents the different item types in Password Depot */
+export type PasswordDepotItemType =
+ (typeof PasswordDepotItemType)[keyof typeof PasswordDepotItemType];
diff --git a/libs/importer/src/importers/spec-data/password-depot-xml/banking.xml.ts b/libs/importer/src/importers/spec-data/password-depot-xml/banking.xml.ts
new file mode 100644
index 00000000000..3a2da1c27bf
--- /dev/null
+++ b/libs/importer/src/importers/spec-data/password-depot-xml/banking.xml.ts
@@ -0,0 +1,283 @@
+export const BankingTestData = `
+
+
+ Password Depot
+ 17.0.0
+
+
+ CCDA8015-7F21-4344-BDF0-9EA6AF8AE31D
+ 12.05.2025 15:16:11
+ False
+ 0
+ 30.12.1899 00:00:00
+
+
+
+
+ -
+ banking type
+ 5
+ somePassword
+ someUser
+ some-bank.com
+ someNote
+ 12.05.2025 15:11:29
+ 02.05.2027
+ 1
+ DEB91652-52C6-402E-9D44-3557829BC6DF
+ <USER><TAB><PASS><ENTER>
+ 127
+ 0
+
+
+ Allgemein
+
+ 0
+
+
+ 12.05.2025 15:09:17
+ 12.05.2025 15:09:17
+ 0
+ 0
+ 0
+ 1
+
+ 0
+ 2
+
+ DJSMI
+
+
+ 0
+
+ 0
+
+ 0
+
+ 161
+ 0
+
+
+
+ IDS_ECHolder
+ account holder
+ -1
+ 0
+ 0
+
+
+ IDS_ECAccountNumber
+ 1234567890
+ -1
+ 0
+ 0
+
+
+ IDS_ECBLZ
+ 12345678
+ -1
+ 0
+ 0
+
+
+ IDS_ECBankName
+ someBank
+ -1
+ 0
+ 0
+
+
+ IDS_ECBIC
+ bic
+ -1
+ 0
+ 0
+
+
+ IDS_ECIBAN
+ iban
+ -1
+ 0
+ 0
+
+
+ IDS_ECCardNumber
+ 12345678
+ -1
+ 0
+ 10
+
+
+ IDS_ECPhone
+ 0049
+ 0
+ 0
+ 0
+
+
+ IDS_ECLegitimacyID
+ 1234
+ -1
+ 0
+ 0
+
+
+ IDS_ECPIN
+ 123
+ 0
+ 0
+ 1
+
+
+
+
+ 1
+ 1234
+ 12.05.2025 15:10:54
+ 100,00
+ some TAN note
+ 123
+
+
+ 2
+ 4321
+ 0,00
+
+
+
+
+
+
+ banking type
+ 5
+ somePassword
+ someUser
+ some-bank.com
+ someNote
+ 12.05.2025 15:10:35
+ 02.05.2027
+ 1
+ DEB91652-52C6-402E-9D44-3557829BC6DF
+ <USER><TAB><PASS><ENTER>
+ 127
+ 0
+
+
+ Allgemein
+
+ 0
+
+
+ 12.05.2025 15:09:17
+ 12.05.2025 15:09:17
+ 0
+ 0
+ 0
+ 1
+
+ 0
+ 2
+
+ DJSMI
+
+
+ 0
+
+ 0
+
+ 0
+
+ 161
+ 0
+
+
+
+ IDS_ECHolder
+ account holder
+ -1
+ 0
+ 0
+
+
+ IDS_ECAccountNumber
+ 1234567890
+ -1
+ 0
+ 0
+
+
+ IDS_ECBLZ
+ 12345678
+ -1
+ 0
+ 0
+
+
+ IDS_ECBankName
+ someBank
+ -1
+ 0
+ 0
+
+
+ IDS_ECBIC
+ bic
+ -1
+ 0
+ 0
+
+
+ IDS_ECIBAN
+ iban
+ -1
+ 0
+ 0
+
+
+ IDS_ECCardNumber
+ 12345678
+ -1
+ 0
+ 10
+
+
+ IDS_ECPhone
+ 0049
+ 0
+ 0
+ 0
+
+
+ IDS_ECLegitimacyID
+ 1234
+ -1
+ 0
+ 0
+
+
+ IDS_ECPIN
+ 123
+ 0
+ 0
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+ QUY3NEZGODYtRkUzOS00NTg0LThFOTYtRkU5NTBDMjg5REY4DQo=
+
+
+
+
+
+ QWxsZ2VtZWluDQpIb21lIEJhbmtpbmcNCkludGVybmV0DQpQcml2YXQNCldpbmRvd3MNCg==
+
+`;
diff --git a/libs/importer/src/importers/spec-data/password-depot-xml/certificate.xml.ts b/libs/importer/src/importers/spec-data/password-depot-xml/certificate.xml.ts
new file mode 100644
index 00000000000..eb8c463d57f
--- /dev/null
+++ b/libs/importer/src/importers/spec-data/password-depot-xml/certificate.xml.ts
@@ -0,0 +1,76 @@
+export const CertificateTestData = `
+
+
+ Password Depot
+ 17.0.0
+
+
+ CCDA8015-7F21-4344-BDF0-9EA6AF8AE31D
+ 12.05.2025 15:16:11
+ False
+ 0
+ 30.12.1899 00:00:00
+
+
+
+
+ -
+ certificate type
+ 11
+ somePassword
+
+
+ someNote
+ 12.05.2025 15:15:57
+ 00.00.0000
+ 1
+ 21288702-B042-46D9-9DDF-B44A5CD04A72
+
+ 130
+ 0
+
+
+ Allgemein
+
+ 0
+
+
+ 12.05.2025 15:15:26
+ 12.05.2025 15:15:26
+ 0
+ 0
+ 0
+ 1
+
+ 0
+ 2
+ someTag
+ DJSMI
+
+
+ 0
+
+ 0
+
+ 0
+
+ 161
+ 0
+
+
+
+
+
+
+
+
+
+ QUY3NEZGODYtRkUzOS00NTg0LThFOTYtRkU5NTBDMjg5REY4DQo=
+
+
+
+
+
+ QWxsZ2VtZWluDQpIb21lIEJhbmtpbmcNCkludGVybmV0DQpQcml2YXQNCldpbmRvd3MNCg==
+
+`;
diff --git a/libs/importer/src/importers/spec-data/password-depot-xml/credit-card.xml.ts b/libs/importer/src/importers/spec-data/password-depot-xml/credit-card.xml.ts
new file mode 100644
index 00000000000..f0af49bbfae
--- /dev/null
+++ b/libs/importer/src/importers/spec-data/password-depot-xml/credit-card.xml.ts
@@ -0,0 +1,148 @@
+export const CreditCardTestData = `
+
+
+ Password Depot
+ 17.0.0
+
+
+ CCDA8015-7F21-4344-BDF0-9EA6AF8AE31D
+ 12.05.2025 15:16:11
+ False
+ 0
+ 30.12.1899 00:00:00
+
+
+
+
+ -
+ some CreditCard
+ 1
+ 4222422242224222
+ some CC holder
+
+ someComment
+ 08.05.2025 12:09:47
+ 08.05.2026
+ 1
+ DD9B52F8-B2CE-42C2-A891-5E20DA23EA20
+
+ 126
+ 0
+
+
+
+
+ 0
+
+
+ 08.05.2025 12:08:48
+ 08.05.2025 12:08:48
+ 0
+ 0
+ 0
+ 1
+
+ 0
+ 2
+
+ DJSMI
+
+
+ 0
+
+ 0
+
+ 0
+
+ 161
+ 0
+
+
+
+ IDS_CardType
+ 0
+ 0
+ 0
+ 4
+
+
+ IDS_CardHolder
+ some CC holder
+ -1
+ 0
+ 0
+
+
+ IDS_CardNumber
+ 4222422242224222
+ -1
+ 0
+ 10
+
+
+ IDS_CardExpires
+ 05/2026
+ -1
+ 0
+ 3
+
+
+ IDS_CardCode
+ 123
+ -1
+ 0
+ 0
+
+
+ IDS_CardPhone
+
+ -1
+ 0
+ 0
+
+
+ IDS_CardURL
+
+ -1
+ 0
+ 8
+
+
+ IDS_CardAdditionalCode
+
+ -1
+ 0
+ 0
+
+
+ IDS_CardAdditionalInfo
+
+ -1
+ 0
+ 0
+
+
+ IDS_CardPIN
+ 123
+ -1
+ 0
+ 1
+
+
+
+
+
+
+
+
+
+
+ QUY3NEZGODYtRkUzOS00NTg0LThFOTYtRkU5NTBDMjg5REY4DQo=
+
+
+
+
+
+ QWxsZ2VtZWluDQpIb21lIEJhbmtpbmcNCkludGVybmV0DQpQcml2YXQNCldpbmRvd3MNCg==
+
+`;
diff --git a/libs/importer/src/importers/spec-data/password-depot-xml/document.xml.ts b/libs/importer/src/importers/spec-data/password-depot-xml/document.xml.ts
new file mode 100644
index 00000000000..4f607c9b048
--- /dev/null
+++ b/libs/importer/src/importers/spec-data/password-depot-xml/document.xml.ts
@@ -0,0 +1,99 @@
+export const DocumentTestData = `
+
+
+ Password Depot
+ 17.0.0
+
+
+ CCDA8015-7F21-4344-BDF0-9EA6AF8AE31D
+ 12.05.2025 15:16:11
+ False
+ 0
+ 30.12.1899 00:00:00
+
+
+
+
+ -
+ document type
+ 7
+
+
+
+ document comment
+ 03.06.2025 17:45:30
+ 00.00.0000
+ 1
+ 1B8E7F2C-9229-43C6-AB89-42101809C822
+
+ 133
+ 0
+
+
+ Allgemein
+
+ 0
+
+
+ 05.06.2025 21:49:49
+ 30.12.1899 00:00:00
+ 0
+ 0
+ 0
+ 1
+
+ 0
+ 2
+
+ DJSMI
+
+
+ 0
+
+ 0
+
+ 0
+
+ 161
+ 0
+
+
+
+ IDS_DocumentSize
+ 27071
+ 0
+ 0
+ 4
+
+
+ IDS_DocumentFolder
+ C:\\Users\\DJSMI\\Downloads\\
+ 0
+ 0
+ 0
+
+
+ IDS_DocumentFile
+ C:\\Users\\DJSMI\\Downloads\\some.pdf
+ 0
+ 0
+ 0
+
+
+
+
+
+
+
+
+
+
+ QUY3NEZGODYtRkUzOS00NTg0LThFOTYtRkU5NTBDMjg5REY4DQo=
+
+
+
+
+
+ QWxsZ2VtZWluDQpIb21lIEJhbmtpbmcNCkludGVybmV0DQpQcml2YXQNCldpbmRvd3MNCg==
+
+`;
diff --git a/libs/importer/src/importers/spec-data/password-depot-xml/encrypted-file.xml.ts b/libs/importer/src/importers/spec-data/password-depot-xml/encrypted-file.xml.ts
new file mode 100644
index 00000000000..2d2e929440a
--- /dev/null
+++ b/libs/importer/src/importers/spec-data/password-depot-xml/encrypted-file.xml.ts
@@ -0,0 +1,76 @@
+export const EncryptedFileTestData = `
+
+
+ Password Depot
+ 17.0.0
+
+
+ CCDA8015-7F21-4344-BDF0-9EA6AF8AE31D
+ 12.05.2025 15:16:11
+ False
+ 0
+ 30.12.1899 00:00:00
+
+
+
+
+ -
+ encrypted file type
+ 6
+ somePassword
+
+
+ some comment
+ 12.05.2025 15:15:17
+ 00.00.0000
+ 1
+ E4CA245D-A326-4359-9488-CC207B33C6C0
+
+ 132
+ 0
+
+
+ Allgemein
+
+ 0
+
+
+ 12.05.2025 15:14:58
+ 12.05.2025 15:14:58
+ 0
+ 0
+ 0
+ 1
+
+ 0
+ 2
+
+ DJSMI
+
+
+ 0
+
+ 0
+
+ 0
+
+ 161
+ 0
+
+
+
+
+
+
+
+
+
+ QUY3NEZGODYtRkUzOS00NTg0LThFOTYtRkU5NTBDMjg5REY4DQo=
+
+
+
+
+
+ QWxsZ2VtZWluDQpIb21lIEJhbmtpbmcNCkludGVybmV0DQpQcml2YXQNCldpbmRvd3MNCg==
+
+`;
diff --git a/libs/importer/src/importers/spec-data/password-depot-xml/identity.xml.ts b/libs/importer/src/importers/spec-data/password-depot-xml/identity.xml.ts
new file mode 100644
index 00000000000..dfa275aa778
--- /dev/null
+++ b/libs/importer/src/importers/spec-data/password-depot-xml/identity.xml.ts
@@ -0,0 +1,197 @@
+export const IdentityTestData = `
+
+
+ Password Depot
+ 17.0.0
+
+
+ CCDA8015-7F21-4344-BDF0-9EA6AF8AE31D
+ 12.05.2025 15:16:11
+ False
+ 0
+ 30.12.1899 00:00:00
+
+
+
+
+ -
+ identity type
+ 3
+
+ account-name/id
+ website
+ someNote
+ 12.05.2025 15:14:33
+ 00.00.0000
+ 1
+ 0E6085E9-7560-4826-814E-EFE1724E1377
+
+ 129
+ 0
+
+
+ Allgemein
+
+ 0
+
+
+ 12.05.2025 15:12:52
+ 12.05.2025 15:12:52
+ 0
+ 0
+ 0
+ 1
+
+ 0
+ 2
+
+ DJSMI
+
+
+ 0
+
+ 0
+
+ 0
+
+ 161
+ 0
+
+
+
+ IDS_IdentityName
+ account-name/id
+ -1
+ 0
+ 0
+
+
+ IDS_IdentityEmail
+ email
+ -1
+ 0
+ 0
+
+
+ IDS_IdentityFirstName
+ firstName
+ -1
+ 0
+ 0
+
+
+ IDS_IdentityLastName
+ surName
+ -1
+ 0
+ 0
+
+
+ IDS_IdentityCompany
+ someCompany
+ -1
+ 0
+ 0
+
+
+ IDS_IdentityAddress1
+ someStreet
+ -1
+ 0
+ 0
+
+
+ IDS_IdentityAddress2
+ address 2
+ -1
+ 0
+ 0
+
+
+ IDS_IdentityCity
+ town
+ -1
+ 0
+ 0
+
+
+ IDS_IdentityState
+ state
+ -1
+ 0
+ 0
+
+
+ IDS_IdentityZIP
+ zipCode
+ -1
+ 0
+ 0
+
+
+ IDS_IdentityCountry
+ country
+ -1
+ 0
+ 0
+
+
+ IDS_IdentityPhone
+ phoneNumber
+ -1
+ 0
+ 0
+
+
+ IDS_IdentityWebsite
+ website
+ -1
+ 0
+ 8
+
+
+ IDS_IdentityBirthDate
+ 45789
+ -1
+ 0
+ 3
+
+
+ IDS_IdentityMobile
+ mobileNumber
+ -1
+ 0
+ 0
+
+
+ IDS_IdentityFax
+ faxNumber
+ -1
+ 0
+ 0
+
+
+ IDS_IdentityHouseNumber
+ 123
+ -1
+ 0
+ 0
+
+
+
+
+
+
+
+
+
+
+ QUY3NEZGODYtRkUzOS00NTg0LThFOTYtRkU5NTBDMjg5REY4DQo=
+
+
+
+
+
+ QWxsZ2VtZWluDQpIb21lIEJhbmtpbmcNCkludGVybmV0DQpQcml2YXQNCldpbmRvd3MNCg==
+
+`;
diff --git a/libs/importer/src/importers/spec-data/password-depot-xml/index.ts b/libs/importer/src/importers/spec-data/password-depot-xml/index.ts
new file mode 100644
index 00000000000..6d5903ed471
--- /dev/null
+++ b/libs/importer/src/importers/spec-data/password-depot-xml/index.ts
@@ -0,0 +1,19 @@
+export { InvalidRootNodeData } from "./missing-root-node.xml";
+export { MissingPasswordsNodeData } from "./missing-passwords-node.xml";
+export { InvalidVersionData } from "./wrong-version.xml";
+export { EncryptedFileData } from "./noop-encrypted-file.xml";
+export { PasswordTestData } from "./password.xml";
+export { CreditCardTestData } from "./credit-card.xml";
+export { IdentityTestData } from "./identity.xml";
+export { RDPTestData } from "./rdp.xml";
+export { SoftwareLicenseTestData } from "./software-license.xml";
+export { TeamViewerTestData } from "./team-viewer.xml";
+export { PuttyTestData } from "./putty.xml";
+export { BankingTestData } from "./banking.xml";
+export { InformationTestData } from "./information.xml";
+export { CertificateTestData } from "./certificate.xml";
+export { EncryptedFileTestData } from "./encrypted-file.xml";
+export { DocumentTestData } from "./document.xml";
+export { MacOS_WrongVersion } from "./macos-wrong-version.xml";
+export { MacOS_PasswordDepotXmlFile } from "./macos-customfields.xml";
+export { MacOS_MultipleFolders } from "./macos-multiple-folders.xml";
diff --git a/libs/importer/src/importers/spec-data/password-depot-xml/information.xml.ts b/libs/importer/src/importers/spec-data/password-depot-xml/information.xml.ts
new file mode 100644
index 00000000000..1f07882ea64
--- /dev/null
+++ b/libs/importer/src/importers/spec-data/password-depot-xml/information.xml.ts
@@ -0,0 +1,85 @@
+export const InformationTestData = `
+
+
+ Password Depot
+ 17.0.0
+
+
+ CCDA8015-7F21-4344-BDF0-9EA6AF8AE31D
+ 12.05.2025 15:16:11
+ False
+ 0
+ 30.12.1899 00:00:00
+
+
+
+
+ -
+ information type
+ 4
+
+
+
+
+ 12.05.2025 15:14:54
+ 00.00.0000
+ 1
+ 546AFAE7-6F64-4040-838B-AFE691580356
+
+ 131
+ 0
+
+
+ Allgemein
+
+ 0
+
+
+ 12.05.2025 15:14:39
+ 12.05.2025 15:14:39
+ 0
+ 0
+ 0
+ 1
+
+ 0
+ 2
+
+ DJSMI
+
+
+ 0
+
+ 0
+
+ 0
+
+ 161
+ 0
+
+
+
+ IDS_InformationText
+ some note content
+ 0
+ 0
+ 2
+
+
+
+
+
+
+
+
+
+
+ QUY3NEZGODYtRkUzOS00NTg0LThFOTYtRkU5NTBDMjg5REY4DQo=
+
+
+
+
+
+ QWxsZ2VtZWluDQpIb21lIEJhbmtpbmcNCkludGVybmV0DQpQcml2YXQNCldpbmRvd3MNCg==
+
+`;
diff --git a/libs/importer/src/importers/spec-data/password-depot-xml/macos-customfields.xml.ts b/libs/importer/src/importers/spec-data/password-depot-xml/macos-customfields.xml.ts
new file mode 100644
index 00000000000..d83eae6bb6d
--- /dev/null
+++ b/libs/importer/src/importers/spec-data/password-depot-xml/macos-customfields.xml.ts
@@ -0,0 +1,42 @@
+export const MacOS_PasswordDepotXmlFile = `
+
+
+ Password Depot
+ 0
+ 0
+ 0
+ 1
+ 1
+ 17
+ 30.12.1899 00:00:00
+ 23.06.2025 16:30:50
+ 25.06.2025 14:30:47
+ 2C1A154A-3BB0-4871-9537-3634DE303F8E
+ 7
+
+
+
+ -
+ card 1
+ 1
+ comment
+ 23.06.2025 16:14:33
+ EBF4AC3D-86C9-49BE-826B-BAE5FF9E3575
+ 23.06.2025 16:13:40
+ 25.06.2025 14:17:10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
diff --git a/libs/importer/src/importers/spec-data/password-depot-xml/macos-multiple-folders.xml.ts b/libs/importer/src/importers/spec-data/password-depot-xml/macos-multiple-folders.xml.ts
new file mode 100644
index 00000000000..174e9415fa1
--- /dev/null
+++ b/libs/importer/src/importers/spec-data/password-depot-xml/macos-multiple-folders.xml.ts
@@ -0,0 +1,215 @@
+export const MacOS_MultipleFolders = `
+
+
+ Password Depot
+ 0
+ 0
+ 0
+ 1
+ 1
+ 17
+ 30.12.1899 00:00:00
+ 27.06.2025 10:39:07
+ 27.06.2025 10:39:27
+ 7DCDD3FA-F512-4CD4-AEED-DE2A4C8375CF
+ 7
+
+
+
+ -
+ remote desktop
+ 8
+ pass
+ username
+ compjter
+ comment
+ 26.06.2025 16:04:57
+ 81316050-9B9C-4D9B-9549-45B52A0BE6BB
+ <USER><TAB><PASS><ENTER>
+ Private
+ 26.06.2025 16:04:32
+ 27.06.2025 10:28:02
+ tgmac
+
+ -
+ teamviewer
+ 10
+ pass
+ partnerid
+ comment
+ 26.06.2025 16:05:28
+ 26.06.2025
+ 8AAACC16-4FD4-4E52-9C1F-6979B051B510
+ Internet
+ 26.06.2025 16:05:03
+ 27.06.2025 10:28:02
+ tag
+
+
+
+
+ -
+ ec card
+ 5
+ pass
+ user
+ url
+ 26.06.2025 16:08:35
+ B5C148A4-C408-427C-B69C-F88E7C529FA4
+ <USER><TAB><PASS><ENTER>
+ 26.06.2025 16:08:00
+ 27.06.2025 10:28:02
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+ identity
+ 3
+ 26.06.2025 16:09:50
+ 87574AD4-8844-4A01-9381-AFF0907198A3
+ 26.06.2025 16:09:19
+ 27.06.2025 10:28:02
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+ credit card
+ 1
+ comment
+ 26.06.2025 19:07:38
+ E98E3CBA-1578-48AD-8E41-CFD3280045BB
+ 26.06.2025 16:06:32
+ 27.06.2025 10:28:02
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+ password
+ passmac
+ usernam
+ comment
+ 26.06.2025 16:04:30
+ DE8AD61B-8EC0-4E72-9BC8-971E80712B50
+ <USER><TAB><PASS><ENTER>
+ General
+ 26.06.2025 16:04:04
+ 27.06.2025 10:28:02
+ tagmac
+
+ -
+ informationb
+ 4
+ 26.06.2025 16:10:01
+ 7E9E6941-BB3B-47F2-9E43-33F900EBBF95
+ Banking
+ 26.06.2025 16:09:53
+ 27.06.2025 10:28:02
+
+
+
+
+ -
+ certificat
+ 11
+ passsss
+ comment
+ 26.06.2025 16:10:28
+ 26.06.2025
+ 1F36748F-0374-445E-B020-282EAE26259F
+ Internet
+ 26.06.2025 16:10:10
+ 27.06.2025 10:28:02
+ tag
+
+
+
+ -
+ putty
+ 9
+ pass
+ username
+ host
+ comment
+ 26.06.2025 16:06:08
+ 26.06.2025
+ 7947A949-98F0-4F26-BE12-5FFAFE7601C8
+ <USER><TAB><PASS><ENTER>
+ Banking
+ 26.06.2025 16:05:38
+ 27.06.2025 10:28:02
+ tag
+
+
+
+
+
+
+
+ -
+ soft license
+ 2
+ 26.06.2025 16:09:02
+ 2A5CF4C1-70D0-4F27-A1DE-4CFEF5FB71CF
+ 26.06.2025 16:08:43
+ 27.06.2025 10:28:02
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
diff --git a/libs/importer/src/importers/spec-data/password-depot-xml/macos-wrong-version.xml.ts b/libs/importer/src/importers/spec-data/password-depot-xml/macos-wrong-version.xml.ts
new file mode 100644
index 00000000000..771bf813e48
--- /dev/null
+++ b/libs/importer/src/importers/spec-data/password-depot-xml/macos-wrong-version.xml.ts
@@ -0,0 +1,21 @@
+export const MacOS_WrongVersion = `
+
+
+ Password Depot
+ 0
+ 0
+ 0
+ 1
+ 1
+ 18
+ 30.12.1899 00:00:00
+ 23.06.2025 16:30:50
+ 25.06.2025 14:30:47
+ 2C1A154A-3BB0-4871-9537-3634DE303F8E
+ 7
+
+
+
+
+
+`;
diff --git a/libs/importer/src/importers/spec-data/password-depot-xml/missing-passwords-node.xml.ts b/libs/importer/src/importers/spec-data/password-depot-xml/missing-passwords-node.xml.ts
new file mode 100644
index 00000000000..d07beb8521c
--- /dev/null
+++ b/libs/importer/src/importers/spec-data/password-depot-xml/missing-passwords-node.xml.ts
@@ -0,0 +1,25 @@
+export const MissingPasswordsNodeData = `
+
+
+ Password Depot
+ 17.0.0
+
+
+ CCDA8015-7F21-4344-BDF0-9EA6AF8AE31D
+ 12.05.2025 15:16:11
+ False
+ 0
+ 30.12.1899 00:00:00
+
+
+
+
+
+
+
+
+
+
+
+
+`;
diff --git a/libs/importer/src/importers/spec-data/password-depot-xml/missing-root-node.xml.ts b/libs/importer/src/importers/spec-data/password-depot-xml/missing-root-node.xml.ts
new file mode 100644
index 00000000000..aca2a2f6fa1
--- /dev/null
+++ b/libs/importer/src/importers/spec-data/password-depot-xml/missing-root-node.xml.ts
@@ -0,0 +1,28 @@
+export const InvalidRootNodeData = `
+
+
+ Password Depot
+ 17.0.0
+
+
+ CCDA8015-7F21-4344-BDF0-9EA6AF8AE31D
+ 12.05.2025 15:16:11
+ False
+ 0
+ 30.12.1899 00:00:00
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
diff --git a/libs/importer/src/importers/spec-data/password-depot-xml/noop-encrypted-file.xml.ts b/libs/importer/src/importers/spec-data/password-depot-xml/noop-encrypted-file.xml.ts
new file mode 100644
index 00000000000..e8050726b25
--- /dev/null
+++ b/libs/importer/src/importers/spec-data/password-depot-xml/noop-encrypted-file.xml.ts
@@ -0,0 +1,27 @@
+export const EncryptedFileData = `
+
+
+ Password Depot
+ 17.0.0
+
+
+ CCDA8015-7F21-4344-BDF0-9EA6AF8AE31D
+ 12.05.2025 15:16:11
+ True
+ 0
+ 30.12.1899 00:00:00
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
diff --git a/libs/importer/src/importers/spec-data/password-depot-xml/password.xml.ts b/libs/importer/src/importers/spec-data/password-depot-xml/password.xml.ts
new file mode 100644
index 00000000000..7d15fce3aa8
--- /dev/null
+++ b/libs/importer/src/importers/spec-data/password-depot-xml/password.xml.ts
@@ -0,0 +1,222 @@
+export const PasswordTestData = `
+
+
+ Password Depot
+ 17.0.0
+
+
+ CCDA8015-7F21-4344-BDF0-9EA6AF8AE31D
+ 12.05.2025 15:16:11
+ False
+ 0
+ 30.12.1899 00:00:00
+
+
+
+
+ -
+ password type
+ 0
+ p6J<]fmjv!:H&iJ7/Mwt@3i8
+ someUser
+ example.com
+ someComment
+ 07.05.2025 13:37:56
+ 07.05.2025
+ 0
+ 27ACAC2D-8DDA-4088-8D3A-E6C5F40ED46E
+ <USER><TAB><PASS><ENTER>
+ 0
+ 0
+
+
+ Allgemein
+
+ 0
+
+
+ 07.05.2025 13:36:48
+ 07.05.2025 13:36:48
+ 0
+ 0
+ 0
+ 1
+
+ 0
+ 2
+ someTag
+ DJSMI
+
+
+ 0
+
+ 0
+
+ 0
+
+ 161
+ 0
+
+
+
+ passwort
+ password
+ -1
+ 0
+ 1
+
+
+ memo
+ memo
+ -1
+ 0
+ 2
+
+
+ datum
+ 45790
+ -1
+ 0
+ 3
+
+
+ nummer
+ 1
+ -1
+ 0
+ 4
+
+
+ boolean
+ 1
+ -1
+ 0
+ 5
+
+
+ decimal
+ 1,01
+ -1
+ 0
+ 6
+
+
+ email
+ who@cares.com
+ -1
+ 0
+ 7
+
+
+ url
+ example.com
+ -1
+ 0
+ 8
+
+
+
+ -
+ password type (2)
+ 0
+ p6J<]fmjv!:H&iJ7/Mwt@3i8
+ someUser
+
+ someComment
+ 07.05.2025 13:37:56
+ 07.05.2025
+ 0
+ AF74FF86-FE39-4584-8E96-FE950C289DF8
+ <USER><TAB><PASS><ENTER>
+ 0
+ 0
+
+
+ Allgemein
+
+ 0
+
+
+ 07.05.2025 13:36:48
+ 07.05.2025 13:36:48
+ 0
+ 0
+ 0
+ 1
+
+ 0
+ 2
+ someTag
+ DJSMI
+
+
+ 0
+
+ 0
+
+ 0
+
+ 161
+ 0
+
+
+ -
+ password type (3)
+ 0
+ p6J<]fmjv!:H&iJ7/Mwt@3i8
+ someUser
+
+ someComment
+ 07.05.2025 13:37:56
+ 07.05.2025
+ 0
+ BF74FF86-FA39-4584-8E96-FA950C249DF8
+ <USER><TAB><PASS><ENTER>
+ 0
+ 0
+
+
+ Allgemein
+
+ 0
+
+
+ 07.05.2025 13:36:48
+ 07.05.2025 13:36:48
+ 0
+ 0
+ 0
+ 1
+
+ 0
+ 2
+ someTag
+ DJSMI
+
+
+ 0
+
+ 0
+
+ 0
+
+ 161
+ 0
+
+
+
+
+
+
+
+
+
+ QUY3NEZGODYtRkUzOS00NTg0LThFOTYtRkU5NTBDMjg5REY4CkJGNzRGRjg2LUZBMzktNDU4NC04RTk2LUZBOTUwQzI0OURGOA==
+
+
+
+
+
+ QWxsZ2VtZWluDQpIb21lIEJhbmtpbmcNCkludGVybmV0DQpQcml2YXQNCldpbmRvd3MNCg==
+
+`;
diff --git a/libs/importer/src/importers/spec-data/password-depot-xml/putty.xml.ts b/libs/importer/src/importers/spec-data/password-depot-xml/putty.xml.ts
new file mode 100644
index 00000000000..d878b04cd3c
--- /dev/null
+++ b/libs/importer/src/importers/spec-data/password-depot-xml/putty.xml.ts
@@ -0,0 +1,106 @@
+export const PuttyTestData = `
+
+
+ Password Depot
+ 17.0.0
+
+
+ CCDA8015-7F21-4344-BDF0-9EA6AF8AE31D
+ 12.05.2025 15:16:11
+ False
+ 0
+ 30.12.1899 00:00:00
+
+
+
+
+ -
+ Putty type
+ 9
+ somePassword
+ someUser
+ localhost
+ someNote
+ 12.05.2025 15:09:09
+ 00.00.0000
+ 1
+ 32207D79-B70B-4987-BC73-3F7AD75D2C63
+
+ 125
+ 0
+
+ cli command
+ Allgemein
+
+ 0
+
+
+ 12.05.2025 15:08:18
+ 12.05.2025 15:08:18
+ 0
+ 0
+ 0
+ 1
+
+ 0
+ 2
+ someTag
+ DJSMI
+
+
+ 0
+
+ 0
+
+ 0
+
+ 161
+ 0
+
+
+
+ IDS_PuTTyProtocol
+ 0
+ 0
+ 0
+ 0
+
+
+ IDS_PuTTyKeyFile
+ pathToKeyFile
+ -1
+ 0
+ 0
+
+
+ IDS_PuTTyKeyPassword
+ passwordForKeyFile
+ -1
+ 0
+ 1
+
+
+ IDS_PuTTyPort
+ 8080
+ -1
+ 0
+ 4
+
+
+
+
+
+
+
+
+
+
+ QUY3NEZGODYtRkUzOS00NTg0LThFOTYtRkU5NTBDMjg5REY4DQo=
+
+
+
+
+
+ QWxsZ2VtZWluDQpIb21lIEJhbmtpbmcNCkludGVybmV0DQpQcml2YXQNCldpbmRvd3MNCg==
+
+`;
diff --git a/libs/importer/src/importers/spec-data/password-depot-xml/rdp.xml.ts b/libs/importer/src/importers/spec-data/password-depot-xml/rdp.xml.ts
new file mode 100644
index 00000000000..fafc375f9a6
--- /dev/null
+++ b/libs/importer/src/importers/spec-data/password-depot-xml/rdp.xml.ts
@@ -0,0 +1,76 @@
+export const RDPTestData = `
+
+
+ Password Depot
+ 17.0.0
+
+
+ CCDA8015-7F21-4344-BDF0-9EA6AF8AE31D
+ 12.05.2025 15:16:11
+ False
+ 0
+ 30.12.1899 00:00:00
+
+
+
+
+ -
+ rdp type
+ 8
+ somePassword
+ someUser
+ ms-rd:subscribe?url=https://contoso.com
+ someNote
+ 12.05.2025 15:07:33
+ 12.05.2025
+ 1
+ 24CFF328-3036-48E3-99A3-85CD337725D3
+
+ 123
+ 0
+
+ commandline command
+ Allgemein
+
+ 0
+
+
+ 12.05.2025 15:06:24
+ 12.05.2025 15:06:24
+ 0
+ 0
+ 0
+ 1
+
+ 0
+ 2
+ sometag
+ DJSMI
+
+
+ 0
+
+ 0
+
+ 0
+
+ 161
+ 0
+
+
+
+
+
+
+
+
+
+ QUY3NEZGODYtRkUzOS00NTg0LThFOTYtRkU5NTBDMjg5REY4DQo=
+
+
+
+
+
+ QWxsZ2VtZWluDQpIb21lIEJhbmtpbmcNCkludGVybmV0DQpQcml2YXQNCldpbmRvd3MNCg==
+
+`;
diff --git a/libs/importer/src/importers/spec-data/password-depot-xml/software-license.xml.ts b/libs/importer/src/importers/spec-data/password-depot-xml/software-license.xml.ts
new file mode 100644
index 00000000000..5ab9437c3d7
--- /dev/null
+++ b/libs/importer/src/importers/spec-data/password-depot-xml/software-license.xml.ts
@@ -0,0 +1,169 @@
+export const SoftwareLicenseTestData = `
+
+
+ Password Depot
+ 17.0.0
+
+
+ CCDA8015-7F21-4344-BDF0-9EA6AF8AE31D
+ 12.05.2025 15:16:11
+ False
+ 0
+ 30.12.1899 00:00:00
+
+
+
+
+ -
+ software-license type
+ 2
+ somePassword
+ someUserName
+ example.com
+ someComment
+ 12.05.2025 15:12:48
+ 00.00.0000
+ 1
+ 220206EB-BE82-4E78-8FFB-9316D854721F
+
+ 128
+ 0
+
+
+ Allgemein
+
+ 0
+
+
+ 12.05.2025 15:11:33
+ 12.05.2025 15:11:33
+ 0
+ 0
+ 0
+ 1
+
+ 0
+ 2
+
+ DJSMI
+
+
+ 0
+
+ 0
+
+ 0
+
+ 161
+ 0
+
+
+
+ IDS_LicenseProduct
+ someProduct
+ 0
+ 0
+ 0
+
+
+ IDS_LicenseVersion
+ someVersion
+ 0
+ 0
+ 0
+
+
+ IDS_LicenseName
+ some User
+ -1
+ 0
+ 0
+
+
+ IDS_LicenseKey
+ license-key
+ -1
+ 0
+ 0
+
+
+ IDS_LicenseAdditionalKey
+ additional-license-key
+ -1
+ 0
+ 0
+
+
+ IDS_LicenseURL
+ example.com
+ -1
+ 0
+ 8
+
+
+ IDS_LicenseProtected
+ 1
+ 0
+ 0
+ 5
+
+
+ IDS_LicenseUserName
+ someUserName
+ -1
+ 0
+ 0
+
+
+ IDS_LicensePassword
+ somePassword
+ -1
+ 0
+ 1
+
+
+ IDS_LicensePurchaseDate
+ 45789
+ 0
+ 0
+ 3
+
+
+ IDS_LicenseOrderNumber
+ order number
+ -1
+ 0
+ 0
+
+
+ IDS_LicenseEmail
+ someEmail
+ -1
+ 0
+ 7
+
+
+ IDS_LicenseExpires
+ Nie
+ 0
+ 0
+ 3
+
+
+
+
+
+
+
+
+
+
+ QUY3NEZGODYtRkUzOS00NTg0LThFOTYtRkU5NTBDMjg5REY4DQo=
+
+
+
+
+
+ QWxsZ2VtZWluDQpIb21lIEJhbmtpbmcNCkludGVybmV0DQpQcml2YXQNCldpbmRvd3MNCg==
+
+`;
diff --git a/libs/importer/src/importers/spec-data/password-depot-xml/team-viewer.xml.ts b/libs/importer/src/importers/spec-data/password-depot-xml/team-viewer.xml.ts
new file mode 100644
index 00000000000..911c621c59a
--- /dev/null
+++ b/libs/importer/src/importers/spec-data/password-depot-xml/team-viewer.xml.ts
@@ -0,0 +1,85 @@
+export const TeamViewerTestData = `
+
+
+ Password Depot
+ 17.0.0
+
+
+ CCDA8015-7F21-4344-BDF0-9EA6AF8AE31D
+ 12.05.2025 15:16:11
+ False
+ 0
+ 30.12.1899 00:00:00
+
+
+
+
+ -
+ TeamViewer type
+ 10
+ somePassword
+
+ partnerId
+ someNote
+ 12.05.2025 15:08:14
+ 00.00.0000
+ 1
+ AE650032-5963-4D93-8E2E-69F216405C29
+
+ 124
+ 0
+
+
+ Allgemein
+
+ 0
+
+
+ 12.05.2025 15:07:41
+ 12.05.2025 15:07:41
+ 0
+ 0
+ 0
+ 1
+
+ 0
+ 2
+ someTag
+ DJSMI
+
+
+ 0
+
+ 0
+
+ 0
+
+ 161
+ 0
+
+
+
+ IDS_TeamViewerMode
+ 0
+ 0
+ 0
+ 0
+
+
+
+
+
+
+
+
+
+
+ QUY3NEZGODYtRkUzOS00NTg0LThFOTYtRkU5NTBDMjg5REY4DQo=
+
+
+
+
+
+ QWxsZ2VtZWluDQpIb21lIEJhbmtpbmcNCkludGVybmV0DQpQcml2YXQNCldpbmRvd3MNCg==
+
+`;
diff --git a/libs/importer/src/importers/spec-data/password-depot-xml/wrong-version.xml.ts b/libs/importer/src/importers/spec-data/password-depot-xml/wrong-version.xml.ts
new file mode 100644
index 00000000000..90b766ded1b
--- /dev/null
+++ b/libs/importer/src/importers/spec-data/password-depot-xml/wrong-version.xml.ts
@@ -0,0 +1,28 @@
+export const InvalidVersionData = `
+
+
+ Password Depot
+ 18.0.0
+
+
+ CCDA8015-7F21-4344-BDF0-9EA6AF8AE31D
+ 12.05.2025 15:16:11
+ False
+ 0
+ 30.12.1899 00:00:00
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
diff --git a/libs/importer/src/models/import-options.ts b/libs/importer/src/models/import-options.ts
index a8c4b4e0a8a..205dbaf0198 100644
--- a/libs/importer/src/models/import-options.ts
+++ b/libs/importer/src/models/import-options.ts
@@ -73,6 +73,7 @@ export const regularImportOptions = [
{ id: "passkyjson", name: "Passky (json)" },
{ id: "passwordxpcsv", name: "Password XP (csv)" },
{ id: "netwrixpasswordsecure", name: "Netwrix Password Secure (csv)" },
+ { id: "passworddepot17xml", name: "Password Depot 17 (xml)" },
] as const;
export type ImportType =
diff --git a/libs/importer/src/services/import.service.ts b/libs/importer/src/services/import.service.ts
index 3789ee7536c..2b9d2e490f7 100644
--- a/libs/importer/src/services/import.service.ts
+++ b/libs/importer/src/services/import.service.ts
@@ -90,6 +90,7 @@ import {
YotiCsvImporter,
ZohoVaultCsvImporter,
PasswordXPCsvImporter,
+ PasswordDepot17XmlImporter,
} from "../importers";
import { Importer } from "../importers/importer";
import {
@@ -348,6 +349,8 @@ export class ImportService implements ImportServiceAbstraction {
return new PasswordXPCsvImporter();
case "netwrixpasswordsecure":
return new NetwrixPasswordSecureCsvImporter();
+ case "passworddepot17xml":
+ return new PasswordDepot17XmlImporter();
default:
return null;
}