mirror of
https://github.com/bitwarden/browser
synced 2025-12-19 17:53:39 +00:00
Merge branch 'main' of github.com:bitwarden/clients
This commit is contained in:
@@ -30,6 +30,19 @@ const filters = {
|
|||||||
safari: ["!build/safari/**/*"],
|
safari: ["!build/safari/**/*"],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a number to a tuple containing two Uint16's
|
||||||
|
* @param num {number} This number is expected to be a integer style number with no decimals
|
||||||
|
*
|
||||||
|
* @returns {number[]} A tuple containing two elements that are both numbers.
|
||||||
|
*/
|
||||||
|
function numToUint16s(num) {
|
||||||
|
var arr = new ArrayBuffer(4);
|
||||||
|
var view = new DataView(arr);
|
||||||
|
view.setUint32(0, num, false);
|
||||||
|
return [view.getUint16(0), view.getUint16(2)];
|
||||||
|
}
|
||||||
|
|
||||||
function buildString() {
|
function buildString() {
|
||||||
var build = "";
|
var build = "";
|
||||||
if (process.env.MANIFEST_VERSION) {
|
if (process.env.MANIFEST_VERSION) {
|
||||||
@@ -258,8 +271,19 @@ function applyBetaLabels(manifest) {
|
|||||||
manifest.short_name = "Bitwarden BETA";
|
manifest.short_name = "Bitwarden BETA";
|
||||||
manifest.description = "THIS EXTENSION IS FOR BETA TESTING BITWARDEN.";
|
manifest.description = "THIS EXTENSION IS FOR BETA TESTING BITWARDEN.";
|
||||||
if (process.env.GITHUB_RUN_ID) {
|
if (process.env.GITHUB_RUN_ID) {
|
||||||
manifest.version_name = `${manifest.version} beta - ${process.env.GITHUB_SHA.slice(0, 8)}`;
|
const existingVersionParts = manifest.version.split("."); // 3 parts expected 2024.4.0
|
||||||
manifest.version = `${manifest.version}.${parseInt(process.env.GITHUB_RUN_ID.slice(-4))}`;
|
|
||||||
|
// GITHUB_RUN_ID is a number like: 8853654662
|
||||||
|
// which will convert to [ 4024, 3206 ]
|
||||||
|
// and a single incremented id of 8853654663 will become [ 4024, 3207 ]
|
||||||
|
const runIdParts = numToUint16s(parseInt(process.env.GITHUB_RUN_ID));
|
||||||
|
|
||||||
|
// Only use the first 2 parts from the given version number and base the other 2 numbers from the GITHUB_RUN_ID
|
||||||
|
// Example: 2024.4.4024.3206
|
||||||
|
const betaVersion = `${existingVersionParts[0]}.${existingVersionParts[1]}.${runIdParts[0]}.${runIdParts[1]}`;
|
||||||
|
|
||||||
|
manifest.version_name = `${betaVersion} beta - ${process.env.GITHUB_SHA.slice(0, 8)}`;
|
||||||
|
manifest.version = betaVersion;
|
||||||
} else {
|
} else {
|
||||||
manifest.version = `${manifest.version}.0`;
|
manifest.version = `${manifest.version}.0`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ import { MemoryStorageService } from "@bitwarden/common/platform/services/memory
|
|||||||
import { MigrationBuilderService } from "@bitwarden/common/platform/services/migration-builder.service";
|
import { MigrationBuilderService } from "@bitwarden/common/platform/services/migration-builder.service";
|
||||||
import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner";
|
import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner";
|
||||||
import { SystemService } from "@bitwarden/common/platform/services/system.service";
|
import { SystemService } from "@bitwarden/common/platform/services/system.service";
|
||||||
import { UserKeyInitService } from "@bitwarden/common/platform/services/user-key-init.service";
|
import { UserAutoUnlockKeyService } from "@bitwarden/common/platform/services/user-auto-unlock-key.service";
|
||||||
import { WebCryptoFunctionService } from "@bitwarden/common/platform/services/web-crypto-function.service";
|
import { WebCryptoFunctionService } from "@bitwarden/common/platform/services/web-crypto-function.service";
|
||||||
import {
|
import {
|
||||||
ActiveUserStateProvider,
|
ActiveUserStateProvider,
|
||||||
@@ -334,7 +334,7 @@ export default class MainBackground {
|
|||||||
billingAccountProfileStateService: BillingAccountProfileStateService;
|
billingAccountProfileStateService: BillingAccountProfileStateService;
|
||||||
// eslint-disable-next-line rxjs/no-exposed-subjects -- Needed to give access to services module
|
// eslint-disable-next-line rxjs/no-exposed-subjects -- Needed to give access to services module
|
||||||
intraprocessMessagingSubject: Subject<Message<object>>;
|
intraprocessMessagingSubject: Subject<Message<object>>;
|
||||||
userKeyInitService: UserKeyInitService;
|
userAutoUnlockKeyService: UserAutoUnlockKeyService;
|
||||||
scriptInjectorService: BrowserScriptInjectorService;
|
scriptInjectorService: BrowserScriptInjectorService;
|
||||||
kdfConfigService: kdfConfigServiceAbstraction;
|
kdfConfigService: kdfConfigServiceAbstraction;
|
||||||
|
|
||||||
@@ -1064,11 +1064,7 @@ export default class MainBackground {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.userKeyInitService = new UserKeyInitService(
|
this.userAutoUnlockKeyService = new UserAutoUnlockKeyService(this.cryptoService);
|
||||||
this.accountService,
|
|
||||||
this.cryptoService,
|
|
||||||
this.logService,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async bootstrap() {
|
async bootstrap() {
|
||||||
@@ -1079,7 +1075,18 @@ export default class MainBackground {
|
|||||||
|
|
||||||
// This is here instead of in in the InitService b/c we don't plan for
|
// This is here instead of in in the InitService b/c we don't plan for
|
||||||
// side effects to run in the Browser InitService.
|
// side effects to run in the Browser InitService.
|
||||||
this.userKeyInitService.listenForActiveUserChangesToSetUserKey();
|
const accounts = await firstValueFrom(this.accountService.accounts$);
|
||||||
|
|
||||||
|
const setUserKeyInMemoryPromises = [];
|
||||||
|
for (const userId of Object.keys(accounts) as UserId[]) {
|
||||||
|
// For each acct, we must await the process of setting the user key in memory
|
||||||
|
// if the auto user key is set to avoid race conditions of any code trying to access
|
||||||
|
// the user key from mem.
|
||||||
|
setUserKeyInMemoryPromises.push(
|
||||||
|
this.userAutoUnlockKeyService.setUserKeyInMemoryIfAutoUserKeySet(userId),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await Promise.all(setUserKeyInMemoryPromises);
|
||||||
|
|
||||||
await (this.i18nService as I18nService).init();
|
await (this.i18nService as I18nService).init();
|
||||||
(this.eventUploadService as EventUploadService).init(true);
|
(this.eventUploadService as EventUploadService).init(true);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import * as path from "path";
|
|||||||
|
|
||||||
import { program } from "commander";
|
import { program } from "commander";
|
||||||
import * as jsdom from "jsdom";
|
import * as jsdom from "jsdom";
|
||||||
|
import { firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
InternalUserDecryptionOptionsServiceAbstraction,
|
InternalUserDecryptionOptionsServiceAbstraction,
|
||||||
@@ -79,7 +80,7 @@ import { MigrationBuilderService } from "@bitwarden/common/platform/services/mig
|
|||||||
import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner";
|
import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner";
|
||||||
import { StateService } from "@bitwarden/common/platform/services/state.service";
|
import { StateService } from "@bitwarden/common/platform/services/state.service";
|
||||||
import { StorageServiceProvider } from "@bitwarden/common/platform/services/storage-service.provider";
|
import { StorageServiceProvider } from "@bitwarden/common/platform/services/storage-service.provider";
|
||||||
import { UserKeyInitService } from "@bitwarden/common/platform/services/user-key-init.service";
|
import { UserAutoUnlockKeyService } from "@bitwarden/common/platform/services/user-auto-unlock-key.service";
|
||||||
import {
|
import {
|
||||||
ActiveUserStateProvider,
|
ActiveUserStateProvider,
|
||||||
DerivedStateProvider,
|
DerivedStateProvider,
|
||||||
@@ -236,7 +237,7 @@ export class Main {
|
|||||||
biometricStateService: BiometricStateService;
|
biometricStateService: BiometricStateService;
|
||||||
billingAccountProfileStateService: BillingAccountProfileStateService;
|
billingAccountProfileStateService: BillingAccountProfileStateService;
|
||||||
providerApiService: ProviderApiServiceAbstraction;
|
providerApiService: ProviderApiServiceAbstraction;
|
||||||
userKeyInitService: UserKeyInitService;
|
userAutoUnlockKeyService: UserAutoUnlockKeyService;
|
||||||
kdfConfigService: KdfConfigServiceAbstraction;
|
kdfConfigService: KdfConfigServiceAbstraction;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -709,11 +710,7 @@ export class Main {
|
|||||||
|
|
||||||
this.providerApiService = new ProviderApiService(this.apiService);
|
this.providerApiService = new ProviderApiService(this.apiService);
|
||||||
|
|
||||||
this.userKeyInitService = new UserKeyInitService(
|
this.userAutoUnlockKeyService = new UserAutoUnlockKeyService(this.cryptoService);
|
||||||
this.accountService,
|
|
||||||
this.cryptoService,
|
|
||||||
this.logService,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async run() {
|
async run() {
|
||||||
@@ -757,7 +754,11 @@ export class Main {
|
|||||||
this.containerService.attachToGlobal(global);
|
this.containerService.attachToGlobal(global);
|
||||||
await this.i18nService.init();
|
await this.i18nService.init();
|
||||||
this.twoFactorService.init();
|
this.twoFactorService.init();
|
||||||
this.userKeyInitService.listenForActiveUserChangesToSetUserKey();
|
|
||||||
|
const activeAccount = await firstValueFrom(this.accountService.activeAccount$);
|
||||||
|
if (activeAccount) {
|
||||||
|
await this.userAutoUnlockKeyService.setUserKeyInMemoryIfAutoUserKeySet(activeAccount.id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import { DOCUMENT } from "@angular/common";
|
import { DOCUMENT } from "@angular/common";
|
||||||
import { Inject, Injectable } from "@angular/core";
|
import { Inject, Injectable } from "@angular/core";
|
||||||
|
import { firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
import { AbstractThemingService } from "@bitwarden/angular/platform/services/theming/theming.service.abstraction";
|
import { AbstractThemingService } from "@bitwarden/angular/platform/services/theming/theming.service.abstraction";
|
||||||
import { WINDOW } from "@bitwarden/angular/services/injection-tokens";
|
import { WINDOW } from "@bitwarden/angular/services/injection-tokens";
|
||||||
import { EventUploadService as EventUploadServiceAbstraction } from "@bitwarden/common/abstractions/event/event-upload.service";
|
import { EventUploadService as EventUploadServiceAbstraction } from "@bitwarden/common/abstractions/event/event-upload.service";
|
||||||
import { NotificationsService as NotificationsServiceAbstraction } from "@bitwarden/common/abstractions/notifications.service";
|
import { NotificationsService as NotificationsServiceAbstraction } from "@bitwarden/common/abstractions/notifications.service";
|
||||||
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { TwoFactorService as TwoFactorServiceAbstraction } from "@bitwarden/common/auth/abstractions/two-factor.service";
|
import { TwoFactorService as TwoFactorServiceAbstraction } from "@bitwarden/common/auth/abstractions/two-factor.service";
|
||||||
import { CryptoService as CryptoServiceAbstraction } from "@bitwarden/common/platform/abstractions/crypto.service";
|
import { CryptoService as CryptoServiceAbstraction } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||||
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
|
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
|
||||||
@@ -12,9 +14,10 @@ import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platfor
|
|||||||
import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
import { StateService as StateServiceAbstraction } from "@bitwarden/common/platform/abstractions/state.service";
|
import { StateService as StateServiceAbstraction } from "@bitwarden/common/platform/abstractions/state.service";
|
||||||
import { ContainerService } from "@bitwarden/common/platform/services/container.service";
|
import { ContainerService } from "@bitwarden/common/platform/services/container.service";
|
||||||
import { UserKeyInitService } from "@bitwarden/common/platform/services/user-key-init.service";
|
import { UserAutoUnlockKeyService } from "@bitwarden/common/platform/services/user-auto-unlock-key.service";
|
||||||
import { EventUploadService } from "@bitwarden/common/services/event/event-upload.service";
|
import { EventUploadService } from "@bitwarden/common/services/event/event-upload.service";
|
||||||
import { VaultTimeoutService } from "@bitwarden/common/services/vault-timeout/vault-timeout.service";
|
import { VaultTimeoutService } from "@bitwarden/common/services/vault-timeout/vault-timeout.service";
|
||||||
|
import { UserId } from "@bitwarden/common/types/guid";
|
||||||
import { SyncService as SyncServiceAbstraction } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
import { SyncService as SyncServiceAbstraction } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
||||||
|
|
||||||
import { I18nRendererService } from "../../platform/services/i18n.renderer.service";
|
import { I18nRendererService } from "../../platform/services/i18n.renderer.service";
|
||||||
@@ -36,7 +39,8 @@ export class InitService {
|
|||||||
private nativeMessagingService: NativeMessagingService,
|
private nativeMessagingService: NativeMessagingService,
|
||||||
private themingService: AbstractThemingService,
|
private themingService: AbstractThemingService,
|
||||||
private encryptService: EncryptService,
|
private encryptService: EncryptService,
|
||||||
private userKeyInitService: UserKeyInitService,
|
private userAutoUnlockKeyService: UserAutoUnlockKeyService,
|
||||||
|
private accountService: AccountService,
|
||||||
@Inject(DOCUMENT) private document: Document,
|
@Inject(DOCUMENT) private document: Document,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@@ -44,7 +48,18 @@ export class InitService {
|
|||||||
return async () => {
|
return async () => {
|
||||||
this.nativeMessagingService.init();
|
this.nativeMessagingService.init();
|
||||||
await this.stateService.init({ runMigrations: false }); // Desktop will run them in main process
|
await this.stateService.init({ runMigrations: false }); // Desktop will run them in main process
|
||||||
this.userKeyInitService.listenForActiveUserChangesToSetUserKey();
|
|
||||||
|
const accounts = await firstValueFrom(this.accountService.accounts$);
|
||||||
|
const setUserKeyInMemoryPromises = [];
|
||||||
|
for (const userId of Object.keys(accounts) as UserId[]) {
|
||||||
|
// For each acct, we must await the process of setting the user key in memory
|
||||||
|
// if the auto user key is set to avoid race conditions of any code trying to access
|
||||||
|
// the user key from mem.
|
||||||
|
setUserKeyInMemoryPromises.push(
|
||||||
|
this.userAutoUnlockKeyService.setUserKeyInMemoryIfAutoUserKeySet(userId),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await Promise.all(setUserKeyInMemoryPromises);
|
||||||
|
|
||||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
// 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
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
|
|||||||
@@ -1,17 +1,19 @@
|
|||||||
import { DOCUMENT } from "@angular/common";
|
import { DOCUMENT } from "@angular/common";
|
||||||
import { Inject, Injectable } from "@angular/core";
|
import { Inject, Injectable } from "@angular/core";
|
||||||
|
import { firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
import { AbstractThemingService } from "@bitwarden/angular/platform/services/theming/theming.service.abstraction";
|
import { AbstractThemingService } from "@bitwarden/angular/platform/services/theming/theming.service.abstraction";
|
||||||
import { WINDOW } from "@bitwarden/angular/services/injection-tokens";
|
import { WINDOW } from "@bitwarden/angular/services/injection-tokens";
|
||||||
import { EventUploadService as EventUploadServiceAbstraction } from "@bitwarden/common/abstractions/event/event-upload.service";
|
import { EventUploadService as EventUploadServiceAbstraction } from "@bitwarden/common/abstractions/event/event-upload.service";
|
||||||
import { NotificationsService as NotificationsServiceAbstraction } from "@bitwarden/common/abstractions/notifications.service";
|
import { NotificationsService as NotificationsServiceAbstraction } from "@bitwarden/common/abstractions/notifications.service";
|
||||||
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { TwoFactorService as TwoFactorServiceAbstraction } from "@bitwarden/common/auth/abstractions/two-factor.service";
|
import { TwoFactorService as TwoFactorServiceAbstraction } from "@bitwarden/common/auth/abstractions/two-factor.service";
|
||||||
import { CryptoService as CryptoServiceAbstraction } from "@bitwarden/common/platform/abstractions/crypto.service";
|
import { CryptoService as CryptoServiceAbstraction } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||||
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
|
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
|
||||||
import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { StateService as StateServiceAbstraction } from "@bitwarden/common/platform/abstractions/state.service";
|
import { StateService as StateServiceAbstraction } from "@bitwarden/common/platform/abstractions/state.service";
|
||||||
import { ContainerService } from "@bitwarden/common/platform/services/container.service";
|
import { ContainerService } from "@bitwarden/common/platform/services/container.service";
|
||||||
import { UserKeyInitService } from "@bitwarden/common/platform/services/user-key-init.service";
|
import { UserAutoUnlockKeyService } from "@bitwarden/common/platform/services/user-auto-unlock-key.service";
|
||||||
import { EventUploadService } from "@bitwarden/common/services/event/event-upload.service";
|
import { EventUploadService } from "@bitwarden/common/services/event/event-upload.service";
|
||||||
import { VaultTimeoutService } from "@bitwarden/common/services/vault-timeout/vault-timeout.service";
|
import { VaultTimeoutService } from "@bitwarden/common/services/vault-timeout/vault-timeout.service";
|
||||||
|
|
||||||
@@ -28,14 +30,21 @@ export class InitService {
|
|||||||
private cryptoService: CryptoServiceAbstraction,
|
private cryptoService: CryptoServiceAbstraction,
|
||||||
private themingService: AbstractThemingService,
|
private themingService: AbstractThemingService,
|
||||||
private encryptService: EncryptService,
|
private encryptService: EncryptService,
|
||||||
private userKeyInitService: UserKeyInitService,
|
private userAutoUnlockKeyService: UserAutoUnlockKeyService,
|
||||||
|
private accountService: AccountService,
|
||||||
@Inject(DOCUMENT) private document: Document,
|
@Inject(DOCUMENT) private document: Document,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
return async () => {
|
return async () => {
|
||||||
await this.stateService.init();
|
await this.stateService.init();
|
||||||
this.userKeyInitService.listenForActiveUserChangesToSetUserKey();
|
|
||||||
|
const activeAccount = await firstValueFrom(this.accountService.activeAccount$);
|
||||||
|
if (activeAccount) {
|
||||||
|
// If there is an active account, we must await the process of setting the user key in memory
|
||||||
|
// if the auto user key is set to avoid race conditions of any code trying to access the user key from mem.
|
||||||
|
await this.userAutoUnlockKeyService.setUserKeyInMemoryIfAutoUserKeySet(activeAccount.id);
|
||||||
|
}
|
||||||
|
|
||||||
setTimeout(() => this.notificationsService.init(), 3000);
|
setTimeout(() => this.notificationsService.init(), 3000);
|
||||||
await this.vaultTimeoutService.init(true);
|
await this.vaultTimeoutService.init(true);
|
||||||
|
|||||||
@@ -53,7 +53,6 @@ import { ProviderApiService } from "@bitwarden/common/admin-console/services/pro
|
|||||||
import { ProviderService } from "@bitwarden/common/admin-console/services/provider.service";
|
import { ProviderService } from "@bitwarden/common/admin-console/services/provider.service";
|
||||||
import { AccountApiService as AccountApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/account-api.service";
|
import { AccountApiService as AccountApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/account-api.service";
|
||||||
import {
|
import {
|
||||||
AccountService,
|
|
||||||
AccountService as AccountServiceAbstraction,
|
AccountService as AccountServiceAbstraction,
|
||||||
InternalAccountService,
|
InternalAccountService,
|
||||||
} from "@bitwarden/common/auth/abstractions/account.service";
|
} from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
@@ -162,7 +161,7 @@ import { MigrationRunner } from "@bitwarden/common/platform/services/migration-r
|
|||||||
import { NoopNotificationsService } from "@bitwarden/common/platform/services/noop-notifications.service";
|
import { NoopNotificationsService } from "@bitwarden/common/platform/services/noop-notifications.service";
|
||||||
import { StateService } from "@bitwarden/common/platform/services/state.service";
|
import { StateService } from "@bitwarden/common/platform/services/state.service";
|
||||||
import { StorageServiceProvider } from "@bitwarden/common/platform/services/storage-service.provider";
|
import { StorageServiceProvider } from "@bitwarden/common/platform/services/storage-service.provider";
|
||||||
import { UserKeyInitService } from "@bitwarden/common/platform/services/user-key-init.service";
|
import { UserAutoUnlockKeyService } from "@bitwarden/common/platform/services/user-auto-unlock-key.service";
|
||||||
import { ValidationService } from "@bitwarden/common/platform/services/validation.service";
|
import { ValidationService } from "@bitwarden/common/platform/services/validation.service";
|
||||||
import { WebCryptoFunctionService } from "@bitwarden/common/platform/services/web-crypto-function.service";
|
import { WebCryptoFunctionService } from "@bitwarden/common/platform/services/web-crypto-function.service";
|
||||||
import {
|
import {
|
||||||
@@ -1128,9 +1127,9 @@ const safeProviders: SafeProvider[] = [
|
|||||||
deps: [StateProvider],
|
deps: [StateProvider],
|
||||||
}),
|
}),
|
||||||
safeProvider({
|
safeProvider({
|
||||||
provide: UserKeyInitService,
|
provide: UserAutoUnlockKeyService,
|
||||||
useClass: UserKeyInitService,
|
useClass: UserAutoUnlockKeyService,
|
||||||
deps: [AccountService, CryptoServiceAbstraction, LogService],
|
deps: [CryptoServiceAbstraction],
|
||||||
}),
|
}),
|
||||||
safeProvider({
|
safeProvider({
|
||||||
provide: ErrorHandler,
|
provide: ErrorHandler,
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
import { mock } from "jest-mock-extended";
|
||||||
|
|
||||||
|
import { CsprngArray } from "../../types/csprng";
|
||||||
|
import { UserId } from "../../types/guid";
|
||||||
|
import { UserKey } from "../../types/key";
|
||||||
|
import { KeySuffixOptions } from "../enums";
|
||||||
|
import { Utils } from "../misc/utils";
|
||||||
|
import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key";
|
||||||
|
|
||||||
|
import { CryptoService } from "./crypto.service";
|
||||||
|
import { UserAutoUnlockKeyService } from "./user-auto-unlock-key.service";
|
||||||
|
|
||||||
|
describe("UserAutoUnlockKeyService", () => {
|
||||||
|
let userAutoUnlockKeyService: UserAutoUnlockKeyService;
|
||||||
|
|
||||||
|
const mockUserId = Utils.newGuid() as UserId;
|
||||||
|
|
||||||
|
const cryptoService = mock<CryptoService>();
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
userAutoUnlockKeyService = new UserAutoUnlockKeyService(cryptoService);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("setUserKeyInMemoryIfAutoUserKeySet", () => {
|
||||||
|
it("does nothing if the userId is null", async () => {
|
||||||
|
// Act
|
||||||
|
await (userAutoUnlockKeyService as any).setUserKeyInMemoryIfAutoUserKeySet(null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(cryptoService.getUserKeyFromStorage).not.toHaveBeenCalled();
|
||||||
|
expect(cryptoService.setUserKey).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does nothing if the autoUserKey is null", async () => {
|
||||||
|
// Arrange
|
||||||
|
const userId = mockUserId;
|
||||||
|
|
||||||
|
cryptoService.getUserKeyFromStorage.mockResolvedValue(null);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await (userAutoUnlockKeyService as any).setUserKeyInMemoryIfAutoUserKeySet(userId);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(cryptoService.getUserKeyFromStorage).toHaveBeenCalledWith(
|
||||||
|
KeySuffixOptions.Auto,
|
||||||
|
userId,
|
||||||
|
);
|
||||||
|
expect(cryptoService.setUserKey).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("sets the user key in memory if the autoUserKey is not null", async () => {
|
||||||
|
// Arrange
|
||||||
|
const userId = mockUserId;
|
||||||
|
|
||||||
|
const mockRandomBytes = new Uint8Array(64) as CsprngArray;
|
||||||
|
const mockAutoUserKey: UserKey = new SymmetricCryptoKey(mockRandomBytes) as UserKey;
|
||||||
|
|
||||||
|
cryptoService.getUserKeyFromStorage.mockResolvedValue(mockAutoUserKey);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await (userAutoUnlockKeyService as any).setUserKeyInMemoryIfAutoUserKeySet(userId);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(cryptoService.getUserKeyFromStorage).toHaveBeenCalledWith(
|
||||||
|
KeySuffixOptions.Auto,
|
||||||
|
userId,
|
||||||
|
);
|
||||||
|
expect(cryptoService.setUserKey).toHaveBeenCalledWith(mockAutoUserKey, userId);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { UserId } from "../../types/guid";
|
||||||
|
import { CryptoService } from "../abstractions/crypto.service";
|
||||||
|
import { KeySuffixOptions } from "../enums";
|
||||||
|
|
||||||
|
// TODO: this is a half measure improvement which allows us to reduce some side effects today (cryptoService.getUserKey setting user key in memory if auto key exists)
|
||||||
|
// but ideally, in the future, we would be able to put this logic into the cryptoService
|
||||||
|
// after the vault timeout settings service is transitioned to state provider so that
|
||||||
|
// the getUserKey logic can simply go to the correct location based on the vault timeout settings
|
||||||
|
// similar to the TokenService (it would either go to secure storage for the auto user key or memory for the user key)
|
||||||
|
|
||||||
|
export class UserAutoUnlockKeyService {
|
||||||
|
constructor(private cryptoService: CryptoService) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The presence of the user key in memory dictates whether the user's vault is locked or unlocked.
|
||||||
|
* However, for users that have the auto unlock user key set, we need to set the user key in memory
|
||||||
|
* on application bootstrap and on active account changes so that the user's vault loads unlocked.
|
||||||
|
* @param userId - The user id to check for an auto user key.
|
||||||
|
*/
|
||||||
|
async setUserKeyInMemoryIfAutoUserKeySet(userId: UserId): Promise<void> {
|
||||||
|
if (userId == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const autoUserKey = await this.cryptoService.getUserKeyFromStorage(
|
||||||
|
KeySuffixOptions.Auto,
|
||||||
|
userId,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (autoUserKey == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.cryptoService.setUserKey(autoUserKey, userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,162 +0,0 @@
|
|||||||
import { mock } from "jest-mock-extended";
|
|
||||||
|
|
||||||
import { FakeAccountService, mockAccountServiceWith } from "../../../spec";
|
|
||||||
import { CsprngArray } from "../../types/csprng";
|
|
||||||
import { UserId } from "../../types/guid";
|
|
||||||
import { UserKey } from "../../types/key";
|
|
||||||
import { LogService } from "../abstractions/log.service";
|
|
||||||
import { KeySuffixOptions } from "../enums";
|
|
||||||
import { Utils } from "../misc/utils";
|
|
||||||
import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key";
|
|
||||||
|
|
||||||
import { CryptoService } from "./crypto.service";
|
|
||||||
import { UserKeyInitService } from "./user-key-init.service";
|
|
||||||
|
|
||||||
describe("UserKeyInitService", () => {
|
|
||||||
let userKeyInitService: UserKeyInitService;
|
|
||||||
|
|
||||||
const mockUserId = Utils.newGuid() as UserId;
|
|
||||||
|
|
||||||
const accountService: FakeAccountService = mockAccountServiceWith(mockUserId);
|
|
||||||
|
|
||||||
const cryptoService = mock<CryptoService>();
|
|
||||||
const logService = mock<LogService>();
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
userKeyInitService = new UserKeyInitService(accountService, cryptoService, logService);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("listenForActiveUserChangesToSetUserKey()", () => {
|
|
||||||
it("calls setUserKeyInMemoryIfAutoUserKeySet if there is an active user", () => {
|
|
||||||
// Arrange
|
|
||||||
accountService.activeAccountSubject.next({
|
|
||||||
id: mockUserId,
|
|
||||||
name: "name",
|
|
||||||
email: "email",
|
|
||||||
});
|
|
||||||
|
|
||||||
(userKeyInitService as any).setUserKeyInMemoryIfAutoUserKeySet = jest.fn();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
|
|
||||||
const subscription = userKeyInitService.listenForActiveUserChangesToSetUserKey();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
|
|
||||||
expect(subscription).not.toBeFalsy();
|
|
||||||
|
|
||||||
expect((userKeyInitService as any).setUserKeyInMemoryIfAutoUserKeySet).toHaveBeenCalledWith(
|
|
||||||
mockUserId,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("calls setUserKeyInMemoryIfAutoUserKeySet if there is an active user and tracks subsequent emissions", () => {
|
|
||||||
// Arrange
|
|
||||||
accountService.activeAccountSubject.next({
|
|
||||||
id: mockUserId,
|
|
||||||
name: "name",
|
|
||||||
email: "email",
|
|
||||||
});
|
|
||||||
|
|
||||||
const mockUser2Id = Utils.newGuid() as UserId;
|
|
||||||
|
|
||||||
jest
|
|
||||||
.spyOn(userKeyInitService as any, "setUserKeyInMemoryIfAutoUserKeySet")
|
|
||||||
.mockImplementation(() => Promise.resolve());
|
|
||||||
|
|
||||||
// Act
|
|
||||||
|
|
||||||
const subscription = userKeyInitService.listenForActiveUserChangesToSetUserKey();
|
|
||||||
|
|
||||||
accountService.activeAccountSubject.next({
|
|
||||||
id: mockUser2Id,
|
|
||||||
name: "name",
|
|
||||||
email: "email",
|
|
||||||
});
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
|
|
||||||
expect(subscription).not.toBeFalsy();
|
|
||||||
|
|
||||||
expect((userKeyInitService as any).setUserKeyInMemoryIfAutoUserKeySet).toHaveBeenCalledTimes(
|
|
||||||
2,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(
|
|
||||||
(userKeyInitService as any).setUserKeyInMemoryIfAutoUserKeySet,
|
|
||||||
).toHaveBeenNthCalledWith(1, mockUserId);
|
|
||||||
expect(
|
|
||||||
(userKeyInitService as any).setUserKeyInMemoryIfAutoUserKeySet,
|
|
||||||
).toHaveBeenNthCalledWith(2, mockUser2Id);
|
|
||||||
|
|
||||||
subscription.unsubscribe();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("does not call setUserKeyInMemoryIfAutoUserKeySet if there is not an active user", () => {
|
|
||||||
// Arrange
|
|
||||||
accountService.activeAccountSubject.next(null);
|
|
||||||
|
|
||||||
(userKeyInitService as any).setUserKeyInMemoryIfAutoUserKeySet = jest.fn();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
|
|
||||||
const subscription = userKeyInitService.listenForActiveUserChangesToSetUserKey();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
|
|
||||||
expect(subscription).not.toBeFalsy();
|
|
||||||
|
|
||||||
expect(
|
|
||||||
(userKeyInitService as any).setUserKeyInMemoryIfAutoUserKeySet,
|
|
||||||
).not.toHaveBeenCalledWith(mockUserId);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("setUserKeyInMemoryIfAutoUserKeySet", () => {
|
|
||||||
it("does nothing if the userId is null", async () => {
|
|
||||||
// Act
|
|
||||||
await (userKeyInitService as any).setUserKeyInMemoryIfAutoUserKeySet(null);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
expect(cryptoService.getUserKeyFromStorage).not.toHaveBeenCalled();
|
|
||||||
expect(cryptoService.setUserKey).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("does nothing if the autoUserKey is null", async () => {
|
|
||||||
// Arrange
|
|
||||||
const userId = mockUserId;
|
|
||||||
|
|
||||||
cryptoService.getUserKeyFromStorage.mockResolvedValue(null);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
await (userKeyInitService as any).setUserKeyInMemoryIfAutoUserKeySet(userId);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
expect(cryptoService.getUserKeyFromStorage).toHaveBeenCalledWith(
|
|
||||||
KeySuffixOptions.Auto,
|
|
||||||
userId,
|
|
||||||
);
|
|
||||||
expect(cryptoService.setUserKey).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("sets the user key in memory if the autoUserKey is not null", async () => {
|
|
||||||
// Arrange
|
|
||||||
const userId = mockUserId;
|
|
||||||
|
|
||||||
const mockRandomBytes = new Uint8Array(64) as CsprngArray;
|
|
||||||
const mockAutoUserKey: UserKey = new SymmetricCryptoKey(mockRandomBytes) as UserKey;
|
|
||||||
|
|
||||||
cryptoService.getUserKeyFromStorage.mockResolvedValue(mockAutoUserKey);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
await (userKeyInitService as any).setUserKeyInMemoryIfAutoUserKeySet(userId);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
expect(cryptoService.getUserKeyFromStorage).toHaveBeenCalledWith(
|
|
||||||
KeySuffixOptions.Auto,
|
|
||||||
userId,
|
|
||||||
);
|
|
||||||
expect(cryptoService.setUserKey).toHaveBeenCalledWith(mockAutoUserKey, userId);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
import { EMPTY, Subscription, catchError, filter, from, switchMap } from "rxjs";
|
|
||||||
|
|
||||||
import { AccountService } from "../../auth/abstractions/account.service";
|
|
||||||
import { UserId } from "../../types/guid";
|
|
||||||
import { CryptoService } from "../abstractions/crypto.service";
|
|
||||||
import { LogService } from "../abstractions/log.service";
|
|
||||||
import { KeySuffixOptions } from "../enums";
|
|
||||||
|
|
||||||
// TODO: this is a half measure improvement which allows us to reduce some side effects today (cryptoService.getUserKey setting user key in memory if auto key exists)
|
|
||||||
// but ideally, in the future, we would be able to put this logic into the cryptoService
|
|
||||||
// after the vault timeout settings service is transitioned to state provider so that
|
|
||||||
// the getUserKey logic can simply go to the correct location based on the vault timeout settings
|
|
||||||
// similar to the TokenService (it would either go to secure storage for the auto user key or memory for the user key)
|
|
||||||
|
|
||||||
export class UserKeyInitService {
|
|
||||||
constructor(
|
|
||||||
private accountService: AccountService,
|
|
||||||
private cryptoService: CryptoService,
|
|
||||||
private logService: LogService,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
// Note: must listen for changes to support account switching
|
|
||||||
listenForActiveUserChangesToSetUserKey(): Subscription {
|
|
||||||
return this.accountService.activeAccount$
|
|
||||||
.pipe(
|
|
||||||
filter((activeAccount) => activeAccount != null),
|
|
||||||
switchMap((activeAccount) =>
|
|
||||||
from(this.setUserKeyInMemoryIfAutoUserKeySet(activeAccount?.id)).pipe(
|
|
||||||
catchError((err: unknown) => {
|
|
||||||
this.logService.warning(
|
|
||||||
`setUserKeyInMemoryIfAutoUserKeySet failed with error: ${err}`,
|
|
||||||
);
|
|
||||||
// Returning EMPTY to protect observable chain from cancellation in case of error
|
|
||||||
return EMPTY;
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.subscribe();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async setUserKeyInMemoryIfAutoUserKeySet(userId: UserId) {
|
|
||||||
if (userId == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const autoUserKey = await this.cryptoService.getUserKeyFromStorage(
|
|
||||||
KeySuffixOptions.Auto,
|
|
||||||
userId,
|
|
||||||
);
|
|
||||||
if (autoUserKey == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.cryptoService.setUserKey(autoUserKey, userId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
36
package-lock.json
generated
36
package-lock.json
generated
@@ -114,7 +114,7 @@
|
|||||||
"@types/node-ipc": "9.2.3",
|
"@types/node-ipc": "9.2.3",
|
||||||
"@types/papaparse": "5.3.14",
|
"@types/papaparse": "5.3.14",
|
||||||
"@types/proper-lockfile": "4.1.4",
|
"@types/proper-lockfile": "4.1.4",
|
||||||
"@types/react": "16.14.57",
|
"@types/react": "16.14.60",
|
||||||
"@types/retry": "0.12.5",
|
"@types/retry": "0.12.5",
|
||||||
"@types/zxcvbn": "4.4.4",
|
"@types/zxcvbn": "4.4.4",
|
||||||
"@typescript-eslint/eslint-plugin": "7.7.1",
|
"@typescript-eslint/eslint-plugin": "7.7.1",
|
||||||
@@ -163,8 +163,8 @@
|
|||||||
"prettier": "3.2.2",
|
"prettier": "3.2.2",
|
||||||
"prettier-plugin-tailwindcss": "0.5.14",
|
"prettier-plugin-tailwindcss": "0.5.14",
|
||||||
"process": "0.11.10",
|
"process": "0.11.10",
|
||||||
"react": "18.2.0",
|
"react": "18.3.1",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.3.1",
|
||||||
"regedit": "^3.0.3",
|
"regedit": "^3.0.3",
|
||||||
"remark-gfm": "3.0.1",
|
"remark-gfm": "3.0.1",
|
||||||
"rimraf": "5.0.5",
|
"rimraf": "5.0.5",
|
||||||
@@ -10618,13 +10618,13 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/react": {
|
"node_modules/@types/react": {
|
||||||
"version": "16.14.57",
|
"version": "16.14.60",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.14.57.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.14.60.tgz",
|
||||||
"integrity": "sha512-fuNq/GV1a6GgqSuVuC457vYeTbm4E1CUBQVZwSPxqYnRhIzSXCJ1gGqyv+PKhqLyfbKCga9dXHJDzv+4XE41fw==",
|
"integrity": "sha512-wIFmnczGsTcgwCBeIYOuy2mdXEiKZ5znU/jNOnMZPQyCcIxauMGWlX0TNG4lZ7NxRKj7YUIZRneJQSSdB2jKgg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/prop-types": "*",
|
"@types/prop-types": "*",
|
||||||
"@types/scheduler": "*",
|
"@types/scheduler": "^0.16",
|
||||||
"csstype": "^3.0.2"
|
"csstype": "^3.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -32085,9 +32085,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react": {
|
"node_modules/react": {
|
||||||
"version": "18.2.0",
|
"version": "18.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
||||||
"integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
|
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"loose-envify": "^1.1.0"
|
"loose-envify": "^1.1.0"
|
||||||
@@ -32107,16 +32107,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-dom": {
|
"node_modules/react-dom": {
|
||||||
"version": "18.2.0",
|
"version": "18.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
|
||||||
"integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
|
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"loose-envify": "^1.1.0",
|
"loose-envify": "^1.1.0",
|
||||||
"scheduler": "^0.23.0"
|
"scheduler": "^0.23.2"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "^18.2.0"
|
"react": "^18.3.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-is": {
|
"node_modules/react-is": {
|
||||||
@@ -33604,9 +33604,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/scheduler": {
|
"node_modules/scheduler": {
|
||||||
"version": "0.23.0",
|
"version": "0.23.2",
|
||||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
|
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
|
||||||
"integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==",
|
"integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"loose-envify": "^1.1.0"
|
"loose-envify": "^1.1.0"
|
||||||
|
|||||||
@@ -75,7 +75,7 @@
|
|||||||
"@types/node-ipc": "9.2.3",
|
"@types/node-ipc": "9.2.3",
|
||||||
"@types/papaparse": "5.3.14",
|
"@types/papaparse": "5.3.14",
|
||||||
"@types/proper-lockfile": "4.1.4",
|
"@types/proper-lockfile": "4.1.4",
|
||||||
"@types/react": "16.14.57",
|
"@types/react": "16.14.60",
|
||||||
"@types/retry": "0.12.5",
|
"@types/retry": "0.12.5",
|
||||||
"@types/zxcvbn": "4.4.4",
|
"@types/zxcvbn": "4.4.4",
|
||||||
"@typescript-eslint/eslint-plugin": "7.7.1",
|
"@typescript-eslint/eslint-plugin": "7.7.1",
|
||||||
@@ -124,8 +124,8 @@
|
|||||||
"prettier": "3.2.2",
|
"prettier": "3.2.2",
|
||||||
"prettier-plugin-tailwindcss": "0.5.14",
|
"prettier-plugin-tailwindcss": "0.5.14",
|
||||||
"process": "0.11.10",
|
"process": "0.11.10",
|
||||||
"react": "18.2.0",
|
"react": "18.3.1",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.3.1",
|
||||||
"regedit": "^3.0.3",
|
"regedit": "^3.0.3",
|
||||||
"remark-gfm": "3.0.1",
|
"remark-gfm": "3.0.1",
|
||||||
"rimraf": "5.0.5",
|
"rimraf": "5.0.5",
|
||||||
@@ -213,7 +213,7 @@
|
|||||||
"replacestream": "4.0.3"
|
"replacestream": "4.0.3"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"@types/react": "16.14.57"
|
"@types/react": "16.14.60"
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"*": "prettier --cache --ignore-unknown --write",
|
"*": "prettier --cache --ignore-unknown --write",
|
||||||
|
|||||||
Reference in New Issue
Block a user