From 45d5b171b857a61bf94b85aaf1a4afb7f03e3eb9 Mon Sep 17 00:00:00 2001 From: Daniel Riera Date: Thu, 20 Mar 2025 09:54:56 -0400 Subject: [PATCH 01/20] PM-19291 Pass relevant folder and vault data to drop down component within notification footer (#13901) * PM-19291 - Pass relevant data into dropdown component - Clean up some files - Pass all data into notificationBarIframeInitData using promise all * fix tests --- .../notification.background.spec.ts | 15 ++-- .../background/notification.background.ts | 69 ++++++++++++------- .../content/components/notification/body.ts | 4 +- .../components/notification/container.ts | 21 ++++-- .../abstractions/notification-bar.ts | 18 +++-- apps/browser/src/autofill/notification/bar.ts | 23 ++++++- .../browser/src/background/main.background.ts | 13 ++-- 7 files changed, 110 insertions(+), 53 deletions(-) diff --git a/apps/browser/src/autofill/background/notification.background.spec.ts b/apps/browser/src/autofill/background/notification.background.spec.ts index e02e3d8d951..d474e303336 100644 --- a/apps/browser/src/autofill/background/notification.background.spec.ts +++ b/apps/browser/src/autofill/background/notification.background.spec.ts @@ -1,6 +1,7 @@ import { mock, MockProxy } from "jest-mock-extended"; import { BehaviorSubject, firstValueFrom } from "rxjs"; +import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/services/policy/policy.service"; import { AccountInfo, AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; @@ -59,6 +60,7 @@ describe("NotificationBackground", () => { const themeStateService = mock(); const configService = mock(); const accountService = mock(); + const organizationService = mock(); const activeAccountSubject = new BehaviorSubject<{ id: UserId } & AccountInfo>({ id: "testId" as UserId, @@ -73,18 +75,19 @@ describe("NotificationBackground", () => { authService.activeAccountStatus$ = activeAccountStatusMock$; accountService.activeAccount$ = activeAccountSubject; notificationBackground = new NotificationBackground( + accountService, + authService, autofillService, cipherService, - authService, - policyService, - folderService, - userNotificationSettingsService, + configService, domainSettingsService, environmentService, + folderService, logService, + organizationService, + policyService, themeStateService, - configService, - accountService, + userNotificationSettingsService, ); }); diff --git a/apps/browser/src/autofill/background/notification.background.ts b/apps/browser/src/autofill/background/notification.background.ts index 11037e7e261..50e0ee0aa75 100644 --- a/apps/browser/src/autofill/background/notification.background.ts +++ b/apps/browser/src/autofill/background/notification.background.ts @@ -2,6 +2,7 @@ // @ts-strict-ignore import { firstValueFrom } from "rxjs"; +import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; @@ -63,46 +64,48 @@ export default class NotificationBackground { ExtensionCommand.AutofillIdentity, ]); private readonly extensionMessageHandlers: NotificationBackgroundExtensionMessageHandlers = { - unlockCompleted: ({ message, sender }) => this.handleUnlockCompleted(message, sender), - bgGetFolderData: () => this.getFolderData(), - bgCloseNotificationBar: ({ message, sender }) => - this.handleCloseNotificationBarMessage(message, sender), + bgAddLogin: ({ message, sender }) => this.addLogin(message, sender), bgAdjustNotificationBar: ({ message, sender }) => this.handleAdjustNotificationBarMessage(message, sender), - bgAddLogin: ({ message, sender }) => this.addLogin(message, sender), bgChangedPassword: ({ message, sender }) => this.changedPassword(message, sender), - bgRemoveTabFromNotificationQueue: ({ sender }) => - this.removeTabFromNotificationQueue(sender.tab), - bgSaveCipher: ({ message, sender }) => this.handleSaveCipherMessage(message, sender), - bgNeverSave: ({ sender }) => this.saveNever(sender.tab), - collectPageDetailsResponse: ({ message }) => - this.handleCollectPageDetailsResponseMessage(message), - bgUnlockPopoutOpened: ({ message, sender }) => this.unlockVault(message, sender.tab), - checkNotificationQueue: ({ sender }) => this.checkNotificationQueue(sender.tab), - bgReopenUnlockPopout: ({ sender }) => this.openUnlockPopout(sender.tab), + bgCloseNotificationBar: ({ message, sender }) => + this.handleCloseNotificationBarMessage(message, sender), + bgGetActiveUserServerConfig: () => this.getActiveUserServerConfig(), + bgGetDecryptedCiphers: () => this.getNotificationCipherData(), bgGetEnableChangedPasswordPrompt: () => this.getEnableChangedPasswordPrompt(), bgGetEnableAddedLoginPrompt: () => this.getEnableAddedLoginPrompt(), bgGetExcludedDomains: () => this.getExcludedDomains(), - bgGetActiveUserServerConfig: () => this.getActiveUserServerConfig(), + bgGetFolderData: () => this.getFolderData(), + bgGetOrgData: () => this.getOrgData(), + bgNeverSave: ({ sender }) => this.saveNever(sender.tab), + bgOpenVault: ({ message, sender }) => this.openVault(message, sender.tab), + bgRemoveTabFromNotificationQueue: ({ sender }) => + this.removeTabFromNotificationQueue(sender.tab), + bgReopenUnlockPopout: ({ sender }) => this.openUnlockPopout(sender.tab), + bgSaveCipher: ({ message, sender }) => this.handleSaveCipherMessage(message, sender), + bgUnlockPopoutOpened: ({ message, sender }) => this.unlockVault(message, sender.tab), + checkNotificationQueue: ({ sender }) => this.checkNotificationQueue(sender.tab), + collectPageDetailsResponse: ({ message }) => + this.handleCollectPageDetailsResponseMessage(message), getWebVaultUrlForNotification: () => this.getWebVaultUrl(), notificationRefreshFlagValue: () => this.getNotificationFlag(), - bgGetDecryptedCiphers: () => this.getNotificationCipherData(), - bgOpenVault: ({ message, sender }) => this.openVault(message, sender.tab), + unlockCompleted: ({ message, sender }) => this.handleUnlockCompleted(message, sender), }; constructor( + private accountService: AccountService, + private authService: AuthService, private autofillService: AutofillService, private cipherService: CipherService, - private authService: AuthService, - private policyService: PolicyService, - private folderService: FolderService, - private userNotificationSettingsService: UserNotificationSettingsServiceAbstraction, + private configService: ConfigService, private domainSettingsService: DomainSettingsService, private environmentService: EnvironmentService, + private folderService: FolderService, private logService: LogService, + private organizationService: OrganizationService, + private policyService: PolicyService, private themeStateService: ThemeStateService, - private configService: ConfigService, - private accountService: AccountService, + private userNotificationSettingsService: UserNotificationSettingsServiceAbstraction, ) {} init() { @@ -744,6 +747,26 @@ export default class NotificationBackground { ); } + /** + * Returns the first value found from the organization service organizations$ observable. + */ + private async getOrgData() { + const activeUserId = await firstValueFrom( + this.accountService.activeAccount$.pipe(getOptionalUserId), + ); + const organizations = await firstValueFrom( + this.organizationService.organizations$(activeUserId), + ); + return organizations.map((org) => { + const { id, name, productTierType } = org; + return { + id, + name, + productTierType, + }; + }); + } + /** * Handles the unlockCompleted extension message. Will close the notification bar * after an attempted autofill action, and retry the autofill action if the message diff --git a/apps/browser/src/autofill/content/components/notification/body.ts b/apps/browser/src/autofill/content/components/notification/body.ts index 2433381dfba..66b580bde43 100644 --- a/apps/browser/src/autofill/content/components/notification/body.ts +++ b/apps/browser/src/autofill/content/components/notification/body.ts @@ -16,12 +16,12 @@ const { css } = createEmotion({ }); export function NotificationBody({ - ciphers, + ciphers = [], notificationType, theme = ThemeTypes.Light, handleEditOrUpdateAction, }: { - ciphers: NotificationCipherData[]; + ciphers?: NotificationCipherData[]; customClasses?: string[]; notificationType?: NotificationType; theme: Theme; diff --git a/apps/browser/src/autofill/content/components/notification/container.ts b/apps/browser/src/autofill/content/components/notification/container.ts index f98ef795749..8d80dc9fb50 100644 --- a/apps/browser/src/autofill/content/components/notification/container.ts +++ b/apps/browser/src/autofill/content/components/notification/container.ts @@ -9,6 +9,7 @@ import { NotificationType, } from "../../../notification/abstractions/notification-bar"; import { NotificationCipherData } from "../cipher/types"; +import { FolderView, OrgView } from "../common-types"; import { themes, spacing } from "../constants/styles"; import { NotificationBody, componentClassPrefix as notificationBodyClassPrefix } from "./body"; @@ -20,20 +21,24 @@ import { export function NotificationContainer({ handleCloseNotification, + handleEditOrUpdateAction, + handleSaveAction, + ciphers, + folders, i18n, + organizations, theme = ThemeTypes.Light, type, - ciphers, - handleSaveAction, - handleEditOrUpdateAction, }: NotificationBarIframeInitData & { handleCloseNotification: (e: Event) => void; handleSaveAction: (e: Event) => void; handleEditOrUpdateAction: (e: Event) => void; } & { + ciphers?: NotificationCipherData[]; + folders?: FolderView[]; i18n: { [key: string]: string }; + organizations?: OrgView[]; type: NotificationType; // @TODO typing override for generic `NotificationBarIframeInitData.type` - ciphers: NotificationCipherData[]; }) { const headerMessage = getHeaderMessage(i18n, type); const showBody = true; @@ -42,8 +47,8 @@ export function NotificationContainer({
${NotificationHeader({ handleCloseNotification, - standalone: showBody, message: headerMessage, + standalone: showBody, theme, })} ${showBody @@ -56,9 +61,11 @@ export function NotificationContainer({ : null} ${NotificationFooter({ handleSaveAction, - theme, - notificationType: type, + folders, i18n, + notificationType: type, + organizations, + theme, })}
`; diff --git a/apps/browser/src/autofill/notification/abstractions/notification-bar.ts b/apps/browser/src/autofill/notification/abstractions/notification-bar.ts index cb14a86dffa..6e7427e3a38 100644 --- a/apps/browser/src/autofill/notification/abstractions/notification-bar.ts +++ b/apps/browser/src/autofill/notification/abstractions/notification-bar.ts @@ -1,5 +1,8 @@ import { Theme } from "@bitwarden/common/platform/enums"; +import { NotificationCipherData } from "../../../autofill/content/components/cipher/types"; +import { FolderView, OrgView } from "../../../autofill/content/components/common-types"; + const NotificationTypes = { Add: "add", Change: "change", @@ -9,21 +12,24 @@ const NotificationTypes = { type NotificationType = (typeof NotificationTypes)[keyof typeof NotificationTypes]; type NotificationBarIframeInitData = { - type?: string; // @TODO use `NotificationType` - isVaultLocked?: boolean; - theme?: Theme; - removeIndividualVault?: boolean; - importType?: string; applyRedesign?: boolean; + ciphers?: NotificationCipherData[]; + folders?: FolderView[]; + importType?: string; + isVaultLocked?: boolean; launchTimestamp?: number; + organizations?: OrgView[]; + removeIndividualVault?: boolean; + theme?: Theme; + type?: string; // @TODO use `NotificationType` }; type NotificationBarWindowMessage = { + cipherId?: string; command: string; error?: string; initData?: NotificationBarIframeInitData; username?: string; - cipherId?: string; }; type NotificationBarWindowMessageHandlers = { diff --git a/apps/browser/src/autofill/notification/bar.ts b/apps/browser/src/autofill/notification/bar.ts index 617b1e58c14..d17c008372d 100644 --- a/apps/browser/src/autofill/notification/bar.ts +++ b/apps/browser/src/autofill/notification/bar.ts @@ -5,6 +5,8 @@ import { ConsoleLogService } from "@bitwarden/common/platform/services/console-l import type { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; import { AdjustNotificationBarMessageData } from "../background/abstractions/notification.background"; +import { NotificationCipherData } from "../content/components/cipher/types"; +import { OrgView } from "../content/components/common-types"; import { NotificationConfirmationContainer } from "../content/components/notification/confirmation-container"; import { NotificationContainer } from "../content/components/notification/container"; import { buildSvgDomElement } from "../utils"; @@ -115,7 +117,7 @@ function setElementText(template: HTMLTemplateElement, elementId: string, text: } } -function initNotificationBar(message: NotificationBarWindowMessage) { +async function initNotificationBar(message: NotificationBarWindowMessage) { const { initData } = message; if (!initData) { return; @@ -131,7 +133,23 @@ function initNotificationBar(message: NotificationBarWindowMessage) { // Current implementations utilize a require for scss files which creates the need to remove the node. document.head.querySelectorAll('link[rel="stylesheet"]').forEach((node) => node.remove()); - sendPlatformMessage({ command: "bgGetDecryptedCiphers" }, (cipherData) => { + await Promise.all([ + new Promise((resolve) => + sendPlatformMessage({ command: "bgGetOrgData" }, resolve), + ), + new Promise((resolve) => + sendPlatformMessage({ command: "bgGetFolderData" }, resolve), + ), + new Promise((resolve) => + sendPlatformMessage({ command: "bgGetDecryptedCiphers" }, resolve), + ), + ]).then(([organizations, folders, ciphers]) => { + notificationBarIframeInitData = { + ...notificationBarIframeInitData, + folders, + ciphers, + organizations, + }; // @TODO use context to avoid prop drilling return render( NotificationContainer({ @@ -142,7 +160,6 @@ function initNotificationBar(message: NotificationBarWindowMessage) { handleSaveAction, handleEditOrUpdateAction, i18n, - ciphers: cipherData, }), document.body, ); diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index f8f86e6a277..74fa6acdf79 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -1173,18 +1173,19 @@ export default class MainBackground { () => this.generatePasswordToClipboard(), ); this.notificationBackground = new NotificationBackground( + this.accountService, + this.authService, this.autofillService, this.cipherService, - this.authService, - this.policyService, - this.folderService, - this.userNotificationSettingsService, + this.configService, this.domainSettingsService, this.environmentService, + this.folderService, this.logService, + this.organizationService, + this.policyService, this.themeStateService, - this.configService, - this.accountService, + this.userNotificationSettingsService, ); this.overlayNotificationsBackground = new OverlayNotificationsBackground( From 23fbb56248cde0a5d6f4d11e707d8baec617b204 Mon Sep 17 00:00:00 2001 From: Justin Baur <19896123+justindbaur@users.noreply.github.com> Date: Thu, 20 Mar 2025 10:56:30 -0400 Subject: [PATCH 02/20] Switch Notifications to only connect on unlocked (#13913) --- .../internal/default-notifications.service.spec.ts | 13 +++++++++---- .../internal/default-notifications.service.ts | 10 +++++----- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/libs/common/src/platform/notifications/internal/default-notifications.service.spec.ts b/libs/common/src/platform/notifications/internal/default-notifications.service.spec.ts index e24069a9fbe..bf834e8dd93 100644 --- a/libs/common/src/platform/notifications/internal/default-notifications.service.spec.ts +++ b/libs/common/src/platform/notifications/internal/default-notifications.service.spec.ts @@ -225,9 +225,10 @@ describe("NotificationsService", () => { }); it.each([ - { initialStatus: AuthenticationStatus.Locked, updatedStatus: AuthenticationStatus.Unlocked }, - { initialStatus: AuthenticationStatus.Unlocked, updatedStatus: AuthenticationStatus.Locked }, - { initialStatus: AuthenticationStatus.Locked, updatedStatus: AuthenticationStatus.Locked }, + // Temporarily rolling back notifications being connected while locked + // { initialStatus: AuthenticationStatus.Locked, updatedStatus: AuthenticationStatus.Unlocked }, + // { initialStatus: AuthenticationStatus.Unlocked, updatedStatus: AuthenticationStatus.Locked }, + // { initialStatus: AuthenticationStatus.Locked, updatedStatus: AuthenticationStatus.Locked }, { initialStatus: AuthenticationStatus.Unlocked, updatedStatus: AuthenticationStatus.Unlocked }, ])( "does not re-connect when the user transitions from $initialStatus to $updatedStatus", @@ -252,7 +253,11 @@ describe("NotificationsService", () => { }, ); - it.each([AuthenticationStatus.Locked, AuthenticationStatus.Unlocked])( + it.each([ + // Temporarily disabling notifications connecting while in a locked state + // AuthenticationStatus.Locked, + AuthenticationStatus.Unlocked, + ])( "connects when a user transitions from logged out to %s", async (newStatus: AuthenticationStatus) => { emitActiveUser(mockUser1); diff --git a/libs/common/src/platform/notifications/internal/default-notifications.service.ts b/libs/common/src/platform/notifications/internal/default-notifications.service.ts index f0586e37ff7..fc505b018ce 100644 --- a/libs/common/src/platform/notifications/internal/default-notifications.service.ts +++ b/libs/common/src/platform/notifications/internal/default-notifications.service.ts @@ -123,13 +123,13 @@ export class DefaultNotificationsService implements NotificationsServiceAbstract ); } + // This method name is a lie currently as we also have an access token + // when locked, this is eventually where we want to be but it increases load + // on signalR so we are rolling back until we can move the load of browser to + // web push. private hasAccessToken$(userId: UserId) { return this.authService.authStatusFor$(userId).pipe( - map( - (authStatus) => - authStatus === AuthenticationStatus.Locked || - authStatus === AuthenticationStatus.Unlocked, - ), + map((authStatus) => authStatus === AuthenticationStatus.Unlocked), distinctUntilChanged(), ); } From bef0e0f5b7c45e345e408f053ff465df0baeb975 Mon Sep 17 00:00:00 2001 From: Github Actions Date: Thu, 20 Mar 2025 14:58:59 +0000 Subject: [PATCH 03/20] Bumped client version(s) --- apps/browser/package.json | 2 +- apps/browser/src/manifest.json | 2 +- apps/browser/src/manifest.v3.json | 2 +- package-lock.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/browser/package.json b/apps/browser/package.json index e3bccf3f0df..5a8ddd03b41 100644 --- a/apps/browser/package.json +++ b/apps/browser/package.json @@ -1,6 +1,6 @@ { "name": "@bitwarden/browser", - "version": "2025.3.0", + "version": "2025.3.1", "scripts": { "build": "npm run build:chrome", "build:chrome": "cross-env BROWSER=chrome MANIFEST_VERSION=3 NODE_OPTIONS=\"--max-old-space-size=8192\" webpack", diff --git a/apps/browser/src/manifest.json b/apps/browser/src/manifest.json index 5bfca440b99..4510c2f342d 100644 --- a/apps/browser/src/manifest.json +++ b/apps/browser/src/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "__MSG_extName__", "short_name": "__MSG_appName__", - "version": "2025.3.0", + "version": "2025.3.1", "description": "__MSG_extDesc__", "default_locale": "en", "author": "Bitwarden Inc.", diff --git a/apps/browser/src/manifest.v3.json b/apps/browser/src/manifest.v3.json index 1e2ac1812ca..fc897c1b1c3 100644 --- a/apps/browser/src/manifest.v3.json +++ b/apps/browser/src/manifest.v3.json @@ -3,7 +3,7 @@ "minimum_chrome_version": "102.0", "name": "__MSG_extName__", "short_name": "__MSG_appName__", - "version": "2025.3.0", + "version": "2025.3.1", "description": "__MSG_extDesc__", "default_locale": "en", "author": "Bitwarden Inc.", diff --git a/package-lock.json b/package-lock.json index eaf5c0f24ed..cb51c157b09 100644 --- a/package-lock.json +++ b/package-lock.json @@ -189,7 +189,7 @@ }, "apps/browser": { "name": "@bitwarden/browser", - "version": "2025.3.0" + "version": "2025.3.1" }, "apps/cli": { "name": "@bitwarden/cli", From e31ffd9b6603939e45b80af4a935c1e8485e51e3 Mon Sep 17 00:00:00 2001 From: Matt Andreko Date: Thu, 20 Mar 2025 12:29:24 -0400 Subject: [PATCH 04/20] Update SARIF upload to use proper branch (#13917) --- .github/workflows/build-web.yml | 2 ++ .github/workflows/scan.yml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.github/workflows/build-web.yml b/.github/workflows/build-web.yml index bd96b388c6a..3cc0df5103c 100644 --- a/.github/workflows/build-web.yml +++ b/.github/workflows/build-web.yml @@ -323,6 +323,8 @@ jobs: uses: github/codeql-action/upload-sarif@d68b2d4edb4189fd2a5366ac14e72027bd4b37dd # v3.28.2 with: sarif_file: ${{ steps.container-scan.outputs.sarif }} + sha: ${{ contains(github.event_name, 'pull_request') && github.event.pull_request.head.sha || github.sha }} + ref: ${{ contains(github.event_name, 'pull_request') && format('refs/pull/{0}/head', github.event.pull_request.number) || github.ref }} - name: Log out of Docker run: docker logout diff --git a/.github/workflows/scan.yml b/.github/workflows/scan.yml index c5e189c4666..77b66ba8bf1 100644 --- a/.github/workflows/scan.yml +++ b/.github/workflows/scan.yml @@ -49,6 +49,8 @@ jobs: uses: github/codeql-action/upload-sarif@d68b2d4edb4189fd2a5366ac14e72027bd4b37dd # v3.28.2 with: sarif_file: cx_result.sarif + sha: ${{ contains(github.event_name, 'pull_request') && github.event.pull_request.head.sha || github.sha }} + ref: ${{ contains(github.event_name, 'pull_request') && format('refs/pull/{0}/head', github.event.pull_request.number) || github.ref }} quality: name: Quality scan From c999c19f07eb7014208035bdedd3c31ac5737904 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Ch=C4=99ci=C5=84ski?= Date: Thu, 20 Mar 2025 17:38:51 +0100 Subject: [PATCH 05/20] fix(workflow): add conditional checks for Docker image scanning and result upload (#13898) --- .github/workflows/build-web.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build-web.yml b/.github/workflows/build-web.yml index 3cc0df5103c..e91fba2e87a 100644 --- a/.github/workflows/build-web.yml +++ b/.github/workflows/build-web.yml @@ -312,6 +312,7 @@ jobs: cosign sign --yes ${images} - name: Scan Docker image + if: ${{ needs.setup.outputs.has_secrets == 'true' }} id: container-scan uses: anchore/scan-action@869c549e657a088dc0441b08ce4fc0ecdac2bb65 # v5.3.0 with: @@ -320,6 +321,7 @@ jobs: output-format: sarif - name: Upload Grype results to GitHub + if: ${{ needs.setup.outputs.has_secrets == 'true' }} uses: github/codeql-action/upload-sarif@d68b2d4edb4189fd2a5366ac14e72027bd4b37dd # v3.28.2 with: sarif_file: ${{ steps.container-scan.outputs.sarif }} From bd0fedc5ce18fc412b6f621dff2a9199503cd2ed Mon Sep 17 00:00:00 2001 From: Alex Rosenfeld Date: Thu, 20 Mar 2025 13:53:17 -0400 Subject: [PATCH 06/20] [PM-18153] add support for importing some older / wonky card formats from msecure (#13328) * add support for importing some older / wonky card formats from msecure * slightly less fuzzy logic --------- Co-authored-by: Daniel James Smith <2670567+djsmith85@users.noreply.github.com> --- .../importers/msecure-csv-importer.spec.ts | 18 ++++++++ .../src/importers/msecure-csv-importer.ts | 45 ++++++++++++------- 2 files changed, 46 insertions(+), 17 deletions(-) diff --git a/libs/importer/src/importers/msecure-csv-importer.spec.ts b/libs/importer/src/importers/msecure-csv-importer.spec.ts index 83e35802fac..3cf7cc713a8 100644 --- a/libs/importer/src/importers/msecure-csv-importer.spec.ts +++ b/libs/importer/src/importers/msecure-csv-importer.spec.ts @@ -8,6 +8,24 @@ describe("MSecureCsvImporter.parse", () => { importer = new MSecureCsvImporter(); }); + it("should correctly parse legacy formatted cards", async () => { + const mockCsvData = + `aWeirdOldStyleCard|1032,Credit Card,,Security code 1234,Card Number|12|5555 4444 3333 2222,Expiration Date|11|04/0029,Name on Card|9|Obi Wan Kenobi,Security Code|9|444,`.trim(); + const result = await importer.parse(mockCsvData); + + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(1); + const cipher = result.ciphers[0]; + expect(cipher.name).toBe("aWeirdOldStyleCard"); + expect(cipher.type).toBe(CipherType.Card); + expect(cipher.card.number).toBe("5555 4444 3333 2222"); + expect(cipher.card.expiration).toBe("04 / 2029"); + expect(cipher.card.code).toBe("444"); + expect(cipher.card.cardholderName).toBe("Obi Wan Kenobi"); + expect(cipher.notes).toBe("Security code 1234"); + expect(cipher.card.brand).toBe(""); + }); + it("should correctly parse credit card entries as Secret Notes", async () => { const mockCsvData = `myCreditCard|155089404,Credit Card,,,Card Number|12|41111111111111111,Expiration Date|11|05/2026,Security Code|9|123,Name on Card|0|John Doe,PIN|9|1234,Issuing Bank|0|Visa,Phone Number|4|,Billing Address|0|,`.trim(); diff --git a/libs/importer/src/importers/msecure-csv-importer.ts b/libs/importer/src/importers/msecure-csv-importer.ts index 322764fa8dc..e78c715976f 100644 --- a/libs/importer/src/importers/msecure-csv-importer.ts +++ b/libs/importer/src/importers/msecure-csv-importer.ts @@ -43,23 +43,34 @@ export class MSecureCsvImporter extends BaseImporter implements Importer { ).split("/"); cipher.card.expMonth = month.trim(); cipher.card.expYear = year.trim(); - cipher.card.code = this.getValueOrDefault(this.splitValueRetainingLastPart(value[6])); - cipher.card.cardholderName = this.getValueOrDefault( - this.splitValueRetainingLastPart(value[7]), + const securityCodeRegex = RegExp("^Security Code\\|\\d*\\|"); + const securityCodeEntry = value.find((entry: string) => securityCodeRegex.test(entry)); + cipher.card.code = this.getValueOrDefault( + this.splitValueRetainingLastPart(securityCodeEntry), ); - cipher.card.brand = this.getValueOrDefault(this.splitValueRetainingLastPart(value[9])); - cipher.notes = - this.getValueOrDefault(value[8].split("|")[0]) + - ": " + - this.getValueOrDefault(this.splitValueRetainingLastPart(value[8]), "") + - "\n" + - this.getValueOrDefault(value[10].split("|")[0]) + - ": " + - this.getValueOrDefault(this.splitValueRetainingLastPart(value[10]), "") + - "\n" + - this.getValueOrDefault(value[11].split("|")[0]) + - ": " + - this.getValueOrDefault(this.splitValueRetainingLastPart(value[11]), ""); + + const cardNameRegex = RegExp("^Name on Card\\|\\d*\\|"); + const nameOnCardEntry = value.find((entry: string) => entry.match(cardNameRegex)); + cipher.card.cardholderName = this.getValueOrDefault( + this.splitValueRetainingLastPart(nameOnCardEntry), + ); + + cipher.card.brand = this.getValueOrDefault(this.splitValueRetainingLastPart(value[9]), ""); + + const noteRegex = RegExp("\\|\\d*\\|"); + const rawNotes = value + .slice(2) + .filter((entry: string) => !this.isNullOrWhitespace(entry) && !noteRegex.test(entry)); + const noteIndexes = [8, 10, 11]; + const indexedNotes = noteIndexes + .filter((idx) => value[idx] && noteRegex.test(value[idx])) + .map((idx) => value[idx]) + .map((val) => { + const key = val.split("|")[0]; + const value = this.getValueOrDefault(this.splitValueRetainingLastPart(val), ""); + return `${key}: ${value}`; + }); + cipher.notes = [...rawNotes, ...indexedNotes].join("\n"); } else if (value.length > 3) { cipher.type = CipherType.SecureNote; cipher.secureNote = new SecureNoteView(); @@ -95,6 +106,6 @@ export class MSecureCsvImporter extends BaseImporter implements Importer { // like "Password|8|myPassword", we want to keep the "myPassword" but also ensure that if // the value contains any "|" it works fine private splitValueRetainingLastPart(value: string) { - return value.split("|").slice(0, 2).concat(value.split("|").slice(2).join("|")).pop(); + return value && value.split("|").slice(0, 2).concat(value.split("|").slice(2).join("|")).pop(); } } From cf827981af3abf9d12d88bdad214b5b9f09b6243 Mon Sep 17 00:00:00 2001 From: Jason Ng Date: Thu, 20 Mar 2025 15:16:18 -0400 Subject: [PATCH 07/20] [PM-19240] Do not show task unless Manage or Edit Permission (#13880) * do not show task for edit except pw --- .../view/emergency-view-dialog.component.spec.ts | 3 +++ libs/vault/src/cipher-view/cipher-view.component.ts | 11 +++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/apps/web/src/app/auth/settings/emergency-access/view/emergency-view-dialog.component.spec.ts b/apps/web/src/app/auth/settings/emergency-access/view/emergency-view-dialog.component.spec.ts index 0021d938f82..6e96b357e3e 100644 --- a/apps/web/src/app/auth/settings/emergency-access/view/emergency-view-dialog.component.spec.ts +++ b/apps/web/src/app/auth/settings/emergency-access/view/emergency-view-dialog.component.spec.ts @@ -13,6 +13,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl import { Utils } from "@bitwarden/common/platform/misc/utils"; import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec"; import { UserId } from "@bitwarden/common/types/guid"; +import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; @@ -65,6 +66,7 @@ describe("EmergencyViewDialogComponent", () => { useValue: ChangeLoginPasswordService, }, { provide: ConfigService, useValue: ConfigService }, + { provide: CipherService, useValue: mock() }, ], }, add: { @@ -79,6 +81,7 @@ describe("EmergencyViewDialogComponent", () => { useValue: mock(), }, { provide: ConfigService, useValue: mock() }, + { provide: CipherService, useValue: mock() }, ], }, }) diff --git a/libs/vault/src/cipher-view/cipher-view.component.ts b/libs/vault/src/cipher-view/cipher-view.component.ts index 1df96656da5..57c2b4dbae4 100644 --- a/libs/vault/src/cipher-view/cipher-view.component.ts +++ b/libs/vault/src/cipher-view/cipher-view.component.ts @@ -15,7 +15,8 @@ import { isCardExpired } from "@bitwarden/common/autofill/utils"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { CollectionId, UserId } from "@bitwarden/common/types/guid"; +import { CipherId, CollectionId, UserId } from "@bitwarden/common/types/guid"; +import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; @@ -87,6 +88,7 @@ export class CipherViewComponent implements OnChanges, OnDestroy { private platformUtilsService: PlatformUtilsService, private changeLoginPasswordService: ChangeLoginPasswordService, private configService: ConfigService, + private cipherService: CipherService, ) {} async ngOnChanges() { @@ -152,7 +154,12 @@ export class CipherViewComponent implements OnChanges, OnDestroy { const userId = await firstValueFrom(this.activeUserId$); - if (this.cipher.edit && this.cipher.viewPassword) { + // Show Tasks for Manage and Edit permissions + // Using cipherService to see if user has access to cipher in a non-AC context to address with Edit Except Password permissions + const allCiphers = await firstValueFrom(this.cipherService.ciphers$(userId)); + const cipherServiceCipher = allCiphers[this.cipher?.id as CipherId]; + + if (cipherServiceCipher?.edit && cipherServiceCipher?.viewPassword) { await this.checkPendingChangePasswordTasks(userId); } From 85c71351fce911af516dd10557bcd62ed34d154f Mon Sep 17 00:00:00 2001 From: Daniel Riera Date: Thu, 20 Mar 2025 15:37:46 -0400 Subject: [PATCH 08/20] PM-19361 Notification bar dropdown folder component displays "No Folder" twice (#13924) * PM-19361 - Remove default folder option - Edit iFrame height * revert testing change --- .../content/components/notification/button-row.ts | 13 ++++--------- .../src/autofill/content/notification-bar.ts | 4 ++-- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/apps/browser/src/autofill/content/components/notification/button-row.ts b/apps/browser/src/autofill/content/components/notification/button-row.ts index 1eb0a4ac5f4..8661f5957e1 100644 --- a/apps/browser/src/autofill/content/components/notification/button-row.ts +++ b/apps/browser/src/autofill/content/components/notification/button-row.ts @@ -60,23 +60,18 @@ export function NotificationButtonRow({ ) : ([] as Option[]); - const noFolderOption: Option = { - default: true, - icon: Folder, - text: "No folder", // @TODO localize - value: "0", - }; const folderOptions: Option[] = folders?.length - ? folders.reduce( + ? folders.reduce( (options, { id, name }: FolderView) => [ ...options, { icon: Folder, text: name, - value: id, + value: id === null ? "0" : id, + default: id === null, }, ], - [noFolderOption], + [], ) : []; diff --git a/apps/browser/src/autofill/content/notification-bar.ts b/apps/browser/src/autofill/content/notification-bar.ts index ae489ea956b..bf3d562a0ef 100644 --- a/apps/browser/src/autofill/content/notification-bar.ts +++ b/apps/browser/src/autofill/content/notification-bar.ts @@ -880,7 +880,7 @@ async function loadNotificationBar() { const baseStyle = useComponentBar ? isNotificationFresh - ? "height: calc(276px + 25px); width: 450px; right: 0; transform:translateX(100%); opacity:0;" + ? "height: calc(276px + 50px); width: 450px; right: 0; transform:translateX(100%); opacity:0;" : "height: calc(276px + 25px); width: 450px; right: 0; transform:translateX(0%); opacity:1;" : "height: 42px; width: 100%;"; @@ -910,7 +910,7 @@ async function loadNotificationBar() { function getFrameStyle(useComponentBar: boolean): string { return ( (useComponentBar - ? "height: calc(276px + 25px); width: 450px; right: 0;" + ? "height: calc(276px + 50px); width: 450px; right: 0;" : "height: 42px; width: 100%; left: 0;") + " top: 0; padding: 0; position: fixed;" + " z-index: 2147483647; visibility: visible;" From 79fd1b32639e2e89ce4648185e7675dcfbd246b4 Mon Sep 17 00:00:00 2001 From: Jeffrey Holland <124393578+jholland-livefront@users.noreply.github.com> Date: Thu, 20 Mar 2025 20:54:33 +0100 Subject: [PATCH 09/20] PM-17187 Autofill new card information in the popout (#13688) --- .../add-edit/add-edit-v2.component.ts | 26 +++++++++++++++++ .../cipher-form-config.service.ts | 5 ++++ .../card-details-section.component.spec.ts | 6 ++++ .../card-details-section.component.ts | 28 ++++++++++++++++++- 4 files changed, 64 insertions(+), 1 deletion(-) diff --git a/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts b/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts index b7ff0718b35..f986bdfca31 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts @@ -440,6 +440,32 @@ const mapAddEditCipherInfoToInitialValues = ( initialValues.name = cipher.name; } + if (cipher.type === CipherType.Card) { + const card = cipher.card; + + if (card != null) { + if (card.cardholderName != null) { + initialValues.cardholderName = card.cardholderName; + } + + if (card.number != null) { + initialValues.number = card.number; + } + + if (card.expMonth != null) { + initialValues.expMonth = card.expMonth; + } + + if (card.expYear != null) { + initialValues.expYear = card.expYear; + } + + if (card.code != null) { + initialValues.code = card.code; + } + } + } + if (cipher.type === CipherType.Login) { const login = cipher.login; diff --git a/libs/vault/src/cipher-form/abstractions/cipher-form-config.service.ts b/libs/vault/src/cipher-form/abstractions/cipher-form-config.service.ts index 3fc473c4465..8a16050804b 100644 --- a/libs/vault/src/cipher-form/abstractions/cipher-form-config.service.ts +++ b/libs/vault/src/cipher-form/abstractions/cipher-form-config.service.ts @@ -25,6 +25,11 @@ export type OptionalInitialValues = { username?: string; password?: string; name?: string; + cardholderName?: string; + number?: string; + expMonth?: string; + expYear?: string; + code?: string; }; /** diff --git a/libs/vault/src/cipher-form/components/card-details-section/card-details-section.component.spec.ts b/libs/vault/src/cipher-form/components/card-details-section/card-details-section.component.spec.ts index 39a59192985..32baad189cf 100644 --- a/libs/vault/src/cipher-form/components/card-details-section/card-details-section.component.spec.ts +++ b/libs/vault/src/cipher-form/components/card-details-section/card-details-section.component.spec.ts @@ -65,6 +65,8 @@ describe("CardDetailsSectionComponent", () => { cardView.cardholderName = "Ron Burgundy"; cardView.number = "4242 4242 4242 4242"; cardView.brand = "Visa"; + cardView.expMonth = ""; + cardView.code = ""; expect(patchCipherSpy).toHaveBeenCalled(); const patchFn = patchCipherSpy.mock.lastCall[0]; @@ -79,6 +81,10 @@ describe("CardDetailsSectionComponent", () => { }); const cardView = new CardView(); + cardView.cardholderName = ""; + cardView.number = ""; + cardView.expMonth = ""; + cardView.code = ""; cardView.expYear = "2022"; expect(patchCipherSpy).toHaveBeenCalled(); diff --git a/libs/vault/src/cipher-form/components/card-details-section/card-details-section.component.ts b/libs/vault/src/cipher-form/components/card-details-section/card-details-section.component.ts index c2b3ebb59aa..cb00c7d24f5 100644 --- a/libs/vault/src/cipher-form/components/card-details-section/card-details-section.component.ts +++ b/libs/vault/src/cipher-form/components/card-details-section/card-details-section.component.ts @@ -97,6 +97,10 @@ export class CardDetailsSectionComponent implements OnInit { EventType = EventType; + get initialValues() { + return this.cipherFormContainer.config.initialValues; + } + constructor( private cipherFormContainer: CipherFormContainer, private formBuilder: FormBuilder, @@ -139,7 +143,9 @@ export class CardDetailsSectionComponent implements OnInit { const prefillCipher = this.cipherFormContainer.getInitialCipherView(); if (prefillCipher) { - this.setInitialValues(prefillCipher); + this.initFromExistingCipher(prefillCipher.card); + } else { + this.initNewCipher(); } if (this.disabled) { @@ -147,6 +153,26 @@ export class CardDetailsSectionComponent implements OnInit { } } + private initFromExistingCipher(existingCard: CardView) { + this.cardDetailsForm.patchValue({ + cardholderName: this.initialValues?.cardholderName ?? existingCard.cardholderName, + number: this.initialValues?.number ?? existingCard.number, + expMonth: this.initialValues?.expMonth ?? existingCard.expMonth, + expYear: this.initialValues?.expYear ?? existingCard.expYear, + code: this.initialValues?.code ?? existingCard.code, + }); + } + + private initNewCipher() { + this.cardDetailsForm.patchValue({ + cardholderName: this.initialValues?.cardholderName || "", + number: this.initialValues?.number || "", + expMonth: this.initialValues?.expMonth || "", + expYear: this.initialValues?.expYear || "", + code: this.initialValues?.code || "", + }); + } + /** Get the section heading based on the card brand */ getSectionHeading(): string { const { brand } = this.cardDetailsForm.value; From 87847dc8067462418acdb29f9b56bbe5444f5b12 Mon Sep 17 00:00:00 2001 From: Alec Rippberger <127791530+alec-livefront@users.noreply.github.com> Date: Thu, 20 Mar 2025 15:23:19 -0500 Subject: [PATCH 10/20] fix: check device id and creationDate for falsey values This commit adds validation to check for falsey values in device 'id' and 'creationDate' fields in the device management component. This prevents potential issues when these string values are empty or otherwise evaluate to false. Resolves PM-18757 --- .../app/auth/settings/security/device-management.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/web/src/app/auth/settings/security/device-management.component.ts b/apps/web/src/app/auth/settings/security/device-management.component.ts index 0f31b8d4639..631ab02db7d 100644 --- a/apps/web/src/app/auth/settings/security/device-management.component.ts +++ b/apps/web/src/app/auth/settings/security/device-management.component.ts @@ -180,7 +180,7 @@ export class DeviceManagementComponent { private updateDeviceTable(devices: Array): void { this.dataSource.data = devices .map((device: DeviceView): DeviceTableData | null => { - if (device.id == undefined) { + if (!device.id) { this.validationService.showError(new Error(this.i18nService.t("deviceIdMissing"))); return null; } @@ -190,7 +190,7 @@ export class DeviceManagementComponent { return null; } - if (device.creationDate == undefined) { + if (!device.creationDate) { this.validationService.showError( new Error(this.i18nService.t("deviceCreationDateMissing")), ); From 1551ab5be97331a9a4d1686ef74775d4c392d4d1 Mon Sep 17 00:00:00 2001 From: "bw-ghapp[bot]" <178206702+bw-ghapp[bot]@users.noreply.github.com> Date: Fri, 21 Mar 2025 11:14:45 +0100 Subject: [PATCH 11/20] Autosync the updated translations (#13937) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/web/src/locales/de/messages.json | 2 +- apps/web/src/locales/hr/messages.json | 122 +++++++++++------------ apps/web/src/locales/zh_CN/messages.json | 2 +- 3 files changed, 63 insertions(+), 63 deletions(-) diff --git a/apps/web/src/locales/de/messages.json b/apps/web/src/locales/de/messages.json index e0a25e99ae2..135ab8af923 100644 --- a/apps/web/src/locales/de/messages.json +++ b/apps/web/src/locales/de/messages.json @@ -429,7 +429,7 @@ "message": "Zum Sortieren ziehen" }, "dragToReorder": { - "message": "Ziehen zum umsortieren" + "message": "Ziehen zum Umsortieren" }, "cfTypeText": { "message": "Text" diff --git a/apps/web/src/locales/hr/messages.json b/apps/web/src/locales/hr/messages.json index eaecafa5c0d..65c42a0cbf0 100644 --- a/apps/web/src/locales/hr/messages.json +++ b/apps/web/src/locales/hr/messages.json @@ -6,7 +6,7 @@ "message": "Kritične aplikacije" }, "noCriticalAppsAtRisk": { - "message": "No critical applications at risk" + "message": "Nema kritičnih aplikacija u opasnosti" }, "accessIntelligence": { "message": "Pristup inteligenciji" @@ -202,7 +202,7 @@ "message": "Bilješke" }, "privateNote": { - "message": "Private note" + "message": "Privatna bilješka" }, "note": { "message": "Bilješka" @@ -429,7 +429,7 @@ "message": "Povuci za sortiranje" }, "dragToReorder": { - "message": "Drag to reorder" + "message": "Povuci za premještanje" }, "cfTypeText": { "message": "Tekst" @@ -474,7 +474,7 @@ "message": "Uredi mapu" }, "editWithName": { - "message": "Edit $ITEM$: $NAME$", + "message": "Uredi $ITEM$: $NAME$", "placeholders": { "item": { "content": "$1", @@ -1037,7 +1037,7 @@ "message": "Ne" }, "location": { - "message": "Location" + "message": "Lokacija" }, "loginOrCreateNewAccount": { "message": "Prijavi se ili stvori novi račun za pristup svojem sigurnom trezoru." @@ -1175,13 +1175,13 @@ "message": "Prijavi se u Bitwarden" }, "enterTheCodeSentToYourEmail": { - "message": "Enter the code sent to your email" + "message": "Unesi kôd poslan e-poštom" }, "enterTheCodeFromYourAuthenticatorApp": { - "message": "Enter the code from your authenticator app" + "message": "Unesi kôd iz svoje aplikacije za autentifikaciju" }, "pressYourYubiKeyToAuthenticate": { - "message": "Press your YubiKey to authenticate" + "message": "Za autentifikaciju dodirni svoj YubiKey" }, "authenticationTimeout": { "message": "Istek vremena za autentifikaciju" @@ -1190,7 +1190,7 @@ "message": "Sesija za autentifikaciju je istekla. Ponovi proces prijave." }, "verifyYourIdentity": { - "message": "Verify your Identity" + "message": "Potvrdi svoj identitet" }, "weDontRecognizeThisDevice": { "message": "Ne prepoznajemo ovaj uređaj. Za potvrdu identiteta unesi kôd poslan e-poštom." @@ -1460,7 +1460,7 @@ "message": "Zapamti me" }, "dontAskAgainOnThisDeviceFor30Days": { - "message": "Don't ask again on this device for 30 days" + "message": "Ne pitaj na ovom uređaju idućih 30 dana" }, "sendVerificationCodeEmailAgain": { "message": "Ponovno slanje kontrolnog koda e-poštom" @@ -1469,11 +1469,11 @@ "message": "Koristiti drugi način prijave dvostrukom autentifikacijom" }, "selectAnotherMethod": { - "message": "Select another method", + "message": "Odaberi drugi način", "description": "Select another two-step login method" }, "useYourRecoveryCode": { - "message": "Use your recovery code" + "message": "Koristi kôd za oporavak" }, "insertYubiKey": { "message": "Umetni svoj YubiKey u USB priključak računala, a zatim dodirni njegovu tipku." @@ -1494,7 +1494,7 @@ "message": "Mogućnosti prijave dvostrukom autentifikacijom" }, "selectTwoStepLoginMethod": { - "message": "Select two-step login method" + "message": "Odaberi način prijave dvostrukom autentifikacijom" }, "recoveryCodeDesc": { "message": "Izgubljen je pristup uređaju za prijavu dvostrukom autentifikacijom? Koristi svoj kôd za oporavak za onemogućavanje svih pružatelja usluga prijave dvostrukom autentifikacijom na svom računu." @@ -1539,7 +1539,7 @@ "message": "(migrirano s FIDO)" }, "openInNewTab": { - "message": "Open in new tab" + "message": "Otvori u novoj kartici" }, "emailTitle": { "message": "E-pošta" @@ -2237,7 +2237,7 @@ "message": "Opozovi pristup" }, "revoke": { - "message": "Revoke" + "message": "Opozovi" }, "twoStepLoginProviderEnabled": { "message": "Ovaj pružatelj prijave dvostrukom autentifikacijom je omogućen na tvojem računu." @@ -4097,10 +4097,10 @@ "message": "Koristiš nepodržani preglednik. Web trezor možda neće ispravno raditi." }, "youHaveAPendingLoginRequest": { - "message": "You have a pending login request from another device." + "message": "Na čekanju je zahtjev za prijavu s drugog uređaja." }, "reviewLoginRequest": { - "message": "Review login request" + "message": "Pregledaj zahtjev za prijavu" }, "freeTrialEndPromptCount": { "message": "Besplatno probno razdoblje završava za $COUNT$ dan/a.", @@ -4491,7 +4491,7 @@ "message": "Ažuriranje ključa za šifriranje ne može se nastaviti" }, "editFieldLabel": { - "message": "Edit $LABEL$", + "message": "Uredi $LABEL$", "placeholders": { "label": { "content": "$1", @@ -4500,7 +4500,7 @@ } }, "reorderToggleButton": { - "message": "Reorder $LABEL$. Use arrow key to move item up or down.", + "message": "Ponovno poredaj $LABEL$. Koristi tipke sa strelicom za pomicanje stavke gore ili dolje.", "placeholders": { "label": { "content": "$1", @@ -4509,7 +4509,7 @@ } }, "reorderFieldUp": { - "message": "$LABEL$ moved up, position $INDEX$ of $LENGTH$", + "message": "$LABEL$ pomaknuto gore, pozicija $INDEX$ od $LENGTH$", "placeholders": { "label": { "content": "$1", @@ -4526,7 +4526,7 @@ } }, "reorderFieldDown": { - "message": "$LABEL$ moved down, position $INDEX$ of $LENGTH$", + "message": "$LABEL$ pomaknuto dolje, pozicija $INDEX$ od$LENGTH$", "placeholders": { "label": { "content": "$1", @@ -4814,7 +4814,7 @@ "message": "Postavi pravila sigurnosti koja mora zadovoljiti glavna lozinka." }, "passwordStrengthScore": { - "message": "Password strength score $SCORE$", + "message": "Ocjena jačine lozinke: $SCORE$", "placeholders": { "score": { "content": "$1", @@ -5103,14 +5103,14 @@ "message": "Pravilo neće biti primjenjeno na Vlasnike i Administratore." }, "limitSendViews": { - "message": "Limit views" + "message": "Ograniči broj pogleda" }, "limitSendViewsHint": { - "message": "No one can view this Send after the limit is reached.", + "message": "Nakon dosegnutog broja, nitko neće moći pogledati Send.", "description": "Displayed under the limit views field on Send" }, "limitSendViewsCount": { - "message": "$ACCESSCOUNT$ views left", + "message": "Preostalo pogleda: $ACCESSCOUNT$", "description": "Displayed under the limit views field on Send", "placeholders": { "accessCount": { @@ -5120,11 +5120,11 @@ } }, "sendDetails": { - "message": "Send details", + "message": "Detalji Senda", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendTypeTextToShare": { - "message": "Text to share" + "message": "Tekst za dijeljenje" }, "sendTypeFile": { "message": "Datoteka" @@ -5133,7 +5133,7 @@ "message": "Tekst" }, "sendPasswordDescV3": { - "message": "Add an optional password for recipients to access this Send.", + "message": "Dodaj neobaveznu lozinku za pristup ovom Sendu.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "createSend": { @@ -5161,14 +5161,14 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "deleteSendPermanentConfirmation": { - "message": "Are you sure you want to permanently delete this Send?", + "message": "Sigurno želiš trajno izbrisati ovaj Send?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "deletionDate": { "message": "Datum brisanja" }, "deletionDateDescV2": { - "message": "The Send will be permanently deleted on this date.", + "message": "Send će na ovaj datum biti trajno izbrisan.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "expirationDate": { @@ -5218,7 +5218,7 @@ "message": "Čeka brisanje" }, "hideTextByDefault": { - "message": "Hide text by default" + "message": "Zadano sakrij tekst" }, "expired": { "message": "Isteklo" @@ -5689,7 +5689,7 @@ "message": "Došlo je do greške kod spremanja vaših datuma isteka i brisanja." }, "hideYourEmail": { - "message": "Hide your email address from viewers." + "message": "Sakriti adresu e-pošte od primatelja." }, "webAuthnFallbackMsg": { "message": "Za ovjeru tvoje 2FA, odaberi donju tipku." @@ -5698,10 +5698,10 @@ "message": "Ovjeri WebAuthn" }, "readSecurityKey": { - "message": "Read security key" + "message": "Pročitaj sigurnosni ključ" }, "awaitingSecurityKeyInteraction": { - "message": "Awaiting security key interaction..." + "message": "Čekanje na interakciju sa sigurnosnim ključem..." }, "webAuthnNotSupported": { "message": "WebAuthn nije podržan u ovom pregledniku." @@ -6997,7 +6997,7 @@ } }, "forwaderInvalidOperation": { - "message": "$SERVICENAME$ refused your request. Please contact your service provider for assistance.", + "message": "$SERVICENAME$ je odbio tvoj zahtjev. Obrati se svom pružatelju usluga za pomoć.", "description": "Displayed when the user is forbidden from using the API by the forwarding service.", "placeholders": { "servicename": { @@ -7007,7 +7007,7 @@ } }, "forwaderInvalidOperationWithMessage": { - "message": "$SERVICENAME$ refused your request: $ERRORMESSAGE$", + "message": "$SERVICENAME$ je odbio tvoj zahtjev: $ERRORMESSAGE$", "description": "Displayed when the user is forbidden from using the API by the forwarding service with an error message.", "placeholders": { "servicename": { @@ -7250,10 +7250,10 @@ "message": "Za tvoj račun je potrebna Duo dvostruka autentifikacija." }, "duoTwoFactorRequiredPageSubtitle": { - "message": "Duo two-step login is required for your account. Follow the steps below to finish logging in." + "message": "Za tvoj je račun potrebna Duo prijava u dva koraka. Za dovršetak prijave, slijedi daljnje korake." }, "followTheStepsBelowToFinishLoggingIn": { - "message": "Follow the steps below to finish logging in." + "message": "Prati korake za dovršetak prijave." }, "launchDuo": { "message": "Pokreni Duo" @@ -9339,13 +9339,13 @@ "message": "Konfiguriraj upravljanje uređajima za Bitwarden pomoću vodiča za implementaciju za svoju platformu." }, "deviceIdMissing": { - "message": "Device ID is missing" + "message": "Nedostaje ID uređaja" }, "deviceTypeMissing": { - "message": "Device type is missing" + "message": "Nedostaje vrsta uređaja" }, "deviceCreationDateMissing": { - "message": "Device creation date is missing" + "message": "Nedostaje datum stvaranja uređaja" }, "desktopRequired": { "message": "Potrebno stolno računalo" @@ -9847,13 +9847,13 @@ "message": "Saznaj više o Bitwarden API" }, "fileSend": { - "message": "File Send" + "message": "Send datoteke" }, "fileSends": { "message": "Send datoteke" }, "textSend": { - "message": "Text Send" + "message": "Send teksta" }, "textSends": { "message": "Send tekstovi" @@ -10349,34 +10349,34 @@ "message": "Naziv organizacije ne može biti duži od 50 znakova." }, "sshKeyWrongPassword": { - "message": "The password you entered is incorrect." + "message": "Unesena lozinka nije ispravna." }, "importSshKey": { - "message": "Import" + "message": "Uvoz" }, "confirmSshKeyPassword": { - "message": "Confirm password" + "message": "Potvrdi lozinku" }, "enterSshKeyPasswordDesc": { - "message": "Enter the password for the SSH key." + "message": "Unesi lozinku za SSH ključ." }, "enterSshKeyPassword": { - "message": "Enter password" + "message": "Unesi lozinku" }, "invalidSshKey": { - "message": "The SSH key is invalid" + "message": "SSH ključ nije valjan" }, "sshKeyTypeUnsupported": { - "message": "The SSH key type is not supported" + "message": "Tip SSH ključa nije podržan" }, "importSshKeyFromClipboard": { - "message": "Import key from clipboard" + "message": "Uvezi ključ iz međuspremnika" }, "sshKeyImported": { - "message": "SSH key imported successfully" + "message": "SSH ključ uspješno uvezen" }, "copySSHPrivateKey": { - "message": "Copy private key" + "message": "Kopiraj privatni ključ" }, "openingExtension": { "message": "Otvaranje Bitwarden proširenja preglednika" @@ -10518,16 +10518,16 @@ "message": "Dodijeljene licence premašuju dostupne licence." }, "changeAtRiskPassword": { - "message": "Change at-risk password" + "message": "Promijeni rizičnu lozinku" }, "removeUnlockWithPinPolicyTitle": { - "message": "Remove Unlock with PIN" + "message": "Ukloni otključavanje PIN-om" }, "removeUnlockWithPinPolicyDesc": { - "message": "Do not allow members to unlock their account with a PIN." + "message": "Ne dozvoli članovima otključavanje računa PIN-om." }, "limitedEventLogs": { - "message": "$PRODUCT_TYPE$ plans do not have access to real event logs", + "message": "$PRODUCT_TYPE$ planovi nemaju pristup stvarnim zapisima događaja", "placeholders": { "product_type": { "content": "$1", @@ -10536,15 +10536,15 @@ } }, "upgradeForFullEvents": { - "message": "Get full access to organization event logs by upgrading to a Teams or Enterprise plan." + "message": "Omogući puni pristup zapisnicima događaja organizacije nadogradnjom na plan Teams ili Enterprise." }, "upgradeEventLogTitle": { - "message": "Upgrade for real event log data" + "message": "Nadogradi za stvarne podatke dnevnika događaja" }, "upgradeEventLogMessage": { - "message": "These events are examples only and do not reflect real events within your Bitwarden organization." + "message": "Ovi događaji su samo primjeri i ne odražavaju stvarne događaje unutar tvoje Bitwarden organizacije." }, "cannotCreateCollection": { - "message": "Free organizations may have up to 2 collections. Upgrade to a paid plan to add more collections." + "message": "Besplatne organizacije mogu imati do 2 zbirke. Nadogradi na plaćeni plan za dodavanje više zbirki." } } diff --git a/apps/web/src/locales/zh_CN/messages.json b/apps/web/src/locales/zh_CN/messages.json index 08a610c47ce..bce969ee722 100644 --- a/apps/web/src/locales/zh_CN/messages.json +++ b/apps/web/src/locales/zh_CN/messages.json @@ -10264,7 +10264,7 @@ "message": "您的账户在以下设备上登录过。" }, "claimedDomains": { - "message": "已声明的域名" + "message": "声明域名" }, "claimDomain": { "message": "声明域名" From 266d6cc8dc8a9642acd7889a114a83299b2dd3c8 Mon Sep 17 00:00:00 2001 From: "bw-ghapp[bot]" <178206702+bw-ghapp[bot]@users.noreply.github.com> Date: Fri, 21 Mar 2025 11:14:54 +0100 Subject: [PATCH 12/20] Autosync the updated translations (#13938) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/desktop/src/locales/hr/messages.json | 52 +++++++++++------------ 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/apps/desktop/src/locales/hr/messages.json b/apps/desktop/src/locales/hr/messages.json index e586b2e062e..cb66437bedd 100644 --- a/apps/desktop/src/locales/hr/messages.json +++ b/apps/desktop/src/locales/hr/messages.json @@ -649,13 +649,13 @@ "message": "Prijavi se u Bitwarden" }, "enterTheCodeSentToYourEmail": { - "message": "Enter the code sent to your email" + "message": "Unesi kôd poslan e-poštom" }, "enterTheCodeFromYourAuthenticatorApp": { - "message": "Enter the code from your authenticator app" + "message": "Unesi kôd iz svoje aplikacije za autentifikaciju" }, "pressYourYubiKeyToAuthenticate": { - "message": "Press your YubiKey to authenticate" + "message": "Za autentifikaciju dodirni svoj YubiKey" }, "logInWithPasskey": { "message": "Prijava pristupnim ključem" @@ -710,7 +710,7 @@ "message": "Podsjetnik glavne lozinke" }, "passwordStrengthScore": { - "message": "Password strength score $SCORE$", + "message": "Ocjena jačine lozinke: $SCORE$", "placeholders": { "score": { "content": "$1", @@ -825,7 +825,7 @@ "message": "Autentifikacija je otkazana ili je trajala predugo. Molimo pokušaj ponovno." }, "openInNewTab": { - "message": "Open in new tab" + "message": "Otvori u novoj kartici" }, "invalidVerificationCode": { "message": "Nevažeći kôd za provjeru" @@ -858,7 +858,7 @@ "message": "Zapamti me" }, "dontAskAgainOnThisDeviceFor30Days": { - "message": "Don't ask again on this device for 30 days" + "message": "Ne pitaj na ovom uređaju idućih 30 dana" }, "sendVerificationCodeEmailAgain": { "message": "Ponovno slanje kontrolnog koda e-poštom" @@ -867,11 +867,11 @@ "message": "Koristiti drugi način prijave dvostrukom autentifikacijom" }, "selectAnotherMethod": { - "message": "Select another method", + "message": "Odaberi drugi način", "description": "Select another two-step login method" }, "useYourRecoveryCode": { - "message": "Use your recovery code" + "message": "Koristi kôd za oporavak" }, "insertYubiKey": { "message": "Umetni svoj YubiKey u USB priključak računala, a zatim dodirni njegovu tipku." @@ -907,7 +907,7 @@ "description": "'Duo Security' and 'Duo Mobile' are product names and should not be translated." }, "verifyYourIdentity": { - "message": "Verify your Identity" + "message": "Potvrdi svoj identitet" }, "weDontRecognizeThisDevice": { "message": "Ne prepoznajemo ovaj uređaj. Za potvrdu identiteta unesi kôd poslan e-poštom." @@ -940,7 +940,7 @@ "message": "Mogućnosti prijave dvostrukom autentifikacijom" }, "selectTwoStepLoginMethod": { - "message": "Select two-step login method" + "message": "Odaberi način prijave dvostrukom autentifikacijom" }, "selfHostedEnvironment": { "message": "Vlastito hosting okruženje" @@ -998,7 +998,7 @@ "message": "Ne" }, "location": { - "message": "Location" + "message": "Lokacija" }, "overwritePassword": { "message": "Prebriši lozinku" @@ -1797,7 +1797,7 @@ "message": "Zahtijevaj lozinku ili PIN pri pokretanju" }, "requirePasswordWithoutPinOnStart": { - "message": "Require password on app start" + "message": "Zahtijevaj lozinku pri pokretanju" }, "recommendedForSecurity": { "message": "Preporučeno za sigurnost." @@ -2269,10 +2269,10 @@ "message": "Ovjeri WebAuthn" }, "readSecurityKey": { - "message": "Read security key" + "message": "Pročitaj sigurnosni ključ" }, "awaitingSecurityKeyInteraction": { - "message": "Awaiting security key interaction..." + "message": "Čekanje na interakciju sa sigurnosnim ključem..." }, "hideEmail": { "message": "Sakrij moju adresu e-pošte od primatelja." @@ -2671,7 +2671,7 @@ } }, "forwaderInvalidOperation": { - "message": "$SERVICENAME$ refused your request. Please contact your service provider for assistance.", + "message": "$SERVICENAME$ je odbio tvoj zahtjev. Obrati se svom pružatelju usluga za pomoć.", "description": "Displayed when the user is forbidden from using the API by the forwarding service.", "placeholders": { "servicename": { @@ -2681,7 +2681,7 @@ } }, "forwaderInvalidOperationWithMessage": { - "message": "$SERVICENAME$ refused your request: $ERRORMESSAGE$", + "message": "$SERVICENAME$ je odbio tvoj zahtjev: $ERRORMESSAGE$", "description": "Displayed when the user is forbidden from using the API by the forwarding service with an error message.", "placeholders": { "servicename": { @@ -3222,10 +3222,10 @@ "message": "Za tvoj račun je potrebna Duo dvostruka autentifikacija." }, "duoTwoFactorRequiredPageSubtitle": { - "message": "Duo two-step login is required for your account. Follow the steps below to finish logging in." + "message": "Za tvoj je račun potrebna Duo prijava u dva koraka. Za dovršetak prijave, slijedi daljnje korake." }, "followTheStepsBelowToFinishLoggingIn": { - "message": "Follow the steps below to finish logging in." + "message": "Prati korake za dovršetak prijave." }, "launchDuo": { "message": "Pokreni Duo u pregledniku" @@ -3485,25 +3485,25 @@ "message": "Potvrdi korištenje SSH ključa" }, "agentForwardingWarningTitle": { - "message": "Warning: Agent Forwarding" + "message": "Upozorenje: Agent proslijeđivanje" }, "agentForwardingWarningText": { - "message": "This request comes from a remote device that you are logged into" + "message": "Ovaj je zahtjev došao s prijavljenog udaljenog uređaja" }, "sshkeyApprovalMessageInfix": { "message": "traži pristup za" }, "sshkeyApprovalMessageSuffix": { - "message": "in order to" + "message": "za" }, "sshActionLogin": { - "message": "authenticate to a server" + "message": "autentifikaciju poslužitelju" }, "sshActionSign": { - "message": "sign a message" + "message": "potpis poruke" }, "sshActionGitSign": { - "message": "sign a git commit" + "message": "potpis git commita" }, "unknownApplication": { "message": "Aplikacija" @@ -3518,7 +3518,7 @@ "message": "Uvezi ključ iz međuspremnika" }, "sshKeyImported": { - "message": "SSH key imported successfully" + "message": "SSH ključ uspješno uvezen" }, "fileSavedToDevice": { "message": "Datoteka spremljena na uređaj. Upravljaj u preuzimanjima svog uređaja." @@ -3578,6 +3578,6 @@ "message": "Proširenje preglednika koje koristitš zastarjelo. Ažuriraj ga ili onemogući provjeru otiska prsta u pregledniku u postavkama aplikacije za stolno računalo." }, "changeAtRiskPassword": { - "message": "Change at-risk password" + "message": "Promijeni rizičnu lozinku" } } From a3d037de3c2efa5430b8efc588947960323a5e33 Mon Sep 17 00:00:00 2001 From: "bw-ghapp[bot]" <178206702+bw-ghapp[bot]@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:26:39 +0000 Subject: [PATCH 13/20] Autosync the updated translations (#13939) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> Co-authored-by: Daniel James Smith <2670567+djsmith85@users.noreply.github.com> --- apps/browser/src/_locales/de/messages.json | 2 +- apps/browser/src/_locales/hr/messages.json | 120 ++++++++++----------- 2 files changed, 61 insertions(+), 61 deletions(-) diff --git a/apps/browser/src/_locales/de/messages.json b/apps/browser/src/_locales/de/messages.json index 4df2fd25b3c..a7439db6432 100644 --- a/apps/browser/src/_locales/de/messages.json +++ b/apps/browser/src/_locales/de/messages.json @@ -1668,7 +1668,7 @@ "message": "Zum Sortieren ziehen" }, "dragToReorder": { - "message": "Ziehen zum umsortieren" + "message": "Ziehen zum Umsortieren" }, "cfTypeText": { "message": "Text" diff --git a/apps/browser/src/_locales/hr/messages.json b/apps/browser/src/_locales/hr/messages.json index 400e70b864f..b88fc45493f 100644 --- a/apps/browser/src/_locales/hr/messages.json +++ b/apps/browser/src/_locales/hr/messages.json @@ -81,7 +81,7 @@ "message": "Podsjetnik glavne lozinke (neobavezno)" }, "passwordStrengthScore": { - "message": "Password strength score $SCORE$", + "message": "Ocjena jačine lozinke: $SCORE$", "placeholders": { "score": { "content": "$1", @@ -186,7 +186,7 @@ "message": "Kopiraj bilješke" }, "copy": { - "message": "Copy", + "message": "Kopiraj", "description": "Copy to clipboard" }, "fill": { @@ -380,7 +380,7 @@ "message": "Uredi mapu" }, "editFolderWithName": { - "message": "Edit folder: $FOLDERNAME$", + "message": "Uredi mapu: $FOLDERNAME$", "placeholders": { "foldername": { "content": "$1", @@ -653,7 +653,7 @@ "message": "Web preglednik ne podržava jednostavno kopiranje međuspremnika. Umjesto toga ručno kopirajte." }, "verifyYourIdentity": { - "message": "Verify your identity" + "message": "Potvrdi svoj identitet" }, "weDontRecognizeThisDevice": { "message": "Ne prepoznajemo ovaj uređaj. Za potvrdu identiteta unesi kôd poslan e-poštom." @@ -869,19 +869,19 @@ "message": "Prijavi se u Bitwarden" }, "enterTheCodeSentToYourEmail": { - "message": "Enter the code sent to your email" + "message": "Unesi kôd poslan e-poštom" }, "enterTheCodeFromYourAuthenticatorApp": { - "message": "Enter the code from your authenticator app" + "message": "Unesi kôd iz svoje aplikacije za autentifikaciju" }, "pressYourYubiKeyToAuthenticate": { - "message": "Press your YubiKey to authenticate" + "message": "Za autentifikaciju dodirni svoj YubiKey" }, "duoTwoFactorRequiredPageSubtitle": { - "message": "Duo two-step login is required for your account. Follow the steps below to finish logging in." + "message": "Za tvoj je račun potrebna Duo prijava u dva koraka. Za dovršetak prijave, slijedi daljnje korake." }, "followTheStepsBelowToFinishLoggingIn": { - "message": "Follow the steps below to finish logging in." + "message": "Prati korake za dovršetak prijave." }, "restartRegistration": { "message": "Ponovno pokreni registraciju" @@ -905,7 +905,7 @@ "message": "Ne" }, "location": { - "message": "Location" + "message": "Lokacija" }, "unexpectedError": { "message": "Došlo je do neočekivane pogreške." @@ -1040,7 +1040,7 @@ "message": "Klikni stavke za auto-ispunu na prikazu trezora" }, "clickToAutofill": { - "message": "Click items in autofill suggestion to fill" + "message": "Kliknite stavku u prijedlogu auto-ispune za popunjavanje" }, "clearClipboard": { "message": "Očisti međuspremnik", @@ -1057,7 +1057,7 @@ "message": "Spremi" }, "loginSaveSuccessDetails": { - "message": "$USERNAME$ saved to Bitwarden.", + "message": "$USERNAME$ spremljeno u Bitwarden.", "placeholders": { "username": { "content": "$1" @@ -1066,7 +1066,7 @@ "description": "Shown to user after login is saved." }, "loginUpdatedSuccessDetails": { - "message": "$USERNAME$ updated in Bitwarden.", + "message": "$USERNAME$ ažurirano u Bitwardenu.", "placeholders": { "username": { "content": "$1" @@ -1075,35 +1075,35 @@ "description": "Shown to user after login is updated." }, "saveAsNewLoginAction": { - "message": "Save as new login", + "message": "Spremi novu prijavu", "description": "Button text for saving login details as a new entry." }, "updateLoginAction": { - "message": "Update login", + "message": "Ažuriraj prijavu", "description": "Button text for updating an existing login entry." }, "saveLoginPrompt": { - "message": "Save login?", + "message": "Spremiti prijavu?", "description": "Prompt asking the user if they want to save their login details." }, "updateLoginPrompt": { - "message": "Update existing login?", + "message": "Ažurirati postojeću prijavu?", "description": "Prompt asking the user if they want to update an existing login entry." }, "loginSaveSuccess": { - "message": "Login saved", + "message": "Prijava spremljena", "description": "Message displayed when login details are successfully saved." }, "loginUpdateSuccess": { - "message": "Login updated", + "message": "Prijava ažurirana", "description": "Message displayed when login details are successfully updated." }, "saveFailure": { - "message": "Error saving", + "message": "Greška kod spremanja", "description": "Error message shown when the system fails to save login details." }, "saveFailureDetails": { - "message": "Oh no! We couldn't save this. Try entering the details manually.", + "message": "Ups! Nismo mogli ovo spasiti. Pokušaj ručno unijeti detalje.", "description": "Detailed error message shown when saving login details fails." }, "enableChangedPasswordNotification": { @@ -1422,7 +1422,7 @@ "message": "Zapamti me" }, "dontAskAgainOnThisDeviceFor30Days": { - "message": "Don't ask again on this device for 30 days" + "message": "Ne pitaj na ovom uređaju idućih 30 dana" }, "sendVerificationCodeEmailAgain": { "message": "Ponovno slanje kontrolnog koda e-poštom" @@ -1431,11 +1431,11 @@ "message": "Koristiti drugi način prijave dvostrukom autentifikacijom" }, "selectAnotherMethod": { - "message": "Select another method", + "message": "Odaberi drugi način", "description": "Select another two-step login method" }, "useYourRecoveryCode": { - "message": "Use your recovery code" + "message": "Koristi kôd za oporavak" }, "insertYubiKey": { "message": "Umetni svoj YubiKey u USB priključak računala, a zatim dodirni njegovu tipku." @@ -1450,16 +1450,16 @@ "message": "Otvori novu karticu" }, "openInNewTab": { - "message": "Open in new tab" + "message": "Otvori u novoj kartici" }, "webAuthnAuthenticate": { "message": "Ovjeri WebAuthn" }, "readSecurityKey": { - "message": "Read security key" + "message": "Pročitaj sigurnosni ključ" }, "awaitingSecurityKeyInteraction": { - "message": "Awaiting security key interaction..." + "message": "Čekanje na interakciju sa sigurnosnim ključem..." }, "loginUnavailable": { "message": "Prijava nije dostupna" @@ -1474,7 +1474,7 @@ "message": "Mogućnosti prijave dvostrukom autentifikacijom" }, "selectTwoStepLoginMethod": { - "message": "Select two-step login method" + "message": "Odaberi način prijave dvostrukom autentifikacijom" }, "recoveryCodeDesc": { "message": "Izgubljen je pristup uređaju za dvostruku autentifikaciju? Koristi svoj kôd za oporavak za onemogućavanje svih pružatelja usluga dvostruke autentifikacije na tvojem računu." @@ -1668,7 +1668,7 @@ "message": "Povuci za sortiranje" }, "dragToReorder": { - "message": "Drag to reorder" + "message": "Povuci za premještanje" }, "cfTypeText": { "message": "Tekst" @@ -2164,7 +2164,7 @@ "description": "This will be used as part of a larger sentence, broken up to include the generator icon. The full sentence will read 'Use the generator [GENERATOR_ICON] to create a strong unique password'" }, "vaultCustomization": { - "message": "Vault customization" + "message": "Prilagodba trezora" }, "vaultTimeoutAction": { "message": "Nakon isteka trezora" @@ -2173,13 +2173,13 @@ "message": "Radnja nakon isteka " }, "newCustomizationOptionsCalloutTitle": { - "message": "New customization options" + "message": "Nove mogućnosti prilagodbe" }, "newCustomizationOptionsCalloutContent": { - "message": "Customize your vault experience with quick copy actions, compact mode, and more!" + "message": "Prilagodi svoje iskustvo trezora brzim kopiranjem, kompaktnim načinom rada i više!" }, "newCustomizationOptionsCalloutLink": { - "message": "View all Appearance settings" + "message": "Pogledaj sve postavke izgleda" }, "lock": { "message": "Zaključaj", @@ -2476,7 +2476,7 @@ "message": "Rizične lozinke" }, "atRiskPasswordDescSingleOrg": { - "message": "$ORGANIZATION$ is requesting you change one password because it is at-risk.", + "message": "$ORGANIZATION$ traži da promijeniš jednu rizičnu lozinku.", "placeholders": { "organization": { "content": "$1", @@ -2485,7 +2485,7 @@ } }, "atRiskPasswordsDescSingleOrgPlural": { - "message": "$ORGANIZATION$ is requesting you change the $COUNT$ passwords because they are at-risk.", + "message": "Broj rizičnih lozinki koje $ORGANIZATION$ traži da promijeniš: $COUNT$.", "placeholders": { "organization": { "content": "$1", @@ -2498,7 +2498,7 @@ } }, "atRiskPasswordsDescMultiOrgPlural": { - "message": "Your organizations are requesting you change the $COUNT$ passwords because they are at-risk.", + "message": "Broj rizičnih lozinki koje tvoja orgnaizacija traži da promijeniš: $COUNT$.", "placeholders": { "count": { "content": "$1", @@ -2525,34 +2525,34 @@ "message": "Ažuriraj svoje postavke kako za brzu auto-ispunu svojih lozinki i generiranje novih" }, "reviewAtRiskLogins": { - "message": "Review at-risk logins" + "message": "Pregledaj rizične prijave" }, "reviewAtRiskPasswords": { - "message": "Review at-risk passwords" + "message": "Pregdledaj rizične lozinke" }, "reviewAtRiskLoginsSlideDesc": { - "message": "Your organization passwords are at-risk because they are weak, reused, and/or exposed.", + "message": "Lozinke tvoje organizacije su rizične jer su slabe, nanovo korištene i/ili iscurile.", "description": "Description of the review at-risk login slide on the at-risk password page carousel" }, "reviewAtRiskLoginSlideImgAlt": { - "message": "Illustration of a list of logins that are at-risk" + "message": "Ilustracija liste rizičnih prijava" }, "generatePasswordSlideDesc": { - "message": "Quickly generate a strong, unique password with the Bitwarden autofill menu on the at-risk site.", + "message": "Brzo generiraj jake, jedinstvene lozinke koristeći Bitwarden dijalog auto-ispune direktno na stranici.", "description": "Description of the generate password slide on the at-risk password page carousel" }, "generatePasswordSlideImgAlt": { - "message": "Illustration of the Bitwarden autofill menu displaying a generated password" + "message": "Ilustracija Bitwarden dijalog auto-ispune s prikazom generirane lozinke" }, "updateInBitwarden": { - "message": "Update in Bitwarden" + "message": "Ažuriraj u Bitwardenu" }, "updateInBitwardenSlideDesc": { - "message": "Bitwarden will then prompt you to update the password in the password manager.", + "message": "Bitwarden će te pitati treba li ažurirati lozinku u upravitelju lozinki.", "description": "Description of the update in Bitwarden slide on the at-risk password page carousel" }, "updateInBitwardenSlideImgAlt": { - "message": "Illustration of a Bitwarden’s notification prompting the user to update the login" + "message": "Ilustracija Bitwarden upita za ažuriranje prijave" }, "turnOnAutofill": { "message": "Uključi auto-ispunu" @@ -3168,7 +3168,7 @@ } }, "forwaderInvalidOperation": { - "message": "$SERVICENAME$ refused your request. Please contact your service provider for assistance.", + "message": "$SERVICENAME$ je odbio tvoj zahtjev. Obrati se svom pružatelju usluga za pomoć.", "description": "Displayed when the user is forbidden from using the API by the forwarding service.", "placeholders": { "servicename": { @@ -3178,7 +3178,7 @@ } }, "forwaderInvalidOperationWithMessage": { - "message": "$SERVICENAME$ refused your request: $ERRORMESSAGE$", + "message": "$SERVICENAME$ je odbio tvoj zahtjev: $ERRORMESSAGE$", "description": "Displayed when the user is forbidden from using the API by the forwarding service with an error message.", "placeholders": { "servicename": { @@ -4080,7 +4080,7 @@ "message": "Aktivni račun" }, "bitwardenAccount": { - "message": "Bitwarden account" + "message": "Bitwarden račun" }, "availableAccounts": { "message": "Dostupni računi" @@ -4281,7 +4281,7 @@ } }, "copyFieldValue": { - "message": "Copy $FIELD$, $VALUE$", + "message": "Kopiraj $FIELD$, $VALUE$", "description": "Title for a button that copies a field value to the clipboard.", "placeholders": { "field": { @@ -4671,7 +4671,7 @@ } }, "reorderWebsiteUriButton": { - "message": "Reorder website URI. Use arrow key to move item up or down." + "message": "Ponovno poredaj URI. Koristi tipke sa strelicom za pomicanje stavke gore ili dolje." }, "reorderFieldUp": { "message": "$LABEL$ pomaknut gore, pozicija $INDEX$ od $LENGTH$", @@ -5096,31 +5096,31 @@ "message": "Ekstra široko" }, "sshKeyWrongPassword": { - "message": "The password you entered is incorrect." + "message": "Unesena lozinka nije ispravna." }, "importSshKey": { - "message": "Import" + "message": "Uvoz" }, "confirmSshKeyPassword": { - "message": "Confirm password" + "message": "Potvrdi lozinku" }, "enterSshKeyPasswordDesc": { - "message": "Enter the password for the SSH key." + "message": "Unesi lozinku za SSH ključ." }, "enterSshKeyPassword": { - "message": "Enter password" + "message": "Unesi lozinku" }, "invalidSshKey": { - "message": "The SSH key is invalid" + "message": "SSH ključ nije valjan" }, "sshKeyTypeUnsupported": { - "message": "The SSH key type is not supported" + "message": "Tip SSH ključa nije podržan" }, "importSshKeyFromClipboard": { - "message": "Import key from clipboard" + "message": "Uvezi ključ iz međuspremnika" }, "sshKeyImported": { - "message": "SSH key imported successfully" + "message": "SSH ključ uspješno uvezen" }, "cannotRemoveViewOnlyCollections": { "message": "S dopuštenjima samo za prikaz ne možeš ukloniti zbirke: $COLLECTIONS$", @@ -5138,6 +5138,6 @@ "message": "Za korištenje biometrijskog otključavanja ažuriraj desktop aplikaciju ili nemogući otključavanje otiskom prsta u desktop aplikaciji." }, "changeAtRiskPassword": { - "message": "Change at-risk password" + "message": "Promijeni rizičnu lozinku" } } From 841e5980d728fd49cc9a1a43f524c8bc3d757e6c Mon Sep 17 00:00:00 2001 From: Patrick-Pimentel-Bitwarden Date: Fri, 21 Mar 2025 10:22:37 -0400 Subject: [PATCH 14/20] fix(device-approval-login): [PM-19379] Approve Device Login - Fix the call so that it works when not in a TDE scenario. (#13934) --- .../login-via-auth-request.component.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/libs/auth/src/angular/login-via-auth-request/login-via-auth-request.component.ts b/libs/auth/src/angular/login-via-auth-request/login-via-auth-request.component.ts index bb2822d67e9..ea753a3f0c5 100644 --- a/libs/auth/src/angular/login-via-auth-request/login-via-auth-request.component.ts +++ b/libs/auth/src/angular/login-via-auth-request/login-via-auth-request.component.ts @@ -187,9 +187,7 @@ export class LoginViaAuthRequestComponent implements OnInit, OnDestroy { private async initStandardAuthRequestFlow(): Promise { this.flow = Flow.StandardAuthRequest; - this.email = await firstValueFrom( - this.accountService.activeAccount$.pipe(map((a) => a?.email)), - ); + this.email = (await firstValueFrom(this.loginEmailService.loginEmail$)) || undefined; if (!this.email) { await this.handleMissingEmail(); From 1c3084eef465676c1b1fd733cefad72edb376d56 Mon Sep 17 00:00:00 2001 From: "bw-ghapp[bot]" <178206702+bw-ghapp[bot]@users.noreply.github.com> Date: Mon, 24 Mar 2025 10:27:12 +0100 Subject: [PATCH 15/20] Autosync the updated translations (#13960) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/desktop/src/locales/pl/messages.json | 18 +++++++++--------- apps/desktop/src/locales/zh_CN/messages.json | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/desktop/src/locales/pl/messages.json b/apps/desktop/src/locales/pl/messages.json index f78313c7fd6..ef8cdc6eb9b 100644 --- a/apps/desktop/src/locales/pl/messages.json +++ b/apps/desktop/src/locales/pl/messages.json @@ -907,7 +907,7 @@ "description": "'Duo Security' and 'Duo Mobile' are product names and should not be translated." }, "verifyYourIdentity": { - "message": "Verify your Identity" + "message": "Potwierdź swoją tożsamość" }, "weDontRecognizeThisDevice": { "message": "Nie rozpoznajemy tego urządzenia. Wpisz kod wysłany na Twój e-mail, aby zweryfikować tożsamość." @@ -998,7 +998,7 @@ "message": "Nie" }, "location": { - "message": "Location" + "message": "Lokalizacja" }, "overwritePassword": { "message": "Zastąp hasło" @@ -3488,22 +3488,22 @@ "message": "Warning: Agent Forwarding" }, "agentForwardingWarningText": { - "message": "This request comes from a remote device that you are logged into" + "message": "To żądanie pochodzi ze zdalnego urządzenia, do którego jesteś zalogowany" }, "sshkeyApprovalMessageInfix": { "message": "wnioskuje o dostęp do" }, "sshkeyApprovalMessageSuffix": { - "message": "in order to" + "message": "w celu" }, "sshActionLogin": { - "message": "authenticate to a server" + "message": "autoryzacji na serwerze" }, "sshActionSign": { - "message": "sign a message" + "message": "podpisania wiadomości" }, "sshActionGitSign": { - "message": "sign a git commit" + "message": "podpisania commita w giciem" }, "unknownApplication": { "message": "Aplikacja" @@ -3518,7 +3518,7 @@ "message": "Importuj klucz ze schowka" }, "sshKeyImported": { - "message": "SSH key imported successfully" + "message": "Klucz SSH zaimportowano pomyślnie" }, "fileSavedToDevice": { "message": "Plik zapisany na urządzeniu. Zarządzaj plikiem na swoim urządzeniu." @@ -3578,6 +3578,6 @@ "message": "Rozszerzenie przeglądarki, którego używasz, jest nieaktualne. Zaktualizuj je lub wyłącz weryfikację odcisku palca integracji przeglądarki w ustawieniach aplikacji desktopowej." }, "changeAtRiskPassword": { - "message": "Change at-risk password" + "message": "Zmień zagrożone hasło" } } diff --git a/apps/desktop/src/locales/zh_CN/messages.json b/apps/desktop/src/locales/zh_CN/messages.json index 8bf4d16c474..6a12138b48f 100644 --- a/apps/desktop/src/locales/zh_CN/messages.json +++ b/apps/desktop/src/locales/zh_CN/messages.json @@ -2470,7 +2470,7 @@ "message": "切换账户" }, "alreadyHaveAccount": { - "message": "已经拥有账户了吗?" + "message": "已经有账户了吗?" }, "options": { "message": "选项" From 714a3f33e40563ebc5734a10f9af0a3ba5d1cdb1 Mon Sep 17 00:00:00 2001 From: "bw-ghapp[bot]" <178206702+bw-ghapp[bot]@users.noreply.github.com> Date: Mon, 24 Mar 2025 10:28:16 +0100 Subject: [PATCH 16/20] Autosync the updated translations (#13961) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/browser/src/_locales/id/messages.json | 64 +++++++++---------- apps/browser/src/_locales/nl/messages.json | 36 +++++------ apps/browser/src/_locales/zh_CN/messages.json | 6 +- 3 files changed, 53 insertions(+), 53 deletions(-) diff --git a/apps/browser/src/_locales/id/messages.json b/apps/browser/src/_locales/id/messages.json index 33f44c60579..0146fb2a000 100644 --- a/apps/browser/src/_locales/id/messages.json +++ b/apps/browser/src/_locales/id/messages.json @@ -186,7 +186,7 @@ "message": "Salin catatan" }, "copy": { - "message": "Copy", + "message": "Salin", "description": "Copy to clipboard" }, "fill": { @@ -380,7 +380,7 @@ "message": "Sunting Folder" }, "editFolderWithName": { - "message": "Edit folder: $FOLDERNAME$", + "message": "Sunting folder: $FOLDERNAME$", "placeholders": { "foldername": { "content": "$1", @@ -462,16 +462,16 @@ "message": "Buat frasa sandi" }, "passwordGenerated": { - "message": "Password generated" + "message": "Kata sandi dibuat" }, "passphraseGenerated": { - "message": "Passphrase generated" + "message": "Frasa sandi dibuat" }, "usernameGenerated": { - "message": "Username generated" + "message": "Nama pengguna dibuat" }, "emailGenerated": { - "message": "Email generated" + "message": "Surel dibuat" }, "regeneratePassword": { "message": "Buat Ulang Kata Sandi" @@ -653,7 +653,7 @@ "message": "Peramban Anda tidak mendukung menyalin clipboard dengan mudah. Salin secara manual." }, "verifyYourIdentity": { - "message": "Verify your identity" + "message": "Verifikasikan identitas Anda" }, "weDontRecognizeThisDevice": { "message": "We don't recognize this device. Enter the code sent to your email to verify your identity." @@ -905,7 +905,7 @@ "message": "Tidak" }, "location": { - "message": "Location" + "message": "Lokasi" }, "unexpectedError": { "message": "Terjadi kesalahan yang tak diduga." @@ -2461,7 +2461,7 @@ "message": "Change this in settings" }, "change": { - "message": "Change" + "message": "Ubah" }, "changeButtonTitle": { "message": "Change password - $ITEMNAME$", @@ -4784,7 +4784,7 @@ "message": "Text Sends" }, "accountActions": { - "message": "Account actions" + "message": "Tindakan akun" }, "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" @@ -4793,22 +4793,22 @@ "message": "Show quick copy actions on Vault" }, "systemDefault": { - "message": "System default" + "message": "Baku sistem" }, "enterprisePolicyRequirementsApplied": { - "message": "Enterprise policy requirements have been applied to this setting" + "message": "Persyaratan kebijakan perusahaan telah diterapkan ke pengaturan ini" }, "sshPrivateKey": { - "message": "Private key" + "message": "Kunci privat" }, "sshPublicKey": { - "message": "Public key" + "message": "Kunci publik" }, "sshFingerprint": { - "message": "Fingerprint" + "message": "Sidik jari" }, "sshKeyAlgorithm": { - "message": "Key type" + "message": "Tipe kunci" }, "sshKeyAlgorithmED25519": { "message": "ED25519" @@ -4823,7 +4823,7 @@ "message": "RSA 4096-Bit" }, "retry": { - "message": "Retry" + "message": "Coba lagi" }, "vaultCustomTimeoutMinimum": { "message": "Minimum custom timeout is 1 minute." @@ -5075,16 +5075,16 @@ } }, "newDeviceVerificationNoticePageOneEmailAccessNo": { - "message": "No, I do not" + "message": "Tidak" }, "newDeviceVerificationNoticePageOneEmailAccessYes": { - "message": "Yes, I can reliably access my email" + "message": "Ya, saya dapat mengakses surel saya secara handla" }, "turnOnTwoStepLogin": { "message": "Turn on two-step login" }, "changeAcctEmail": { - "message": "Change account email" + "message": "Ubah surel akun" }, "extensionWidth": { "message": "Lebar ekstensi" @@ -5096,31 +5096,31 @@ "message": "Ekstra lebar" }, "sshKeyWrongPassword": { - "message": "The password you entered is incorrect." + "message": "Kata sandi yang Anda masukkan tidak benar." }, "importSshKey": { - "message": "Import" + "message": "Impor" }, "confirmSshKeyPassword": { - "message": "Confirm password" + "message": "Konfirmasi kata sandi" }, "enterSshKeyPasswordDesc": { - "message": "Enter the password for the SSH key." + "message": "Masukkan kata sandi untuk kunci SSH." }, "enterSshKeyPassword": { - "message": "Enter password" + "message": "Masukkan kata sandi" }, "invalidSshKey": { - "message": "The SSH key is invalid" + "message": "Kunci SSH tidak valid" }, "sshKeyTypeUnsupported": { - "message": "The SSH key type is not supported" + "message": "Tipe kunci SSH tidak didukung" }, "importSshKeyFromClipboard": { - "message": "Import key from clipboard" + "message": "Impor kunci dari papan klip" }, "sshKeyImported": { - "message": "SSH key imported successfully" + "message": "Kunci SSH sukses diimpor" }, "cannotRemoveViewOnlyCollections": { "message": "Anda tidak dapat menghapus koleksi dengan izin hanya lihat: $COLLECTIONS$", @@ -5132,12 +5132,12 @@ } }, "updateDesktopAppOrDisableFingerprintDialogTitle": { - "message": "Please update your desktop application" + "message": "Harap perbarui aplikasi desktop Anda" }, "updateDesktopAppOrDisableFingerprintDialogMessage": { - "message": "To use biometric unlock, please update your desktop application, or disable fingerprint unlock in the desktop settings." + "message": "Untuk memakai pembuka kunci biometrik, harap perbarui aplikasi desktop Anda, atau matikan buka kunci sidik jari dalam pengaturan desktop." }, "changeAtRiskPassword": { - "message": "Change at-risk password" + "message": "Ubah kata sandi yang berrisiko" } } diff --git a/apps/browser/src/_locales/nl/messages.json b/apps/browser/src/_locales/nl/messages.json index 65d8d6f740f..1c2b09ca3e3 100644 --- a/apps/browser/src/_locales/nl/messages.json +++ b/apps/browser/src/_locales/nl/messages.json @@ -81,7 +81,7 @@ "message": "Hoofdwachtwoordhint (optioneel)" }, "passwordStrengthScore": { - "message": "Score wachtwoordsterkte $SCORE$", + "message": "Wachtwoordsterkte score $SCORE$", "placeholders": { "score": { "content": "$1", @@ -878,10 +878,10 @@ "message": "Druk op je YubiKey om te verifiëren" }, "duoTwoFactorRequiredPageSubtitle": { - "message": "Jouw account vereist Duo-tweestapsaanmelding. Volg de onderstaande stappen om het inloggen te voltooien." + "message": "Jouw account vereist Duo tweestapslogin. Volg de onderstaande stappen om het inloggen te voltooien." }, "followTheStepsBelowToFinishLoggingIn": { - "message": "Volg de onderstaande stappen om in te loggen." + "message": "Volg de onderstaande stappen om het inloggen af te ronden." }, "restartRegistration": { "message": "Registratie herstarten" @@ -1040,7 +1040,7 @@ "message": "Klik op items om automatisch in te vullen op de kluisweergave" }, "clickToAutofill": { - "message": "Klik op gesuggereerde items om deze automatisch invullen" + "message": "Klik items in de automatisch invullen suggestie om in te vullen" }, "clearClipboard": { "message": "Klembord wissen", @@ -1075,7 +1075,7 @@ "description": "Shown to user after login is updated." }, "saveAsNewLoginAction": { - "message": "Als nieuwe login opslaan", + "message": "Opslaan als nieuwe login", "description": "Button text for saving login details as a new entry." }, "updateLoginAction": { @@ -1422,7 +1422,7 @@ "message": "Mijn gegevens onthouden" }, "dontAskAgainOnThisDeviceFor30Days": { - "message": "30 dagen niet meer vragen op dit apparaat" + "message": "30 dagen niet opnieuw vragen op dit apparaat" }, "sendVerificationCodeEmailAgain": { "message": "E-mail met verificatiecode opnieuw versturen" @@ -1459,7 +1459,7 @@ "message": "Beveiligingssleutel lezen" }, "awaitingSecurityKeyInteraction": { - "message": "Wacht op interactie met beveiligingssleutel..." + "message": "Wacht op interactie met beveiligingssleutel…" }, "loginUnavailable": { "message": "Login niet beschikbaar" @@ -1474,7 +1474,7 @@ "message": "Opties voor tweestapsaanmelding" }, "selectTwoStepLoginMethod": { - "message": "Kies methode voor tweestapsaanmelding" + "message": "Kies methode voor tweestapslogin" }, "recoveryCodeDesc": { "message": "Ben je de toegang tot al je tweestapsaanbieders verloren? Gebruik dan je herstelcode om alle tweestapsaanbieders op je account uit te schakelen." @@ -2164,7 +2164,7 @@ "description": "This will be used as part of a larger sentence, broken up to include the generator icon. The full sentence will read 'Use the generator [GENERATOR_ICON] to create a strong unique password'" }, "vaultCustomization": { - "message": "Kluis-aanpassingen" + "message": "Kluis aanpassingen" }, "vaultTimeoutAction": { "message": "Actie bij time-out" @@ -2176,10 +2176,10 @@ "message": "Nieuwe aanpassingsopties" }, "newCustomizationOptionsCalloutContent": { - "message": "Personaliseer je kluiservaring met snelle kopieeracties, compacte modus en meer!" + "message": "Pas je kluis ervaring aan met snelle kopieeracties, compacte modus en meer!" }, "newCustomizationOptionsCalloutLink": { - "message": "Alle personalisatie-instellingen bekijken" + "message": "Alle weergave-instellingen bekijken" }, "lock": { "message": "Vergrendelen", @@ -2498,7 +2498,7 @@ } }, "atRiskPasswordsDescMultiOrgPlural": { - "message": "Je organisatie(s) vragen je de $COUNT$ wachtwoorden te wijzigen omdat ze een risico vormen.", + "message": "Je organisaties vragen je de $COUNT$ wachtwoorden te wijzigen omdat ze een risico vormen.", "placeholders": { "count": { "content": "$1", @@ -2531,14 +2531,14 @@ "message": "Risicovolle wachtwoorden bekijken" }, "reviewAtRiskLoginsSlideDesc": { - "message": "De wachtwoorden van je organisatie zijn in gevaar omdat ze zwak, hergebruikt en/of blootgelegd zijn.", + "message": "De wachtwoorden van je organisatie zijn in gevaar omdat ze zwak, hergebruikt en/of gelekt zijn.", "description": "Description of the review at-risk login slide on the at-risk password page carousel" }, "reviewAtRiskLoginSlideImgAlt": { "message": "Voorbeeld van een lijst van risicovolle logins" }, "generatePasswordSlideDesc": { - "message": "Genereer snel een sterk, uniek wachtwoord met het automatisch invulmenu van Bitwaren op de risicovolle website.", + "message": "Genereer snel een sterk, uniek wachtwoord met het automatisch invulmenu van Bitwarden op de risicovolle website.", "description": "Description of the generate password slide on the at-risk password page carousel" }, "generatePasswordSlideImgAlt": { @@ -2552,7 +2552,7 @@ "description": "Description of the update in Bitwarden slide on the at-risk password page carousel" }, "updateInBitwardenSlideImgAlt": { - "message": "Voorbeeld van een Bitwarden-melding die de gebruiker aanspoort tot het bijwerken van de login" + "message": "Voorbeeld van een Bitwarden melding die de gebruiker aanspoort tot het bijwerken van de login" }, "turnOnAutofill": { "message": "Automatisch invullen inschakelen" @@ -4080,7 +4080,7 @@ "message": "Actief account" }, "bitwardenAccount": { - "message": "Bitwarden-account" + "message": "Bitwarden account" }, "availableAccounts": { "message": "Beschikbare accounts" @@ -5096,7 +5096,7 @@ "message": "Extra breed" }, "sshKeyWrongPassword": { - "message": "Het door jou ingevoerde wachtwoord is onjuist." + "message": "Het wachtwoord dat je hebt ingevoerd is onjuist." }, "importSshKey": { "message": "Importeren" @@ -5117,7 +5117,7 @@ "message": "Het type SSH-sleutel is niet ondersteund" }, "importSshKeyFromClipboard": { - "message": "Sleutel van klembord importeren" + "message": "Sleutel importeren van klembord" }, "sshKeyImported": { "message": "SSH-sleutel succesvol geïmporteerd" diff --git a/apps/browser/src/_locales/zh_CN/messages.json b/apps/browser/src/_locales/zh_CN/messages.json index 4595b20ddbf..bdfaeb92531 100644 --- a/apps/browser/src/_locales/zh_CN/messages.json +++ b/apps/browser/src/_locales/zh_CN/messages.json @@ -2208,7 +2208,7 @@ "message": "项目已恢复" }, "alreadyHaveAccount": { - "message": "已经拥有账户了吗?" + "message": "已经有账户了吗?" }, "vaultTimeoutLogOutConfirmation": { "message": "超时后注销账户将解除对密码库的所有访问权限,并需要进行在线身份验证。确定使用此设置吗?" @@ -3628,7 +3628,7 @@ "message": "正在获取选项..." }, "multiSelectNotFound": { - "message": "未找到任何条目" + "message": "未找到任何项目" }, "multiSelectClearAll": { "message": "清除全部" @@ -4209,7 +4209,7 @@ "message": "建议的项目" }, "autofillSuggestionsTip": { - "message": "将此站点保存为登录项目以用于自动填充" + "message": "为这个站点保存一个登录项目以自动填充" }, "yourVaultIsEmpty": { "message": "您的密码库是空的" From d4116c05d7ac03ae474ba93aaad052a5c2718a34 Mon Sep 17 00:00:00 2001 From: "bw-ghapp[bot]" <178206702+bw-ghapp[bot]@users.noreply.github.com> Date: Mon, 24 Mar 2025 10:28:51 +0100 Subject: [PATCH 17/20] Autosync the updated translations (#13962) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/web/src/locales/nl/messages.json | 10 +++++----- apps/web/src/locales/zh_CN/messages.json | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/web/src/locales/nl/messages.json b/apps/web/src/locales/nl/messages.json index d193b2496f3..6b752b5a195 100644 --- a/apps/web/src/locales/nl/messages.json +++ b/apps/web/src/locales/nl/messages.json @@ -9847,13 +9847,13 @@ "message": "Lees meer over Bitwarden's API" }, "fileSend": { - "message": "Bestand verzenden" + "message": "Bestand Send" }, "fileSends": { "message": "Bestand-Sends" }, "textSend": { - "message": "Tekst-Sends" + "message": "Tekst Send" }, "textSends": { "message": "Tekst-Sends" @@ -10382,7 +10382,7 @@ "message": "Bitwarden-browserextensie openen" }, "somethingWentWrong": { - "message": "Er is iets fout gegaan..." + "message": "Er is iets fout gegaan…" }, "openingExtensionError": { "message": "We konden de Bitwarden-browserextensie niet openen. Klik op de knop om deze nu te openen." @@ -10407,7 +10407,7 @@ "description": "This will be used as part of a larger sentence, broken up to include the Bitwarden icon. The full sentence will read 'We had trouble opening the Bitwarden browser extension. Open the Bitwarden icon [Bitwarden Icon] from the toolbar.'" }, "openExtensionManuallyPart2": { - "message": "vanaf de werkbank.", + "message": "vanaf de werkbalk.", "description": "This will be used as part of a larger sentence, broken up to include the Bitwarden icon. The full sentence will read 'We had trouble opening the Bitwarden browser extension. Open the Bitwarden icon [Bitwarden Icon] from the toolbar.'" }, "resellerRenewalWarningMsg": { @@ -10542,7 +10542,7 @@ "message": "Upgrade voor echte event log gegevens" }, "upgradeEventLogMessage": { - "message": "Deze events zijn voorbeelden en weerspiegelen geen echte evenementen binnen je Bitwarden-organisatie." + "message": "Deze evenementen zijn alleen voorbeelden en weerspiegelen geen echte evenementen binnen je Bitwarden organisatie." }, "cannotCreateCollection": { "message": "Gratis organisaties kunnen maximaal twee collecties hebben. Upgrade naar een betaald abonnement voor het toevoegen van meer collecties." diff --git a/apps/web/src/locales/zh_CN/messages.json b/apps/web/src/locales/zh_CN/messages.json index bce969ee722..00bf9bc5973 100644 --- a/apps/web/src/locales/zh_CN/messages.json +++ b/apps/web/src/locales/zh_CN/messages.json @@ -2500,7 +2500,7 @@ } }, "noInactive2fa": { - "message": "没有在您的密码库发现未配置两步登录的网站。" + "message": "在您的密码库中没有发现未配置两步登录的网站。" }, "instructions": { "message": "说明" @@ -2596,7 +2596,7 @@ } }, "noReusedPasswords": { - "message": "您密码库中没有密码重复使用的项目。" + "message": "您密码库中没有密码重复使用的登录项目。" }, "timesReused": { "message": "重复使用次数" @@ -7921,7 +7921,7 @@ } }, "domainNotVerifiedEvent": { - "message": "$DOMAIN$ 未验证", + "message": "$DOMAIN$ 无法验证", "placeholders": { "DOMAIN": { "content": "$1", @@ -8779,7 +8779,7 @@ "description": "Label for field requesting a self-hosted integration service URL" }, "alreadyHaveAccount": { - "message": "已经拥有账户了吗?" + "message": "已经有账户了吗?" }, "toggleSideNavigation": { "message": "切换侧边导航" @@ -10294,7 +10294,7 @@ "message": "已声明" }, "domainStatusUnderVerification": { - "message": "正在验证" + "message": "验证中" }, "claimedDomainsDesc": { "message": "声明一个域名,以拥有电子邮箱地址与该域名匹配的所有成员账户。成员登录时将可以跳过 SSO 标识符。管理员也可以删除成员账户。" From a6e785d63cf71de0342446d29631efc8398bca5a Mon Sep 17 00:00:00 2001 From: Github Actions Date: Mon, 24 Mar 2025 10:49:32 +0000 Subject: [PATCH 18/20] Bumped client version(s) --- apps/web/package.json | 2 +- package-lock.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/web/package.json b/apps/web/package.json index 25890cf4b6e..2af524c92b7 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,6 +1,6 @@ { "name": "@bitwarden/web-vault", - "version": "2025.3.0", + "version": "2025.3.1", "scripts": { "build:oss": "cross-env NODE_OPTIONS=\"--max-old-space-size=8192\" webpack", "build:bit": "cross-env NODE_OPTIONS=\"--max-old-space-size=8192\" webpack -c ../../bitwarden_license/bit-web/webpack.config.js", diff --git a/package-lock.json b/package-lock.json index cb51c157b09..7331fcdde44 100644 --- a/package-lock.json +++ b/package-lock.json @@ -243,7 +243,7 @@ }, "apps/web": { "name": "@bitwarden/web-vault", - "version": "2025.3.0" + "version": "2025.3.1" }, "libs/admin-console": { "name": "@bitwarden/admin-console", From 8e455007c028db3f98cd2affed6a7fd63e5f6331 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20=C3=85berg?= Date: Mon, 24 Mar 2025 12:50:11 +0100 Subject: [PATCH 19/20] PM-19095: Wire passkey autofill to UI (#13051) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Passkey stuff Co-authored-by: Anders Åberg * Ugly hacks * Work On Modal State Management * Applying modalStyles * modal * Improved hide/show * fixed promise * File name * fix prettier * Protecting against null API's and undefined data * Only show fake popup to devs * cleanup mock code * rename minmimal-app to modal-app * Added comment * Added comment * removed old comment * Avoided changing minimum size * Add small comment * Rename component * adress feedback * Fixed uppercase file * Fixed build * Added codeowners * added void * commentary * feat: reset setting on app start * Moved reset to be in main / process launch * Add comment to create window * Added a little bit of styling * Use Messaging service to loadUrl * Enable passkeysautofill * Add logging * halfbaked * Integration working * And now it works without extra delay * Clean up * add note about messaging * lb * removed console.logs * Cleanup and adress review feedback * This hides the swift UI * pick credential, draft * Remove logger * a whole lot of wiring * not working * Improved wiring * Cancel after 90s * Introduced observable * Launching bitwarden if its not running * Passing position from native to electron * Rename inModalMode to modalMode * remove tap * revert spaces * added back isDev * cleaned up a bit * Cleanup swift file * tweaked logging * clean up * Update apps/desktop/macos/autofill-extension/CredentialProviderViewController.swift Co-authored-by: Andreas Coroiu * Update apps/desktop/src/platform/main/autofill/native-autofill.main.ts Co-authored-by: Andreas Coroiu * Update apps/desktop/src/platform/services/desktop-settings.service.ts Co-authored-by: Andreas Coroiu * adress position feedback * Update apps/desktop/macos/autofill-extension/CredentialProviderViewController.swift Co-authored-by: Andreas Coroiu * Removed extra logging * Adjusted error logging * Use .error to log errors * remove dead code * Update desktop-autofill.service.ts * use parseCredentialId instead of guidToRawFormat * Update apps/desktop/src/autofill/services/desktop-autofill.service.ts Co-authored-by: Andreas Coroiu * Change windowXy to a Record instead of [number,number] * Update apps/desktop/src/autofill/services/desktop-fido2-user-interface.service.ts Co-authored-by: Andreas Coroiu * Remove unsued dep and comment * changed timeout to be spec recommended maxium, 10 minutes, for now. * Correctly assume UP * Removed extra cancelRequest in deinint * Add timeout and UV to confirmChoseCipher UV is performed by UI, not the service * Improved docs regarding undefined cipherId * cleanup: UP is no longer undefined * Run completeError if ipc messages conversion failed * don't throw, instead return undefined * Disabled passkey provider * Throw error if no activeUserId was found * removed comment * Fixed lint * removed unsued service * reset entitlement formatting * Update entitlements.mas.plist --------- Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com> Co-authored-by: Colton Hurst Co-authored-by: Andreas Coroiu Co-authored-by: Andreas Coroiu --- .../macos_provider/src/assertion.rs | 14 +- .../desktop_native/macos_provider/src/lib.rs | 20 +- .../macos_provider/src/registration.rs | 3 +- apps/desktop/desktop_native/napi/index.d.ts | 15 +- apps/desktop/desktop_native/napi/src/lib.rs | 48 ++++ .../CredentialProviderViewController.xib | 23 +- .../CredentialProviderViewController.swift | 261 ++++++++++++------ apps/desktop/src/app/app-routing.module.ts | 4 + .../components/fido2placeholder.component.ts | 90 +++++- .../src/app/services/services.module.ts | 17 +- apps/desktop/src/autofill/preload.ts | 38 +++ .../services/desktop-autofill.service.ts | 153 ++++++---- .../desktop-fido2-user-interface.service.ts | 191 ++++++++++++- apps/desktop/src/main.ts | 12 +- apps/desktop/src/main/messaging.main.ts | 4 + apps/desktop/src/main/tray.main.ts | 36 +-- apps/desktop/src/main/window.main.ts | 42 ++- .../main/autofill/native-autofill.main.ts | 17 +- .../platform/models/domain/window-state.ts | 5 + .../src/platform/popup-modal-styles.ts | 20 +- .../services/desktop-settings.service.ts | 19 +- ...ido2-user-interface.service.abstraction.ts | 2 +- 22 files changed, 826 insertions(+), 208 deletions(-) diff --git a/apps/desktop/desktop_native/macos_provider/src/assertion.rs b/apps/desktop/desktop_native/macos_provider/src/assertion.rs index 762ceaaed48..c5b43bb87fa 100644 --- a/apps/desktop/desktop_native/macos_provider/src/assertion.rs +++ b/apps/desktop/desktop_native/macos_provider/src/assertion.rs @@ -2,11 +2,22 @@ use std::sync::Arc; use serde::{Deserialize, Serialize}; -use crate::{BitwardenError, Callback, UserVerification}; +use crate::{BitwardenError, Callback, Position, UserVerification}; #[derive(uniffi::Record, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PasskeyAssertionRequest { + rp_id: String, + client_data_hash: Vec, + user_verification: UserVerification, + allowed_credentials: Vec>, + window_xy: Position, + //extension_input: Vec, TODO: Implement support for extensions +} + +#[derive(uniffi::Record, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct PasskeyAssertionWithoutUserInterfaceRequest { rp_id: String, credential_id: Vec, user_name: String, @@ -14,6 +25,7 @@ pub struct PasskeyAssertionRequest { record_identifier: Option, client_data_hash: Vec, user_verification: UserVerification, + window_xy: Position, } #[derive(uniffi::Record, Serialize, Deserialize)] diff --git a/apps/desktop/desktop_native/macos_provider/src/lib.rs b/apps/desktop/desktop_native/macos_provider/src/lib.rs index 5623436d874..8f2499ae68d 100644 --- a/apps/desktop/desktop_native/macos_provider/src/lib.rs +++ b/apps/desktop/desktop_native/macos_provider/src/lib.rs @@ -15,7 +15,10 @@ uniffi::setup_scaffolding!(); mod assertion; mod registration; -use assertion::{PasskeyAssertionRequest, PreparePasskeyAssertionCallback}; +use assertion::{ + PasskeyAssertionRequest, PasskeyAssertionWithoutUserInterfaceRequest, + PreparePasskeyAssertionCallback, +}; use registration::{PasskeyRegistrationRequest, PreparePasskeyRegistrationCallback}; #[derive(uniffi::Enum, Debug, Serialize, Deserialize)] @@ -26,6 +29,13 @@ pub enum UserVerification { Discouraged, } +#[derive(uniffi::Record, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Position { + pub x: i32, + pub y: i32, +} + #[derive(Debug, uniffi::Error, Serialize, Deserialize)] pub enum BitwardenError { Internal(String), @@ -141,6 +151,14 @@ impl MacOSProviderClient { ) { self.send_message(request, Box::new(callback)); } + + pub fn prepare_passkey_assertion_without_user_interface( + &self, + request: PasskeyAssertionWithoutUserInterfaceRequest, + callback: Arc, + ) { + self.send_message(request, Box::new(callback)); + } } #[derive(Serialize, Deserialize)] diff --git a/apps/desktop/desktop_native/macos_provider/src/registration.rs b/apps/desktop/desktop_native/macos_provider/src/registration.rs index d484af58b6c..9e697b75c16 100644 --- a/apps/desktop/desktop_native/macos_provider/src/registration.rs +++ b/apps/desktop/desktop_native/macos_provider/src/registration.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use serde::{Deserialize, Serialize}; -use crate::{BitwardenError, Callback, UserVerification}; +use crate::{BitwardenError, Callback, Position, UserVerification}; #[derive(uniffi::Record, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -13,6 +13,7 @@ pub struct PasskeyRegistrationRequest { client_data_hash: Vec, user_verification: UserVerification, supported_algorithms: Vec, + window_xy: Position, } #[derive(uniffi::Record, Serialize, Deserialize)] diff --git a/apps/desktop/desktop_native/napi/index.d.ts b/apps/desktop/desktop_native/napi/index.d.ts index 92f31cf5f89..ca1fe29e254 100644 --- a/apps/desktop/desktop_native/napi/index.d.ts +++ b/apps/desktop/desktop_native/napi/index.d.ts @@ -118,6 +118,10 @@ export declare namespace autofill { Required = 'required', Discouraged = 'discouraged' } + export interface Position { + x: number + y: number + } export interface PasskeyRegistrationRequest { rpId: string userName: string @@ -125,6 +129,7 @@ export declare namespace autofill { clientDataHash: Array userVerification: UserVerification supportedAlgorithms: Array + windowXy: Position } export interface PasskeyRegistrationResponse { rpId: string @@ -133,6 +138,13 @@ export declare namespace autofill { attestationObject: Array } export interface PasskeyAssertionRequest { + rpId: string + clientDataHash: Array + userVerification: UserVerification + allowedCredentials: Array> + windowXy: Position + } + export interface PasskeyAssertionWithoutUserInterfaceRequest { rpId: string credentialId: Array userName: string @@ -140,6 +152,7 @@ export declare namespace autofill { recordIdentifier?: string clientDataHash: Array userVerification: UserVerification + windowXy: Position } export interface PasskeyAssertionResponse { rpId: string @@ -156,7 +169,7 @@ export declare namespace autofill { * @param name The endpoint name to listen on. This name uniquely identifies the IPC connection and must be the same for both the server and client. * @param callback This function will be called whenever a message is received from a client. */ - static listen(name: string, registrationCallback: (error: null | Error, clientId: number, sequenceNumber: number, message: PasskeyRegistrationRequest) => void, assertionCallback: (error: null | Error, clientId: number, sequenceNumber: number, message: PasskeyAssertionRequest) => void): Promise + static listen(name: string, registrationCallback: (error: null | Error, clientId: number, sequenceNumber: number, message: PasskeyRegistrationRequest) => void, assertionCallback: (error: null | Error, clientId: number, sequenceNumber: number, message: PasskeyAssertionRequest) => void, assertionWithoutUserInterfaceCallback: (error: null | Error, clientId: number, sequenceNumber: number, message: PasskeyAssertionWithoutUserInterfaceRequest) => void): Promise /** Return the path to the IPC server. */ getPath(): string /** Stop the IPC server. */ diff --git a/apps/desktop/desktop_native/napi/src/lib.rs b/apps/desktop/desktop_native/napi/src/lib.rs index d0c859d427c..f02be2b27b6 100644 --- a/apps/desktop/desktop_native/napi/src/lib.rs +++ b/apps/desktop/desktop_native/napi/src/lib.rs @@ -515,6 +515,14 @@ pub mod autofill { pub value: Result, } + #[napi(object)] + #[derive(Debug, Serialize, Deserialize)] + #[serde(rename_all = "camelCase")] + pub struct Position { + pub x: i32, + pub y: i32, + } + #[napi(object)] #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -525,6 +533,7 @@ pub mod autofill { pub client_data_hash: Vec, pub user_verification: UserVerification, pub supported_algorithms: Vec, + pub window_xy: Position, } #[napi(object)] @@ -541,6 +550,18 @@ pub mod autofill { #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PasskeyAssertionRequest { + pub rp_id: String, + pub client_data_hash: Vec, + pub user_verification: UserVerification, + pub allowed_credentials: Vec>, + pub window_xy: Position, + //extension_input: Vec, TODO: Implement support for extensions + } + + #[napi(object)] + #[derive(Debug, Serialize, Deserialize)] + #[serde(rename_all = "camelCase")] + pub struct PasskeyAssertionWithoutUserInterfaceRequest { pub rp_id: String, pub credential_id: Vec, pub user_name: String, @@ -548,6 +569,7 @@ pub mod autofill { pub record_identifier: Option, pub client_data_hash: Vec, pub user_verification: UserVerification, + pub window_xy: Position, } #[napi(object)] @@ -592,6 +614,13 @@ pub mod autofill { (u32, u32, PasskeyAssertionRequest), ErrorStrategy::CalleeHandled, >, + #[napi( + ts_arg_type = "(error: null | Error, clientId: number, sequenceNumber: number, message: PasskeyAssertionWithoutUserInterfaceRequest) => void" + )] + assertion_without_user_interface_callback: ThreadsafeFunction< + (u32, u32, PasskeyAssertionWithoutUserInterfaceRequest), + ErrorStrategy::CalleeHandled, + >, ) -> napi::Result { let (send, mut recv) = tokio::sync::mpsc::channel::(32); tokio::spawn(async move { @@ -628,6 +657,25 @@ pub mod autofill { } } + match serde_json::from_str::< + PasskeyMessage, + >(&message) + { + Ok(msg) => { + let value = msg + .value + .map(|value| (client_id, msg.sequence_number, value)) + .map_err(|e| napi::Error::from_reason(format!("{e:?}"))); + + assertion_without_user_interface_callback + .call(value, ThreadsafeFunctionCallMode::NonBlocking); + continue; + } + Err(e) => { + println!("[ERROR] Error deserializing message1: {e}"); + } + } + match serde_json::from_str::>( &message, ) { diff --git a/apps/desktop/macos/autofill-extension/Base.lproj/CredentialProviderViewController.xib b/apps/desktop/macos/autofill-extension/Base.lproj/CredentialProviderViewController.xib index ace3497a58b..1e47cc54de2 100644 --- a/apps/desktop/macos/autofill-extension/Base.lproj/CredentialProviderViewController.xib +++ b/apps/desktop/macos/autofill-extension/Base.lproj/CredentialProviderViewController.xib @@ -1,22 +1,23 @@ - + - + + - + - +