1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-17 16:53:34 +00:00

Merge branch 'main' into vault/pm-5273

# Conflicts:
#	apps/browser/src/popup/app.component.ts
#	libs/common/src/state-migrations/migrate.ts
This commit is contained in:
Carlos Gonçalves
2024-03-12 20:05:58 +00:00
129 changed files with 3197 additions and 1250 deletions

View File

@@ -26,6 +26,10 @@ import {
factory,
FactoryOptions,
} from "../../../platform/background/service-factories/factory-options";
import {
globalStateProviderFactory,
GlobalStateProviderInitOptions,
} from "../../../platform/background/service-factories/global-state-provider.factory";
import {
i18nServiceFactory,
I18nServiceInitOptions,
@@ -84,7 +88,8 @@ export type LoginStrategyServiceInitOptions = LoginStrategyServiceFactoryOptions
PolicyServiceInitOptions &
PasswordStrengthServiceInitOptions &
DeviceTrustCryptoServiceInitOptions &
AuthRequestServiceInitOptions;
AuthRequestServiceInitOptions &
GlobalStateProviderInitOptions;
export function loginStrategyServiceFactory(
cache: { loginStrategyService?: LoginStrategyServiceAbstraction } & CachedServices,
@@ -113,6 +118,7 @@ export function loginStrategyServiceFactory(
await policyServiceFactory(cache, opts),
await deviceTrustCryptoServiceFactory(cache, opts),
await authRequestServiceFactory(cache, opts),
await globalStateProviderFactory(cache, opts),
),
);
}

View File

@@ -1,3 +1,4 @@
import { NeverDomains } from "@bitwarden/common/models/domain/domain-service";
import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
import { NotificationQueueMessageTypes } from "../../enums/notification-queue-message-type.enum";
@@ -111,6 +112,7 @@ type NotificationBackgroundExtensionMessageHandlers = {
collectPageDetailsResponse: ({ message }: BackgroundMessageParam) => Promise<void>;
bgGetEnableChangedPasswordPrompt: () => Promise<boolean>;
bgGetEnableAddedLoginPrompt: () => Promise<boolean>;
bgGetExcludedDomains: () => Promise<NeverDomains>;
getWebVaultUrlForNotification: () => string;
};

View File

