Skip to content

[PM-18668] Create importer for xml files from Password Depot 17 #14760

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 25 commits into
base: main
Choose a base branch
from

Conversation

djsmith85
Copy link
Contributor

@djsmith85 djsmith85 commented May 13, 2025

🎟️ Tracking

https://bitwarden.atlassian.net/browse/PM-18668

📔 Objective

Adding support for importing xml-exports from Password Depot 17

Item type mapping:

Logins

  • Passwords
  • RDP
  • Putty
  • TeamViewer
  • Banking
    • TANs are added as custom fields and named: tan_number_fieldName
  • Certificate
  • EncryptedFile

Credit Cards

  • Credit Cards

Secure Note

  • Software License
  • Information
  • Document

Identity

  • Identity

Field mapping

  • Based on the group tag(s) and it's name attribute folders are created. In case a user is importing into an organization, the names are used to create collections.
  • Items that were marked as favorite are also marked as favorite within Bitwarden
  • To ensure a maximum retention of data when migrating, all unmapped fields are imported as custom fields.
  • Based on the source field types the appropiate custom field types are chosen
    • Password type or visible attribute set-> Hidden
    • Date is converted from the Excel Date format into a JS locale DateString
    • Boolean -> Boolean

📸 Screenshots

⏰ Reminders before review

  • Contributor guidelines followed
  • All formatters and local linters executed and passed
  • Written new unit and / or integration tests where applicable
  • Protected functional changes with optionality (feature flags)
  • Used internationalization (i18n) for all UI strings
  • CI builds passed
  • Communicated to DevOps any deployment requirements
  • Updated any necessary documentation (Confluence, contributing docs) or informed the documentation team

🦮 Reviewer guidelines

  • 👍 (:+1:) or similar for great changes
  • 📝 (:memo:) or ℹ️ (:information_source:) for notes or general info
  • ❓ (:question:) for questions
  • 🤔 (:thinking:) or 💭 (:thought_balloon:) for more open inquiry that's not quite a confirmed issue and could potentially benefit from discussion
  • 🎨 (:art:) for suggestions / improvements
  • ❌ (:x:) or ⚠️ (:warning:) for more significant problems or concerns needing attention
  • 🌱 (:seedling:) or ♻️ (:recycle:) for future improvements or indications of technical debt
  • ⛏ (:pick:) for minor or nitpick changes

- Create importer
- Create test data
- Create unit tests
Copy link

codecov bot commented May 13, 2025

Codecov Report

Attention: Patch coverage is 90.94203% with 25 lines in your changes missing coverage. Please review.

Project coverage is 36.92%. Comparing base (e8e2181) to head (d45e5fe).
Report is 15 commits behind head on main.

✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...s/password-depot/password-depot-17-xml-importer.ts 89.78% 19 Missing and 5 partials ⚠️
libs/importer/src/services/import.service.ts 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #14760      +/-   ##
==========================================
+ Coverage   36.82%   36.92%   +0.10%     
==========================================
  Files        3201     3222      +21     
  Lines       92743    92913     +170     
  Branches    13929    13988      +59     
==========================================
+ Hits        34149    34307     +158     
- Misses      57187    57195       +8     
- Partials     1407     1411       +4     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Contributor

github-actions bot commented May 13, 2025

Logo
Checkmarx One – Scan Summary & Detailsd8e17a28-6c37-40e5-bd88-753ac3857e88

Great job, no security vulnerabilities found in this Pull Request

@djsmith85 djsmith85 force-pushed the tools/pm-18668/create-xml-importer-for-password-depot-17 branch from 6e347bc to 63a7592 Compare June 5, 2025 13:45
@djsmith85 djsmith85 marked this pull request as ready for review June 6, 2025 09:53
@djsmith85 djsmith85 requested a review from a team as a code owner June 6, 2025 09:53
Copy link

sonarqubecloud bot commented Jun 6, 2025

Copy link
Member

@audreyality audreyality left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ You got dinged for missing docs. The remaining feedback is optional.

Comment on lines +431 to +436
<ng-container *ngIf="format === 'passworddepot17xml'">
On the desktop application, go to Tools &rarr; Export &rarr; Enter your master password
&rarr; Select XML Format (*.xml) as Export format &rarr; Click on next &rarr; Choose which
entries should be included in the export &rarr; Click on next to export into the location
previously chosen.
</ng-container>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⛏️ Shouldn't this text be in a messages.json file?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 I'm not a fan of isolating the test data from the test. We end up with magic strings in assertions that aren't easy to relate the test body.

💭 Importers are kind of special in that they have to deal with large amounts of data. Password depot's XML is particularly verbose, but it's not exceptional in that respect. Maybe this is a case where we should have one .spec.ts per case?

📝 The line-level feedback cites examples that apply broadly across this file.

Comment on lines +305 to +319
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");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎨 These cases should use asymmetric matchers, specifically expect.arrayContaining and expect.objectContaining.

// find an item that is structurally equal
expect(cipher.fields).toEqual(
  expect.arrayContaining({ name: 'IDS_PuTTyProtocol', value: 0 })
);

// find an item with matching properties:
expect(cipher.fields).toEqual(
  expect.arrayContaining(
    expect.objectContaining({ name: 'IDS_PuTTyProtocol', value: 0 })
  )
);

Comment on lines +332 to +335
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");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎨 These tests could be simplified using toMatchObject, unless there's a reason toBe is necessary:

Suggested change
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");
expect(cipher.login?.password).toEqual({
password: "somePassword",
username: "someUser",
uri: "http://some-bank.com"
);


const cipher = result.ciphers.shift();

expect(cipher.type).toBe(CipherType.SecureNote);
Copy link
Member

@audreyality audreyality Jun 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⛏️ Using a toBe check with an enum-like is almost certainly going to cause trouble as we implement ADR-0025.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍🏻 Already using the enum-like pattern! w00t!

Comment on lines +13 to +17
export type PasswordDepotCustomFieldType =
_PasswordDepotCustomFieldType[keyof _PasswordDepotCustomFieldType];
export const PasswordDepotCustomFieldType: Readonly<{
[K in keyof typeof _PasswordDepotCustomFieldType]: PasswordDepotCustomFieldType;
}> = Object.freeze(_PasswordDepotCustomFieldType);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Missing docs on your exported members.

📝 This also applies to the other enum-likes in sibling files.

@@ -348,6 +349,8 @@ export class ImportService implements ImportServiceAbstraction {
return new PasswordXPCsvImporter();
case "netwrixpasswordsecure":
return new NetwrixPasswordSecureCsvImporter();
case "passworddepot17xml":
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎨 Seems like the strings here might be better represented as an enum-like.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants