diff --git a/.github/workflows/build-browser.yml b/.github/workflows/build-browser.yml index ef2c91f0a7d..6a334e31a18 100644 --- a/.github/workflows/build-browser.yml +++ b/.github/workflows/build-browser.yml @@ -565,7 +565,7 @@ jobs: uses: bitwarden/gh-actions/azure-logout@main - name: Upload Sources - uses: crowdin/github-action@60debf382ee245b21794321190ad0501db89d8c1 # v2.13.0 + uses: crowdin/github-action@b4b468cffefb50bdd99dd83e5d2eaeb63c880380 # v2.14.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }} diff --git a/.github/workflows/build-desktop.yml b/.github/workflows/build-desktop.yml index 6818064a808..c500e59d536 100644 --- a/.github/workflows/build-desktop.yml +++ b/.github/workflows/build-desktop.yml @@ -1007,7 +1007,7 @@ jobs: node-version: ${{ env._NODE_VERSION }} - name: Set up Python - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: '3.14.2' @@ -1247,7 +1247,7 @@ jobs: node-version: ${{ env._NODE_VERSION }} - name: Set up Python - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: '3.14.2' @@ -1522,7 +1522,7 @@ jobs: node-version: ${{ env._NODE_VERSION }} - name: Set up Python - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: '3.14.2' @@ -1873,7 +1873,7 @@ jobs: uses: bitwarden/gh-actions/azure-logout@main - name: Upload Sources - uses: crowdin/github-action@60debf382ee245b21794321190ad0501db89d8c1 # v2.13.0 + uses: crowdin/github-action@b4b468cffefb50bdd99dd83e5d2eaeb63c880380 # v2.14.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }} diff --git a/.github/workflows/build-web.yml b/.github/workflows/build-web.yml index 71a2c62ec1a..688bd30bfe5 100644 --- a/.github/workflows/build-web.yml +++ b/.github/workflows/build-web.yml @@ -352,7 +352,7 @@ jobs: - name: Scan Docker image if: ${{ needs.setup.outputs.has_secrets == 'true' }} id: container-scan - uses: anchore/scan-action@62b74fb7bb810d2c45b1865f47a77655621862a5 # v7.2.3 + uses: anchore/scan-action@0d444ed77d83ee2ba7f5ced0d90d640a1281d762 # v7.3.0 with: image: ${{ steps.image-name.outputs.name }} fail-build: false @@ -408,7 +408,7 @@ jobs: uses: bitwarden/gh-actions/azure-logout@main - name: Upload Sources - uses: crowdin/github-action@60debf382ee245b21794321190ad0501db89d8c1 # v2.13.0 + uses: crowdin/github-action@b4b468cffefb50bdd99dd83e5d2eaeb63c880380 # v2.14.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }} diff --git a/.github/workflows/lint-crowdin-config.yml b/.github/workflows/lint-crowdin-config.yml index 61e2b3631e6..e1e620c864d 100644 --- a/.github/workflows/lint-crowdin-config.yml +++ b/.github/workflows/lint-crowdin-config.yml @@ -45,7 +45,7 @@ jobs: uses: bitwarden/gh-actions/azure-logout@main - name: Lint ${{ matrix.app.name }} config - uses: crowdin/github-action@60debf382ee245b21794321190ad0501db89d8c1 # v2.13.0 + uses: crowdin/github-action@b4b468cffefb50bdd99dd83e5d2eaeb63c880380 # v2.14.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CROWDIN_PROJECT_ID: ${{ matrix.app.project_id }} diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 7862c14c186..efc8c25fc5e 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -142,7 +142,7 @@ jobs: run: cargo +nightly udeps --workspace --all-features --all-targets - name: Install cargo-deny - uses: taiki-e/install-action@542cebaaed782771e619bd5609d97659d109c492 # v2.66.7 + uses: taiki-e/install-action@887bc4e03483810873d617344dd5189cd82e7b8b # v2.67.11 with: tool: cargo-deny@0.18.6 diff --git a/.github/workflows/publish-desktop.yml b/.github/workflows/publish-desktop.yml index c5db7ea9295..45665f459e8 100644 --- a/.github/workflows/publish-desktop.yml +++ b/.github/workflows/publish-desktop.yml @@ -331,7 +331,7 @@ jobs: run: wget "https://github.com/bitwarden/clients/releases/download/${_RELEASE_TAG}/macos-build-number.json" - name: Setup Ruby and Install Fastlane - uses: ruby/setup-ruby@708024e6c902387ab41de36e1669e43b5ee7085e # v1.283.0 + uses: ruby/setup-ruby@90be1154f987f4dc0fe0dd0feedac9e473aa4ba8 # v1.286.0 with: ruby-version: '3.4.7' bundler-cache: false diff --git a/apps/browser/src/autofill/overlay/inline-menu/pages/list/__snapshots__/autofill-inline-menu-list.spec.ts.snap b/apps/browser/src/autofill/overlay/inline-menu/pages/list/__snapshots__/autofill-inline-menu-list.spec.ts.snap index 22e3a765666..53a055075fe 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/pages/list/__snapshots__/autofill-inline-menu-list.spec.ts.snap +++ b/apps/browser/src/autofill/overlay/inline-menu/pages/list/__snapshots__/autofill-inline-menu-list.spec.ts.snap @@ -716,7 +716,7 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f stroke-dasharray="78.5" stroke-dashoffset="78.5" stroke-width="3" - style="stroke-dashoffset: NaN;" + style="stroke-dashoffset: 34.033920413889426;" transform="rotate(-90 14.5 14.5)" /> @@ -737,7 +737,7 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f bittypography="helper" class="totp-sec-span" > - NaN + 17 @@ -2115,7 +2115,7 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f stroke-dasharray="78.5" stroke-dashoffset="78.5" stroke-width="3" - style="stroke-dashoffset: NaN;" + style="stroke-dashoffset: 34.033920413889426;" transform="rotate(-90 14.5 14.5)" /> @@ -2136,7 +2136,7 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f bittypography="helper" class="totp-sec-span" > - NaN + 17 @@ -2227,7 +2227,7 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f stroke-dasharray="78.5" stroke-dashoffset="78.5" stroke-width="3" - style="stroke-dashoffset: NaN;" + style="stroke-dashoffset: 34.033920413889426;" transform="rotate(-90 14.5 14.5)" /> @@ -2248,7 +2248,7 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f bittypography="helper" class="totp-sec-span" > - NaN + 17 diff --git a/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.spec.ts b/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.spec.ts index 81bf7240230..1e99ac9df90 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.spec.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.spec.ts @@ -157,6 +157,8 @@ describe("AutofillInlineMenuList", () => { }); it("creates the view for a totp field", async () => { + jest.spyOn(Date, "now").mockReturnValue(13000); + postWindowMessage( createInitAutofillInlineMenuListMessageMock({ inlineMenuFillType: CipherType.Login, @@ -184,6 +186,8 @@ describe("AutofillInlineMenuList", () => { }); it("renders correctly when there are multiple TOTP elements with username displayed", async () => { + jest.spyOn(Date, "now").mockReturnValue(13000); + const totpCipher1 = createAutofillOverlayCipherDataMock(1, { type: CipherType.Login, login: { diff --git a/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts b/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts index c680fe4745c..c13c523e30a 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts @@ -1,5 +1,3 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import "@webcomponents/custom-elements"; import "lit/polyfill-support.js"; @@ -33,27 +31,36 @@ import { import { AutofillInlineMenuPageElement } from "../shared/autofill-inline-menu-page-element"; export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { - private inlineMenuListContainer: HTMLDivElement; - private passwordGeneratorContainer: HTMLDivElement; + /** Non-null asserted. Set in initAutofillInlineMenuList before any read. */ + private inlineMenuListContainer!: HTMLDivElement; + /** Non-null asserted. Set in initAutofillInlineMenuList before any read. */ + private passwordGeneratorContainer!: HTMLDivElement; private resizeObserver: ResizeObserver; private eventHandlersMemo: { [key: string]: EventListener } = {}; private ciphers: InlineMenuCipherData[] = []; - private ciphersList: HTMLUListElement; + /** Non-null asserted. Set in buildInlineMenuList before any read. */ + private ciphersList!: HTMLUListElement; private cipherListScrollIsDebounced = false; - private cipherListScrollDebounceTimeout: number | NodeJS.Timeout; + private cipherListScrollDebounceTimeout: number | ReturnType = 0; private currentCipherIndex = 0; - private inlineMenuFillType: InlineMenuFillType; - private showInlineMenuAccountCreation: boolean; - private showPasskeysLabels: boolean; - private newItemButtonElement: HTMLButtonElement; - private passkeysHeadingElement: HTMLLIElement; - private loginHeadingElement: HTMLLIElement; - private lastPasskeysListItem: HTMLLIElement; - private passkeysHeadingHeight: number; - private lastPasskeysListItemHeight: number; - private ciphersListHeight: number; + /** Non-null asserted. Set in initAutofillInlineMenuList from message. */ + private inlineMenuFillType!: InlineMenuFillType; + private showInlineMenuAccountCreation = false; + private showPasskeysLabels = false; + /** Non-null asserted. Set in buildNewItemButton before any read. */ + private newItemButtonElement!: HTMLButtonElement; + /** Conditionally set in buildPasskeysHeadingElements, may be undefined when no passkeys. */ + private passkeysHeadingElement?: HTMLLIElement; + /** Conditionally set in buildPasskeysHeadingElements, may be undefined when no login heading. */ + private loginHeadingElement?: HTMLLIElement; + /** Conditionally set in buildInlineMenuListActionsItem when showPasskeysLabels and passkey cipher. */ + private lastPasskeysListItem?: HTMLLIElement; + private passkeysHeadingHeight = 0; + private lastPasskeysListItemHeight = 0; + private ciphersListHeight = 0; private isPasskeyAuthInProgress = false; - private authStatus: AuthenticationStatus; + private authStatus: AuthenticationStatus = AuthenticationStatus.Locked; + private isInitialized = false; private readonly showCiphersPerPage = 6; private readonly headingBorderClass = "inline-menu-list-heading--bordered"; private readonly inlineMenuListWindowMessageHandlers: AutofillInlineMenuListWindowMessageHandlers = @@ -70,6 +77,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { constructor() { super(); + this.resizeObserver = new ResizeObserver(this.handleResizeObserver); this.setupInlineMenuListGlobalListeners(); } @@ -85,11 +93,11 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { styleSheetUrl, theme, authStatus, - ciphers, + ciphers = [], portKey, - inlineMenuFillType, - showInlineMenuAccountCreation, - showPasskeysLabels, + inlineMenuFillType = CipherType.Login, + showInlineMenuAccountCreation = false, + showPasskeysLabels = false, generatedPassword, showSaveLoginMenu, } = message; @@ -112,6 +120,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { this.resizeObserver.observe(this.inlineMenuListContainer); this.shadowDom.append(linkElement, this.inlineMenuListContainer); + this.isInitialized = true; if (authStatus !== AuthenticationStatus.Unlocked) { this.buildLockedInlineMenu(); @@ -368,7 +377,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { event.target.nextElementSibling ) { (event.target.nextElementSibling as HTMLElement).focus(); - event.target.parentElement.classList.add("remove-outline"); + event.target.parentElement?.classList.add("remove-outline"); return; } }; @@ -409,7 +418,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { event.target.previousElementSibling ) { (event.target.previousElementSibling as HTMLElement).focus(); - event.target.parentElement.classList.remove("remove-outline"); + event.target.parentElement?.classList.remove("remove-outline"); return; } }; @@ -473,8 +482,8 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { * @param showInlineMenuAccountCreation - Whether identity ciphers are shown on login fields. */ private updateListItems({ - ciphers, - showInlineMenuAccountCreation, + ciphers = [], + showInlineMenuAccountCreation = false, }: UpdateAutofillInlineMenuListCiphersParams) { if (this.isPasskeyAuthInProgress) { return; @@ -655,7 +664,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { * scroll listeners that reposition the passkeys and login headings when the user scrolls. */ private setupCipherListScrollListeners() { - const options = { passive: true }; + const options: AddEventListenerOptions = { passive: true }; this.ciphersList.addEventListener(EVENTS.SCROLL, this.updateCiphersListOnScroll, options); if (this.showPasskeysLabels) { this.ciphersList.addEventListener( @@ -673,8 +682,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { * Handles updating the list of ciphers when the * user scrolls to the bottom of the list. */ - private updateCiphersListOnScroll = (event: MouseEvent) => { - event.preventDefault(); + private updateCiphersListOnScroll = (event: Event) => { event.stopPropagation(); if (this.cipherListScrollIsDebounced) { @@ -721,8 +729,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { * * @param event - The scroll event. */ - private handleThrottledOnScrollEvent = (event: MouseEvent) => { - event.preventDefault(); + private handleThrottledOnScrollEvent = (event: Event) => { event.stopPropagation(); this.updatePasskeysHeadingsOnScroll(this.ciphersList.scrollTop); @@ -754,6 +761,9 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { * @param cipherListScrollTop - The current scroll top position of the ciphers list. */ private togglePasskeysHeadingAnchored(cipherListScrollTop: number) { + if (!this.passkeysHeadingElement || !this.lastPasskeysListItem) { + return; + } if (!this.passkeysHeadingHeight) { this.passkeysHeadingHeight = this.passkeysHeadingElement.offsetHeight; } @@ -776,6 +786,9 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { * @param cipherListScrollTop - The current scroll top position of the ciphers list. */ private togglePasskeysHeadingBorder(cipherListScrollTop: number) { + if (!this.passkeysHeadingElement) { + return; + } if (cipherListScrollTop < 1) { this.passkeysHeadingElement.classList.remove(this.headingBorderClass); return; @@ -791,6 +804,9 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { * @param cipherListScrollTop - The current scroll top position of the ciphers list. */ private toggleLoginHeadingBorder(cipherListScrollTop: number) { + if (!this.loginHeadingElement || !this.lastPasskeysListItem) { + return; + } if (!this.lastPasskeysListItemHeight) { this.lastPasskeysListItemHeight = this.lastPasskeysListItem.offsetHeight; } @@ -884,7 +900,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { ); this.addFillCipherElementAriaDescription(fillCipherElement, cipher); - fillCipherElement.append(cipherIcon, cipherDetailsElement); + fillCipherElement.append(cipherIcon, ...(cipherDetailsElement ? [cipherDetailsElement] : [])); fillCipherElement.addEventListener(EVENTS.CLICK, this.handleFillCipherClickEvent(cipher)); fillCipherElement.addEventListener(EVENTS.KEYUP, this.handleFillCipherKeyUpEvent); @@ -1126,7 +1142,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { cipherIcon.appendChild(totpContainer); - const intervalSeconds = cipher.login.totpCodeTimeInterval; + const intervalSeconds = cipher.login.totpCodeTimeInterval ?? 30; const updateCountdown = () => { const epoch = Math.round(Date.now() / 1000); @@ -1266,7 +1282,9 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { if (this.multipleTotpElements() && username) { const usernameSubtitle = this.buildCipherSubtitleElement(username); - containerElement.appendChild(usernameSubtitle); + if (usernameSubtitle) { + containerElement.appendChild(usernameSubtitle); + } } const totpCodeSpan = document.createElement("span"); @@ -1326,19 +1344,25 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { cipher: InlineMenuCipherData, cipherDetailsElement: HTMLSpanElement, ): HTMLSpanElement { - let rpNameSubtitle: HTMLSpanElement; + const login = cipher.login; + const passkey = login?.passkey; + if (!login || !passkey) { + return cipherDetailsElement; + } - if (cipher.name !== cipher.login.passkey.rpName) { - rpNameSubtitle = this.buildCipherSubtitleElement(cipher.login.passkey.rpName); - if (rpNameSubtitle) { + let rpNameSubtitle: HTMLSpanElement | undefined; + if (cipher.name !== passkey.rpName) { + const element = this.buildCipherSubtitleElement(passkey.rpName); + if (element) { + rpNameSubtitle = element; rpNameSubtitle.prepend(buildSvgDomElement(passkeyIcon)); rpNameSubtitle.classList.add("cipher-subtitle--passkey"); cipherDetailsElement.appendChild(rpNameSubtitle); } } - if (cipher.login.username) { - const usernameSubtitle = this.buildCipherSubtitleElement(cipher.login.username); + if (login.username) { + const usernameSubtitle = this.buildCipherSubtitleElement(login.username); if (usernameSubtitle) { if (!rpNameSubtitle) { usernameSubtitle.prepend(buildSvgDomElement(passkeyIcon)); @@ -1350,7 +1374,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { return cipherDetailsElement; } - const passkeySubtitle = this.buildCipherSubtitleElement(cipher.login.passkey.userName); + const passkeySubtitle = this.buildCipherSubtitleElement(passkey.userName); if (passkeySubtitle) { if (!rpNameSubtitle) { passkeySubtitle.prepend(buildSvgDomElement(passkeyIcon)); @@ -1412,6 +1436,9 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { * If not focused, will check if the button element is focused. */ private checkInlineMenuListFocused() { + if (!this.isInitialized) { + return; + } if (globalThis.document.hasFocus()) { return; } @@ -1450,6 +1477,9 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { * the first cipher button. */ private focusInlineMenuList() { + if (!this.isInitialized) { + return; + } this.inlineMenuListContainer.setAttribute("role", "dialog"); this.inlineMenuListContainer.setAttribute("aria-modal", "true"); @@ -1472,8 +1502,6 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { */ private setupInlineMenuListGlobalListeners() { this.setupGlobalListeners(this.inlineMenuListWindowMessageHandlers); - - this.resizeObserver = new ResizeObserver(this.handleResizeObserver); } /** diff --git a/apps/browser/src/autofill/utils/index.ts b/apps/browser/src/autofill/utils/index.ts index dc07ca1e258..fa47ddd943b 100644 --- a/apps/browser/src/autofill/utils/index.ts +++ b/apps/browser/src/autofill/utils/index.ts @@ -368,20 +368,21 @@ export function getPropertyOrAttribute(element: HTMLElement, attributeName: stri /** * Throttles a callback function to run at most once every `limit` milliseconds. * - * @param callback - The callback function to throttle. + * @param callback - The callback function to throttle (must return void). * @param limit - The time in milliseconds to throttle the callback. */ -export function throttle unknown>( - callback: FunctionType, +export function throttle( + callback: (this: TypeContext, ...args: Args) => void, limit: number, -): (this: ThisParameterType, ...args: Parameters) => void { +): (this: TypeContext, ...args: Args) => void { let waitingDelay = false; - return function (this: ThisParameterType, ...args: Parameters) { - if (!waitingDelay) { - callback.apply(this, args); - waitingDelay = true; - globalThis.setTimeout(() => (waitingDelay = false), limit); + return function (this: TypeContext, ...args: Args) { + if (waitingDelay) { + return; } + callback.apply(this, args); + waitingDelay = true; + globalThis.setTimeout(() => (waitingDelay = false), limit); }; } diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index 65eb88156ae..585942d7537 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -1008,7 +1008,6 @@ export default class MainBackground { this.keyGenerationService, this.sendStateProvider, this.encryptService, - this.cryptoFunctionService, this.configService, ); this.sendApiService = new SendApiService( diff --git a/apps/browser/src/platform/badge/badge.service.ts b/apps/browser/src/platform/badge/badge.service.ts index f6d799b2a80..0ecb8210dd0 100644 --- a/apps/browser/src/platform/badge/badge.service.ts +++ b/apps/browser/src/platform/badge/badge.service.ts @@ -1,5 +1,6 @@ import { BehaviorSubject, + catchError, combineLatest, combineLatestWith, concatMap, @@ -73,9 +74,25 @@ export class BadgeService { map((evt) => evt.tab), combineLatestWith(this.stateFunctions), switchMap(([tab, dynamicStateFunctions]) => { - const functions = [...Object.values(dynamicStateFunctions), defaultTabStateFunction]; + const functions = [ + ...Object.entries(dynamicStateFunctions), + ["default" as string, defaultTabStateFunction] as const, + ]; - return combineLatest(functions.map((f) => f(tab).pipe(startWith(undefined)))).pipe( + return combineLatest( + functions.map(([name, f]) => + f(tab).pipe( + startWith(undefined), + catchError((error: unknown) => { + this.logService.error( + `BadgeService: State function "${name}" threw an error`, + error, + ); + return of(undefined); + }), + ), + ), + ).pipe( map((states) => ({ tab, states: states.filter((s): s is BadgeStateSetting => s !== undefined), diff --git a/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.html b/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.html index 8b4d2d21b8b..f8238a188e0 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.html @@ -7,7 +7,7 @@ [backAction]="handleBackButton" showBackButton > - @if (config?.originalCipher?.archivedDate) { + @if (config?.originalCipher?.archivedDate && (archiveFlagEnabled$ | async)) { {{ "archived" | i18n }} diff --git a/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.html b/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.html index 8ac6de75997..a3d65522022 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.html @@ -1,7 +1,7 @@ - @if (cipher?.isArchived) { + @if (cipher?.isArchived && (archiveFlagEnabled$ | async)) { {{ "archived" | i18n }} diff --git a/apps/cli/src/service-container/service-container.ts b/apps/cli/src/service-container/service-container.ts index 848a0e08b29..2033a2dd064 100644 --- a/apps/cli/src/service-container/service-container.ts +++ b/apps/cli/src/service-container/service-container.ts @@ -615,7 +615,6 @@ export class ServiceContainer { this.keyGenerationService, this.sendStateProvider, this.encryptService, - this.cryptoFunctionService, this.configService, ); diff --git a/apps/desktop/custom-appx-manifest.xml b/apps/desktop/custom-appx-manifest.xml index 2f7796c97cf..166b852588b 100644 --- a/apps/desktop/custom-appx-manifest.xml +++ b/apps/desktop/custom-appx-manifest.xml @@ -13,7 +13,7 @@ xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/re IgnorableNamespaces="uap rescap com uap10 build" xmlns:build="http://schemas.microsoft.com/developer/appx/2015/build"> - diff --git a/apps/desktop/electron-builder.beta.json b/apps/desktop/electron-builder.beta.json index 3e1ca673c3c..9c66b17aa1f 100644 --- a/apps/desktop/electron-builder.beta.json +++ b/apps/desktop/electron-builder.beta.json @@ -64,7 +64,7 @@ "customManifestPath": "./custom-appx-manifest.xml", "applicationId": "BitwardenBeta", "identityName": "8bitSolutionsLLC.BitwardenBeta", - "publisher": "CN=Bitwarden Inc., O=Bitwarden Inc., L=Santa Barbara, S=California, C=US, SERIALNUMBER=7654941, OID.2.5.4.15=Private Organization, OID.1.3.6.1.4.1.311.60.2.1.2=Delaware, OID.1.3.6.1.4.1.311.60.2.1.3=US", + "publisher": "CN=14D52771-DE3C-4886-B8BF-825BA7690418", "publisherDisplayName": "Bitwarden Inc", "languages": [ "en-US", diff --git a/apps/desktop/electron-builder.json b/apps/desktop/electron-builder.json index 481d12f02b4..151ce72182d 100644 --- a/apps/desktop/electron-builder.json +++ b/apps/desktop/electron-builder.json @@ -179,7 +179,7 @@ "customManifestPath": "./custom-appx-manifest.xml", "applicationId": "bitwardendesktop", "identityName": "8bitSolutionsLLC.bitwardendesktop", - "publisher": "CN=Bitwarden Inc., O=Bitwarden Inc., L=Santa Barbara, S=California, C=US, SERIALNUMBER=7654941, OID.2.5.4.15=Private Organization, OID.1.3.6.1.4.1.311.60.2.1.2=Delaware, OID.1.3.6.1.4.1.311.60.2.1.3=US", + "publisher": "CN=14D52771-DE3C-4886-B8BF-825BA7690418", "publisherDisplayName": "Bitwarden Inc", "languages": [ "en-US", diff --git a/apps/desktop/scripts/appx-cross-build.ps1 b/apps/desktop/scripts/appx-cross-build.ps1 index 62619d5ea37..ef2ab09104c 100755 --- a/apps/desktop/scripts/appx-cross-build.ps1 +++ b/apps/desktop/scripts/appx-cross-build.ps1 @@ -176,6 +176,7 @@ $translationMap = @{ 'applicationId' = $builderConfig.appx.applicationId 'displayName' = $productName 'executable' = "app\${productName}.exe" + 'identityName' = $builderConfig.appx.identityName 'publisher' = $builderConfig.appx.publisher 'publisherDisplayName' = $builderConfig.appx.publisherDisplayName 'version' = $version diff --git a/apps/desktop/sign.js b/apps/desktop/sign.js index f115e9b8097..a01388c703c 100644 --- a/apps/desktop/sign.js +++ b/apps/desktop/sign.js @@ -3,7 +3,7 @@ const child_process = require("child_process"); exports.default = async function (configuration) { const ext = configuration.path.split(".").at(-1); - if (parseInt(process.env.ELECTRON_BUILDER_SIGN) === 1 && ["exe", "appx"].includes(ext)) { + if (parseInt(process.env.ELECTRON_BUILDER_SIGN) === 1 && ["exe"].includes(ext)) { console.log(`[*] Signing file: ${configuration.path}`); child_process.execFileSync( "azuresigntool", diff --git a/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.html b/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.html index ec06c740f24..73670339ca8 100644 --- a/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.html +++ b/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.html @@ -3,7 +3,7 @@ {{ title }} - @if (isCipherArchived && !params.isAdminConsoleAction) { + @if (isCipherArchived && !params.isAdminConsoleAction && (archiveFlagEnabled$ | async)) { {{ "archived" | i18n }} } @@ -86,8 +86,8 @@ @if (showActionButtons) {
- @if ((userCanArchive$ | async) && !params.isAdminConsoleAction) { - @if (isCipherArchived && !cipher?.isDeleted) { + @if (showArchiveOptions) { + @if (showUnarchiveBtn) { } - @if (cipher?.canBeArchived) { + @if (showArchiveBtn) { - + {{ "howToManageMyVault" | i18n }} - + diff --git a/libs/vault/src/components/vault-items-transfer/leave-confirmation-dialog.component.ts b/libs/vault/src/components/vault-items-transfer/leave-confirmation-dialog.component.ts index af106376a79..44788a8234a 100644 --- a/libs/vault/src/components/vault-items-transfer/leave-confirmation-dialog.component.ts +++ b/libs/vault/src/components/vault-items-transfer/leave-confirmation-dialog.component.ts @@ -10,6 +10,7 @@ import { DialogService, ButtonModule, DialogModule, + IconModule, LinkModule, TypographyModule, CenterPositionStrategy, @@ -35,7 +36,7 @@ export type LeaveConfirmationDialogResultType = UnionOfValues(DIALOG_DATA); diff --git a/libs/vault/src/components/vault-items-transfer/transfer-items-dialog.component.html b/libs/vault/src/components/vault-items-transfer/transfer-items-dialog.component.html index 3cf626baaf7..5d1c3ba9aed 100644 --- a/libs/vault/src/components/vault-items-transfer/transfer-items-dialog.component.html +++ b/libs/vault/src/components/vault-items-transfer/transfer-items-dialog.component.html @@ -14,9 +14,9 @@ {{ "declineAndLeave" | i18n }} - + {{ "whyAmISeeingThis" | i18n }} - + diff --git a/libs/vault/src/components/vault-items-transfer/transfer-items-dialog.component.ts b/libs/vault/src/components/vault-items-transfer/transfer-items-dialog.component.ts index 619181f37fc..45f6305b5b3 100644 --- a/libs/vault/src/components/vault-items-transfer/transfer-items-dialog.component.ts +++ b/libs/vault/src/components/vault-items-transfer/transfer-items-dialog.component.ts @@ -10,6 +10,7 @@ import { DialogService, ButtonModule, DialogModule, + IconModule, LinkModule, TypographyModule, CenterPositionStrategy, @@ -35,7 +36,7 @@ export type TransferItemsDialogResultType = UnionOfValues(DIALOG_DATA);