diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts
index 27d83af1321..fb92ebe04ae 100644
--- a/apps/browser/src/background/main.background.ts
+++ b/apps/browser/src/background/main.background.ts
@@ -257,12 +257,9 @@ import { BrowserPlatformUtilsService } from "../platform/services/platform-utils
import { PopupViewCacheBackgroundService } from "../platform/services/popup-view-cache-background.service";
import { BrowserSdkClientFactory } from "../platform/services/sdk/browser-sdk-client-factory";
import { BackgroundTaskSchedulerService } from "../platform/services/task-scheduler/background-task-scheduler.service";
-import { ForegroundTaskSchedulerService } from "../platform/services/task-scheduler/foreground-task-scheduler.service";
import { BackgroundMemoryStorageService } from "../platform/storage/background-memory-storage.service";
import { BrowserStorageServiceProvider } from "../platform/storage/browser-storage-service.provider";
-import { ForegroundMemoryStorageService } from "../platform/storage/foreground-memory-storage.service";
import { OffscreenStorageService } from "../platform/storage/offscreen-storage.service";
-import { ForegroundSyncService } from "../platform/sync/foreground-sync.service";
import { SyncServiceListener } from "../platform/sync/sync-service.listener";
import { fromChromeRuntimeMessaging } from "../platform/utils/from-chrome-runtime-messaging";
import VaultTimeoutService from "../services/vault-timeout/vault-timeout.service";
@@ -401,7 +398,7 @@ export default class MainBackground {
private popupViewCacheBackgroundService: PopupViewCacheBackgroundService;
- constructor(public popupOnlyContext: boolean = false) {
+ constructor() {
// Services
const lockedCallback = async (userId?: string) => {
if (this.notificationsService != null) {
@@ -460,45 +457,6 @@ export default class MainBackground {
this.offscreenDocumentService,
);
- // Creates a session key for mv3 storage of large memory items
- const sessionKey = new Lazy(async () => {
- // Key already in session storage
- const sessionStorage = new BrowserMemoryStorageService();
- const existingKey = await sessionStorage.get("session-key");
- if (existingKey) {
- if (sessionStorage.valuesRequireDeserialization) {
- return SymmetricCryptoKey.fromJSON(existingKey);
- }
- return existingKey;
- }
-
- // New key
- const { derivedKey } = await this.keyGenerationService.createKeyWithPurpose(
- 128,
- "ephemeral",
- "bitwarden-ephemeral",
- );
- await sessionStorage.save("session-key", derivedKey);
- return derivedKey;
- });
-
- const mv3MemoryStorageCreator = () => {
- if (this.popupOnlyContext) {
- return new ForegroundMemoryStorageService();
- }
-
- // For local backed session storage, we expect that the encrypted data on disk will persist longer than the encryption key in memory
- // and failures to decrypt because of that are completely expected. For this reason, we pass in `false` to the `EncryptServiceImplementation`
- // so that MAC failures are not logged.
- return new LocalBackedSessionStorageService(
- sessionKey,
- this.storageService,
- new EncryptServiceImplementation(this.cryptoFunctionService, this.logService, false),
- this.platformUtilsService,
- this.logService,
- );
- };
-
this.secureStorageService = this.storageService; // secure storage is not supported in browsers, so we use local storage and warn users when it is used
if (BrowserApi.isManifestVersion(3)) {
@@ -506,18 +464,47 @@ export default class MainBackground {
this.memoryStorageForStateProviders = new BrowserMemoryStorageService(); // mv3 stores to storage.session
this.memoryStorageService = this.memoryStorageForStateProviders;
} else {
- if (popupOnlyContext) {
- this.memoryStorageForStateProviders = new ForegroundMemoryStorageService();
- this.memoryStorageService = new ForegroundMemoryStorageService();
- } else {
- this.memoryStorageForStateProviders = new BackgroundMemoryStorageService(); // mv2 stores to memory
- this.memoryStorageService = this.memoryStorageForStateProviders;
- }
+ this.memoryStorageForStateProviders = new BackgroundMemoryStorageService(); // mv2 stores to memory
+ this.memoryStorageService = this.memoryStorageForStateProviders;
}
- this.largeObjectMemoryStorageForStateProviders = BrowserApi.isManifestVersion(3)
- ? mv3MemoryStorageCreator() // mv3 stores to local-backed session storage
- : this.memoryStorageForStateProviders; // mv2 stores to the same location
+ if (BrowserApi.isManifestVersion(3)) {
+ // Creates a session key for mv3 storage of large memory items
+ const sessionKey = new Lazy(async () => {
+ // Key already in session storage
+ const sessionStorage = new BrowserMemoryStorageService();
+ const existingKey = await sessionStorage.get("session-key");
+ if (existingKey) {
+ if (sessionStorage.valuesRequireDeserialization) {
+ return SymmetricCryptoKey.fromJSON(existingKey);
+ }
+ return existingKey;
+ }
+
+ // New key
+ const { derivedKey } = await this.keyGenerationService.createKeyWithPurpose(
+ 128,
+ "ephemeral",
+ "bitwarden-ephemeral",
+ );
+ await sessionStorage.save("session-key", derivedKey);
+ return derivedKey;
+ });
+
+ this.largeObjectMemoryStorageForStateProviders = new LocalBackedSessionStorageService(
+ sessionKey,
+ this.storageService,
+ // For local backed session storage, we expect that the encrypted data on disk will persist longer than the encryption key in memory
+ // and failures to decrypt because of that are completely expected. For this reason, we pass in `false` to the `EncryptServiceImplementation`
+ // so that MAC failures are not logged.
+ new EncryptServiceImplementation(this.cryptoFunctionService, this.logService, false),
+ this.platformUtilsService,
+ this.logService,
+ );
+ } else {
+ // mv2 stores to the same location
+ this.largeObjectMemoryStorageForStateProviders = this.memoryStorageForStateProviders;
+ }
const localStorageStorageService = BrowserApi.isManifestVersion(3)
? new OffscreenStorageService(this.offscreenDocumentService)
@@ -575,9 +562,10 @@ export default class MainBackground {
this.derivedStateProvider,
);
- this.taskSchedulerService = this.popupOnlyContext
- ? new ForegroundTaskSchedulerService(this.logService, this.stateProvider)
- : new BackgroundTaskSchedulerService(this.logService, this.stateProvider);
+ this.taskSchedulerService = new BackgroundTaskSchedulerService(
+ this.logService,
+ this.stateProvider,
+ );
this.taskSchedulerService.registerTaskHandler(ScheduledTaskNames.scheduleNextSyncInterval, () =>
this.fullSync(),
);
@@ -873,26 +861,24 @@ export default class MainBackground {
this.vaultSettingsService = new VaultSettingsService(this.stateProvider);
- if (!this.popupOnlyContext) {
- this.vaultTimeoutService = new VaultTimeoutService(
- this.accountService,
- this.masterPasswordService,
- this.cipherService,
- this.folderService,
- this.collectionService,
- this.platformUtilsService,
- this.messagingService,
- this.searchService,
- this.stateService,
- this.authService,
- this.vaultTimeoutSettingsService,
- this.stateEventRunnerService,
- this.taskSchedulerService,
- this.logService,
- lockedCallback,
- logoutCallback,
- );
- }
+ this.vaultTimeoutService = new VaultTimeoutService(
+ this.accountService,
+ this.masterPasswordService,
+ this.cipherService,
+ this.folderService,
+ this.collectionService,
+ this.platformUtilsService,
+ this.messagingService,
+ this.searchService,
+ this.stateService,
+ this.authService,
+ this.vaultTimeoutSettingsService,
+ this.stateEventRunnerService,
+ this.taskSchedulerService,
+ this.logService,
+ lockedCallback,
+ logoutCallback,
+ );
this.containerService = new ContainerService(this.keyService, this.encryptService);
this.sendStateProvider = new SendStateProvider(this.stateProvider);
@@ -913,59 +899,41 @@ export default class MainBackground {
this.providerService = new ProviderService(this.stateProvider);
- if (this.popupOnlyContext) {
- this.syncService = new ForegroundSyncService(
- this.stateService,
- this.folderService,
- this.folderApiService,
- this.messagingService,
- this.logService,
- this.cipherService,
- this.collectionService,
- this.apiService,
- this.accountService,
- this.authService,
- this.sendService,
- this.sendApiService,
- messageListener,
- this.stateProvider,
- );
- } else {
- this.syncService = new DefaultSyncService(
- this.masterPasswordService,
- this.accountService,
- this.apiService,
- this.domainSettingsService,
- this.folderService,
- this.cipherService,
- this.keyService,
- this.collectionService,
- this.messagingService,
- this.policyService,
- this.sendService,
- this.logService,
- this.keyConnectorService,
- this.stateService,
- this.providerService,
- this.folderApiService,
- this.organizationService,
- this.sendApiService,
- this.userDecryptionOptionsService,
- this.avatarService,
- logoutCallback,
- this.billingAccountProfileStateService,
- this.tokenService,
- this.authService,
- this.stateProvider,
- );
+ this.syncService = new DefaultSyncService(
+ this.masterPasswordService,
+ this.accountService,
+ this.apiService,
+ this.domainSettingsService,
+ this.folderService,
+ this.cipherService,
+ this.keyService,
+ this.collectionService,
+ this.messagingService,
+ this.policyService,
+ this.sendService,
+ this.logService,
+ this.keyConnectorService,
+ this.stateService,
+ this.providerService,
+ this.folderApiService,
+ this.organizationService,
+ this.sendApiService,
+ this.userDecryptionOptionsService,
+ this.avatarService,
+ logoutCallback,
+ this.billingAccountProfileStateService,
+ this.tokenService,
+ this.authService,
+ this.stateProvider,
+ );
+
+ this.syncServiceListener = new SyncServiceListener(
+ this.syncService,
+ messageListener,
+ this.messagingService,
+ this.logService,
+ );
- this.syncServiceListener = new SyncServiceListener(
- this.syncService,
- messageListener,
- this.messagingService,
- this.logService,
- );
- }
this.eventUploadService = new EventUploadService(
this.apiService,
this.stateProvider,
@@ -1112,122 +1080,128 @@ export default class MainBackground {
this.isSafari = this.platformUtilsService.isSafari();
// Background
- if (!this.popupOnlyContext) {
- this.fido2Background = new Fido2Background(
- this.logService,
- this.fido2ActiveRequestManager,
- this.fido2ClientService,
- this.vaultSettingsService,
- this.scriptInjectorService,
- this.configService,
- this.authService,
- );
- const lockService = new DefaultLockService(this.accountService, this.vaultTimeoutService);
+ this.fido2Background = new Fido2Background(
+ this.logService,
+ this.fido2ActiveRequestManager,
+ this.fido2ClientService,
+ this.vaultSettingsService,
+ this.scriptInjectorService,
+ this.configService,
+ this.authService,
+ );
- this.runtimeBackground = new RuntimeBackground(
- this,
- this.autofillService,
- this.platformUtilsService as BrowserPlatformUtilsService,
- this.notificationsService,
- this.autofillSettingsService,
- this.processReloadService,
- this.environmentService,
- this.messagingService,
- this.logService,
- this.configService,
- messageListener,
- this.accountService,
- lockService,
- );
- this.nativeMessagingBackground = new NativeMessagingBackground(
- this.keyService,
- this.encryptService,
- this.cryptoFunctionService,
- this.runtimeBackground,
- this.messagingService,
- this.appIdService,
- this.platformUtilsService,
- this.logService,
- this.authService,
- this.biometricStateService,
- this.accountService,
- );
- this.commandsBackground = new CommandsBackground(
- this,
- this.platformUtilsService,
- this.vaultTimeoutService,
- this.authService,
- () => this.generatePasswordToClipboard(),
- );
- this.notificationBackground = new NotificationBackground(
- this.autofillService,
- this.cipherService,
- this.authService,
- this.policyService,
- this.folderService,
- this.userNotificationSettingsService,
- this.domainSettingsService,
- this.environmentService,
- this.logService,
- this.themeStateService,
- this.configService,
- this.accountService,
- );
+ const lockService = new DefaultLockService(this.accountService, this.vaultTimeoutService);
- this.overlayNotificationsBackground = new OverlayNotificationsBackground(
- this.logService,
- this.configService,
- this.notificationBackground,
- );
+ this.runtimeBackground = new RuntimeBackground(
+ this,
+ this.autofillService,
+ this.platformUtilsService as BrowserPlatformUtilsService,
+ this.notificationsService,
+ this.autofillSettingsService,
+ this.processReloadService,
+ this.environmentService,
+ this.messagingService,
+ this.logService,
+ this.configService,
+ messageListener,
+ this.accountService,
+ lockService,
+ );
+ this.nativeMessagingBackground = new NativeMessagingBackground(
+ this.keyService,
+ this.encryptService,
+ this.cryptoFunctionService,
+ this.runtimeBackground,
+ this.messagingService,
+ this.appIdService,
+ this.platformUtilsService,
+ this.logService,
+ this.authService,
+ this.biometricStateService,
+ this.accountService,
+ );
+ this.commandsBackground = new CommandsBackground(
+ this,
+ this.platformUtilsService,
+ this.vaultTimeoutService,
+ this.authService,
+ () => this.generatePasswordToClipboard(),
+ );
+ this.notificationBackground = new NotificationBackground(
+ this.autofillService,
+ this.cipherService,
+ this.authService,
+ this.policyService,
+ this.folderService,
+ this.userNotificationSettingsService,
+ this.domainSettingsService,
+ this.environmentService,
+ this.logService,
+ this.themeStateService,
+ this.configService,
+ this.accountService,
+ );
- this.filelessImporterBackground = new FilelessImporterBackground(
- this.configService,
- this.authService,
- this.policyService,
- this.notificationBackground,
- this.importService,
- this.syncService,
- this.scriptInjectorService,
- );
+ this.overlayNotificationsBackground = new OverlayNotificationsBackground(
+ this.logService,
+ this.configService,
+ this.notificationBackground,
+ );
- this.autoSubmitLoginBackground = new AutoSubmitLoginBackground(
- this.logService,
- this.autofillService,
- this.scriptInjectorService,
- this.authService,
- this.configService,
- this.platformUtilsService,
- this.policyService,
- );
+ this.filelessImporterBackground = new FilelessImporterBackground(
+ this.configService,
+ this.authService,
+ this.policyService,
+ this.notificationBackground,
+ this.importService,
+ this.syncService,
+ this.scriptInjectorService,
+ );
- const contextMenuClickedHandler = new ContextMenuClickedHandler(
- (options) => this.platformUtilsService.copyToClipboard(options.text),
- async () => this.generatePasswordToClipboard(),
- async (tab, cipher) => {
- this.loginToAutoFill = cipher;
- if (tab == null) {
- return;
- }
+ this.autoSubmitLoginBackground = new AutoSubmitLoginBackground(
+ this.logService,
+ this.autofillService,
+ this.scriptInjectorService,
+ this.authService,
+ this.configService,
+ this.platformUtilsService,
+ this.policyService,
+ );
- // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
- BrowserApi.tabSendMessage(tab, {
- command: "collectPageDetails",
- tab: tab,
- sender: "contextMenu",
- });
- },
- this.authService,
- this.cipherService,
- this.totpService,
- this.eventCollectionService,
- this.userVerificationService,
- this.accountService,
- );
+ const contextMenuClickedHandler = new ContextMenuClickedHandler(
+ (options) => this.platformUtilsService.copyToClipboard(options.text),
+ async (_tab) => {
+ const options = (await this.passwordGenerationService.getOptions())?.[0] ?? {};
+ const password = await this.passwordGenerationService.generatePassword(options);
+ this.platformUtilsService.copyToClipboard(password);
+ // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
+ this.passwordGenerationService.addHistory(password);
+ },
+ async (tab, cipher) => {
+ this.loginToAutoFill = cipher;
+ if (tab == null) {
+ return;
+ }
- this.contextMenusBackground = new ContextMenusBackground(contextMenuClickedHandler);
- }
+ // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
+ BrowserApi.tabSendMessage(tab, {
+ command: "collectPageDetails",
+ tab: tab,
+ sender: "contextMenu",
+ });
+ },
+ this.authService,
+ this.cipherService,
+ this.totpService,
+ this.eventCollectionService,
+ this.userVerificationService,
+ this.accountService,
+ );
+
+ this.contextMenusBackground = new ContextMenusBackground(contextMenuClickedHandler);
this.idleBackground = new IdleBackground(
this.vaultTimeoutService,
@@ -1246,29 +1220,27 @@ export default class MainBackground {
this.stateProvider,
);
- if (!this.popupOnlyContext) {
- this.mainContextMenuHandler = new MainContextMenuHandler(
- this.stateService,
- this.autofillSettingsService,
- this.i18nService,
- this.logService,
- this.billingAccountProfileStateService,
- );
+ this.mainContextMenuHandler = new MainContextMenuHandler(
+ this.stateService,
+ this.autofillSettingsService,
+ this.i18nService,
+ this.logService,
+ this.billingAccountProfileStateService,
+ );
- this.cipherContextMenuHandler = new CipherContextMenuHandler(
- this.mainContextMenuHandler,
- this.authService,
+ this.cipherContextMenuHandler = new CipherContextMenuHandler(
+ this.mainContextMenuHandler,
+ this.authService,
+ this.cipherService,
+ );
+
+ if (chrome.webRequest != null && chrome.webRequest.onAuthRequired != null) {
+ this.webRequestBackground = new WebRequestBackground(
+ this.platformUtilsService,
this.cipherService,
+ this.authService,
+ chrome.webRequest,
);
-
- if (chrome.webRequest != null && chrome.webRequest.onAuthRequired != null) {
- this.webRequestBackground = new WebRequestBackground(
- this.platformUtilsService,
- this.cipherService,
- this.authService,
- chrome.webRequest,
- );
- }
}
this.userAutoUnlockKeyService = new UserAutoUnlockKeyService(this.keyService);
@@ -1283,7 +1255,7 @@ export default class MainBackground {
this.containerService.attachToGlobal(self);
// Only the "true" background should run migrations
- await this.stateService.init({ runMigrations: !this.popupOnlyContext });
+ await this.stateService.init({ runMigrations: true });
// This is here instead of in in the InitService b/c we don't plan for
// side effects to run in the Browser InitService.
@@ -1305,10 +1277,6 @@ export default class MainBackground {
this.popupViewCacheBackgroundService.startObservingTabChanges();
- if (this.popupOnlyContext) {
- return;
- }
-
await this.vaultTimeoutService.init(true);
this.fido2Background.init();
await this.runtimeBackground.init();
@@ -1637,7 +1605,6 @@ export default class MainBackground {
*/
async initOverlayAndTabsBackground() {
if (
- this.popupOnlyContext ||
this.overlayBackground ||
this.tabsBackground ||
(await firstValueFrom(this.authService.activeAccountStatus$)) ===
diff --git a/apps/browser/src/popup/services/services.module.ts b/apps/browser/src/popup/services/services.module.ts
index 14ebfb4a175..6ef0c278dc3 100644
--- a/apps/browser/src/popup/services/services.module.ts
+++ b/apps/browser/src/popup/services/services.module.ts
@@ -24,8 +24,8 @@ import {
LockComponentService,
} from "@bitwarden/auth/angular";
import { LockService, LoginEmailService, PinServiceAbstraction } from "@bitwarden/auth/common";
+import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { EventCollectionService as EventCollectionServiceAbstraction } from "@bitwarden/common/abstractions/event/event-collection.service";
-import { NotificationsService } from "@bitwarden/common/abstractions/notifications.service";
import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
@@ -92,9 +92,15 @@ import { InlineDerivedStateProvider } from "@bitwarden/common/platform/state/imp
import { PrimarySecondaryStorageService } from "@bitwarden/common/platform/storage/primary-secondary-storage.service";
import { WindowStorageService } from "@bitwarden/common/platform/storage/window-storage.service";
import { SyncService } from "@bitwarden/common/platform/sync";
+import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction";
+import { InternalSendService } from "@bitwarden/common/tools/send/services/send.service.abstraction";
import { VaultTimeoutStringType } from "@bitwarden/common/types/vault-timeout.type";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
-import { FolderService as FolderServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
+import { FolderApiServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder-api.service.abstraction";
+import {
+ FolderService as FolderServiceAbstraction,
+ InternalFolderService,
+} from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
import { TotpService as TotpServiceAbstraction } from "@bitwarden/common/vault/abstractions/totp.service";
import { TotpService } from "@bitwarden/common/vault/services/totp.service";
import { DialogService, ToastService } from "@bitwarden/components";
@@ -107,7 +113,6 @@ import { ExtensionAnonLayoutWrapperDataService } from "../../auth/popup/extensio
import { ExtensionLoginComponentService } from "../../auth/popup/login/extension-login-component.service";
import { AutofillService as AutofillServiceAbstraction } from "../../autofill/services/abstractions/autofill.service";
import AutofillService from "../../autofill/services/autofill.service";
-import MainBackground from "../../background/main.background";
import { ForegroundBrowserBiometricsService } from "../../key-management/biometrics/foreground-browser-biometrics";
import { BrowserKeyService } from "../../key-management/browser-key.service";
import { BrowserApi } from "../../platform/browser/browser-api";
@@ -117,12 +122,12 @@ import { ChromeMessageSender } from "../../platform/messaging/chrome-message.sen
/* eslint-enable no-restricted-imports */
import { OffscreenDocumentService } from "../../platform/offscreen-document/abstractions/offscreen-document";
import { DefaultOffscreenDocumentService } from "../../platform/offscreen-document/offscreen-document.service";
-import BrowserPopupUtils from "../../platform/popup/browser-popup-utils";
import { BrowserFileDownloadService } from "../../platform/popup/services/browser-file-download.service";
import { PopupViewCacheService } from "../../platform/popup/view-cache/popup-view-cache.service";
import { ScriptInjectorService } from "../../platform/services/abstractions/script-injector.service";
import { BrowserEnvironmentService } from "../../platform/services/browser-environment.service";
import BrowserLocalStorageService from "../../platform/services/browser-local-storage.service";
+import BrowserMemoryStorageService from "../../platform/services/browser-memory-storage.service";
import { BrowserScriptInjectorService } from "../../platform/services/browser-script-injector.service";
import I18nService from "../../platform/services/i18n.service";
import { ForegroundPlatformUtilsService } from "../../platform/services/platform-utils/foreground-platform-utils.service";
@@ -130,6 +135,7 @@ import { BrowserSdkClientFactory } from "../../platform/services/sdk/browser-sdk
import { ForegroundTaskSchedulerService } from "../../platform/services/task-scheduler/foreground-task-scheduler.service";
import { BrowserStorageServiceProvider } from "../../platform/storage/browser-storage-service.provider";
import { ForegroundMemoryStorageService } from "../../platform/storage/foreground-memory-storage.service";
+import { ForegroundSyncService } from "../../platform/sync/foreground-sync.service";
import { fromChromeRuntimeMessaging } from "../../platform/utils/from-chrome-runtime-messaging";
import { ExtensionLockComponentService } from "../../services/extension-lock-component.service";
import { ForegroundVaultTimeoutService } from "../../services/vault-timeout/foreground-vault-timeout.service";
@@ -151,26 +157,6 @@ const DISK_BACKUP_LOCAL_STORAGE = new SafeInjectionToken<
AbstractStorageService & ObservableStorageService
>("DISK_BACKUP_LOCAL_STORAGE");
-const needsBackgroundInit = BrowserPopupUtils.backgroundInitializationRequired();
-const mainBackground: MainBackground = needsBackgroundInit
- ? createLocalBgService()
- : BrowserApi.getBackgroundPage().bitwardenMain;
-
-function createLocalBgService() {
- const localBgService = new MainBackground(true);
- // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
- localBgService.bootstrap();
- return localBgService;
-}
-
-/** @deprecated This method needs to be removed as part of MV3 conversion. Please do not add more and actively try to remove usages */
-function getBgService(service: keyof MainBackground) {
- return (): T => {
- return mainBackground ? (mainBackground[service] as any as T) : null;
- };
-}
-
/**
* Provider definitions used in the ngModule.
* Add your provider definition here using the safeProvider function as a wrapper. This will give you type safety.
@@ -307,8 +293,23 @@ const safeProviders: SafeProvider[] = [
}),
safeProvider({
provide: SyncService,
- useFactory: getBgService("syncService"),
- deps: [],
+ useClass: ForegroundSyncService,
+ deps: [
+ StateService,
+ InternalFolderService,
+ FolderApiServiceAbstraction,
+ MessageSender,
+ LogService,
+ CipherService,
+ CollectionService,
+ ApiService,
+ AccountServiceAbstraction,
+ AuthService,
+ InternalSendService,
+ SendApiService,
+ MessageListener,
+ StateProvider,
+ ],
}),
safeProvider({
provide: DomainSettingsService,
@@ -358,11 +359,6 @@ const safeProviders: SafeProvider[] = [
useClass: ForegroundVaultTimeoutService,
deps: [MessagingServiceAbstraction],
}),
- safeProvider({
- provide: NotificationsService,
- useFactory: getBgService("notificationsService"),
- deps: [],
- }),
safeProvider({
provide: VaultFilterService,
useClass: VaultFilterService,
@@ -382,8 +378,8 @@ const safeProviders: SafeProvider[] = [
}),
safeProvider({
provide: MEMORY_STORAGE,
- useFactory: getBgService("memoryStorageService"),
- deps: [],
+ useFactory: (memoryStorage: AbstractStorageService) => memoryStorage,
+ deps: [OBSERVABLE_MEMORY_STORAGE],
}),
safeProvider({
provide: OBSERVABLE_MEMORY_STORAGE,
@@ -392,9 +388,7 @@ const safeProviders: SafeProvider[] = [
return new ForegroundMemoryStorageService();
}
- return getBgService(
- "memoryStorageForStateProviders",
- )();
+ return new BrowserMemoryStorageService();
},
deps: [],
}),
@@ -407,9 +401,7 @@ const safeProviders: SafeProvider[] = [
return regularMemoryStorageService;
}
- return getBgService(
- "largeObjectMemoryStorageForStateProviders",
- )();
+ return new ForegroundMemoryStorageService();
},
deps: [OBSERVABLE_MEMORY_STORAGE],
}),
@@ -494,15 +486,7 @@ const safeProviders: SafeProvider[] = [
}),
safeProvider({
provide: INTRAPROCESS_MESSAGING_SUBJECT,
- useFactory: () => {
- if (BrowserPopupUtils.backgroundInitializationRequired()) {
- // There is no persistent main background which means we have one in memory,
- // we need the same instance that our in memory background is utilizing.
- return getBgService("intraprocessMessagingSubject")();
- } else {
- return new Subject>>();
- }
- },
+ useFactory: () => new Subject>>(),
deps: [],
}),
safeProvider({
@@ -514,23 +498,6 @@ const safeProviders: SafeProvider[] = [
),
deps: [INTRAPROCESS_MESSAGING_SUBJECT, LogService],
}),
- safeProvider({
- provide: INTRAPROCESS_MESSAGING_SUBJECT,
- useFactory: () => {
- if (needsBackgroundInit) {
- // We will have created a popup within this context, in that case
- // we want to make sure we have the same subject as that context so we
- // can message with it.
- return getBgService("intraprocessMessagingSubject")();
- } else {
- // There isn't a locally created background so we will communicate with
- // the true background through chrome apis, in that case, we can just create
- // one for ourself.
- return new Subject>>();
- }
- },
- deps: [],
- }),
safeProvider({
provide: DISK_BACKUP_LOCAL_STORAGE,
useFactory: (diskStorage: AbstractStorageService & ObservableStorageService) =>
@@ -572,13 +539,7 @@ const safeProviders: SafeProvider[] = [
}),
safeProvider({
provide: ForegroundTaskSchedulerService,
- useFactory: (logService: LogService, stateProvider: StateProvider) => {
- if (needsBackgroundInit) {
- return getBgService("taskSchedulerService")();
- }
-
- return new ForegroundTaskSchedulerService(logService, stateProvider);
- },
+ useClass: ForegroundTaskSchedulerService,
deps: [LogService, StateProvider],
}),
safeProvider({
diff --git a/apps/web/src/app/admin-console/organizations/members/members.component.ts b/apps/web/src/app/admin-console/organizations/members/members.component.ts
index e61348e3841..dd3965f94cf 100644
--- a/apps/web/src/app/admin-console/organizations/members/members.component.ts
+++ b/apps/web/src/app/admin-console/organizations/members/members.component.ts
@@ -486,7 +486,7 @@ export class MembersComponent extends BaseMembersComponent
const enableUpgradePasswordManagerSub = await firstValueFrom(
this.enableUpgradePasswordManagerSub$,
);
- if (enableUpgradePasswordManagerSub) {
+ if (enableUpgradePasswordManagerSub && this.organization.canEditSubscription) {
const reference = openChangePlanDialog(this.dialogService, {
data: {
organizationId: this.organization.id,
diff --git a/apps/web/src/app/tools/access-intelligence/access-intelligence.component.html b/apps/web/src/app/tools/access-intelligence/access-intelligence.component.html
index 738b3433890..0f62a434648 100644
--- a/apps/web/src/app/tools/access-intelligence/access-intelligence.component.html
+++ b/apps/web/src/app/tools/access-intelligence/access-intelligence.component.html
@@ -15,7 +15,7 @@
{{ "refresh" | i18n }}
-
+
diff --git a/apps/web/src/app/tools/access-intelligence/access-intelligence.component.ts b/apps/web/src/app/tools/access-intelligence/access-intelligence.component.ts
index da48039ce3e..557ae73625a 100644
--- a/apps/web/src/app/tools/access-intelligence/access-intelligence.component.ts
+++ b/apps/web/src/app/tools/access-intelligence/access-intelligence.component.ts
@@ -1,8 +1,7 @@
import { CommonModule } from "@angular/common";
import { Component } from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
-import { ActivatedRoute } from "@angular/router";
-import { first } from "rxjs";
+import { ActivatedRoute, Router } from "@angular/router";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { AsyncActionsModule, ButtonModule, TabsModule } from "@bitwarden/components";
@@ -18,7 +17,7 @@ import { PasswordHealthComponent } from "./password-health.component";
export enum AccessIntelligenceTabType {
AllApps = 0,
- PriorityApps = 1,
+ CriticalApps = 1,
NotifiedMembers = 2,
}
@@ -58,8 +57,19 @@ export class AccessIntelligenceComponent {
);
}
- constructor(route: ActivatedRoute) {
- route.queryParams.pipe(takeUntilDestroyed(), first()).subscribe(({ tabIndex }) => {
+ onTabChange = async (newIndex: number) => {
+ await this.router.navigate([], {
+ relativeTo: this.route,
+ queryParams: { tabIndex: newIndex },
+ queryParamsHandling: "merge",
+ });
+ };
+
+ constructor(
+ protected route: ActivatedRoute,
+ private router: Router,
+ ) {
+ route.queryParams.pipe(takeUntilDestroyed()).subscribe(({ tabIndex }) => {
this.tabIndex = !isNaN(tabIndex) ? tabIndex : AccessIntelligenceTabType.AllApps;
});
}
diff --git a/apps/web/src/app/tools/access-intelligence/all-applications.component.html b/apps/web/src/app/tools/access-intelligence/all-applications.component.html
index bd0a90859ac..5dfaa202402 100644
--- a/apps/web/src/app/tools/access-intelligence/all-applications.component.html
+++ b/apps/web/src/app/tools/access-intelligence/all-applications.component.html
@@ -14,13 +14,15 @@
-
- {{ "noAppsInOrgDescription" | i18n }}
+
-
@@ -50,7 +52,15 @@
class="tw-grow"
[formControl]="searchControl"
>
-
+
{{ "markAppAsCritical" | i18n }}
diff --git a/apps/web/src/app/tools/access-intelligence/all-applications.component.ts b/apps/web/src/app/tools/access-intelligence/all-applications.component.ts
index 245314673a5..1b648567df2 100644
--- a/apps/web/src/app/tools/access-intelligence/all-applications.component.ts
+++ b/apps/web/src/app/tools/access-intelligence/all-applications.component.ts
@@ -40,6 +40,7 @@ export class AllApplicationsComponent implements OnInit {
protected loading = false;
protected organization: Organization;
noItemsIcon = Icons.Security;
+ protected markingAsCritical = false;
// MOCK DATA
protected mockData = applicationTableMockData;
@@ -76,8 +77,18 @@ export class AllApplicationsComponent implements OnInit {
.subscribe((v) => (this.dataSource.filter = v));
}
+ goToCreateNewLoginItem = async () => {
+ // TODO: implement
+ this.toastService.showToast({
+ variant: "warning",
+ title: null,
+ message: "Not yet implemented",
+ });
+ };
+
markAppsAsCritical = async () => {
// TODO: Send to API once implemented
+ this.markingAsCritical = true;
return new Promise((resolve) => {
setTimeout(() => {
this.selectedIds.clear();
@@ -87,6 +98,7 @@ export class AllApplicationsComponent implements OnInit {
message: this.i18nService.t("appsMarkedAsCritical"),
});
resolve(true);
+ this.markingAsCritical = false;
}, 1000);
});
};
diff --git a/apps/web/src/app/tools/access-intelligence/critical-applications.component.html b/apps/web/src/app/tools/access-intelligence/critical-applications.component.html
index e03988fbf9e..1c503f3d786 100644
--- a/apps/web/src/app/tools/access-intelligence/critical-applications.component.html
+++ b/apps/web/src/app/tools/access-intelligence/critical-applications.component.html
@@ -19,7 +19,9 @@
- {{ "markCriticalApps" | i18n }}
+
+ {{ "markCriticalApps" | i18n }}
+
diff --git a/apps/web/src/app/tools/access-intelligence/critical-applications.component.ts b/apps/web/src/app/tools/access-intelligence/critical-applications.component.ts
index 545ba14d21c..a5df519fd80 100644
--- a/apps/web/src/app/tools/access-intelligence/critical-applications.component.ts
+++ b/apps/web/src/app/tools/access-intelligence/critical-applications.component.ts
@@ -1,7 +1,7 @@
import { Component, DestroyRef, inject, OnInit } from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { FormControl } from "@angular/forms";
-import { ActivatedRoute } from "@angular/router";
+import { ActivatedRoute, Router } from "@angular/router";
import { debounceTime, map } from "rxjs";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
@@ -12,6 +12,7 @@ import { HeaderModule } from "../../layouts/header/header.module";
import { SharedModule } from "../../shared";
import { PipesModule } from "../../vault/individual-vault/pipes/pipes.module";
+import { AccessIntelligenceTabType } from "./access-intelligence.component";
import { applicationTableMockData } from "./application-table.mock";
@Component({
@@ -26,8 +27,10 @@ export class CriticalApplicationsComponent implements OnInit {
protected searchControl = new FormControl("", { nonNullable: true });
private destroyRef = inject(DestroyRef);
protected loading = false;
+ protected organizationId: string;
noItemsIcon = Icons.Security;
// MOCK DATA
+ protected mockData = applicationTableMockData;
protected mockAtRiskMembersCount = 0;
protected mockAtRiskAppsCount = 0;
protected mockTotalMembersCount = 0;
@@ -38,18 +41,26 @@ export class CriticalApplicationsComponent implements OnInit {
.pipe(
takeUntilDestroyed(this.destroyRef),
map(async (params) => {
- // const organizationId = params.get("organizationId");
+ this.organizationId = params.get("organizationId");
// TODO: use organizationId to fetch data
}),
)
.subscribe();
}
+ goToAllAppsTab = async () => {
+ await this.router.navigate([`organizations/${this.organizationId}/access-intelligence`], {
+ queryParams: { tabIndex: AccessIntelligenceTabType.AllApps },
+ queryParamsHandling: "merge",
+ });
+ };
+
constructor(
protected i18nService: I18nService,
protected activatedRoute: ActivatedRoute,
+ protected router: Router,
) {
- this.dataSource.data = applicationTableMockData;
+ this.dataSource.data = []; //applicationTableMockData;
this.searchControl.valueChanges
.pipe(debounceTime(200), takeUntilDestroyed())
.subscribe((v) => (this.dataSource.filter = v));
diff --git a/libs/common/src/auth/services/user-verification/user-verification.service.spec.ts b/libs/common/src/auth/services/user-verification/user-verification.service.spec.ts
index 02cd6056efb..1538f571cfd 100644
--- a/libs/common/src/auth/services/user-verification/user-verification.service.spec.ts
+++ b/libs/common/src/auth/services/user-verification/user-verification.service.spec.ts
@@ -216,7 +216,7 @@ describe("UserVerificationService", () => {
});
it("returns if verification is successful", async () => {
- keyService.compareAndUpdateKeyHash.mockResolvedValueOnce(true);
+ keyService.compareKeyHash.mockResolvedValueOnce(true);
const result = await sut.verifyUserByMasterPassword(
{
@@ -227,7 +227,7 @@ describe("UserVerificationService", () => {
"email",
);
- expect(keyService.compareAndUpdateKeyHash).toHaveBeenCalled();
+ expect(keyService.compareKeyHash).toHaveBeenCalled();
expect(masterPasswordService.setMasterKeyHash).toHaveBeenCalledWith(
"localHash",
mockUserId,
@@ -240,7 +240,7 @@ describe("UserVerificationService", () => {
});
it("throws if verification fails", async () => {
- keyService.compareAndUpdateKeyHash.mockResolvedValueOnce(false);
+ keyService.compareKeyHash.mockResolvedValueOnce(false);
await expect(
sut.verifyUserByMasterPassword(
@@ -253,7 +253,7 @@ describe("UserVerificationService", () => {
),
).rejects.toThrow("Invalid master password");
- expect(keyService.compareAndUpdateKeyHash).toHaveBeenCalled();
+ expect(keyService.compareKeyHash).toHaveBeenCalled();
expect(masterPasswordService.setMasterKeyHash).not.toHaveBeenCalledWith();
expect(masterPasswordService.setMasterKey).not.toHaveBeenCalledWith();
});
@@ -285,7 +285,7 @@ describe("UserVerificationService", () => {
"email",
);
- expect(keyService.compareAndUpdateKeyHash).not.toHaveBeenCalled();
+ expect(keyService.compareKeyHash).not.toHaveBeenCalled();
expect(masterPasswordService.setMasterKeyHash).toHaveBeenCalledWith(
"localHash",
mockUserId,
@@ -318,7 +318,7 @@ describe("UserVerificationService", () => {
),
).rejects.toThrow("Invalid master password");
- expect(keyService.compareAndUpdateKeyHash).not.toHaveBeenCalled();
+ expect(keyService.compareKeyHash).not.toHaveBeenCalled();
expect(masterPasswordService.setMasterKeyHash).not.toHaveBeenCalledWith();
expect(masterPasswordService.setMasterKey).not.toHaveBeenCalledWith();
});
diff --git a/libs/common/src/auth/services/user-verification/user-verification.service.ts b/libs/common/src/auth/services/user-verification/user-verification.service.ts
index b31ba59c983..5446558a540 100644
--- a/libs/common/src/auth/services/user-verification/user-verification.service.ts
+++ b/libs/common/src/auth/services/user-verification/user-verification.service.ts
@@ -206,9 +206,10 @@ export class UserVerificationService implements UserVerificationServiceAbstracti
let policyOptions: MasterPasswordPolicyResponse | null;
// Client-side verification
if (await this.hasMasterPasswordAndMasterKeyHash(userId)) {
- const passwordValid = await this.keyService.compareAndUpdateKeyHash(
+ const passwordValid = await this.keyService.compareKeyHash(
verification.secret,
masterKey,
+ userId,
);
if (!passwordValid) {
throw new Error(this.i18nService.t("invalidMasterPassword"));
diff --git a/libs/key-management/src/abstractions/key.service.ts b/libs/key-management/src/abstractions/key.service.ts
index 55ffea9db79..0ec3aaafdc6 100644
--- a/libs/key-management/src/abstractions/key.service.ts
+++ b/libs/key-management/src/abstractions/key.service.ts
@@ -204,14 +204,18 @@ export abstract class KeyService {
hashPurpose?: HashPurpose,
): Promise;
/**
- * Compares the provided master password to the stored password hash and server password hash.
- * Updates the stored hash if outdated.
+ * Compares the provided master password to the stored password hash.
* @param masterPassword The user's master password
* @param key The user's master key
+ * @param userId The id of the user to do the operation for.
* @returns True if the provided master password matches either the stored
* key hash or the server key hash
*/
- abstract compareAndUpdateKeyHash(masterPassword: string, masterKey: MasterKey): Promise;
+ abstract compareKeyHash(
+ masterPassword: string,
+ masterKey: MasterKey,
+ userId: UserId,
+ ): Promise;
/**
* Stores the encrypted organization keys and clears any decrypted
* organization keys currently in memory
diff --git a/libs/key-management/src/key.service.spec.ts b/libs/key-management/src/key.service.spec.ts
index 263779f59b3..2b2c6514eb4 100644
--- a/libs/key-management/src/key.service.spec.ts
+++ b/libs/key-management/src/key.service.spec.ts
@@ -733,4 +733,63 @@ describe("keyService", () => {
});
});
});
+
+ describe("compareKeyHash", () => {
+ type TestCase = {
+ masterKey: MasterKey;
+ masterPassword: string | null;
+ storedMasterKeyHash: string;
+ mockReturnedHash: string;
+ expectedToMatch: boolean;
+ };
+
+ const data: TestCase[] = [
+ {
+ masterKey: makeSymmetricCryptoKey(64),
+ masterPassword: "my_master_password",
+ storedMasterKeyHash: "bXlfaGFzaA==",
+ mockReturnedHash: "bXlfaGFzaA==",
+ expectedToMatch: true,
+ },
+ {
+ masterKey: makeSymmetricCryptoKey(64),
+ masterPassword: null,
+ storedMasterKeyHash: "bXlfaGFzaA==",
+ mockReturnedHash: "bXlfaGFzaA==",
+ expectedToMatch: false,
+ },
+ {
+ masterKey: makeSymmetricCryptoKey(64),
+ masterPassword: null,
+ storedMasterKeyHash: null,
+ mockReturnedHash: "bXlfaGFzaA==",
+ expectedToMatch: false,
+ },
+ ];
+
+ it.each(data)(
+ "returns expected match value when calculated hash equals stored hash",
+ async ({
+ masterKey,
+ masterPassword,
+ storedMasterKeyHash,
+ mockReturnedHash,
+ expectedToMatch,
+ }) => {
+ masterPasswordService.masterKeyHashSubject.next(storedMasterKeyHash);
+
+ cryptoFunctionService.pbkdf2
+ .calledWith(masterKey.key, masterPassword, "sha256", 2)
+ .mockResolvedValue(Utils.fromB64ToArray(mockReturnedHash));
+
+ const actualDidMatch = await keyService.compareKeyHash(
+ masterPassword,
+ masterKey,
+ mockUserId,
+ );
+
+ expect(actualDidMatch).toBe(expectedToMatch);
+ },
+ );
+ });
});
diff --git a/libs/key-management/src/key.service.ts b/libs/key-management/src/key.service.ts
index b12db176cec..f2ba24ef5df 100644
--- a/libs/key-management/src/key.service.ts
+++ b/libs/key-management/src/key.service.ts
@@ -319,34 +319,43 @@ export class DefaultKeyService implements KeyServiceAbstraction {
}
// TODO: move to MasterPasswordService
- async compareAndUpdateKeyHash(masterPassword: string, masterKey: MasterKey): Promise {
- const userId = await firstValueFrom(this.stateProvider.activeUserId$);
+ async compareKeyHash(
+ masterPassword: string,
+ masterKey: MasterKey,
+ userId: UserId,
+ ): Promise {
+ if (masterKey == null) {
+ throw new Error("'masterKey' is required to be non-null.");
+ }
+
+ if (masterPassword == null) {
+ // If they don't give us a master password, we can't hash it, and therefore
+ // it will never match what we have stored.
+ return false;
+ }
+
+ // Retrieve the current password hash
const storedPasswordHash = await firstValueFrom(
this.masterPasswordService.masterKeyHash$(userId),
);
- if (masterPassword != null && storedPasswordHash != null) {
- const localKeyHash = await this.hashMasterKey(
- masterPassword,
- masterKey,
- HashPurpose.LocalAuthorization,
- );
- if (localKeyHash != null && storedPasswordHash === localKeyHash) {
- return true;
- }
- // TODO: remove serverKeyHash check in 1-2 releases after everyone's keyHash has been updated
- const serverKeyHash = await this.hashMasterKey(
- masterPassword,
- masterKey,
- HashPurpose.ServerAuthorization,
- );
- if (serverKeyHash != null && storedPasswordHash === serverKeyHash) {
- await this.masterPasswordService.setMasterKeyHash(localKeyHash, userId);
- return true;
- }
+ if (storedPasswordHash == null) {
+ return false;
}
- return false;
+ // Hash the key for local use
+ const localKeyHash = await this.hashMasterKey(
+ masterPassword,
+ masterKey,
+ HashPurpose.LocalAuthorization,
+ );
+
+ // Check if the stored hash is already equal to the hash we create locally
+ if (localKeyHash == null || storedPasswordHash !== localKeyHash) {
+ return false;
+ }
+
+ return true;
}
async setOrgKeys(
diff --git a/libs/vault/src/components/password-reprompt.component.ts b/libs/vault/src/components/password-reprompt.component.ts
index 3cbdfa1416a..21646d6e6aa 100644
--- a/libs/vault/src/components/password-reprompt.component.ts
+++ b/libs/vault/src/components/password-reprompt.component.ts
@@ -1,8 +1,10 @@
import { DialogRef } from "@angular/cdk/dialog";
import { Component } from "@angular/core";
import { FormBuilder, ReactiveFormsModule, Validators } from "@angular/forms";
+import { firstValueFrom, map } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
+import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import {
@@ -43,16 +45,25 @@ export class PasswordRepromptComponent {
protected i18nService: I18nService,
protected formBuilder: FormBuilder,
protected dialogRef: DialogRef,
+ protected accountService: AccountService,
) {}
submit = async () => {
+ const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(map((a) => a?.id)));
+
+ if (userId == null) {
+ throw new Error("An active user is expected while doing password reprompt.");
+ }
+
const storedMasterKey = await this.keyService.getOrDeriveMasterKey(
this.formGroup.value.masterPassword,
+ userId,
);
if (
- !(await this.keyService.compareAndUpdateKeyHash(
+ !(await this.keyService.compareKeyHash(
this.formGroup.value.masterPassword,
storedMasterKey,
+ userId,
))
) {
this.platformUtilsService.showToast(
diff --git a/package-lock.json b/package-lock.json
index 624bceaf25e..8994cfcd373 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -69,7 +69,7 @@
"rxjs": "7.8.1",
"tabbable": "6.2.0",
"tldts": "6.1.58",
- "utf-8-validate": "6.0.4",
+ "utf-8-validate": "6.0.5",
"zone.js": "0.14.10",
"zxcvbn": "4.4.2"
},
@@ -319,6 +319,7 @@
"license": "GPL-3.0"
},
"libs/tools/export/vault-export/vault-export-core": {
+ "name": "@bitwarden/vault-export-core",
"version": "0.0.0",
"license": "GPL-3.0",
"dependencies": {
@@ -34702,10 +34703,11 @@
}
},
"node_modules/utf-8-validate": {
- "version": "6.0.4",
- "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-6.0.4.tgz",
- "integrity": "sha512-xu9GQDeFp+eZ6LnCywXN/zBancWvOpUMzgjLPSjy4BRHSmTelvn2E0DG0o1sTiw5hkCKBHo8rwSKncfRfv2EEQ==",
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-6.0.5.tgz",
+ "integrity": "sha512-EYZR+OpIXp9Y1eG1iueg8KRsY8TuT8VNgnanZ0uA3STqhHQTLwbl+WX76/9X5OY12yQubymBpaBSmMPkSTQcKA==",
"hasInstallScript": true,
+ "license": "MIT",
"dependencies": {
"node-gyp-build": "^4.3.0"
},
diff --git a/package.json b/package.json
index 6f66663ea1e..9b38df0c061 100644
--- a/package.json
+++ b/package.json
@@ -203,7 +203,7 @@
"rxjs": "7.8.1",
"tabbable": "6.2.0",
"tldts": "6.1.58",
- "utf-8-validate": "6.0.4",
+ "utf-8-validate": "6.0.5",
"zone.js": "0.14.10",
"zxcvbn": "4.4.2"
},