From c339d684f8f7b7e4f1ca8996a51c5c54cc8c9a48 Mon Sep 17 00:00:00 2001 From: Jonathan Prusik Date: Mon, 16 Dec 2024 11:58:35 -0500 Subject: [PATCH] update tests and cleanup --- .../settings/blocked-domains.component.ts | 35 +++--- .../settings/excluded-domains.component.ts | 37 +++--- .../services/autofill.service.spec.ts | 5 +- .../browser-script-injector.service.spec.ts | 116 +++++++++++++++++- .../browser-script-injector.service.ts | 29 +++-- .../fileless-importer.background.spec.ts | 10 +- .../autofill-vault-list-items.component.ts | 2 +- .../vault-list-items-container.component.ts | 2 +- .../components/vault/vault-v2.component.ts | 32 ++--- .../service-container/service-container.ts | 43 ++++--- .../src/services/jslib-services.module.ts | 10 +- .../services/domain-settings.service.spec.ts | 9 +- .../services/domain-settings.service.ts | 4 +- 13 files changed, 234 insertions(+), 100 deletions(-) diff --git a/apps/browser/src/autofill/popup/settings/blocked-domains.component.ts b/apps/browser/src/autofill/popup/settings/blocked-domains.component.ts index de5707f5c6f..461f62da6dc 100644 --- a/apps/browser/src/autofill/popup/settings/blocked-domains.component.ts +++ b/apps/browser/src/autofill/popup/settings/blocked-domains.component.ts @@ -15,7 +15,6 @@ import { JslibModule } from "@bitwarden/angular/jslib.module"; import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service"; import { NeverDomains } 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 { Utils } from "@bitwarden/common/platform/misc/utils"; import { ButtonModule, @@ -26,6 +25,7 @@ import { LinkModule, SectionComponent, SectionHeaderComponent, + ToastService, TypographyModule, } from "@bitwarden/components"; @@ -59,7 +59,8 @@ import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.co ], }) export class BlockedDomainsComponent implements AfterViewInit, OnDestroy { - @ViewChildren("uriInput") uriInputElements: QueryList>; + @ViewChildren("uriInput") uriInputElements: QueryList> = + new QueryList(); dataIsPristine = true; isLoading = false; @@ -73,7 +74,7 @@ export class BlockedDomainsComponent implements AfterViewInit, OnDestroy { constructor( private domainSettingsService: DomainSettingsService, private i18nService: I18nService, - private platformUtilsService: PlatformUtilsService, + private toastService: ToastService, ) {} async ngAfterViewInit() { @@ -150,11 +151,11 @@ export class BlockedDomainsComponent implements AfterViewInit, OnDestroy { const validatedHost = Utils.getHostname(uri); if (!validatedHost) { - this.platformUtilsService.showToast( - "error", - null, - this.i18nService.t("excludedDomainsInvalidDomain", uri), - ); + this.toastService.showToast({ + message: this.i18nService.t("excludedDomainsInvalidDomain", uri), + title: "", + variant: "error", + }); // Don't reset via `handleStateUpdate` to allow existing input value correction this.isLoading = false; @@ -176,7 +177,7 @@ export class BlockedDomainsComponent implements AfterViewInit, OnDestroy { if (stateIsUnchanged) { // Reset UI state directly const constructedNeverDomainsState = this.storedBlockedDomains.reduce( - (neverDomains, uri) => ({ ...neverDomains, [uri]: null }), + (neverDomains: NeverDomains, uri: string) => ({ ...neverDomains, [uri]: null }), {}, ); this.handleStateUpdate(constructedNeverDomainsState); @@ -184,13 +185,17 @@ export class BlockedDomainsComponent implements AfterViewInit, OnDestroy { await this.domainSettingsService.setBlockedInteractionsUris(newBlockedDomainsSaveState); } - this.platformUtilsService.showToast( - "success", - null, - this.i18nService.t("blockedDomainsSavedSuccess"), - ); + this.toastService.showToast({ + message: this.i18nService.t("blockedDomainsSavedSuccess"), + title: "", + variant: "success", + }); } catch { - this.platformUtilsService.showToast("error", null, this.i18nService.t("unexpectedError")); + this.toastService.showToast({ + message: this.i18nService.t("unexpectedError"), + title: "", + variant: "error", + }); // Don't reset via `handleStateUpdate` to preserve input values this.isLoading = false; diff --git a/apps/browser/src/autofill/popup/settings/excluded-domains.component.ts b/apps/browser/src/autofill/popup/settings/excluded-domains.component.ts index 1391ad516fb..7d429bfe4f0 100644 --- a/apps/browser/src/autofill/popup/settings/excluded-domains.component.ts +++ b/apps/browser/src/autofill/popup/settings/excluded-domains.component.ts @@ -1,5 +1,3 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { CommonModule } from "@angular/common"; import { QueryList, @@ -17,7 +15,6 @@ import { JslibModule } from "@bitwarden/angular/jslib.module"; import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service"; import { NeverDomains } 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 { Utils } from "@bitwarden/common/platform/misc/utils"; import { ButtonModule, @@ -28,6 +25,7 @@ import { LinkModule, SectionComponent, SectionHeaderComponent, + ToastService, TypographyModule, } from "@bitwarden/components"; @@ -62,7 +60,8 @@ import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.co ], }) export class ExcludedDomainsComponent implements AfterViewInit, OnDestroy { - @ViewChildren("uriInput") uriInputElements: QueryList>; + @ViewChildren("uriInput") uriInputElements: QueryList> = + new QueryList(); accountSwitcherEnabled = false; dataIsPristine = true; @@ -77,7 +76,7 @@ export class ExcludedDomainsComponent implements AfterViewInit, OnDestroy { constructor( private domainSettingsService: DomainSettingsService, private i18nService: I18nService, - private platformUtilsService: PlatformUtilsService, + private toastService: ToastService, ) { this.accountSwitcherEnabled = enableAccountSwitching(); } @@ -156,11 +155,11 @@ export class ExcludedDomainsComponent implements AfterViewInit, OnDestroy { const validatedHost = Utils.getHostname(uri); if (!validatedHost) { - this.platformUtilsService.showToast( - "error", - null, - this.i18nService.t("excludedDomainsInvalidDomain", uri), - ); + this.toastService.showToast({ + message: this.i18nService.t("excludedDomainsInvalidDomain", uri), + title: "", + variant: "error", + }); // Don't reset via `handleStateUpdate` to allow existing input value correction this.isLoading = false; @@ -182,7 +181,7 @@ export class ExcludedDomainsComponent implements AfterViewInit, OnDestroy { if (stateIsUnchanged) { // Reset UI state directly const constructedNeverDomainsState = this.storedExcludedDomains.reduce( - (neverDomains, uri) => ({ ...neverDomains, [uri]: null }), + (neverDomains: NeverDomains, uri: string) => ({ ...neverDomains, [uri]: null }), {}, ); this.handleStateUpdate(constructedNeverDomainsState); @@ -190,13 +189,17 @@ export class ExcludedDomainsComponent implements AfterViewInit, OnDestroy { await this.domainSettingsService.setNeverDomains(newExcludedDomainsSaveState); } - this.platformUtilsService.showToast( - "success", - null, - this.i18nService.t("excludedDomainsSavedSuccess"), - ); + this.toastService.showToast({ + message: this.i18nService.t("excludedDomainsSavedSuccess"), + title: "", + variant: "success", + }); } catch { - this.platformUtilsService.showToast("error", null, this.i18nService.t("unexpectedError")); + this.toastService.showToast({ + message: this.i18nService.t("unexpectedError"), + title: "", + variant: "error", + }); // Don't reset via `handleStateUpdate` to preserve input values this.isLoading = false; diff --git a/apps/browser/src/autofill/services/autofill.service.spec.ts b/apps/browser/src/autofill/services/autofill.service.spec.ts index f648631baf7..77d73d7ae65 100644 --- a/apps/browser/src/autofill/services/autofill.service.spec.ts +++ b/apps/browser/src/autofill/services/autofill.service.spec.ts @@ -98,6 +98,8 @@ describe("AutofillService", () => { let messageListener: MockProxy; beforeEach(() => { + configService = mock(); + configService.getFeatureFlag$.mockImplementation(() => of(false)); scriptInjectorService = new BrowserScriptInjectorService( domainSettingsService, platformUtilsService, @@ -110,10 +112,10 @@ describe("AutofillService", () => { autofillSettingsService.inlineMenuVisibility$ = inlineMenuVisibilityMock$; autofillSettingsService.showInlineMenuCards$ = showInlineMenuCardsMock$; autofillSettingsService.showInlineMenuIdentities$ = showInlineMenuIdentitiesMock$; + autofillSettingsService.autofillOnPageLoad$ = of(true); activeAccountStatusMock$ = new BehaviorSubject(AuthenticationStatus.Unlocked); authService = mock(); authService.activeAccountStatus$ = activeAccountStatusMock$; - configService = mock(); messageListener = mock(); enableChangedPasswordPromptMock$ = new BehaviorSubject(true); enableAddedLoginPromptMock$ = new BehaviorSubject(true); @@ -389,6 +391,7 @@ describe("AutofillService", () => { ); tabMock = createChromeTabMock(); sender = { tab: tabMock, frameId: 1 }; + jest.spyOn(BrowserApi, "getTab").mockImplementation(async () => tabMock); jest.spyOn(BrowserApi, "executeScriptInTab").mockImplementation(); jest .spyOn(autofillService, "getInlineMenuVisibility") diff --git a/apps/browser/src/platform/services/browser-script-injector.service.spec.ts b/apps/browser/src/platform/services/browser-script-injector.service.spec.ts index fb5b76fa402..1a1227d10c0 100644 --- a/apps/browser/src/platform/services/browser-script-injector.service.spec.ts +++ b/apps/browser/src/platform/services/browser-script-injector.service.spec.ts @@ -1,9 +1,22 @@ -import { mock } from "jest-mock-extended"; +import { mock, MockProxy } from "jest-mock-extended"; +import { of } from "rxjs"; -import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service"; +import { + DomainSettingsService, + DefaultDomainSettingsService, +} from "@bitwarden/common/autofill/services/domain-settings.service"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { + FakeStateProvider, + FakeAccountService, + mockAccountServiceWith, +} from "@bitwarden/common/spec"; +import { UserId } from "@bitwarden/common/types/guid"; +import { createChromeTabMock } from "../../autofill/spec/autofill-mocks"; import { BrowserApi } from "../browser/browser-api"; import { @@ -12,8 +25,19 @@ import { } from "./abstractions/script-injector.service"; import { BrowserScriptInjectorService } from "./browser-script-injector.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("ScriptInjectorService", () => { const tabId = 1; + const tabMock = createChromeTabMock({ id: tabId }); + const mockBlockedURI = new URL(tabMock.url); + jest.spyOn(BrowserApi, "executeScriptInTab").mockImplementation(); + jest.spyOn(BrowserApi, "isManifestVersion"); + const combinedManifestVersionFile = "content/autofill-init.js"; const mv2SpecificFile = "content/autofill-init-mv2.js"; const mv2Details = { file: mv2SpecificFile }; @@ -23,14 +47,24 @@ describe("ScriptInjectorService", () => { runAt: "document_start", }; const manifestVersionSpy = jest.spyOn(BrowserApi, "manifestVersion", "get"); + let scriptInjectorService: BrowserScriptInjectorService; - jest.spyOn(BrowserApi, "executeScriptInTab").mockImplementation(); - jest.spyOn(BrowserApi, "isManifestVersion"); - const domainSettingsService = mock(); - const platformUtilsService = mock(); const logService = mock(); + const platformUtilsService = mock(); + const mockUserId = Utils.newGuid() as UserId; + const accountService: FakeAccountService = mockAccountServiceWith(mockUserId); + const fakeStateProvider: FakeStateProvider = new FakeStateProvider(accountService); + let configService: MockProxy; + let domainSettingsService: DomainSettingsService; + const expectedBlockedURIError = new Error("This URI of this tab is on the blocked domains list."); beforeEach(() => { + jest.spyOn(BrowserApi, "getTab").mockImplementation(async () => tabMock); + configService = mock(); + configService.getFeatureFlag$.mockImplementation(() => of(false)); + domainSettingsService = new DefaultDomainSettingsService(fakeStateProvider, configService); + domainSettingsService.equivalentDomains$ = of(mockEquivalentDomains); + domainSettingsService.blockedInteractionsUris$ = of(null); scriptInjectorService = new BrowserScriptInjectorService( domainSettingsService, platformUtilsService, @@ -77,6 +111,76 @@ describe("ScriptInjectorService", () => { { world: "ISOLATED" }, ); }); + + it("skips injecting the script in manifest v3 when the tab domain is a blocked domain", async () => { + domainSettingsService.blockedInteractionsUris$ = of({ [mockBlockedURI.host]: null }); + manifestVersionSpy.mockReturnValue(3); + + await expect( + scriptInjectorService.inject({ + tabId, + injectDetails: { + file: combinedManifestVersionFile, + frame: 10, + ...sharedInjectDetails, + }, + }), + ).rejects.toThrow(expectedBlockedURIError); + }); + + it("skips injecting the script in manifest v2 when the tab domain is a blocked domain", async () => { + domainSettingsService.blockedInteractionsUris$ = of({ [mockBlockedURI.host]: null }); + manifestVersionSpy.mockReturnValue(2); + + await expect( + scriptInjectorService.inject({ + tabId, + injectDetails: { + file: combinedManifestVersionFile, + frame: "all_frames", + ...sharedInjectDetails, + }, + }), + ).rejects.toThrow(expectedBlockedURIError); + }); + + it("injects the script in manifest v2 when given combined injection details", async () => { + manifestVersionSpy.mockReturnValue(2); + + await scriptInjectorService.inject({ + tabId, + injectDetails: { + file: combinedManifestVersionFile, + frame: "all_frames", + ...sharedInjectDetails, + }, + }); + + expect(BrowserApi.executeScriptInTab).toHaveBeenCalledWith(tabId, { + ...sharedInjectDetails, + allFrames: true, + file: combinedManifestVersionFile, + }); + }); + + it("injects the script in manifest v3 when given combined injection details", async () => { + manifestVersionSpy.mockReturnValue(3); + + await scriptInjectorService.inject({ + tabId, + injectDetails: { + file: combinedManifestVersionFile, + frame: 10, + ...sharedInjectDetails, + }, + }); + + expect(BrowserApi.executeScriptInTab).toHaveBeenCalledWith( + tabId, + { ...sharedInjectDetails, frameId: 10, file: combinedManifestVersionFile }, + { world: "ISOLATED" }, + ); + }); }); describe("injection of mv2 specific details", () => { diff --git a/apps/browser/src/platform/services/browser-script-injector.service.ts b/apps/browser/src/platform/services/browser-script-injector.service.ts index 70708833f19..294bd0036b8 100644 --- a/apps/browser/src/platform/services/browser-script-injector.service.ts +++ b/apps/browser/src/platform/services/browser-script-injector.service.ts @@ -1,7 +1,6 @@ -import { Subject, takeUntil } from "rxjs"; +import { firstValueFrom } from "rxjs"; import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service"; -import { NeverDomains } from "@bitwarden/common/models/domain/domain-service"; // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; @@ -18,22 +17,12 @@ import { export class BrowserScriptInjectorService extends ScriptInjectorService { blockedDomains: Set = null; - private destroy$ = new Subject(); - constructor( private readonly domainSettingsService: DomainSettingsService, private readonly platformUtilsService: PlatformUtilsService, private readonly logService: LogService, ) { super(); - - this.domainSettingsService.blockedInteractionsUris$ - .pipe(takeUntil(this.destroy$)) - .subscribe((neverDomains: NeverDomains) => { - if (neverDomains) { - this.blockedDomains = new Set(Object.keys(neverDomains)); - } - }); } /** @@ -49,10 +38,20 @@ export class BrowserScriptInjectorService extends ScriptInjectorService { throw new Error("No file specified for script injection"); } + const tab = tabId && (await BrowserApi.getTab(tabId)); + const tabURL = tab?.url ? new URL(tab.url) : null; + // Check if the tab URI is on the disabled URIs list - const tab = await BrowserApi.getTab(tabId); - const tabURL = tab.url ? new URL(tab.url) : null; - const injectionAllowedInTab = !(tabURL && this.blockedDomains?.has(tabURL.hostname)); + let injectionAllowedInTab = true; + const blockedDomains = await firstValueFrom( + this.domainSettingsService.blockedInteractionsUris$, + ); + + if (blockedDomains && tabURL?.hostname) { + const blockedDomainsSet = new Set(Object.keys(blockedDomains)); + + injectionAllowedInTab = !(tabURL && blockedDomainsSet.has(tabURL.hostname)); + } if (!injectionAllowedInTab) { throw new Error("This URI of this tab is on the blocked domains list."); diff --git a/apps/browser/src/tools/background/fileless-importer.background.spec.ts b/apps/browser/src/tools/background/fileless-importer.background.spec.ts index 83a38201f29..9c8a6c886f3 100644 --- a/apps/browser/src/tools/background/fileless-importer.background.spec.ts +++ b/apps/browser/src/tools/background/fileless-importer.background.spec.ts @@ -1,5 +1,5 @@ import { mock } from "jest-mock-extended"; -import { firstValueFrom } from "rxjs"; +import { firstValueFrom, of } from "rxjs"; import { PolicyService } from "@bitwarden/common/admin-console/services/policy/policy.service"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; @@ -45,8 +45,10 @@ describe("FilelessImporterBackground ", () => { const platformUtilsService = mock(); const logService = mock(); let scriptInjectorService: BrowserScriptInjectorService; + let tabMock: chrome.tabs.Tab; beforeEach(() => { + domainSettingsService.blockedInteractionsUris$ = of(null); scriptInjectorService = new BrowserScriptInjectorService( domainSettingsService, platformUtilsService, @@ -81,12 +83,13 @@ describe("FilelessImporterBackground ", () => { beforeEach(() => { lpImporterPort = createPortSpyMock(FilelessImportPort.LpImporter); + tabMock = lpImporterPort.sender.tab; + jest.spyOn(BrowserApi, "getTab").mockImplementation(async () => tabMock); manifestVersionSpy = jest.spyOn(BrowserApi, "manifestVersion", "get"); executeScriptInTabSpy = jest.spyOn(BrowserApi, "executeScriptInTab").mockResolvedValue(null); jest.spyOn(authService, "getAuthStatus").mockResolvedValue(AuthenticationStatus.Unlocked); jest.spyOn(configService, "getFeatureFlag").mockResolvedValue(true); jest.spyOn(filelessImporterBackground as any, "removeIndividualVault"); - (firstValueFrom as jest.Mock).mockResolvedValue(false); }); it("ignores the port connection if the port name is not present in the set of filelessImportNames", async () => { @@ -99,6 +102,7 @@ describe("FilelessImporterBackground ", () => { }); it("posts a message to the port indicating that the fileless import feature is disabled if the user's auth status is not unlocked", async () => { + (firstValueFrom as jest.Mock).mockResolvedValue(false); jest.spyOn(authService, "getAuthStatus").mockResolvedValue(AuthenticationStatus.Locked); triggerRuntimeOnConnectEvent(lpImporterPort); @@ -135,6 +139,7 @@ describe("FilelessImporterBackground ", () => { }); it("posts a message to the port indicating that the fileless import feature is enabled", async () => { + (firstValueFrom as jest.Mock).mockResolvedValue(false); triggerRuntimeOnConnectEvent(lpImporterPort); await flushPromises(); @@ -158,6 +163,7 @@ describe("FilelessImporterBackground ", () => { }); it("triggers an injection of the `lp-suppress-import-download-script-append-mv2.js` script in manifest v2", async () => { + (firstValueFrom as jest.Mock).mockResolvedValueOnce(false); manifestVersionSpy.mockReturnValue(2); triggerRuntimeOnConnectEvent(lpImporterPort); diff --git a/apps/browser/src/vault/popup/components/vault-v2/autofill-vault-list-items/autofill-vault-list-items.component.ts b/apps/browser/src/vault/popup/components/vault-v2/autofill-vault-list-items/autofill-vault-list-items.component.ts index d4fd8404447..565fd5fcaeb 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/autofill-vault-list-items/autofill-vault-list-items.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/autofill-vault-list-items/autofill-vault-list-items.component.ts @@ -44,7 +44,7 @@ export class AutofillVaultListItemsComponent implements OnInit { /** * Indicators for the section. */ - @Input() sectionIndicators: string[]; + @Input() sectionIndicators: string[] = []; /** * Observable that determines whether the empty autofill tip should be shown. diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-list-items-container/vault-list-items-container.component.ts b/apps/browser/src/vault/popup/components/vault-v2/vault-list-items-container/vault-list-items-container.component.ts index d207b5ae08b..b4e130c10d3 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-list-items-container/vault-list-items-container.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-list-items-container/vault-list-items-container.component.ts @@ -103,7 +103,7 @@ export class VaultListItemsContainerComponent implements AfterViewInit { /** * Indicators for the section. */ - @Input() sectionIndicators: string[]; + @Input() sectionIndicators: string[] = []; /** * Optional description for the vault list item section. Will be shown below the title even when diff --git a/apps/browser/src/vault/popup/components/vault/vault-v2.component.ts b/apps/browser/src/vault/popup/components/vault/vault-v2.component.ts index cfbcc1b6d1f..e0894be9594 100644 --- a/apps/browser/src/vault/popup/components/vault/vault-v2.component.ts +++ b/apps/browser/src/vault/popup/components/vault/vault-v2.component.ts @@ -62,7 +62,7 @@ export class VaultV2Component implements OnInit, OnDestroy { protected loading$ = this.vaultPopupItemsService.loading$; protected scriptInjectionIsBlocked = false; protected showScriptInjectionIsBlockedBanner = false; - protected autofillTabHostname: string = null; + protected autofillTabHostname: string | null = null; protected sectionIndicators: string[] = []; protected newItemItemValues$: Observable = @@ -151,20 +151,24 @@ export class VaultV2Component implements OnInit, OnDestroy { ngOnDestroy(): void {} handleScriptInjectionIsBlockedBannerDismiss() { - firstValueFrom(this.domainSettingsService.blockedInteractionsUris$) - .then((blockedURIs) => { - this.showScriptInjectionIsBlockedBanner = false; - this.domainSettingsService - .setBlockedInteractionsUris({ + if (!this.autofillTabHostname) { + return; + } + + try { + void firstValueFrom(this.domainSettingsService.blockedInteractionsUris$).then( + (blockedURIs) => { + this.showScriptInjectionIsBlockedBanner = false; + void this.domainSettingsService.setBlockedInteractionsUris({ ...blockedURIs, - [this.autofillTabHostname]: { bannerIsDismissed: true }, - }) - .catch(() => { - /* no-op */ + [this.autofillTabHostname as string]: { bannerIsDismissed: true }, }); - }) - .catch(() => { - /* no-op */ - }); + }, + ); + } catch (e) { + throw new Error( + "There was a problem dismissing the blocked interaction URI notification banner", + ); + } } } diff --git a/apps/cli/src/service-container/service-container.ts b/apps/cli/src/service-container/service-container.ts index 2afbae0782f..9f9e45e86d4 100644 --- a/apps/cli/src/service-container/service-container.ts +++ b/apps/cli/src/service-container/service-container.ts @@ -483,7 +483,29 @@ export class ServiceContainer { this.containerService = new ContainerService(this.keyService, this.encryptService); - this.domainSettingsService = new DefaultDomainSettingsService(this.stateProvider); + this.configApiService = new ConfigApiService(this.apiService, this.tokenService); + + this.authService = new AuthService( + this.accountService, + this.messagingService, + this.keyService, + this.apiService, + this.stateService, + this.tokenService, + ); + + this.configService = new DefaultConfigService( + this.configApiService, + this.environmentService, + this.logService, + this.stateProvider, + this.authService, + ); + + this.domainSettingsService = new DefaultDomainSettingsService( + this.stateProvider, + this.configService, + ); this.fileUploadService = new FileUploadService(this.logService, this.apiService); @@ -579,25 +601,6 @@ export class ServiceContainer { this.taskSchedulerService = new DefaultTaskSchedulerService(this.logService); - this.authService = new AuthService( - this.accountService, - this.messagingService, - this.keyService, - this.apiService, - this.stateService, - this.tokenService, - ); - - this.configApiService = new ConfigApiService(this.apiService, this.tokenService); - - this.configService = new DefaultConfigService( - this.configApiService, - this.environmentService, - this.logService, - this.stateProvider, - this.authService, - ); - this.devicesApiService = new DevicesApiServiceImplementation(this.apiService); this.deviceTrustService = new DeviceTrustService( this.keyGenerationService, diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index f623e9cf286..583ba82fc98 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -463,6 +463,11 @@ const safeProviders: SafeProvider[] = [ useClass: CipherFileUploadService, deps: [ApiServiceAbstraction, FileUploadServiceAbstraction], }), + safeProvider({ + provide: DomainSettingsService, + useClass: DefaultDomainSettingsService, + deps: [StateProvider, ConfigService], + }), safeProvider({ provide: CipherServiceAbstraction, useFactory: ( @@ -1243,11 +1248,6 @@ const safeProviders: SafeProvider[] = [ useClass: BadgeSettingsService, deps: [StateProvider], }), - safeProvider({ - provide: DomainSettingsService, - useClass: DefaultDomainSettingsService, - deps: [StateProvider, ConfigService], - }), safeProvider({ provide: BiometricStateService, useClass: DefaultBiometricStateService, diff --git a/libs/common/src/autofill/services/domain-settings.service.spec.ts b/libs/common/src/autofill/services/domain-settings.service.spec.ts index 24e3763eb45..1f27ddcde5b 100644 --- a/libs/common/src/autofill/services/domain-settings.service.spec.ts +++ b/libs/common/src/autofill/services/domain-settings.service.spec.ts @@ -1,5 +1,8 @@ +import { MockProxy, mock } from "jest-mock-extended"; import { firstValueFrom, of } from "rxjs"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; + import { FakeStateProvider, FakeAccountService, mockAccountServiceWith } from "../../../spec"; import { Utils } from "../../platform/misc/utils"; import { UserId } from "../../types/guid"; @@ -8,8 +11,10 @@ import { DefaultDomainSettingsService, DomainSettingsService } from "./domain-se describe("DefaultDomainSettingsService", () => { let domainSettingsService: DomainSettingsService; + const configServiceMock = mock(); const mockUserId = Utils.newGuid() as UserId; const accountService: FakeAccountService = mockAccountServiceWith(mockUserId); + let mockConfigService: MockProxy; const fakeStateProvider: FakeStateProvider = new FakeStateProvider(accountService); const mockEquivalentDomains = [ @@ -19,10 +24,12 @@ describe("DefaultDomainSettingsService", () => { ]; beforeEach(() => { - domainSettingsService = new DefaultDomainSettingsService(fakeStateProvider); + domainSettingsService = new DefaultDomainSettingsService(fakeStateProvider, mockConfigService); + jest.spyOn(configServiceMock, "getFeatureFlag$").mockReturnValue(of(false)); jest.spyOn(domainSettingsService, "getUrlEquivalentDomains"); domainSettingsService.equivalentDomains$ = of(mockEquivalentDomains); + domainSettingsService.blockedInteractionsUris$ = of(null); }); describe("getUrlEquivalentDomains", () => { diff --git a/libs/common/src/autofill/services/domain-settings.service.ts b/libs/common/src/autofill/services/domain-settings.service.ts index c49cfff6b02..b61fd2bb997 100644 --- a/libs/common/src/autofill/services/domain-settings.service.ts +++ b/libs/common/src/autofill/services/domain-settings.service.ts @@ -3,7 +3,6 @@ import { combineLatest, map, Observable } from "rxjs"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { NeverDomains, @@ -11,6 +10,7 @@ import { UriMatchStrategySetting, UriMatchStrategy, } from "../../models/domain/domain-service"; +import { ConfigService } from "../../platform/abstractions/config/config.service"; import { Utils } from "../../platform/misc/utils"; import { DOMAIN_SETTINGS_DISK, @@ -99,7 +99,7 @@ export class DefaultDomainSettingsService implements DomainSettingsService { this.blockedInteractionsUris$ = combineLatest([ this.blockedInteractionsUrisState.state$, - this.configService.getFeatureFlag$(FeatureFlag.BlockBrowserInjectionsByDomain), + this.configService?.getFeatureFlag$(FeatureFlag.BlockBrowserInjectionsByDomain), ]).pipe( map(([blockedUris, blockBrowserInjectionsByDomainEnabled]) => { if (!blockBrowserInjectionsByDomainEnabled) {