@@ -4,6 +4,7 @@ import { firstValueFrom } from "rxjs";
import { PolicyService } from "@bitwarden/common/admin-console/services/policy/policy.service";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { AuthService } from "@bitwarden/common/auth/services/auth.service";
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
import { UserNotificationSettingsService } from "@bitwarden/common/autofill/services/user-notification-settings.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { EnvironmentService } from "@bitwarden/common/platform/services/environment.service";
@@ -47,6 +48,7 @@ describe("NotificationBackground", () => {
const folderService = mock<FolderService>();
const stateService = mock<BrowserStateService>();
const userNotificationSettingsService = mock<UserNotificationSettingsService>();
const domainSettingsService = mock<DomainSettingsService>();
const environmentService = mock<EnvironmentService>();
const logService = mock<LogService>();
@@ -59,6 +61,7 @@ describe("NotificationBackground", () => {
folderService,
stateService,
userNotificationSettingsService,
domainSettingsService,
environmentService,
logService,
);

View File

@@ -5,7 +5,9 @@ import { PolicyType } from "@bitwarden/common/admin-console/enums";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { NOTIFICATION_BAR_LIFESPAN_MS } from "@bitwarden/common/autofill/constants";
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
import { UserNotificationSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/user-notification-settings.service";
import { NeverDomains } from "@bitwarden/common/models/domain/domain-service";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
@@ -60,6 +62,7 @@ export default class NotificationBackground {
bgReopenUnlockPopout: ({ sender }) => this.openUnlockPopout(sender.tab),
bgGetEnableChangedPasswordPrompt: () => this.getEnableChangedPasswordPrompt(),
bgGetEnableAddedLoginPrompt: () => this.getEnableAddedLoginPrompt(),
bgGetExcludedDomains: () => this.getExcludedDomains(),
getWebVaultUrlForNotification: () => this.getWebVaultUrl(),
};
@@ -71,6 +74,7 @@ export default class NotificationBackground {
private folderService: FolderService,
private stateService: BrowserStateService,
private userNotificationSettingsService: UserNotificationSettingsServiceAbstraction,
private domainSettingsService: DomainSettingsService,
private environmentService: EnvironmentService,
private logService: LogService,
) {}
@@ -99,6 +103,13 @@ export default class NotificationBackground {
return await firstValueFrom(this.userNotificationSettingsService.enableAddedLoginPrompt$);
}
/**
* Gets the neverDomains setting from the domain settings service.
*/
async getExcludedDomains(): Promise<NeverDomains> {
return await firstValueFrom(this.domainSettingsService.neverDomains$);
}
/**
* Checks the notification queue for any messages that need to be sent to the
* specified tab. If no tab is specified, the current tab will be used.

View File

@@ -17,8 +17,8 @@ import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { CipherService } from "@bitwarden/common/vault/services/cipher.service";
import { BrowserApi } from "../../platform/browser/browser-api";
import BrowserPlatformUtilsService from "../../platform/services/browser-platform-utils.service";
import { BrowserStateService } from "../../platform/services/browser-state.service";
import { BrowserPlatformUtilsService } from "../../platform/services/platform-utils/browser-platform-utils.service";
import { AutofillService } from "../services/abstractions/autofill.service";
import {
createAutofillPageDetailsMock,

View File

@@ -6,10 +6,6 @@ import {
EventCollectionServiceInitOptions,
eventCollectionServiceFactory,
} from "../../../background/service-factories/event-collection-service.factory";
import {
settingsServiceFactory,
SettingsServiceInitOptions,
} from "../../../background/service-factories/settings-service.factory";
import {
CachedServices,
factory,
@@ -38,6 +34,10 @@ import {
AutofillSettingsServiceInitOptions,
autofillSettingsServiceFactory,
} from "./autofill-settings-service.factory";
import {
DomainSettingsServiceInitOptions,
domainSettingsServiceFactory,
} from "./domain-settings-service.factory";
type AutoFillServiceOptions = FactoryOptions;
@@ -48,8 +48,8 @@ export type AutoFillServiceInitOptions = AutoFillServiceOptions &
TotpServiceInitOptions &
EventCollectionServiceInitOptions &
LogServiceInitOptions &
SettingsServiceInitOptions &
UserVerificationServiceInitOptions;
UserVerificationServiceInitOptions &
DomainSettingsServiceInitOptions;
export function autofillServiceFactory(
cache: { autofillService?: AbstractAutoFillService } & CachedServices,
@@ -67,7 +67,7 @@ export function autofillServiceFactory(
await totpServiceFactory(cache, opts),
await eventCollectionServiceFactory(cache, opts),
await logServiceFactory(cache, opts),
await settingsServiceFactory(cache, opts),
await domainSettingsServiceFactory(cache, opts),
await userVerificationServiceFactory(cache, opts),
),
);

View File

@@ -0,0 +1,25 @@
import { DefaultDomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
import {
CachedServices,
factory,
FactoryOptions,
} from "../../../platform/background/service-factories/factory-options";
import {
stateProviderFactory,
StateProviderInitOptions,
} from "../../../platform/background/service-factories/state-provider.factory";
export type DomainSettingsServiceInitOptions = FactoryOptions & StateProviderInitOptions;
export function domainSettingsServiceFactory(
cache: { domainSettingsService?: DefaultDomainSettingsService } & CachedServices,
opts: DomainSettingsServiceInitOptions,
): Promise<DefaultDomainSettingsService> {
return factory(
cache,
"domainSettingsService",
opts,
async () => new DefaultDomainSettingsService(await stateProviderFactory(cache, opts)),
);
}

View File

@@ -1,8 +1,8 @@
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { UriMatchStrategy } from "@bitwarden/common/models/domain/domain-service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { UriMatchType } from "@bitwarden/common/vault/enums";
import { BrowserApi } from "../../platform/browser/browser-api";
@@ -73,7 +73,7 @@ export default class WebRequestBackground {
const ciphers = await this.cipherService.getAllDecryptedForUrl(
domain,
null,
UriMatchType.Host,
UriMatchStrategy.Host,
);
if (ciphers == null || ciphers.length !== 1) {
error();

View File

@@ -6,7 +6,7 @@ import AutofillField from "../models/autofill-field";
import { WatchedForm } from "../models/watched-form";
import { NotificationBarIframeInitData } from "../notification/abstractions/notification-bar";
import { FormData } from "../services/abstractions/autofill.service";
import { GlobalSettings, UserSettings } from "../types";
import { UserSettings } from "../types";
import {
getFromLocalStorage,
sendExtensionMessage,
@@ -94,10 +94,11 @@ async function loadNotificationBar() {
"bgGetEnableChangedPasswordPrompt",
);
const enableAddedLoginPrompt = await sendExtensionMessage("bgGetEnableAddedLoginPrompt");
const excludedDomains = await sendExtensionMessage("bgGetExcludedDomains");
let showNotificationBar = true;
// Look up the active user id from storage
const activeUserIdKey = "activeUserId";
const globalStorageKey = "global";
let activeUserId: string;
const activeUserStorageValue = await getFromLocalStorage(activeUserIdKey);
@@ -109,9 +110,6 @@ async function loadNotificationBar() {
const userSettingsStorageValue = await getFromLocalStorage(activeUserId);
if (userSettingsStorageValue[activeUserId]) {
const userSettings: UserSettings = userSettingsStorageValue[activeUserId].settings;
const globalSettings: GlobalSettings = (await getFromLocalStorage(globalStorageKey))[
globalStorageKey
];
// Do not show the notification bar on the Bitwarden vault
// because they can add logins and change passwords there
@@ -122,8 +120,8 @@ async function loadNotificationBar() {
// show the notification bar on (for login detail collection or password change).
// It is managed in the Settings > Excluded Domains page in the browser extension.
// Example: '{"bitwarden.com":null}'
const excludedDomainsDict = globalSettings.neverDomains;
if (!excludedDomainsDict || !(window.location.hostname in excludedDomainsDict)) {
if (!excludedDomains || !(window.location.hostname in excludedDomains)) {
if (enableAddedLoginPrompt || enableChangedPasswordPrompt) {
// If the user has not disabled both notifications, then handle the initial page change (null -> actual page)
handlePageChange();

View File

@@ -1,14 +1,16 @@
import { Component, OnInit } from "@angular/core";
import { firstValueFrom } from "rxjs";
import { SettingsService } from "@bitwarden/common/abstractions/settings.service";
import { AutofillOverlayVisibility } from "@bitwarden/common/autofill/constants";
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
import { InlineMenuVisibilitySetting } from "@bitwarden/common/autofill/types";
import {
UriMatchStrategy,
UriMatchStrategySetting,
} from "@bitwarden/common/models/domain/domain-service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { UriMatchType } from "@bitwarden/common/vault/enums";
import { DialogService } from "@bitwarden/components";
import { BrowserApi } from "../../../platform/browser/browser-api";
@@ -28,16 +30,15 @@ export class AutofillComponent implements OnInit {
enableAutoFillOnPageLoad = false;
autoFillOnPageLoadDefault = false;
autoFillOnPageLoadOptions: any[];
defaultUriMatch = UriMatchType.Domain;
defaultUriMatch: UriMatchStrategySetting = UriMatchStrategy.Domain;
uriMatchOptions: any[];
autofillKeyboardHelperText: string;
accountSwitcherEnabled = false;
constructor(
private stateService: StateService,
private i18nService: I18nService,
private platformUtilsService: PlatformUtilsService,
private settingsService: SettingsService,
private domainSettingsService: DomainSettingsService,
private autofillService: AutofillService,
private dialogService: DialogService,
private autofillSettingsService: AutofillSettingsServiceAbstraction,
@@ -61,12 +62,12 @@ export class AutofillComponent implements OnInit {
{ name: i18nService.t("autoFillOnPageLoadNo"), value: false },
];
this.uriMatchOptions = [
{ name: i18nService.t("baseDomain"), value: UriMatchType.Domain },
{ name: i18nService.t("host"), value: UriMatchType.Host },
{ name: i18nService.t("startsWith"), value: UriMatchType.StartsWith },
{ name: i18nService.t("regEx"), value: UriMatchType.RegularExpression },
{ name: i18nService.t("exact"), value: UriMatchType.Exact },
{ name: i18nService.t("never"), value: UriMatchType.Never },
{ name: i18nService.t("baseDomain"), value: UriMatchStrategy.Domain },
{ name: i18nService.t("host"), value: UriMatchStrategy.Host },
{ name: i18nService.t("startsWith"), value: UriMatchStrategy.StartsWith },
{ name: i18nService.t("regEx"), value: UriMatchStrategy.RegularExpression },
{ name: i18nService.t("exact"), value: UriMatchStrategy.Exact },
{ name: i18nService.t("never"), value: UriMatchStrategy.Never },
];
this.accountSwitcherEnabled = enableAccountSwitching();
@@ -94,8 +95,10 @@ export class AutofillComponent implements OnInit {
this.autofillSettingsService.autofillOnPageLoadDefault$,
);
const defaultUriMatch = await this.stateService.getDefaultUriMatch();
this.defaultUriMatch = defaultUriMatch == null ? UriMatchType.Domain : defaultUriMatch;
const defaultUriMatch = await firstValueFrom(
this.domainSettingsService.defaultUriMatchStrategy$,
);
this.defaultUriMatch = defaultUriMatch == null ? UriMatchStrategy.Domain : defaultUriMatch;
const command = await this.platformUtilsService.getAutofillKeyboardShortcut();
await this.setAutofillKeyboardHelperText(command);
@@ -119,7 +122,7 @@ export class AutofillComponent implements OnInit {
}
async saveDefaultUriMatch() {
await this.stateService.setDefaultUriMatch(this.defaultUriMatch);
await this.domainSettingsService.setDefaultUriMatchStrategy(this.defaultUriMatch);
}
private async setAutofillKeyboardHelperText(command: string) {

View File

@@ -1,4 +1,5 @@
import { UriMatchType, CipherType } from "@bitwarden/common/vault/enums";
import { UriMatchStrategySetting } from "@bitwarden/common/models/domain/domain-service";
import { CipherType } from "@bitwarden/common/vault/enums";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import AutofillField from "../../models/autofill-field";
@@ -40,7 +41,7 @@ export interface GenerateFillScriptOptions {
allowTotpAutofill: boolean;
cipher: CipherView;
tabUrl: string;
defaultUriMatch: UriMatchType;
defaultUriMatch: UriMatchStrategySetting;
}
export abstract class AutofillService {

View File

@@ -1,19 +1,25 @@
import { mock, mockReset } from "jest-mock-extended";
import { of } from "rxjs";
import { UserVerificationService } from "@bitwarden/common/auth/services/user-verification/user-verification.service";
import { AutofillOverlayVisibility } from "@bitwarden/common/autofill/constants";
import { AutofillSettingsService } from "@bitwarden/common/autofill/services/autofill-settings.service";
import { EventType } from "@bitwarden/common/enums";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { EventCollectionService } from "@bitwarden/common/services/event/event-collection.service";
import { SettingsService } from "@bitwarden/common/services/settings.service";
import {
FieldType,
LinkedIdType,
LoginLinkedId,
UriMatchType,
CipherType,
} from "@bitwarden/common/vault/enums";
DefaultDomainSettingsService,
DomainSettingsService,
} from "@bitwarden/common/autofill/services/domain-settings.service";
import { EventType } from "@bitwarden/common/enums";
import { UriMatchStrategy } from "@bitwarden/common/models/domain/domain-service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { EventCollectionService } from "@bitwarden/common/services/event/event-collection.service";
import {
FakeStateProvider,
FakeAccountService,
mockAccountServiceWith,
} from "@bitwarden/common/spec";
import { UserId } from "@bitwarden/common/types/guid";
import { FieldType, LinkedIdType, LoginLinkedId, CipherType } from "@bitwarden/common/vault/enums";
import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
import { CardView } from "@bitwarden/common/vault/models/view/card.view";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
@@ -47,15 +53,24 @@ import {
import { AutoFillConstants, IdentityAutoFillConstants } from "./autofill-constants";
import AutofillService from "./autofill.service";
const mockEquivalentDomains = [
["example.com", "exampleapp.com", "example.co.uk", "ejemplo.es"],
["bitwarden.com", "bitwarden.co.uk", "sm-bitwarden.com"],
["example.co.uk", "exampleapp.co.uk"],
];
describe("AutofillService", () => {
let autofillService: AutofillService;
const cipherService = mock<CipherService>();
const stateService = mock<BrowserStateService>();
const autofillSettingsService = mock<AutofillSettingsService>();
const mockUserId = Utils.newGuid() as UserId;
const accountService: FakeAccountService = mockAccountServiceWith(mockUserId);
const fakeStateProvider: FakeStateProvider = new FakeStateProvider(accountService);
let domainSettingsService: DomainSettingsService;
const totpService = mock<TotpService>();
const eventCollectionService = mock<EventCollectionService>();
const logService = mock<LogService>();
const settingsService = mock<SettingsService>();
const userVerificationService = mock<UserVerificationService>();
beforeEach(() => {
@@ -66,9 +81,12 @@ describe("AutofillService", () => {
totpService,
eventCollectionService,
logService,
settingsService,
domainSettingsService,
userVerificationService,
);
domainSettingsService = new DefaultDomainSettingsService(fakeStateProvider);
domainSettingsService.equivalentDomains$ = of(mockEquivalentDomains);
});
afterEach(() => {
@@ -407,6 +425,8 @@ describe("AutofillService", () => {
autofillOptions.cipher.login.matchesUri = jest.fn().mockReturnValue(true);
autofillOptions.cipher.login.username = "username";
autofillOptions.cipher.login.password = "password";
jest.spyOn(autofillService, "getDefaultUriMatchStrategy").mockResolvedValue(0);
});
describe("given a set of autofill options that are incomplete", () => {
@@ -468,7 +488,6 @@ describe("AutofillService", () => {
it("will autofill login data for a page", async () => {
jest.spyOn(stateService, "getCanAccessPremium");
jest.spyOn(stateService, "getDefaultUriMatch");
jest.spyOn(autofillService as any, "generateFillScript");
jest.spyOn(autofillService as any, "generateLoginFillScript");
jest.spyOn(logService, "info");
@@ -479,7 +498,7 @@ describe("AutofillService", () => {
const currentAutofillPageDetails = autofillOptions.pageDetails[0];
expect(stateService.getCanAccessPremium).toHaveBeenCalled();
expect(stateService.getDefaultUriMatch).toHaveBeenCalled();
expect(autofillService["getDefaultUriMatchStrategy"]).toHaveBeenCalled();
expect(autofillService["generateFillScript"]).toHaveBeenCalledWith(
currentAutofillPageDetails.details,
{
@@ -1488,7 +1507,7 @@ describe("AutofillService", () => {
};
defaultLoginUriView = mock<LoginUriView>({
uri: "https://www.example.com",
match: UriMatchType.Domain,
match: UriMatchStrategy.Domain,
});
options = createGenerateFillScriptOptionsMock();
options.cipher.login = mock<LoginView>({
@@ -1559,13 +1578,13 @@ describe("AutofillService", () => {
]);
});
it("skips adding any login uri views that have a UriMatchType of Never to the list of saved urls", async () => {
it("skips adding any login uri views that have a UriMatchStrategySetting of Never to the list of saved urls", async () => {
const secondUriView = mock<LoginUriView>({
uri: "https://www.second-example.com",
});
const thirdUriView = mock<LoginUriView>({
uri: "https://www.third-example.com",
match: UriMatchType.Never,
match: UriMatchStrategy.Never,
});
options.cipher.login.uris = [defaultLoginUriView, secondUriView, thirdUriView];
@@ -2752,31 +2771,32 @@ describe("AutofillService", () => {
});
describe("inUntrustedIframe", () => {
it("returns a false value if the passed pageUrl is equal to the options tabUrl", () => {
it("returns a false value if the passed pageUrl is equal to the options tabUrl", async () => {
const pageUrl = "https://www.example.com";
const tabUrl = "https://www.example.com";
const generateFillScriptOptions = createGenerateFillScriptOptionsMock({ tabUrl });
generateFillScriptOptions.cipher.login.matchesUri = jest.fn().mockReturnValueOnce(true);
jest.spyOn(settingsService, "getEquivalentDomains");
const result = autofillService["inUntrustedIframe"](pageUrl, generateFillScriptOptions);
const result = await autofillService["inUntrustedIframe"](pageUrl, generateFillScriptOptions);
expect(settingsService.getEquivalentDomains).not.toHaveBeenCalled();
expect(generateFillScriptOptions.cipher.login.matchesUri).not.toHaveBeenCalled();
expect(result).toBe(false);
});
it("returns a false value if the passed pageUrl matches the domain of the tabUrl", () => {
it("returns a false value if the passed pageUrl matches the domain of the tabUrl", async () => {
const pageUrl = "https://subdomain.example.com";
const tabUrl = "https://www.example.com";
const equivalentDomains = new Set(["example.com"]);
const equivalentDomains = new Set([
"ejemplo.es",
"example.co.uk",
"example.com",
"exampleapp.com",
]);
const generateFillScriptOptions = createGenerateFillScriptOptionsMock({ tabUrl });
generateFillScriptOptions.cipher.login.matchesUri = jest.fn().mockReturnValueOnce(true);
jest.spyOn(settingsService as any, "getEquivalentDomains").mockReturnValue(equivalentDomains);
const result = autofillService["inUntrustedIframe"](pageUrl, generateFillScriptOptions);
const result = await autofillService["inUntrustedIframe"](pageUrl, generateFillScriptOptions);
expect(settingsService.getEquivalentDomains).toHaveBeenCalledWith(pageUrl);
expect(generateFillScriptOptions.cipher.login.matchesUri).toHaveBeenCalledWith(
pageUrl,
equivalentDomains,
@@ -2785,17 +2805,21 @@ describe("AutofillService", () => {
expect(result).toBe(false);
});
it("returns a true value if the passed pageUrl does not match the domain of the tabUrl", () => {
it("returns a true value if the passed pageUrl does not match the domain of the tabUrl", async () => {
const equivalentDomains = new Set([
"ejemplo.es",
"example.co.uk",
"example.com",
"exampleapp.com",
]);
domainSettingsService.equivalentDomains$ = of([["not-example.com"]]);
const pageUrl = "https://subdomain.example.com";
const tabUrl = "https://www.not-example.com";
const equivalentDomains = new Set(["not-example.com"]);
const generateFillScriptOptions = createGenerateFillScriptOptionsMock({ tabUrl });
generateFillScriptOptions.cipher.login.matchesUri = jest.fn().mockReturnValueOnce(false);
jest.spyOn(settingsService as any, "getEquivalentDomains").mockReturnValue(equivalentDomains);
const result = autofillService["inUntrustedIframe"](pageUrl, generateFillScriptOptions);
const result = await autofillService["inUntrustedIframe"](pageUrl, generateFillScriptOptions);
expect(settingsService.getEquivalentDomains).toHaveBeenCalledWith(pageUrl);
expect(generateFillScriptOptions.cipher.login.matchesUri).toHaveBeenCalledWith(
pageUrl,
equivalentDomains,

View File

@@ -1,15 +1,19 @@
import { firstValueFrom } from "rxjs";
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
import { SettingsService } from "@bitwarden/common/abstractions/settings.service";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
import { InlineMenuVisibilitySetting } from "@bitwarden/common/autofill/types";
import { EventType } from "@bitwarden/common/enums";
import {
UriMatchStrategySetting,
UriMatchStrategy,
} from "@bitwarden/common/models/domain/domain-service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service";
import { FieldType, UriMatchType, CipherType } from "@bitwarden/common/vault/enums";
import { FieldType, CipherType } from "@bitwarden/common/vault/enums";
import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { FieldView } from "@bitwarden/common/vault/models/view/field.view";
@@ -48,7 +52,7 @@ export default class AutofillService implements AutofillServiceInterface {
private totpService: TotpService,
private eventCollectionService: EventCollectionService,
private logService: LogService,
private settingsService: SettingsService,
private domainSettingsService: DomainSettingsService,
private userVerificationService: UserVerificationService,
) {}
@@ -215,6 +219,13 @@ export default class AutofillService implements AutofillServiceInterface {
return await firstValueFrom(this.autofillSettingsService.autofillOnPageLoad$);
}
/**
* Gets the default URI match strategy setting from the domain settings service.
*/
async getDefaultUriMatchStrategy(): Promise<UriMatchStrategySetting> {
return await firstValueFrom(this.domainSettingsService.defaultUriMatchStrategy$);
}
/**
* Autofill a given tab with a given login item
* @param {AutoFillOptions} options Instructions about the autofill operation, including tab and login item
@@ -229,7 +240,7 @@ export default class AutofillService implements AutofillServiceInterface {
let totp: string | null = null;
const canAccessPremium = await this.stateService.getCanAccessPremium();
const defaultUriMatch = (await this.stateService.getDefaultUriMatch()) ?? UriMatchType.Domain;
const defaultUriMatch = await this.getDefaultUriMatchStrategy();
if (!canAccessPremium) {
options.cipher.login.totp = null;
@@ -579,9 +590,9 @@ export default class AutofillService implements AutofillServiceInterface {
let totp: AutofillField = null;
const login = options.cipher.login;
fillScript.savedUrls =
login?.uris?.filter((u) => u.match != UriMatchType.Never).map((u) => u.uri) ?? [];
login?.uris?.filter((u) => u.match != UriMatchStrategy.Never).map((u) => u.uri) ?? [];
fillScript.untrustedIframe = this.inUntrustedIframe(pageDetails.url, options);
fillScript.untrustedIframe = await this.inUntrustedIframe(pageDetails.url, options);
let passwordFields = AutofillService.loadPasswordFields(
pageDetails,
@@ -1066,7 +1077,10 @@ export default class AutofillService implements AutofillServiceInterface {
* @returns {boolean} `true` if the iframe is untrusted and a warning should be shown, `false` otherwise
* @private
*/
private inUntrustedIframe(pageUrl: string, options: GenerateFillScriptOptions): boolean {
private async inUntrustedIframe(
pageUrl: string,
options: GenerateFillScriptOptions,
): Promise<boolean> {
// If the pageUrl (from the content script) matches the tabUrl (from the sender tab), we are not in an iframe
// This also avoids a false positive if no URI is saved and the user triggers auto-fill anyway
if (pageUrl === options.tabUrl) {
@@ -1076,7 +1090,9 @@ export default class AutofillService implements AutofillServiceInterface {
// Check the pageUrl against cipher URIs using the configured match detection.
// Remember: if we are in this function, the tabUrl already matches a saved URI for the login.
// We need to verify the pageUrl also matches.
const equivalentDomains = this.settingsService.getEquivalentDomains(pageUrl);
const equivalentDomains = await firstValueFrom(
this.domainSettingsService.getUrlEquivalentDomains(pageUrl),
);
const matchesUri = options.cipher.login.matchesUri(
pageUrl,
equivalentDomains,

View File

@@ -1,8 +1,9 @@
import { mock } from "jest-mock-extended";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { UriMatchStrategy } from "@bitwarden/common/models/domain/domain-service";
import { ThemeType } from "@bitwarden/common/platform/enums";
import { UriMatchType, CipherType } from "@bitwarden/common/vault/enums";
import { CipherType } from "@bitwarden/common/vault/enums";
import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
@@ -112,7 +113,7 @@ function createGenerateFillScriptOptionsMock(customFields = {}): GenerateFillScr
allowTotpAutofill: false,
cipher: mock<CipherView>(),
tabUrl: "https://jest-testing-website.com",
defaultUriMatch: UriMatchType.Domain,
defaultUriMatch: UriMatchStrategy.Domain,
...customFields,
};
}

View File

@@ -1,5 +1,4 @@
import { Region } from "@bitwarden/common/platform/abstractions/environment.service";
import { GlobalState } from "@bitwarden/common/platform/models/domain/global-state";
import { VaultTimeoutAction } from "@bitwarden/common/src/enums/vault-timeout-action.enum";
import { CipherType } from "@bitwarden/common/vault/enums";
@@ -32,15 +31,10 @@ export type UserSettings = {
utcDate: string;
version: string;
};
settings: {
equivalentDomains: string[][];
};
vaultTimeout: number;
vaultTimeoutAction: VaultTimeoutAction;
};
export type GlobalSettings = Pick<GlobalState, "neverDomains">;
/**
* A HTMLElement (usually a form element) with additional custom properties added by this script
*/

View File

@@ -56,6 +56,10 @@ import {
BadgeSettingsServiceAbstraction,
BadgeSettingsService,
} from "@bitwarden/common/autofill/services/badge-settings.service";
import {
DomainSettingsService,
DefaultDomainSettingsService,
} from "@bitwarden/common/autofill/services/domain-settings.service";
import {
UserNotificationSettingsService,
UserNotificationSettingsServiceAbstraction,
@@ -198,10 +202,11 @@ import { BrowserEnvironmentService } from "../platform/services/browser-environm
import BrowserLocalStorageService from "../platform/services/browser-local-storage.service";
import BrowserMessagingPrivateModeBackgroundService from "../platform/services/browser-messaging-private-mode-background.service";
import BrowserMessagingService from "../platform/services/browser-messaging.service";
import BrowserPlatformUtilsService from "../platform/services/browser-platform-utils.service";
import { BrowserStateService } from "../platform/services/browser-state.service";
import I18nService from "../platform/services/i18n.service";
import { LocalBackedSessionStorageService } from "../platform/services/local-backed-session-storage.service";
import { BackgroundPlatformUtilsService } from "../platform/services/platform-utils/background-platform-utils.service";
import { BrowserPlatformUtilsService } from "../platform/services/platform-utils/browser-platform-utils.service";
import { BackgroundDerivedStateProvider } from "../platform/state/background-derived-state.provider";
import { BackgroundMemoryStorageService } from "../platform/storage/background-memory-storage.service";
import { BrowserSendService } from "../services/browser-send.service";
@@ -258,6 +263,7 @@ export default class MainBackground {
userNotificationSettingsService: UserNotificationSettingsServiceAbstraction;
autofillSettingsService: AutofillSettingsServiceAbstraction;
badgeSettingsService: BadgeSettingsServiceAbstraction;
domainSettingsService: DomainSettingsService;
systemService: SystemServiceAbstraction;
eventCollectionService: EventCollectionServiceAbstraction;
eventUploadService: EventUploadServiceAbstraction;
@@ -438,28 +444,10 @@ export default class MainBackground {
migrationRunner,
);
this.userNotificationSettingsService = new UserNotificationSettingsService(this.stateProvider);
this.platformUtilsService = new BrowserPlatformUtilsService(
this.platformUtilsService = new BackgroundPlatformUtilsService(
this.messagingService,
(clipboardValue, clearMs) => {
if (this.systemService != null) {
// 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.systemService.clearClipboard(clipboardValue, clearMs);
}
},
async () => {
if (this.nativeMessagingBackground != null) {
const promise = this.nativeMessagingBackground.getResponse();
try {
await this.nativeMessagingBackground.send({ command: "biometricUnlock" });
} catch (e) {
return Promise.reject(e);
}
return promise.then((result) => result.response === "unlocked");
}
},
(clipboardValue, clearMs) => this.clearClipboard(clipboardValue, clearMs),
async () => this.biometricUnlock(),
self,
);
this.i18nService = new I18nService(BrowserApi.getUILanguage(), this.globalStateProvider);
@@ -475,7 +463,7 @@ export default class MainBackground {
this.biometricStateService,
);
this.tokenService = new TokenService(this.stateService);
this.appIdService = new AppIdService(this.storageService);
this.appIdService = new AppIdService(this.globalStateProvider);
this.apiService = new ApiService(
this.tokenService,
this.platformUtilsService,
@@ -483,6 +471,7 @@ export default class MainBackground {
this.appIdService,
(expired: boolean) => this.logout(expired),
);
this.domainSettingsService = new DefaultDomainSettingsService(this.stateProvider);
this.settingsService = new BrowserSettingsService(this.stateService);
this.fileUploadService = new FileUploadService(this.logService);
this.cipherFileUploadService = new CipherFileUploadService(
@@ -588,6 +577,7 @@ export default class MainBackground {
this.policyService,
this.deviceTrustCryptoService,
this.authRequestService,
this.globalStateProvider,
);
this.ssoLoginService = new SsoLoginService(this.stateProvider);
@@ -607,7 +597,7 @@ export default class MainBackground {
this.cipherService = new CipherService(
this.cryptoService,
this.settingsService,
this.domainSettingsService,
this.apiService,
this.i18nService,
this.searchService,
@@ -695,7 +685,7 @@ export default class MainBackground {
this.providerService = new ProviderService(this.stateProvider);
this.syncService = new SyncService(
this.apiService,
this.settingsService,
this.domainSettingsService,
this.folderService,
this.cipherService,
this.cryptoService,
@@ -732,7 +722,7 @@ export default class MainBackground {
this.totpService,
this.eventCollectionService,
this.logService,
this.settingsService,
this.domainSettingsService,
this.userVerificationService,
);
this.auditService = new AuditService(this.cryptoFunctionService, this.apiService);
@@ -796,6 +786,7 @@ export default class MainBackground {
this.authService,
this.stateService,
this.vaultSettingsService,
this.domainSettingsService,
this.logService,
);
@@ -865,6 +856,7 @@ export default class MainBackground {
this.folderService,
this.stateService,
this.userNotificationSettingsService,
this.domainSettingsService,
this.environmentService,
this.logService,
);
@@ -1098,7 +1090,6 @@ export default class MainBackground {
await Promise.all([
this.syncService.setLastSync(new Date(0), userId),
this.cryptoService.clearKeys(userId),
this.settingsService.clear(userId),
this.cipherService.clear(userId),
this.folderService.clear(userId),
this.collectionService.clear(userId),
@@ -1223,6 +1214,23 @@ export default class MainBackground {
}
}
async clearClipboard(clipboardValue: string, clearMs: number) {
if (this.systemService != null) {
await this.systemService.clearClipboard(clipboardValue, clearMs);
}
}
async biometricUnlock(): Promise<boolean> {
if (this.nativeMessagingBackground == null) {
return false;
}
const responsePromise = this.nativeMessagingBackground.getResponse();
await this.nativeMessagingBackground.send({ command: "biometricUnlock" });
const response = await responsePromise;
return response.response === "unlocked";
}
private async fullSync(override = false) {
const syncInternal = 6 * 60 * 60 * 1000; // 6 hours
const lastSync = await this.syncService.getLastSync();

View File

@@ -122,7 +122,7 @@ export class NativeMessagingBackground {
break;
case "disconnected":
if (this.connecting) {
reject("startDesktop");
reject(new Error("startDesktop"));
}
this.connected = false;
this.port.disconnect();
@@ -203,7 +203,7 @@ export class NativeMessagingBackground {
this.connected = false;
const reason = error != null ? "desktopIntegrationDisabled" : null;
reject(reason);
reject(new Error(reason));
});
});
}

View File

@@ -19,7 +19,7 @@ import { AutofillService } from "../autofill/services/abstractions/autofill.serv
import { BrowserApi } from "../platform/browser/browser-api";
import { BrowserStateService } from "../platform/services/abstractions/browser-state.service";
import { BrowserEnvironmentService } from "../platform/services/browser-environment.service";
import BrowserPlatformUtilsService from "../platform/services/browser-platform-utils.service";
import { BrowserPlatformUtilsService } from "../platform/services/platform-utils/browser-platform-utils.service";
import { AbortManager } from "../vault/background/abort-manager";
import { Fido2Service } from "../vault/services/abstractions/fido2.service";
@@ -68,6 +68,7 @@ export default class RuntimeBackground {
"checkFido2FeatureEnabled",
"fido2RegisterCredentialRequest",
"fido2GetCredentialRequest",
"biometricUnlock",
];
if (messagesWithResponse.includes(msg.command)) {
@@ -305,6 +306,14 @@ export default class RuntimeBackground {
);
case "switchAccount": {
await this.main.switchAccount(msg.userId);
break;
}
case "clearClipboard": {
await this.main.clearClipboard(msg.clipboardValue, msg.timeoutMs);
break;
}
case "biometricUnlock": {
return await this.main.biometricUnlock();
}
}
}

View File

@@ -1,14 +1,15 @@
import { DiskStorageOptions } from "@koa/multer";
import { AppIdService as AbstractAppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
import { AppIdService } from "@bitwarden/common/platform/services/app-id.service";
import { FactoryOptions, CachedServices, factory } from "./factory-options";
import { diskStorageServiceFactory } from "./storage-service.factory";
import {
GlobalStateProviderInitOptions,
globalStateProviderFactory,
} from "./global-state-provider.factory";
type AppIdServiceFactoryOptions = FactoryOptions;
export type AppIdServiceInitOptions = AppIdServiceFactoryOptions & DiskStorageOptions;
export type AppIdServiceInitOptions = AppIdServiceFactoryOptions & GlobalStateProviderInitOptions;
export function appIdServiceFactory(
cache: { appIdService?: AbstractAppIdService } & CachedServices,
@@ -18,6 +19,6 @@ export function appIdServiceFactory(
cache,
"appIdService",
opts,
async () => new AppIdService(await diskStorageServiceFactory(cache, opts)),
async () => new AppIdService(await globalStateProviderFactory(cache, opts)),
);
}

View File

@@ -1,6 +1,6 @@
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import BrowserPlatformUtilsService from "../../services/browser-platform-utils.service";
import { BackgroundPlatformUtilsService } from "../../services/platform-utils/background-platform-utils.service";
import { CachedServices, factory, FactoryOptions } from "./factory-options";
import { MessagingServiceInitOptions, messagingServiceFactory } from "./messaging-service.factory";
@@ -25,7 +25,7 @@ export function platformUtilsServiceFactory(
"platformUtilsService",
opts,
async () =>
new BrowserPlatformUtilsService(
new BackgroundPlatformUtilsService(
await messagingServiceFactory(cache, opts),
opts.platformUtilsServiceOptions.clipboardWriteCallback,
opts.platformUtilsServiceOptions.biometricCallback,

View File

@@ -3,7 +3,7 @@ import { Observable } from "rxjs";
import { DeviceType } from "@bitwarden/common/enums";
import { TabMessage } from "../../types/tab-messages";
import BrowserPlatformUtilsService from "../services/browser-platform-utils.service";
import { BrowserPlatformUtilsService } from "../services/platform-utils/browser-platform-utils.service";
export class BrowserApi {
static isWebExtensionsApi: boolean = typeof browser !== "undefined";

View File

@@ -17,7 +17,7 @@ import { Account } from "../../models/account";
import IconDetails from "../../vault/background/models/icon-details";
import { cipherServiceFactory } from "../../vault/background/service_factories/cipher-service.factory";
import { BrowserApi } from "../browser/browser-api";
import BrowserPlatformUtilsService from "../services/browser-platform-utils.service";
import { BrowserPlatformUtilsService } from "../services/platform-utils/browser-platform-utils.service";
export type BadgeOptions = {
tab?: chrome.tabs.Tab;

View File

@@ -0,0 +1,28 @@
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { BrowserPlatformUtilsService } from "./browser-platform-utils.service";
export class BackgroundPlatformUtilsService extends BrowserPlatformUtilsService {
constructor(
private messagingService: MessagingService,
clipboardWriteCallback: (clipboardValue: string, clearMs: number) => void,
biometricCallback: () => Promise<boolean>,
win: Window & typeof globalThis,
) {
super(clipboardWriteCallback, biometricCallback, win);
}
override showToast(
type: "error" | "success" | "warning" | "info",
title: string,
text: string | string[],
options?: any,
): void {
this.messagingService.send("showToast", {
text: text,
title: title,
type: type,
options: options,
});
}
}

View File

@@ -1,11 +1,26 @@
import { DeviceType } from "@bitwarden/common/enums";
import { flushPromises } from "../../autofill/spec/testing-utils";
import { SafariApp } from "../../browser/safariApp";
import { BrowserApi } from "../browser/browser-api";
import { flushPromises } from "../../../autofill/spec/testing-utils";
import { SafariApp } from "../../../browser/safariApp";
import { BrowserApi } from "../../browser/browser-api";
import BrowserClipboardService from "../browser-clipboard.service";
import BrowserClipboardService from "./browser-clipboard.service";
import BrowserPlatformUtilsService from "./browser-platform-utils.service";
import { BrowserPlatformUtilsService } from "./browser-platform-utils.service";
class TestBrowserPlatformUtilsService extends BrowserPlatformUtilsService {
constructor(clipboardSpy: jest.Mock, win: Window & typeof globalThis) {
super(clipboardSpy, null, win);
}
showToast(
type: "error" | "success" | "warning" | "info",
title: string,
text: string | string[],
options?: any,
): void {
throw new Error("Method not implemented.");
}
}
describe("Browser Utils Service", () => {
let browserPlatformUtilsService: BrowserPlatformUtilsService;
@@ -13,10 +28,8 @@ describe("Browser Utils Service", () => {
beforeEach(() => {
(window as any).matchMedia = jest.fn().mockReturnValueOnce({});
browserPlatformUtilsService = new BrowserPlatformUtilsService(
null,
browserPlatformUtilsService = new TestBrowserPlatformUtilsService(
clipboardWriteCallbackSpy,
null,
window,
);
});

View File

@@ -1,20 +1,17 @@
import { ClientType, DeviceType } from "@bitwarden/common/enums";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import {
ClipboardOptions,
PlatformUtilsService,
} from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { SafariApp } from "../../browser/safariApp";
import { BrowserApi } from "../browser/browser-api";
import { SafariApp } from "../../../browser/safariApp";
import { BrowserApi } from "../../browser/browser-api";
import BrowserClipboardService from "../browser-clipboard.service";
import BrowserClipboardService from "./browser-clipboard.service";
export default class BrowserPlatformUtilsService implements PlatformUtilsService {
export abstract class BrowserPlatformUtilsService implements PlatformUtilsService {
private static deviceCache: DeviceType = null;
constructor(
private messagingService: MessagingService,
private clipboardWriteCallback: (clipboardValue: string, clearMs: number) => void,
private biometricCallback: () => Promise<boolean>,
private globalContext: Window | ServiceWorkerGlobalScope,
@@ -193,19 +190,12 @@ export default class BrowserPlatformUtilsService implements PlatformUtilsService
return true;
}
showToast(
abstract showToast(
type: "error" | "success" | "warning" | "info",
title: string,
text: string | string[],
options?: any,
): void {
this.messagingService.send("showToast", {
text: text,
title: title,
type: type,
options: options,
});
}
): void;
isDev(): boolean {
return process.env.ENV === "development";
@@ -279,11 +269,10 @@ export default class BrowserPlatformUtilsService implements PlatformUtilsService
async supportsBiometric() {
const platformInfo = await BrowserApi.getPlatformInfo();
if (platformInfo.os === "android") {
return false;
if (platformInfo.os === "mac" || platformInfo.os === "win") {
return true;
}
return true;
return false;
}
authenticateBiometric() {

View File

@@ -0,0 +1,40 @@
import { SecurityContext } from "@angular/core";
import { DomSanitizer } from "@angular/platform-browser";
import { ToastrService } from "ngx-toastr";
import { BrowserPlatformUtilsService } from "./browser-platform-utils.service";
export class ForegroundPlatformUtilsService extends BrowserPlatformUtilsService {
constructor(
private sanitizer: DomSanitizer,
private toastrService: ToastrService,
clipboardWriteCallback: (clipboardValue: string, clearMs: number) => void,
biometricCallback: () => Promise<boolean>,
win: Window & typeof globalThis,
) {
super(clipboardWriteCallback, biometricCallback, win);
}
override showToast(
type: "error" | "success" | "warning" | "info",
title: string,
text: string | string[],
options?: any,
): void {
if (typeof text === "string") {
// Already in the correct format
} else if (text.length === 1) {
text = text[0];
} else {
let message = "";
text.forEach(
(t: string) =>
(message += "<p>" + this.sanitizer.sanitize(SecurityContext.HTML, t) + "</p>"),
);
text = message;
options.enableHtml = true;
}
this.toastrService.show(text, title, options, "toast-" + type);
// noop
}
}

View File

@@ -1,27 +1,18 @@
import {
ChangeDetectorRef,
Component,
NgZone,
OnDestroy,
OnInit,
SecurityContext,
} from "@angular/core";
import { DomSanitizer } from "@angular/platform-browser";
import { ChangeDetectorRef, Component, NgZone, OnDestroy, OnInit } from "@angular/core";
import { NavigationEnd, Router, RouterOutlet } from "@angular/router";
import { IndividualConfig, ToastrService } from "ngx-toastr";
import { ToastrService } from "ngx-toastr";
import { filter, concatMap, Subject, takeUntil, firstValueFrom } from "rxjs";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { DialogService, SimpleDialogOptions } from "@bitwarden/components";
import { BrowserApi } from "../platform/browser/browser-api";
import { ZonedMessageListenerService } from "../platform/browser/zoned-message-listener.service";
import { BrowserStateService } from "../platform/services/abstractions/browser-state.service";
import { ForegroundPlatformUtilsService } from "../platform/services/platform-utils/foreground-platform-utils.service";
import { routerTransition } from "./app-routing.animations";
import { DesktopSyncVerificationDialogComponent } from "./components/desktop-sync-verification-dialog.component";
@@ -48,11 +39,9 @@ export class AppComponent implements OnInit, OnDestroy {
private router: Router,
private stateService: BrowserStateService,
private cipherService: CipherService,
private messagingService: MessagingService,
private changeDetectorRef: ChangeDetectorRef,
private ngZone: NgZone,
private sanitizer: DomSanitizer,
private platformUtilsService: PlatformUtilsService,
private platformUtilsService: ForegroundPlatformUtilsService,
private dialogService: DialogService,
private browserMessagingApi: ZonedMessageListenerService,
) {}
@@ -219,31 +208,7 @@ export class AppComponent implements OnInit, OnDestroy {
}
private showToast(msg: any) {
let message = "";
const options: Partial<IndividualConfig> = {};
if (typeof msg.text === "string") {
message = msg.text;
} else if (msg.text.length === 1) {
message = msg.text[0];
} else {
msg.text.forEach(
(t: string) =>
(message += "<p>" + this.sanitizer.sanitize(SecurityContext.HTML, t) + "</p>"),
);
options.enableHtml = true;
}
if (msg.options != null) {
if (msg.options.trustedHtml === true) {
options.enableHtml = true;
}
if (msg.options.timeout != null && msg.options.timeout > 0) {
options.timeOut = msg.options.timeout;
}
}
this.toastrService.show(message, msg.title, options, "toast-" + msg.type);
this.platformUtilsService.showToast(msg.type, msg.title, msg.text, msg.options);
}
private async showDialog(msg: SimpleDialogOptions) {

View File

@@ -1,7 +1,7 @@
import { enableProdMode } from "@angular/core";
import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
import BrowserPlatformUtilsService from "../platform/services/browser-platform-utils.service";
import { BrowserPlatformUtilsService } from "../platform/services/platform-utils/browser-platform-utils.service";
require("./scss/popup.scss");
require("./scss/tailwind.css");

View File

@@ -1,4 +1,6 @@
import { APP_INITIALIZER, NgModule, NgZone } from "@angular/core";
import { DomSanitizer } from "@angular/platform-browser";
import { ToastrService } from "ngx-toastr";
import { UnauthGuard as BaseUnauthGuardService } from "@bitwarden/angular/auth/guards";
import { ThemingService } from "@bitwarden/angular/platform/services/theming/theming.service";
@@ -44,7 +46,6 @@ import {
UserNotificationSettingsService,
UserNotificationSettingsServiceAbstraction,
} from "@bitwarden/common/autofill/services/user-notification-settings.service";
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
import { ConfigApiServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config-api.service.abstraction";
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
@@ -118,6 +119,7 @@ import BrowserMessagingPrivateModePopupService from "../../platform/services/bro
import BrowserMessagingService from "../../platform/services/browser-messaging.service";
import { BrowserStateService } from "../../platform/services/browser-state.service";
import I18nService from "../../platform/services/i18n.service";
import { ForegroundPlatformUtilsService } from "../../platform/services/platform-utils/foreground-platform-utils.service";
import { ForegroundDerivedStateProvider } from "../../platform/state/foreground-derived-state.provider";
import { ForegroundMemoryStorageService } from "../../platform/storage/foreground-memory-storage.service";
import { BrowserSendService } from "../../services/browser-send.service";
@@ -291,8 +293,32 @@ function getBgService<T>(service: keyof MainBackground) {
},
{
provide: PlatformUtilsService,
useFactory: getBgService<PlatformUtilsService>("platformUtilsService"),
deps: [],
useExisting: ForegroundPlatformUtilsService,
},
{
provide: ForegroundPlatformUtilsService,
useClass: ForegroundPlatformUtilsService,
useFactory: (sanitizer: DomSanitizer, toastrService: ToastrService) => {
return new ForegroundPlatformUtilsService(
sanitizer,
toastrService,
(clipboardValue: string, clearMs: number) => {
void BrowserApi.sendMessage("clearClipboard", { clipboardValue, clearMs });
},
async () => {
const response = await BrowserApi.sendMessageWithResponse<{
result: boolean;
error: string;
}>("biometricUnlock");
if (!response.result) {
throw response.error;
}
return response.result;
},
window,
);
},
deps: [DomSanitizer, ToastrService],
},
{
provide: PasswordStrengthServiceAbstraction,
@@ -349,7 +375,6 @@ function getBgService<T>(service: keyof MainBackground) {
useClass: BrowserLocalStorageService,
deps: [],
},
{ provide: AppIdService, useFactory: getBgService<AppIdService>("appIdService"), deps: [] },
{
provide: AutofillService,
useFactory: getBgService<AutofillService>("autofillService"),

View File

@@ -1,6 +1,8 @@
import { Component, NgZone, OnDestroy, OnInit } from "@angular/core";
import { Router } from "@angular/router";
import { firstValueFrom } from "rxjs";
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
@@ -30,6 +32,7 @@ export class ExcludedDomainsComponent implements OnInit, OnDestroy {
constructor(
private stateService: StateService,
private domainSettingsService: DomainSettingsService,
private i18nService: I18nService,
private router: Router,
private broadcasterService: BroadcasterService,
@@ -40,7 +43,7 @@ export class ExcludedDomainsComponent implements OnInit, OnDestroy {
}
async ngOnInit() {
const savedDomains = await this.stateService.getNeverDomains();
const savedDomains = await firstValueFrom(this.domainSettingsService.neverDomains$);
if (savedDomains) {
for (const uri of Object.keys(savedDomains)) {
this.excludedDomains.push({ uri: uri, showCurrentUris: false });
@@ -107,7 +110,7 @@ export class ExcludedDomainsComponent implements OnInit, OnDestroy {
}
}
await this.stateService.setNeverDomains(savedDomains);
await this.domainSettingsService.setNeverDomains(savedDomains);
// 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.router.navigate(["/tabs/settings"]);

View File

@@ -5,14 +5,18 @@ import { AbstractThemingService } from "@bitwarden/angular/platform/services/the
import { SettingsService } from "@bitwarden/common/abstractions/settings.service";
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
import { BadgeSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/badge-settings.service";
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
import { UserNotificationSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/user-notification-settings.service";
import { ClearClipboardDelaySetting } from "@bitwarden/common/autofill/types";
import {
UriMatchStrategy,
UriMatchStrategySetting,
} from "@bitwarden/common/models/domain/domain-service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { ThemeType } from "@bitwarden/common/platform/enums";
import { VaultSettingsService } from "@bitwarden/common/vault/abstractions/vault-settings/vault-settings.service";
import { UriMatchType } from "@bitwarden/common/vault/enums";
import { enableAccountSwitching } from "../../platform/flags";
@@ -36,7 +40,7 @@ export class OptionsComponent implements OnInit {
showClearClipboard = true;
theme: ThemeType;
themeOptions: any[];
defaultUriMatch = UriMatchType.Domain;
defaultUriMatch: UriMatchStrategySetting = UriMatchStrategy.Domain;
uriMatchOptions: any[];
clearClipboard: ClearClipboardDelaySetting;
clearClipboardOptions: any[];
@@ -50,6 +54,7 @@ export class OptionsComponent implements OnInit {
private stateService: StateService,
private userNotificationSettingsService: UserNotificationSettingsServiceAbstraction,
private autofillSettingsService: AutofillSettingsServiceAbstraction,
private domainSettingsService: DomainSettingsService,
private badgeSettingsService: BadgeSettingsServiceAbstraction,
i18nService: I18nService,
private themingService: AbstractThemingService,
@@ -64,12 +69,12 @@ export class OptionsComponent implements OnInit {
{ name: i18nService.t("solarizedDark"), value: ThemeType.SolarizedDark },
];
this.uriMatchOptions = [
{ name: i18nService.t("baseDomain"), value: UriMatchType.Domain },
{ name: i18nService.t("host"), value: UriMatchType.Host },
{ name: i18nService.t("startsWith"), value: UriMatchType.StartsWith },
{ name: i18nService.t("regEx"), value: UriMatchType.RegularExpression },
{ name: i18nService.t("exact"), value: UriMatchType.Exact },
{ name: i18nService.t("never"), value: UriMatchType.Never },
{ name: i18nService.t("baseDomain"), value: UriMatchStrategy.Domain },
{ name: i18nService.t("host"), value: UriMatchStrategy.Host },
{ name: i18nService.t("startsWith"), value: UriMatchStrategy.StartsWith },
{ name: i18nService.t("regEx"), value: UriMatchStrategy.RegularExpression },
{ name: i18nService.t("exact"), value: UriMatchStrategy.Exact },
{ name: i18nService.t("never"), value: UriMatchStrategy.Never },
];
this.clearClipboardOptions = [
{ name: i18nService.t("never"), value: null },
@@ -122,8 +127,10 @@ export class OptionsComponent implements OnInit {
this.theme = await this.stateService.getTheme();
const defaultUriMatch = await this.stateService.getDefaultUriMatch();
this.defaultUriMatch = defaultUriMatch == null ? UriMatchType.Domain : defaultUriMatch;
const defaultUriMatch = await firstValueFrom(
this.domainSettingsService.defaultUriMatchStrategy$,
);
this.defaultUriMatch = defaultUriMatch == null ? UriMatchStrategy.Domain : defaultUriMatch;
this.clearClipboard = await firstValueFrom(this.autofillSettingsService.clearClipboardDelay$);
}
@@ -182,10 +189,6 @@ export class OptionsComponent implements OnInit {
await this.themingService.updateConfiguredTheme(this.theme);
}
async saveDefaultUriMatch() {
await this.stateService.setDefaultUriMatch(this.defaultUriMatch);
}
async saveClearClipboard() {
await this.autofillSettingsService.setClearClipboardDelay(this.clearClipboard);
}

View File

@@ -397,7 +397,7 @@ export class SettingsComponent implements OnInit {
// Handle connection errors
this.form.controls.biometric.setValue(false);
const error = BiometricErrors[e as BiometricErrorTypes];
const error = BiometricErrors[e.message as BiometricErrorTypes];
// 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

View File

@@ -1,15 +1,11 @@
import { BehaviorSubject } from "rxjs";
import { AccountSettingsSettings } from "@bitwarden/common/platform/models/domain/account";
import { SettingsService } from "@bitwarden/common/services/settings.service";
import { browserSession, sessionSync } from "../platform/decorators/session-sync-observable";
@browserSession
export class BrowserSettingsService extends SettingsService {
@sessionSync({ initializer: (obj: string[][]) => obj })
protected _settings: BehaviorSubject<AccountSettingsSettings>;
@sessionSync({ initializer: (b: boolean) => b })
protected _disableFavicon: BehaviorSubject<boolean>;
}

View File

@@ -5,6 +5,10 @@ import {
AutofillSettingsServiceInitOptions,
autofillSettingsServiceFactory,
} from "../../../autofill/background/service_factories/autofill-settings-service.factory";
import {
DomainSettingsServiceInitOptions,
domainSettingsServiceFactory,
} from "../../../autofill/background/service_factories/domain-settings-service.factory";
import {
CipherFileUploadServiceInitOptions,
cipherFileUploadServiceFactory,
@@ -13,10 +17,6 @@ import {
searchServiceFactory,
SearchServiceInitOptions,
} from "../../../background/service-factories/search-service.factory";
import {
SettingsServiceInitOptions,
settingsServiceFactory,
} from "../../../background/service-factories/settings-service.factory";
import {
apiServiceFactory,
ApiServiceInitOptions,
@@ -52,13 +52,13 @@ type CipherServiceFactoryOptions = FactoryOptions;
export type CipherServiceInitOptions = CipherServiceFactoryOptions &
CryptoServiceInitOptions &
SettingsServiceInitOptions &
ApiServiceInitOptions &
CipherFileUploadServiceInitOptions &
I18nServiceInitOptions &
SearchServiceInitOptions &
StateServiceInitOptions &
AutofillSettingsServiceInitOptions &
DomainSettingsServiceInitOptions &
EncryptServiceInitOptions &
ConfigServiceInitOptions;
@@ -73,7 +73,7 @@ export function cipherServiceFactory(
async () =>
new CipherService(
await cryptoServiceFactory(cache, opts),
await settingsServiceFactory(cache, opts),
await domainSettingsServiceFactory(cache, opts),
await apiServiceFactory(cache, opts),
await i18nServiceFactory(cache, opts),
await searchServiceFactory(cache, opts),

View File

@@ -3,6 +3,7 @@ import { ConnectedPosition } from "@angular/cdk/overlay";
import { Component } from "@angular/core";
import { firstValueFrom } from "rxjs";
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
@@ -52,6 +53,7 @@ export class Fido2UseBrowserLinkComponent {
constructor(
private stateService: StateService,
private domainSettingsService: DomainSettingsService,
private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService,
) {}
@@ -89,7 +91,7 @@ export class Fido2UseBrowserLinkComponent {
* @param uri - The domain uri to exclude from future FIDO2 prompts.
*/
private async handleDomainExclusion(uri: string) {
const exisitingDomains = await this.stateService.getNeverDomains();
const exisitingDomains = await firstValueFrom(this.domainSettingsService.neverDomains$);
const validDomain = Utils.getHostname(uri);
const savedDomains: { [name: string]: unknown } = {
@@ -97,9 +99,7 @@ export class Fido2UseBrowserLinkComponent {
};
savedDomains[validDomain] = null;
// 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.stateService.setNeverDomains(savedDomains);
await this.domainSettingsService.setNeverDomains(savedDomains);
this.platformUtilsService.showToast(
"success",

View File

@@ -5,6 +5,7 @@ import {
combineLatest,
concatMap,
filter,
firstValueFrom,
map,
Observable,
Subject,
@@ -13,7 +14,7 @@ import {
} from "rxjs";
import { SearchService } from "@bitwarden/common/abstractions/search.service";
import { SettingsService } from "@bitwarden/common/abstractions/settings.service";
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
@@ -72,7 +73,7 @@ export class Fido2Component implements OnInit, OnDestroy {
private cipherService: CipherService,
private passwordRepromptService: PasswordRepromptService,
private platformUtilsService: PlatformUtilsService,
private settingsService: SettingsService,
private domainSettingsService: DomainSettingsService,
private searchService: SearchService,
private logService: LogService,
private dialogService: DialogService,
@@ -133,7 +134,9 @@ export class Fido2Component implements OnInit, OnDestroy {
concatMap(async (message) => {
switch (message.type) {
case "ConfirmNewCredentialRequest": {
const equivalentDomains = this.settingsService.getEquivalentDomains(this.url);
const equivalentDomains = await firstValueFrom(
this.domainSettingsService.getUrlEquivalentDomains(this.url),
);
this.ciphers = (await this.cipherService.getAllDecrypted()).filter(
(cipher) => cipher.type === CipherType.Login && !cipher.isDeleted,
@@ -317,7 +320,9 @@ export class Fido2Component implements OnInit, OnDestroy {
this.ciphers,
);
} else {
const equivalentDomains = this.settingsService.getEquivalentDomains(this.url);
const equivalentDomains = await firstValueFrom(
this.domainSettingsService.getUrlEquivalentDomains(this.url),
);
this.displayedCiphers = this.ciphers.filter((cipher) =>
cipher.login.matchesUri(this.url, equivalentDomains),
);

View File

@@ -1,6 +1,5 @@
const path = require("path");
const webpack = require("webpack");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
@@ -138,9 +137,6 @@ const plugins = [
entryModule: "src/popup/app.module#AppModule",
sourceMap: true,
}),
new CleanWebpackPlugin({
cleanAfterEveryBuildPatterns: ["!popup/fonts/**/*"],
}),
new webpack.ProvidePlugin({
process: "process/browser.js",
}),
@@ -244,6 +240,7 @@ const mainConfig = {
output: {
filename: "[name].js",
path: path.resolve(__dirname, "build"),
clean: true,
},
module: {
noParse: /\.wasm$/,

View File

@@ -273,8 +273,8 @@ export class LoginCommand {
selectedProvider.type === TwoFactorProviderType.Email
) {
const emailReq = new TwoFactorEmailRequest();
emailReq.email = this.loginStrategyService.email;
emailReq.masterPasswordHash = this.loginStrategyService.masterPasswordHash;
emailReq.email = await this.loginStrategyService.getEmail();
emailReq.masterPasswordHash = await this.loginStrategyService.getMasterPasswordHash();
await this.apiService.postTwoFactorEmail(emailReq);
}

View File

@@ -35,6 +35,10 @@ import { TwoFactorService } from "@bitwarden/common/auth/services/two-factor.ser
import { UserVerificationApiService } from "@bitwarden/common/auth/services/user-verification/user-verification-api.service";
import { UserVerificationService } from "@bitwarden/common/auth/services/user-verification/user-verification.service";
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
import {
DefaultDomainSettingsService,
DomainSettingsService,
} from "@bitwarden/common/autofill/services/domain-settings.service";
import { ClientType } from "@bitwarden/common/enums";
import { ConfigApiServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config-api.service.abstraction";
import { KeyGenerationService as KeyGenerationServiceAbstraction } from "@bitwarden/common/platform/abstractions/key-generation.service";
@@ -190,6 +194,7 @@ export class Main {
pinCryptoService: PinCryptoServiceAbstraction;
stateService: StateService;
autofillSettingsService: AutofillSettingsServiceAbstraction;
domainSettingsService: DomainSettingsService;
organizationService: OrganizationService;
providerService: ProviderService;
twoFactorService: TwoFactorService;
@@ -333,7 +338,7 @@ export class Main {
this.stateProvider,
);
this.appIdService = new AppIdService(this.storageService);
this.appIdService = new AppIdService(this.globalStateProvider);
this.tokenService = new TokenService(this.stateService);
const customUserAgent =
@@ -358,6 +363,7 @@ export class Main {
this.containerService = new ContainerService(this.cryptoService, this.encryptService);
this.settingsService = new SettingsService(this.stateService);
this.domainSettingsService = new DefaultDomainSettingsService(this.stateProvider);
this.fileUploadService = new FileUploadService(this.logService);
@@ -458,6 +464,7 @@ export class Main {
this.policyService,
this.deviceTrustCryptoService,
this.authRequestService,
this.globalStateProvider,
);
this.authService = new AuthService(
@@ -480,7 +487,7 @@ export class Main {
this.cipherService = new CipherService(
this.cryptoService,
this.settingsService,
this.domainSettingsService,
this.apiService,
this.i18nService,
this.searchService,
@@ -551,7 +558,7 @@ export class Main {
this.syncService = new SyncService(
this.apiService,
this.settingsService,
this.domainSettingsService,
this.folderService,
this.cipherService,
this.cryptoService,
@@ -647,7 +654,6 @@ export class Main {
await Promise.all([
this.syncService.setLastSync(new Date(0)),
this.cryptoService.clearKeys(),
this.settingsService.clear(userId),
this.cipherService.clear(userId),
this.folderService.clear(userId),
this.collectionService.clear(userId as UserId),

View File

@@ -1,6 +1,5 @@
const path = require("path");
const webpack = require("webpack");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const nodeExternals = require("webpack-node-externals");
const TsconfigPathsPlugin = require("tsconfig-paths-webpack-plugin");
@@ -23,7 +22,6 @@ const moduleRules = [
];
const plugins = [
new CleanWebpackPlugin(),
new CopyWebpackPlugin({
patterns: [{ from: "./src/locales", to: "locales" }],
}),
@@ -71,6 +69,7 @@ const webpackConfig = {
output: {
filename: "[name].js",
path: path.resolve(__dirname, "build"),
clean: true,
},
module: { rules: moduleRules },
plugins: plugins,

View File

@@ -1,7 +1,7 @@
{
"name": "@bitwarden/desktop",
"description": "A secure and free password manager for all of your devices.",
"version": "2024.3.0",
"version": "2024.3.1",
"keywords": [
"bitwarden",
"password",

View File

@@ -577,7 +577,6 @@ export class AppComponent implements OnInit, OnDestroy {
await this.eventUploadService.uploadEvents(userBeingLoggedOut);
await this.syncService.setLastSync(new Date(0), userBeingLoggedOut);
await this.cryptoService.clearKeys(userBeingLoggedOut);
await this.settingsService.clear(userBeingLoggedOut);
await this.cipherService.clear(userBeingLoggedOut);
await this.folderService.clear(userBeingLoggedOut);
await this.collectionService.clear(userBeingLoggedOut);

View File

@@ -1,12 +1,12 @@
{
"name": "@bitwarden/desktop",
"version": "2024.3.0",
"version": "2024.3.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@bitwarden/desktop",
"version": "2024.3.0",
"version": "2024.3.1",
"license": "GPL-3.0",
"dependencies": {
"@bitwarden/desktop-native": "file:../desktop_native"

View File

@@ -2,7 +2,7 @@
"name": "@bitwarden/desktop",
"productName": "Bitwarden",
"description": "A secure and free password manager for all of your devices.",
"version": "2024.3.0",
"version": "2024.3.1",
"author": "Bitwarden Inc. <hello@bitwarden.com> (https://bitwarden.com)",
"homepage": "https://bitwarden.com",
"license": "GPL-3.0",

View File

@@ -1,7 +1,6 @@
const path = require("path");
const { merge } = require("webpack-merge");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const TsconfigPathsPlugin = require("tsconfig-paths-webpack-plugin");
const configurator = require("./config/config");
const { EnvironmentPlugin } = require("webpack");

View File

@@ -1,7 +1,6 @@
const path = require("path");
const { merge } = require("webpack-merge");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const TsconfigPathsPlugin = require("tsconfig-paths-webpack-plugin");
const configurator = require("./config/config");
const { EnvironmentPlugin } = require("webpack");

View File

@@ -275,7 +275,6 @@ export class AppComponent implements OnDestroy, OnInit {
await Promise.all([
this.syncService.setLastSync(new Date(0)),
this.cryptoService.clearKeys(),
this.settingsService.clear(userId),
this.cipherService.clear(userId),
this.folderService.clear(userId),
this.collectionService.clear(userId),

View File

@@ -2,7 +2,6 @@ const fs = require("fs");
const path = require("path");
const { AngularWebpackPlugin } = require("@ngtools/webpack");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const HtmlWebpackInjector = require("html-webpack-injector");
const HtmlWebpackPlugin = require("html-webpack-plugin");
@@ -87,7 +86,6 @@ const moduleRules = [
];
const plugins = [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: "./src/index.html",
filename: "index.html",
@@ -371,6 +369,7 @@ const webpackConfig = {
output: {
filename: "[name].[contenthash].js",
path: path.resolve(__dirname, "build"),
clean: true,
},
module: {
noParse: /\.wasm$/,