From 43d428b3dfa21d29e1aec0a722e30a8f714232c4 Mon Sep 17 00:00:00 2001 From: Justin Baur Date: Tue, 9 Aug 2022 21:30:26 -0400 Subject: [PATCH] [PS-816] Add Autofill Shortcut to MV3 Extension (#3131) * Work on background service worker. * Work on shortcuts * Work on supporting service worker * Put new background behind version check * Fix build * Use new storage service * create commands from crypto service (#2995) * Work on service worker autofill * Got basic autofill working * Final touches * Work on tests * Revert some changes * Add modifications * Remove unused ciphers for now * Cleanup * Address PR feedback * Update lock file * Update noop service * Add chrome type * Handle "/" in branch names Updates web workflow to handle the `/` in branch names when it's a PR. * Remove any Co-authored-by: Jake Fink Co-authored-by: Micaiah Martin <77340197+mimartin12@users.noreply.github.com> --- .storybook/tsconfig.json | 2 +- apps/browser/src/background.ts | 15 +- .../src/background/contextMenus.background.ts | 4 + .../src/background/webRequest.background.ts | 5 +- .../src/commands/autoFillActiveTabCommand.ts | 44 ++++ apps/browser/src/content/autofill.js | 6 + .../src/listeners/onCommandListener.ts | 140 ++++++++++ apps/browser/src/manifest.v3.json | 3 +- apps/browser/src/models/autofillField.ts | 1 + .../abstractChromeStorageApi.service.ts | 2 +- .../services/abstractions/autofill.service.ts | 42 ++- apps/browser/src/services/autofill.service.ts | 244 +++++++++++------- .../services/browserLocalStorage.service.ts | 2 +- .../services/browserMemoryStorage.service.ts | 2 +- apps/browser/webpack.config.js | 19 +- .../app/settings/verify-email.component.ts | 2 +- .../spec/services/consoleLog.service.spec.ts | 21 -- libs/common/src/abstractions/log.service.ts | 2 - libs/common/src/misc/utils.ts | 34 ++- libs/common/src/models/domain/attachment.ts | 2 +- libs/common/src/models/domain/encString.ts | 2 +- libs/common/src/services/cipher.service.ts | 2 +- .../common/src/services/consoleLog.service.ts | 15 -- libs/common/src/services/noopEvent.service.ts | 24 ++ libs/common/src/services/search.service.ts | 14 +- libs/common/src/services/state.service.ts | 2 +- .../src/services/webCryptoFunction.service.ts | 2 +- package-lock.json | 15 +- package.json | 3 +- 29 files changed, 488 insertions(+), 183 deletions(-) create mode 100644 apps/browser/src/commands/autoFillActiveTabCommand.ts create mode 100644 apps/browser/src/listeners/onCommandListener.ts create mode 100644 libs/common/src/services/noopEvent.service.ts diff --git a/.storybook/tsconfig.json b/.storybook/tsconfig.json index e411a7a39c..397be6b000 100644 --- a/.storybook/tsconfig.json +++ b/.storybook/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "../tsconfig", "compilerOptions": { - "types": ["node", "jest"], + "types": ["node", "jest", "chrome"], "allowSyntheticDefaultImports": true }, "exclude": ["../src/test.setup.ts", "../apps/src/**/*.spec.ts", "../libs/**/*.spec.ts"], diff --git a/apps/browser/src/background.ts b/apps/browser/src/background.ts index 939a481bd5..ea24defb8c 100644 --- a/apps/browser/src/background.ts +++ b/apps/browser/src/background.ts @@ -1,6 +1,13 @@ import MainBackground from "./background/main.background"; +import { onCommandListener } from "./listeners/onCommandListener"; -const bitwardenMain = ((window as any).bitwardenMain = new MainBackground()); -bitwardenMain.bootstrap().then(() => { - // Finished bootstrapping -}); +const manifest = chrome.runtime.getManifest(); + +if (manifest.manifest_version === 3) { + chrome.commands.onCommand.addListener(onCommandListener); +} else { + const bitwardenMain = ((window as any).bitwardenMain = new MainBackground()); + bitwardenMain.bootstrap().then(() => { + // Finished bootstrapping + }); +} diff --git a/apps/browser/src/background/contextMenus.background.ts b/apps/browser/src/background/contextMenus.background.ts index b14ef79cac..22d9d56bbf 100644 --- a/apps/browser/src/background/contextMenus.background.ts +++ b/apps/browser/src/background/contextMenus.background.ts @@ -81,6 +81,10 @@ export default class ContextMenusBackground { } private async cipherAction(tab: chrome.tabs.Tab, info: chrome.contextMenus.OnClickData) { + if (typeof info.menuItemId !== "string") { + return; + } + const id = info.menuItemId.split("_")[1]; if ((await this.authService.getAuthStatus()) < AuthenticationStatus.Unlocked) { diff --git a/apps/browser/src/background/webRequest.background.ts b/apps/browser/src/background/webRequest.background.ts index 0afbfc4112..26dd86a908 100644 --- a/apps/browser/src/background/webRequest.background.ts +++ b/apps/browser/src/background/webRequest.background.ts @@ -14,7 +14,10 @@ export default class WebRequestBackground { private cipherService: CipherService, private authService: AuthService ) { - this.webRequest = (window as any).chrome.webRequest; + const manifest = chrome.runtime.getManifest(); + if (manifest.manifest_version === 2) { + this.webRequest = (window as any).chrome.webRequest; + } this.isFirefox = platformUtilsService.isFirefox(); } diff --git a/apps/browser/src/commands/autoFillActiveTabCommand.ts b/apps/browser/src/commands/autoFillActiveTabCommand.ts new file mode 100644 index 0000000000..74cdad55d7 --- /dev/null +++ b/apps/browser/src/commands/autoFillActiveTabCommand.ts @@ -0,0 +1,44 @@ +import AutofillPageDetails from "../models/autofillPageDetails"; +import { AutofillService } from "../services/abstractions/autofill.service"; + +export class AutoFillActiveTabCommand { + constructor(private autofillService: AutofillService) {} + + async doAutoFillActiveTabCommand(tab: chrome.tabs.Tab) { + if (!tab.id) { + throw new Error("Tab does not have an id, cannot complete autofill."); + } + + const details = await this.collectPageDetails(tab.id); + await this.autofillService.doAutoFillOnTab( + [ + { + frameId: 0, + tab: tab, + details: details, + }, + ], + tab, + true + ); + } + + private async collectPageDetails(tabId: number): Promise { + return new Promise((resolve, reject) => { + chrome.tabs.sendMessage( + tabId, + { + command: "collectPageDetailsImmediately", + }, + (response: AutofillPageDetails) => { + if (chrome.runtime.lastError) { + reject(chrome.runtime.lastError); + return; + } + + resolve(response); + } + ); + }); + } +} diff --git a/apps/browser/src/content/autofill.js b/apps/browser/src/content/autofill.js index 7b285d4cf4..d4c05f7e8c 100644 --- a/apps/browser/src/content/autofill.js +++ b/apps/browser/src/content/autofill.js @@ -39,6 +39,7 @@ 6. Rename com.agilebits.* stuff to com.bitwarden.* 7. Remove "some useful globals" on window 8. Add ability to autofill span[data-bwautofill] elements + 9. Add new handler, for new command that responds with page details in response callback */ function collect(document, undefined) { @@ -1037,6 +1038,11 @@ fill(document, msg.fillScript); sendResponse(); return true; + } else if (msg.command === 'collectPageDetailsImmediately') { + var pageDetails = collect(document); + var pageDetailsObj = JSON.parse(pageDetails); + sendResponse(pageDetailsObj); + return true; } }); })(); diff --git a/apps/browser/src/listeners/onCommandListener.ts b/apps/browser/src/listeners/onCommandListener.ts new file mode 100644 index 0000000000..0e64f8b77e --- /dev/null +++ b/apps/browser/src/listeners/onCommandListener.ts @@ -0,0 +1,140 @@ +import { AuthenticationStatus } from "@bitwarden/common/enums/authenticationStatus"; +import { StateFactory } from "@bitwarden/common/factories/stateFactory"; +import { GlobalState } from "@bitwarden/common/models/domain/globalState"; +import { AuthService } from "@bitwarden/common/services/auth.service"; +import { CipherService } from "@bitwarden/common/services/cipher.service"; +import { ConsoleLogService } from "@bitwarden/common/services/consoleLog.service"; +import { EncryptService } from "@bitwarden/common/services/encrypt.service"; +import NoOpEventService from "@bitwarden/common/services/noOpEvent.service"; +import { SearchService } from "@bitwarden/common/services/search.service"; +import { SettingsService } from "@bitwarden/common/services/settings.service"; +import { StateMigrationService } from "@bitwarden/common/services/stateMigration.service"; +import { WebCryptoFunctionService } from "@bitwarden/common/services/webCryptoFunction.service"; + +import { AutoFillActiveTabCommand } from "../commands/autoFillActiveTabCommand"; +import { Account } from "../models/account"; +import { StateService as AbstractStateService } from "../services/abstractions/state.service"; +import AutofillService from "../services/autofill.service"; +import { BrowserCryptoService } from "../services/browserCrypto.service"; +import BrowserLocalStorageService from "../services/browserLocalStorage.service"; +import BrowserPlatformUtilsService from "../services/browserPlatformUtils.service"; +import I18nService from "../services/i18n.service"; +import { KeyGenerationService } from "../services/keyGeneration.service"; +import { LocalBackedSessionStorageService } from "../services/localBackedSessionStorage.service"; +import { StateService } from "../services/state.service"; + +export const onCommandListener = async (command: string, tab: chrome.tabs.Tab) => { + switch (command) { + case "autofill_login": + await doAutoFillLogin(tab); + break; + } +}; + +const doAutoFillLogin = async (tab: chrome.tabs.Tab): Promise => { + const logService = new ConsoleLogService(false); + + const cryptoFunctionService = new WebCryptoFunctionService(self); + + const storageService = new BrowserLocalStorageService(); + + const secureStorageService = new BrowserLocalStorageService(); + + const memoryStorageService = new LocalBackedSessionStorageService( + new EncryptService(cryptoFunctionService, logService, false), + new KeyGenerationService(cryptoFunctionService) + ); + + const stateFactory = new StateFactory(GlobalState, Account); + + const stateMigrationService = new StateMigrationService( + storageService, + secureStorageService, + stateFactory + ); + + const stateService: AbstractStateService = new StateService( + storageService, + secureStorageService, + memoryStorageService, // AbstractStorageService + logService, + stateMigrationService, + stateFactory + ); + + await stateService.init(); + + const platformUtils = new BrowserPlatformUtilsService( + null, // MessagingService + stateService, + null, // clipboardWriteCallback + null // biometricCallback + ); + + const cryptoService = new BrowserCryptoService( + cryptoFunctionService, + null, // AbstractEncryptService + platformUtils, + logService, + stateService + ); + + const settingsService = new SettingsService(stateService); + + const i18nService = new I18nService(chrome.i18n.getUILanguage()); + + await i18nService.init(); + + // Don't love this pt.1 + let searchService: SearchService = null; + + const cipherService = new CipherService( + cryptoService, + settingsService, + null, // ApiService + null, // FileUploadService, + i18nService, + () => searchService, // Don't love this pt.2 + logService, + stateService + ); + + // Don't love this pt.3 + searchService = new SearchService(cipherService, logService, i18nService); + + // TODO: Remove this before we encourage anyone to start using this + const eventService = new NoOpEventService(); + + const autofillService = new AutofillService( + cipherService, + stateService, + null, // TotpService + eventService, + logService + ); + + const authService = new AuthService( + cryptoService, // CryptoService + null, // ApiService + null, // TokenService + null, // AppIdService + platformUtils, + null, // MessagingService + logService, + null, // KeyConnectorService + null, // EnvironmentService + stateService, + null, // TwoFactorService + i18nService + ); + + const authStatus = await authService.getAuthStatus(); + if (authStatus < AuthenticationStatus.Unlocked) { + // TODO: Add back in unlock on autofill + logService.info("Currently not unlocked, MV3 does not support unlock on autofill currently."); + return; + } + + const command = new AutoFillActiveTabCommand(autofillService); + await command.doAutoFillActiveTabCommand(tab); +}; diff --git a/apps/browser/src/manifest.v3.json b/apps/browser/src/manifest.v3.json index 0acb6ccea1..5c04b6847b 100644 --- a/apps/browser/src/manifest.v3.json +++ b/apps/browser/src/manifest.v3.json @@ -47,7 +47,8 @@ } ], "background": { - "service_worker": "background.js" + "service_worker": "background.js", + "type": "module" }, "action": { "default_icon": { diff --git a/apps/browser/src/models/autofillField.ts b/apps/browser/src/models/autofillField.ts index fa08602a32..96da54f677 100644 --- a/apps/browser/src/models/autofillField.ts +++ b/apps/browser/src/models/autofillField.ts @@ -22,4 +22,5 @@ export default class AutofillField { selectInfo: any; maxLength: number; tagName: string; + [key: string]: any; } diff --git a/apps/browser/src/services/abstractChromeStorageApi.service.ts b/apps/browser/src/services/abstractChromeStorageApi.service.ts index e6784570af..4d83e36972 100644 --- a/apps/browser/src/services/abstractChromeStorageApi.service.ts +++ b/apps/browser/src/services/abstractChromeStorageApi.service.ts @@ -1,7 +1,7 @@ import { AbstractStorageService } from "@bitwarden/common/abstractions/storage.service"; export default abstract class AbstractChromeStorageService implements AbstractStorageService { - protected abstract chromeStorageApi: any; + protected abstract chromeStorageApi: chrome.storage.StorageArea; async get(key: string): Promise { return new Promise((resolve) => { diff --git a/apps/browser/src/services/abstractions/autofill.service.ts b/apps/browser/src/services/abstractions/autofill.service.ts index 68fb1b8dba..ceab1d589f 100644 --- a/apps/browser/src/services/abstractions/autofill.service.ts +++ b/apps/browser/src/services/abstractions/autofill.service.ts @@ -1,7 +1,41 @@ +import { CipherView } from "@bitwarden/common/models/view/cipherView"; + +import AutofillField from "../../models/autofillField"; +import AutofillForm from "../../models/autofillForm"; import AutofillPageDetails from "../../models/autofillPageDetails"; -export abstract class AutofillService { - getFormsWithPasswordFields: (pageDetails: AutofillPageDetails) => any[]; - doAutoFill: (options: any) => Promise; - doAutoFillActiveTab: (pageDetails: any, fromCommand: boolean) => Promise; +export interface PageDetail { + frameId: number; + tab: chrome.tabs.Tab; + details: AutofillPageDetails; +} + +export interface AutoFillOptions { + cipher: CipherView; + pageDetails: PageDetail[]; + doc?: typeof window.document; + tab: chrome.tabs.Tab; + skipUsernameOnlyFill?: boolean; + onlyEmptyFields?: boolean; + onlyVisibleFields?: boolean; + fillNewPassword?: boolean; + skipLastUsed?: boolean; +} + +export interface FormData { + form: AutofillForm; + password: AutofillField; + username: AutofillField; + passwords: AutofillField[]; +} + +export abstract class AutofillService { + getFormsWithPasswordFields: (pageDetails: AutofillPageDetails) => FormData[]; + doAutoFill: (options: AutoFillOptions) => Promise; + doAutoFillOnTab: ( + pageDetails: PageDetail[], + tab: chrome.tabs.Tab, + fromCommand: boolean + ) => Promise; + doAutoFillActiveTab: (pageDetails: PageDetail[], fromCommand: boolean) => Promise; } diff --git a/apps/browser/src/services/autofill.service.ts b/apps/browser/src/services/autofill.service.ts index 341eb723d1..ff8eca9347 100644 --- a/apps/browser/src/services/autofill.service.ts +++ b/apps/browser/src/services/autofill.service.ts @@ -15,13 +15,26 @@ import AutofillPageDetails from "../models/autofillPageDetails"; import AutofillScript from "../models/autofillScript"; import { StateService } from "../services/abstractions/state.service"; -import { AutofillService as AutofillServiceInterface } from "./abstractions/autofill.service"; +import { + AutoFillOptions, + AutofillService as AutofillServiceInterface, + PageDetail, + FormData, +} from "./abstractions/autofill.service"; import { AutoFillConstants, CreditCardAutoFillConstants, IdentityAutoFillConstants, } from "./autofillConstants"; +export interface GenerateFillScriptOptions { + skipUsernameOnlyFill: boolean; + onlyEmptyFields: boolean; + onlyVisibleFields: boolean; + fillNewPassword: boolean; + cipher: CipherView; +} + export default class AutofillService implements AutofillServiceInterface { constructor( private cipherService: CipherService, @@ -31,10 +44,16 @@ export default class AutofillService implements AutofillServiceInterface { private logService: LogService ) {} - getFormsWithPasswordFields(pageDetails: AutofillPageDetails): any[] { - const formData: any[] = []; + getFormsWithPasswordFields(pageDetails: AutofillPageDetails): FormData[] { + const formData: FormData[] = []; - const passwordFields = this.loadPasswordFields(pageDetails, true, true, false, false); + const passwordFields = AutofillService.loadPasswordFields( + pageDetails, + true, + true, + false, + false + ); if (passwordFields.length === 0) { return formData; } @@ -64,16 +83,17 @@ export default class AutofillService implements AutofillServiceInterface { return formData; } - async doAutoFill(options: any) { - let totpPromise: Promise = null; + async doAutoFill(options: AutoFillOptions) { const tab = options.tab; if (!tab || !options.cipher || !options.pageDetails || !options.pageDetails.length) { throw new Error("Nothing to auto-fill."); } + let totpPromise: Promise = null; + const canAccessPremium = await this.stateService.getCanAccessPremium(); let didAutofill = false; - options.pageDetails.forEach((pd: any) => { + options.pageDetails.forEach((pd) => { // make sure we're still on correct tab if (pd.tab.id !== tab.id || pd.tab.url !== tab.url) { return; @@ -138,12 +158,7 @@ export default class AutofillService implements AutofillServiceInterface { } } - async doAutoFillActiveTab(pageDetails: any, fromCommand: boolean) { - const tab = await this.getActiveTab(); - if (!tab || !tab.url) { - return; - } - + async doAutoFillOnTab(pageDetails: PageDetail[], tab: chrome.tabs.Tab, fromCommand: boolean) { let cipher: CipherView; if (fromCommand) { cipher = await this.cipherService.getNextCipherForUrl(tab.url); @@ -186,9 +201,18 @@ export default class AutofillService implements AutofillServiceInterface { return totpCode; } + async doAutoFillActiveTab(pageDetails: PageDetail[], fromCommand: boolean) { + const tab = await this.getActiveTab(); + if (!tab || !tab.url) { + return; + } + + return await this.doAutoFillOnTab(pageDetails, tab, fromCommand); + } + // Helpers - private async getActiveTab(): Promise { + private async getActiveTab(): Promise { const tab = await BrowserApi.getTabFromCurrentWindow(); if (!tab) { throw new Error("No tab found."); @@ -197,7 +221,10 @@ export default class AutofillService implements AutofillServiceInterface { return tab; } - private generateFillScript(pageDetails: AutofillPageDetails, options: any): AutofillScript { + private generateFillScript( + pageDetails: AutofillPageDetails, + options: GenerateFillScriptOptions + ): AutofillScript { if (!pageDetails || !options.cipher) { return null; } @@ -209,13 +236,13 @@ export default class AutofillService implements AutofillServiceInterface { if (fields && fields.length) { const fieldNames: string[] = []; - fields.forEach((f: any) => { - if (this.hasValue(f.name)) { + fields.forEach((f) => { + if (AutofillService.hasValue(f.name)) { fieldNames.push(f.name.toLowerCase()); } }); - pageDetails.fields.forEach((field: any) => { + pageDetails.fields.forEach((field) => { // eslint-disable-next-line if (filledFields.hasOwnProperty(field.opid)) { return; @@ -228,10 +255,10 @@ export default class AutofillService implements AutofillServiceInterface { const matchingIndex = this.findMatchingFieldIndex(field, fieldNames); if (matchingIndex > -1) { const matchingField: FieldView = fields[matchingIndex]; - let val; + let val: string; if (matchingField.type === FieldType.Linked) { // Assumption: Linked Field is not being used to autofill a boolean value - val = options.cipher.linkedFieldValue(matchingField.linkedId); + val = options.cipher.linkedFieldValue(matchingField.linkedId) as string; } else { val = matchingField.value; if (val == null && matchingField.type === FieldType.Boolean) { @@ -240,7 +267,7 @@ export default class AutofillService implements AutofillServiceInterface { } filledFields[field.opid] = field; - this.fillByOpid(fillScript, field, val); + AutofillService.fillByOpid(fillScript, field, val); } }); } @@ -269,9 +296,9 @@ export default class AutofillService implements AutofillServiceInterface { private generateLoginFillScript( fillScript: AutofillScript, - pageDetails: any, + pageDetails: AutofillPageDetails, filledFields: { [id: string]: AutofillField }, - options: any + options: GenerateFillScriptOptions ): AutofillScript { if (!options.cipher.login) { return null; @@ -285,11 +312,11 @@ export default class AutofillService implements AutofillServiceInterface { if (!login.password || login.password === "") { // No password for this login. Maybe they just wanted to auto-fill some custom fields? - fillScript = this.setFillScriptForFocus(filledFields, fillScript); + fillScript = AutofillService.setFillScriptForFocus(filledFields, fillScript); return fillScript; } - let passwordFields = this.loadPasswordFields( + let passwordFields = AutofillService.loadPasswordFields( pageDetails, false, false, @@ -298,7 +325,7 @@ export default class AutofillService implements AutofillServiceInterface { ); if (!passwordFields.length && !options.onlyVisibleFields) { // not able to find any viewable password fields. maybe there are some "hidden" ones? - passwordFields = this.loadPasswordFields( + passwordFields = AutofillService.loadPasswordFields( pageDetails, true, true, @@ -362,11 +389,11 @@ export default class AutofillService implements AutofillServiceInterface { if (!passwordFields.length && !options.skipUsernameOnlyFill) { // No password fields on this page. Let's try to just fuzzy fill the username. - pageDetails.fields.forEach((f: any) => { + pageDetails.fields.forEach((f) => { if ( f.viewable && (f.type === "text" || f.type === "email" || f.type === "tel") && - this.fieldIsFuzzyMatch(f, AutoFillConstants.UsernameFieldNames) + AutofillService.fieldIsFuzzyMatch(f, AutoFillConstants.UsernameFieldNames) ) { usernames.push(f); } @@ -380,7 +407,7 @@ export default class AutofillService implements AutofillServiceInterface { } filledFields[u.opid] = u; - this.fillByOpid(fillScript, u, login.username); + AutofillService.fillByOpid(fillScript, u, login.username); }); passwords.forEach((p) => { @@ -390,18 +417,18 @@ export default class AutofillService implements AutofillServiceInterface { } filledFields[p.opid] = p; - this.fillByOpid(fillScript, p, login.password); + AutofillService.fillByOpid(fillScript, p, login.password); }); - fillScript = this.setFillScriptForFocus(filledFields, fillScript); + fillScript = AutofillService.setFillScriptForFocus(filledFields, fillScript); return fillScript; } private generateCardFillScript( fillScript: AutofillScript, - pageDetails: any, + pageDetails: AutofillPageDetails, filledFields: { [id: string]: AutofillField }, - options: any + options: GenerateFillScriptOptions ): AutofillScript { if (!options.cipher.card) { return null; @@ -409,8 +436,8 @@ export default class AutofillService implements AutofillServiceInterface { const fillFields: { [id: string]: AutofillField } = {}; - pageDetails.fields.forEach((f: any) => { - if (this.forCustomFieldsOnly(f)) { + pageDetails.fields.forEach((f) => { + if (AutofillService.forCustomFieldsOnly(f)) { return; } @@ -429,7 +456,7 @@ export default class AutofillService implements AutofillServiceInterface { // ref https://developers.google.com/web/fundamentals/design-and-ux/input/forms/ if ( !fillFields.cardholderName && - this.isFieldMatch( + AutofillService.isFieldMatch( f[attr], CreditCardAutoFillConstants.CardHolderFieldNames, CreditCardAutoFillConstants.CardHolderFieldNameValues @@ -439,7 +466,7 @@ export default class AutofillService implements AutofillServiceInterface { break; } else if ( !fillFields.number && - this.isFieldMatch( + AutofillService.isFieldMatch( f[attr], CreditCardAutoFillConstants.CardNumberFieldNames, CreditCardAutoFillConstants.CardNumberFieldNameValues @@ -449,7 +476,7 @@ export default class AutofillService implements AutofillServiceInterface { break; } else if ( !fillFields.exp && - this.isFieldMatch( + AutofillService.isFieldMatch( f[attr], CreditCardAutoFillConstants.CardExpiryFieldNames, CreditCardAutoFillConstants.CardExpiryFieldNameValues @@ -459,25 +486,25 @@ export default class AutofillService implements AutofillServiceInterface { break; } else if ( !fillFields.expMonth && - this.isFieldMatch(f[attr], CreditCardAutoFillConstants.ExpiryMonthFieldNames) + AutofillService.isFieldMatch(f[attr], CreditCardAutoFillConstants.ExpiryMonthFieldNames) ) { fillFields.expMonth = f; break; } else if ( !fillFields.expYear && - this.isFieldMatch(f[attr], CreditCardAutoFillConstants.ExpiryYearFieldNames) + AutofillService.isFieldMatch(f[attr], CreditCardAutoFillConstants.ExpiryYearFieldNames) ) { fillFields.expYear = f; break; } else if ( !fillFields.code && - this.isFieldMatch(f[attr], CreditCardAutoFillConstants.CVVFieldNames) + AutofillService.isFieldMatch(f[attr], CreditCardAutoFillConstants.CVVFieldNames) ) { fillFields.code = f; break; } else if ( !fillFields.brand && - this.isFieldMatch(f[attr], CreditCardAutoFillConstants.CardBrandFieldNames) + AutofillService.isFieldMatch(f[attr], CreditCardAutoFillConstants.CardBrandFieldNames) ) { fillFields.brand = f; break; @@ -491,7 +518,7 @@ export default class AutofillService implements AutofillServiceInterface { this.makeScriptAction(fillScript, card, fillFields, filledFields, "code"); this.makeScriptAction(fillScript, card, fillFields, filledFields, "brand"); - if (fillFields.expMonth && this.hasValue(card.expMonth)) { + if (fillFields.expMonth && AutofillService.hasValue(card.expMonth)) { let expMonth: string = card.expMonth; if (fillFields.expMonth.selectInfo && fillFields.expMonth.selectInfo.options) { @@ -526,10 +553,10 @@ export default class AutofillService implements AutofillServiceInterface { } filledFields[fillFields.expMonth.opid] = fillFields.expMonth; - this.fillByOpid(fillScript, fillFields.expMonth, expMonth); + AutofillService.fillByOpid(fillScript, fillFields.expMonth, expMonth); } - if (fillFields.expYear && this.hasValue(card.expYear)) { + if (fillFields.expYear && AutofillService.hasValue(card.expYear)) { let expYear: string = card.expYear; if (fillFields.expYear.selectInfo && fillFields.expYear.selectInfo.options) { for (let i = 0; i < fillFields.expYear.selectInfo.options.length; i++) { @@ -572,10 +599,14 @@ export default class AutofillService implements AutofillServiceInterface { } filledFields[fillFields.expYear.opid] = fillFields.expYear; - this.fillByOpid(fillScript, fillFields.expYear, expYear); + AutofillService.fillByOpid(fillScript, fillFields.expYear, expYear); } - if (fillFields.exp && this.hasValue(card.expMonth) && this.hasValue(card.expYear)) { + if ( + fillFields.exp && + AutofillService.hasValue(card.expMonth) && + AutofillService.hasValue(card.expYear) + ) { const fullMonth = ("0" + card.expMonth).slice(-2); let fullYear: string = card.expYear; @@ -712,7 +743,7 @@ export default class AutofillService implements AutofillServiceInterface { return fillScript; } - private fieldAttrsContain(field: any, containsVal: string) { + private fieldAttrsContain(field: AutofillField, containsVal: string) { if (!field) { return false; } @@ -734,9 +765,9 @@ export default class AutofillService implements AutofillServiceInterface { private generateIdentityFillScript( fillScript: AutofillScript, - pageDetails: any, + pageDetails: AutofillPageDetails, filledFields: { [id: string]: AutofillField }, - options: any + options: GenerateFillScriptOptions ): AutofillScript { if (!options.cipher.identity) { return null; @@ -744,8 +775,8 @@ export default class AutofillService implements AutofillServiceInterface { const fillFields: { [id: string]: AutofillField } = {}; - pageDetails.fields.forEach((f: any) => { - if (this.forCustomFieldsOnly(f)) { + pageDetails.fields.forEach((f) => { + if (AutofillService.forCustomFieldsOnly(f)) { return; } @@ -764,7 +795,7 @@ export default class AutofillService implements AutofillServiceInterface { // ref https://developers.google.com/web/fundamentals/design-and-ux/input/forms/ if ( !fillFields.name && - this.isFieldMatch( + AutofillService.isFieldMatch( f[attr], IdentityAutoFillConstants.FullNameFieldNames, IdentityAutoFillConstants.FullNameFieldNameValues @@ -774,37 +805,37 @@ export default class AutofillService implements AutofillServiceInterface { break; } else if ( !fillFields.firstName && - this.isFieldMatch(f[attr], IdentityAutoFillConstants.FirstnameFieldNames) + AutofillService.isFieldMatch(f[attr], IdentityAutoFillConstants.FirstnameFieldNames) ) { fillFields.firstName = f; break; } else if ( !fillFields.middleName && - this.isFieldMatch(f[attr], IdentityAutoFillConstants.MiddlenameFieldNames) + AutofillService.isFieldMatch(f[attr], IdentityAutoFillConstants.MiddlenameFieldNames) ) { fillFields.middleName = f; break; } else if ( !fillFields.lastName && - this.isFieldMatch(f[attr], IdentityAutoFillConstants.LastnameFieldNames) + AutofillService.isFieldMatch(f[attr], IdentityAutoFillConstants.LastnameFieldNames) ) { fillFields.lastName = f; break; } else if ( !fillFields.title && - this.isFieldMatch(f[attr], IdentityAutoFillConstants.TitleFieldNames) + AutofillService.isFieldMatch(f[attr], IdentityAutoFillConstants.TitleFieldNames) ) { fillFields.title = f; break; } else if ( !fillFields.email && - this.isFieldMatch(f[attr], IdentityAutoFillConstants.EmailFieldNames) + AutofillService.isFieldMatch(f[attr], IdentityAutoFillConstants.EmailFieldNames) ) { fillFields.email = f; break; } else if ( !fillFields.address && - this.isFieldMatch( + AutofillService.isFieldMatch( f[attr], IdentityAutoFillConstants.AddressFieldNames, IdentityAutoFillConstants.AddressFieldNameValues @@ -814,61 +845,61 @@ export default class AutofillService implements AutofillServiceInterface { break; } else if ( !fillFields.address1 && - this.isFieldMatch(f[attr], IdentityAutoFillConstants.Address1FieldNames) + AutofillService.isFieldMatch(f[attr], IdentityAutoFillConstants.Address1FieldNames) ) { fillFields.address1 = f; break; } else if ( !fillFields.address2 && - this.isFieldMatch(f[attr], IdentityAutoFillConstants.Address2FieldNames) + AutofillService.isFieldMatch(f[attr], IdentityAutoFillConstants.Address2FieldNames) ) { fillFields.address2 = f; break; } else if ( !fillFields.address3 && - this.isFieldMatch(f[attr], IdentityAutoFillConstants.Address3FieldNames) + AutofillService.isFieldMatch(f[attr], IdentityAutoFillConstants.Address3FieldNames) ) { fillFields.address3 = f; break; } else if ( !fillFields.postalCode && - this.isFieldMatch(f[attr], IdentityAutoFillConstants.PostalCodeFieldNames) + AutofillService.isFieldMatch(f[attr], IdentityAutoFillConstants.PostalCodeFieldNames) ) { fillFields.postalCode = f; break; } else if ( !fillFields.city && - this.isFieldMatch(f[attr], IdentityAutoFillConstants.CityFieldNames) + AutofillService.isFieldMatch(f[attr], IdentityAutoFillConstants.CityFieldNames) ) { fillFields.city = f; break; } else if ( !fillFields.state && - this.isFieldMatch(f[attr], IdentityAutoFillConstants.StateFieldNames) + AutofillService.isFieldMatch(f[attr], IdentityAutoFillConstants.StateFieldNames) ) { fillFields.state = f; break; } else if ( !fillFields.country && - this.isFieldMatch(f[attr], IdentityAutoFillConstants.CountryFieldNames) + AutofillService.isFieldMatch(f[attr], IdentityAutoFillConstants.CountryFieldNames) ) { fillFields.country = f; break; } else if ( !fillFields.phone && - this.isFieldMatch(f[attr], IdentityAutoFillConstants.PhoneFieldNames) + AutofillService.isFieldMatch(f[attr], IdentityAutoFillConstants.PhoneFieldNames) ) { fillFields.phone = f; break; } else if ( !fillFields.username && - this.isFieldMatch(f[attr], IdentityAutoFillConstants.UserNameFieldNames) + AutofillService.isFieldMatch(f[attr], IdentityAutoFillConstants.UserNameFieldNames) ) { fillFields.username = f; break; } else if ( !fillFields.company && - this.isFieldMatch(f[attr], IdentityAutoFillConstants.CompanyFieldNames) + AutofillService.isFieldMatch(f[attr], IdentityAutoFillConstants.CompanyFieldNames) ) { fillFields.company = f; break; @@ -923,16 +954,16 @@ export default class AutofillService implements AutofillServiceInterface { if (fillFields.name && (identity.firstName || identity.lastName)) { let fullName = ""; - if (this.hasValue(identity.firstName)) { + if (AutofillService.hasValue(identity.firstName)) { fullName = identity.firstName; } - if (this.hasValue(identity.middleName)) { + if (AutofillService.hasValue(identity.middleName)) { if (fullName !== "") { fullName += " "; } fullName += identity.middleName; } - if (this.hasValue(identity.lastName)) { + if (AutofillService.hasValue(identity.lastName)) { if (fullName !== "") { fullName += " "; } @@ -942,18 +973,18 @@ export default class AutofillService implements AutofillServiceInterface { this.makeScriptActionWithValue(fillScript, fullName, fillFields.name, filledFields); } - if (fillFields.address && this.hasValue(identity.address1)) { + if (fillFields.address && AutofillService.hasValue(identity.address1)) { let address = ""; - if (this.hasValue(identity.address1)) { + if (AutofillService.hasValue(identity.address1)) { address = identity.address1; } - if (this.hasValue(identity.address2)) { + if (AutofillService.hasValue(identity.address2)) { if (address !== "") { address += ", "; } address += identity.address2; } - if (this.hasValue(identity.address3)) { + if (AutofillService.hasValue(identity.address3)) { if (address !== "") { address += ", "; } @@ -970,7 +1001,11 @@ export default class AutofillService implements AutofillServiceInterface { return excludedTypes.indexOf(type) > -1; } - private isFieldMatch(value: string, options: string[], containsOptions?: string[]): boolean { + private static isFieldMatch( + value: string, + options: string[], + containsOptions?: string[] + ): boolean { value = value .trim() .toLowerCase() @@ -1011,12 +1046,15 @@ export default class AutofillService implements AutofillServiceInterface { filledFields: { [id: string]: AutofillField } ) { let doFill = false; - if (this.hasValue(dataValue) && field) { + if (AutofillService.hasValue(dataValue) && field) { if (field.type === "select-one" && field.selectInfo && field.selectInfo.options) { for (let i = 0; i < field.selectInfo.options.length; i++) { const option = field.selectInfo.options[i]; for (let j = 0; j < option.length; j++) { - if (this.hasValue(option[j]) && option[j].toLowerCase() === dataValue.toLowerCase()) { + if ( + AutofillService.hasValue(option[j]) && + option[j].toLowerCase() === dataValue.toLowerCase() + ) { doFill = true; if (option.length > 1) { dataValue = option[1]; @@ -1036,11 +1074,11 @@ export default class AutofillService implements AutofillServiceInterface { if (doFill) { filledFields[field.opid] = field; - this.fillByOpid(fillScript, field, dataValue); + AutofillService.fillByOpid(fillScript, field, dataValue); } } - private loadPasswordFields( + static loadPasswordFields( pageDetails: AutofillPageDetails, canBeHidden: boolean, canBeReadOnly: boolean, @@ -1049,7 +1087,7 @@ export default class AutofillService implements AutofillServiceInterface { ) { const arr: AutofillField[] = []; pageDetails.fields.forEach((f) => { - if (this.forCustomFieldsOnly(f)) { + if (AutofillService.forCustomFieldsOnly(f)) { return; } @@ -1111,7 +1149,7 @@ export default class AutofillService implements AutofillServiceInterface { let usernameField: AutofillField = null; for (let i = 0; i < pageDetails.fields.length; i++) { const f = pageDetails.fields[i]; - if (this.forCustomFieldsOnly(f)) { + if (AutofillService.forCustomFieldsOnly(f)) { continue; } @@ -1195,7 +1233,7 @@ export default class AutofillService implements AutofillServiceInterface { private fieldPropertyIsMatch(field: any, property: string, name: string): boolean { let fieldVal = field[property] as string; - if (!this.hasValue(fieldVal)) { + if (!AutofillService.hasValue(fieldVal)) { return false; } @@ -1227,33 +1265,45 @@ export default class AutofillService implements AutofillServiceInterface { return fieldVal.toLowerCase() === name; } - private fieldIsFuzzyMatch(field: AutofillField, names: string[]): boolean { - if (this.hasValue(field.htmlID) && this.fuzzyMatch(names, field.htmlID)) { + static fieldIsFuzzyMatch(field: AutofillField, names: string[]): boolean { + if (AutofillService.hasValue(field.htmlID) && this.fuzzyMatch(names, field.htmlID)) { return true; } - if (this.hasValue(field.htmlName) && this.fuzzyMatch(names, field.htmlName)) { + if (AutofillService.hasValue(field.htmlName) && this.fuzzyMatch(names, field.htmlName)) { return true; } - if (this.hasValue(field["label-tag"]) && this.fuzzyMatch(names, field["label-tag"])) { + if ( + AutofillService.hasValue(field["label-tag"]) && + this.fuzzyMatch(names, field["label-tag"]) + ) { return true; } - if (this.hasValue(field.placeholder) && this.fuzzyMatch(names, field.placeholder)) { + if (AutofillService.hasValue(field.placeholder) && this.fuzzyMatch(names, field.placeholder)) { return true; } - if (this.hasValue(field["label-left"]) && this.fuzzyMatch(names, field["label-left"])) { + if ( + AutofillService.hasValue(field["label-left"]) && + this.fuzzyMatch(names, field["label-left"]) + ) { return true; } - if (this.hasValue(field["label-top"]) && this.fuzzyMatch(names, field["label-top"])) { + if ( + AutofillService.hasValue(field["label-top"]) && + this.fuzzyMatch(names, field["label-top"]) + ) { return true; } - if (this.hasValue(field["label-aria"]) && this.fuzzyMatch(names, field["label-aria"])) { + if ( + AutofillService.hasValue(field["label-aria"]) && + this.fuzzyMatch(names, field["label-aria"]) + ) { return true; } return false; } - private fuzzyMatch(options: string[], value: string): boolean { + private static fuzzyMatch(options: string[], value: string): boolean { if (options == null || options.length === 0 || value == null || value === "") { return false; } @@ -1272,11 +1322,11 @@ export default class AutofillService implements AutofillServiceInterface { return false; } - private hasValue(str: string): boolean { + static hasValue(str: string): boolean { return str && str !== ""; } - private setFillScriptForFocus( + static setFillScriptForFocus( filledFields: { [id: string]: AutofillField }, fillScript: AutofillScript ): AutofillScript { @@ -1304,7 +1354,7 @@ export default class AutofillService implements AutofillServiceInterface { return fillScript; } - private fillByOpid(fillScript: AutofillScript, field: AutofillField, value: string): void { + static fillByOpid(fillScript: AutofillScript, field: AutofillField, value: string): void { if (field.maxLength && value && value.length > field.maxLength) { value = value.substr(0, value.length); } @@ -1315,7 +1365,7 @@ export default class AutofillService implements AutofillServiceInterface { fillScript.script.push(["fill_by_opid", field.opid, value]); } - private forCustomFieldsOnly(field: AutofillField): boolean { + static forCustomFieldsOnly(field: AutofillField): boolean { return field.tagName === "span"; } } diff --git a/apps/browser/src/services/browserLocalStorage.service.ts b/apps/browser/src/services/browserLocalStorage.service.ts index 9556a6e4e2..2c93920df0 100644 --- a/apps/browser/src/services/browserLocalStorage.service.ts +++ b/apps/browser/src/services/browserLocalStorage.service.ts @@ -1,5 +1,5 @@ import AbstractChromeStorageService from "./abstractChromeStorageApi.service"; export default class BrowserLocalStorageService extends AbstractChromeStorageService { - protected chromeStorageApi: any = chrome.storage.local; + protected chromeStorageApi = chrome.storage.local; } diff --git a/apps/browser/src/services/browserMemoryStorage.service.ts b/apps/browser/src/services/browserMemoryStorage.service.ts index a1195d1a44..993ae8a16e 100644 --- a/apps/browser/src/services/browserMemoryStorage.service.ts +++ b/apps/browser/src/services/browserMemoryStorage.service.ts @@ -1,5 +1,5 @@ import AbstractChromeStorageService from "./abstractChromeStorageApi.service"; export default class BrowserMemoryStorageService extends AbstractChromeStorageService { - protected chromeStorageApi: any = (chrome.storage as any).session; + protected chromeStorageApi = chrome.storage.session; } diff --git a/apps/browser/webpack.config.js b/apps/browser/webpack.config.js index e4cc10060a..3f16579e44 100644 --- a/apps/browser/webpack.config.js +++ b/apps/browser/webpack.config.js @@ -176,13 +176,6 @@ const config = { return chunk.name === "popup/main"; }, }, - commons2: { - test: /[\\/]node_modules[\\/]/, - name: "vendor", - chunks: (chunk) => { - return chunk.name === "background"; - }, - }, }, }, }, @@ -209,4 +202,16 @@ const config = { plugins: plugins, }; +if (manifestVersion == 2) { + // We can't use this in manifest v3 + // Ideally we understand why this breaks it and we don't have to do this + config.optimization.splitChunks.cacheGroups.commons2 = { + test: /[\\/]node_modules[\\/]/, + name: "vendor", + chunks: (chunk) => { + return chunk.name === "background"; + }, + }; +} + module.exports = config; diff --git a/apps/web/src/app/settings/verify-email.component.ts b/apps/web/src/app/settings/verify-email.component.ts index bd59441689..665bb9975a 100644 --- a/apps/web/src/app/settings/verify-email.component.ts +++ b/apps/web/src/app/settings/verify-email.component.ts @@ -10,7 +10,7 @@ import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUti templateUrl: "verify-email.component.html", }) export class VerifyEmailComponent { - actionPromise: Promise; + actionPromise: Promise; constructor( private apiService: ApiService, diff --git a/libs/common/spec/services/consoleLog.service.spec.ts b/libs/common/spec/services/consoleLog.service.spec.ts index fc487ce704..5f8e9ead01 100644 --- a/libs/common/spec/services/consoleLog.service.spec.ts +++ b/libs/common/spec/services/consoleLog.service.spec.ts @@ -78,25 +78,4 @@ describe("ConsoleLogService", () => { error: { 0: "this is an error message" }, }); }); - - it("times with output to info", async () => { - logService.time(); - await new Promise((r) => setTimeout(r, 250)); - const duration = logService.timeEnd(); - expect(duration[0]).toBe(0); - expect(duration[1]).toBeGreaterThan(0); - expect(duration[1]).toBeLessThan(500 * 10e6); - - expect(caughtMessage).toEqual(expect.arrayContaining([])); - expect(caughtMessage.log.length).toBe(1); - expect(caughtMessage.log[0]).toEqual(expect.stringMatching(/^default: \d+\.?\d*ms$/)); - }); - - it("filters time output", async () => { - logService = new ConsoleLogService(true, () => true); - logService.time(); - logService.timeEnd(); - - expect(caughtMessage).toEqual({}); - }); }); diff --git a/libs/common/src/abstractions/log.service.ts b/libs/common/src/abstractions/log.service.ts index 9997e480d6..f5ba7fab3c 100644 --- a/libs/common/src/abstractions/log.service.ts +++ b/libs/common/src/abstractions/log.service.ts @@ -6,6 +6,4 @@ export abstract class LogService { warning: (message: string) => void; error: (message: string) => void; write: (level: LogLevelType, message: string) => void; - time: (label: string) => void; - timeEnd: (label: string) => [number, number]; } diff --git a/libs/common/src/misc/utils.ts b/libs/common/src/misc/utils.ts index 1d96e08287..524c438470 100644 --- a/libs/common/src/misc/utils.ts +++ b/libs/common/src/misc/utils.ts @@ -1,17 +1,28 @@ /* eslint-disable no-useless-escape */ import * as tldjs from "tldjs"; +import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; + import { I18nService } from "../abstractions/i18n.service"; const nodeURL = typeof window === "undefined" ? require("url") : null; +declare global { + /* eslint-disable-next-line no-var */ + var bitwardenContainerService: BitwardenContainerService; +} + +interface BitwardenContainerService { + getCryptoService: () => CryptoService; +} + export class Utils { static inited = false; static isNode = false; static isBrowser = true; static isMobileBrowser = false; static isAppleMobileBrowser = false; - static global: any = null; + static global: typeof global = null; static tldEndingRegex = /.*\.(com|net|org|edu|uk|gov|ca|de|jp|fr|au|ru|ch|io|es|us|co|xyz|info|ly|mil)$/; // Transpiled version of /\p{Emoji_Presentation}/gu using https://mothereff.in/regexpu. Used for compatability in older browsers. @@ -29,16 +40,25 @@ export class Utils { (process as any).release != null && (process as any).release.name === "node"; Utils.isBrowser = typeof window !== "undefined"; + Utils.isMobileBrowser = Utils.isBrowser && this.isMobile(window); Utils.isAppleMobileBrowser = Utils.isBrowser && this.isAppleMobile(window); - Utils.global = Utils.isNode && !Utils.isBrowser ? global : window; + + if (Utils.isNode) { + Utils.global = global; + } else if (Utils.isBrowser) { + Utils.global = window; + } else { + // If it's not browser or node then it must be a service worker + Utils.global = self; + } } static fromB64ToArray(str: string): Uint8Array { if (Utils.isNode) { return new Uint8Array(Buffer.from(str, "base64")); } else { - const binaryString = window.atob(str); + const binaryString = Utils.global.atob(str); const bytes = new Uint8Array(binaryString.length); for (let i = 0; i < binaryString.length; i++) { bytes[i] = binaryString.charCodeAt(i); @@ -93,7 +113,7 @@ export class Utils { for (let i = 0; i < bytes.byteLength; i++) { binary += String.fromCharCode(bytes[i]); } - return window.btoa(binary); + return Utils.global.btoa(binary); } } @@ -157,7 +177,7 @@ export class Utils { if (Utils.isNode) { return Buffer.from(utfStr, "utf8").toString("base64"); } else { - return decodeURIComponent(escape(window.btoa(utfStr))); + return decodeURIComponent(escape(Utils.global.btoa(utfStr))); } } @@ -169,7 +189,7 @@ export class Utils { if (Utils.isNode) { return Buffer.from(b64Str, "base64").toString("utf8"); } else { - return decodeURIComponent(escape(window.atob(b64Str))); + return decodeURIComponent(escape(Utils.global.atob(b64Str))); } } @@ -384,7 +404,7 @@ export class Utils { return new nodeURL.URL(uriString); } else if (typeof URL === "function") { return new URL(uriString); - } else if (window != null) { + } else if (typeof window !== "undefined") { const hasProtocol = uriString.indexOf("://") > -1; if (!hasProtocol && uriString.indexOf(".") > -1) { uriString = "http://" + uriString; diff --git a/libs/common/src/models/domain/attachment.ts b/libs/common/src/models/domain/attachment.ts index cbfffdcf68..053438390d 100644 --- a/libs/common/src/models/domain/attachment.ts +++ b/libs/common/src/models/domain/attachment.ts @@ -48,7 +48,7 @@ export class Attachment extends Domain { if (this.key != null) { let cryptoService: CryptoService; - const containerService = (Utils.global as any).bitwardenContainerService; + const containerService = Utils.global.bitwardenContainerService; if (containerService) { cryptoService = containerService.getCryptoService(); } else { diff --git a/libs/common/src/models/domain/encString.ts b/libs/common/src/models/domain/encString.ts index 9c237a485b..a11ba3a58c 100644 --- a/libs/common/src/models/domain/encString.ts +++ b/libs/common/src/models/domain/encString.ts @@ -104,7 +104,7 @@ export class EncString implements IEncrypted { } let cryptoService: CryptoService; - const containerService = (Utils.global as any).bitwardenContainerService; + const containerService = Utils.global.bitwardenContainerService; if (containerService) { cryptoService = containerService.getCryptoService(); } else { diff --git a/libs/common/src/services/cipher.service.ts b/libs/common/src/services/cipher.service.ts index c8e588616d..ebc18b2184 100644 --- a/libs/common/src/services/cipher.service.ts +++ b/libs/common/src/services/cipher.service.ts @@ -341,7 +341,7 @@ export class CipherService implements CipherServiceAbstraction { throw new Error("No key."); } - const promises: any[] = []; + const promises: Promise[] = []; const ciphers = await this.getAll(); ciphers.forEach(async (cipher) => { promises.push(cipher.decrypt().then((c) => decCiphers.push(c))); diff --git a/libs/common/src/services/consoleLog.service.ts b/libs/common/src/services/consoleLog.service.ts index 9959ef6229..8d48013bfa 100644 --- a/libs/common/src/services/consoleLog.service.ts +++ b/libs/common/src/services/consoleLog.service.ts @@ -1,5 +1,3 @@ -import * as hrtime from "browser-hrtime"; - import { LogService as LogServiceAbstraction } from "../abstractions/log.service"; import { LogLevelType } from "../enums/logLevelType"; @@ -56,17 +54,4 @@ export class ConsoleLogService implements LogServiceAbstraction { break; } } - - time(label = "default") { - if (!this.timersMap.has(label)) { - this.timersMap.set(label, hrtime()); - } - } - - timeEnd(label = "default"): [number, number] { - const elapsed = hrtime(this.timersMap.get(label)); - this.timersMap.delete(label); - this.write(LogLevelType.Info, `${label}: ${elapsed[0] * 1000 + elapsed[1] / 10e6}ms`); - return elapsed; - } } diff --git a/libs/common/src/services/noopEvent.service.ts b/libs/common/src/services/noopEvent.service.ts new file mode 100644 index 0000000000..63d045524e --- /dev/null +++ b/libs/common/src/services/noopEvent.service.ts @@ -0,0 +1,24 @@ +import { EventService } from "@bitwarden/common/abstractions/event.service"; +import { EventType } from "@bitwarden/common/enums/eventType"; + +/** + * If you want to use this, don't. + * If you think you should use that after the warning, don't. + */ +export default class NoOpEventService implements EventService { + constructor() { + if (chrome.runtime.getManifest().manifest_version !== 3) { + throw new Error("You are not allowed to use this when not in manifest_version 3"); + } + } + + collect(eventType: EventType, cipherId?: string, uploadImmediately?: boolean) { + return Promise.resolve(); + } + uploadEvents(userId?: string) { + return Promise.resolve(); + } + clearEvents(userId?: string) { + return Promise.resolve(); + } +} diff --git a/libs/common/src/services/search.service.ts b/libs/common/src/services/search.service.ts index fe2c87c33d..919a58941e 100644 --- a/libs/common/src/services/search.service.ts +++ b/libs/common/src/services/search.service.ts @@ -11,6 +11,8 @@ import { CipherView } from "../models/view/cipherView"; import { SendView } from "../models/view/sendView"; export class SearchService implements SearchServiceAbstraction { + private static registeredPipeline = false; + indexedEntityId?: string = null; private indexing = false; private index: lunr.Index = null; @@ -31,8 +33,13 @@ export class SearchService implements SearchServiceAbstraction { } }); - //register lunr pipeline function - lunr.Pipeline.registerFunction(this.normalizeAccentsPipelineFunction, "normalizeAccents"); + // Currently have to ensure this is only done a single time. Lunr allows you to register a function + // multiple times but they will add a warning message to the console. The way they do that breaks when ran on a service worker. + if (!SearchService.registeredPipeline) { + SearchService.registeredPipeline = true; + //register lunr pipeline function + lunr.Pipeline.registerFunction(this.normalizeAccentsPipelineFunction, "normalizeAccents"); + } } clearIndex(): void { @@ -54,7 +61,6 @@ export class SearchService implements SearchServiceAbstraction { return; } - this.logService.time("search indexing"); this.indexing = true; this.indexedEntityId = indexedEntityId; this.index = null; @@ -95,7 +101,7 @@ export class SearchService implements SearchServiceAbstraction { this.indexing = false; - this.logService.timeEnd("search indexing"); + this.logService.info("Finished search indexing"); } async searchCiphers( diff --git a/libs/common/src/services/state.service.ts b/libs/common/src/services/state.service.ts index 39f3488649..24a2e630e3 100644 --- a/libs/common/src/services/state.service.ts +++ b/libs/common/src/services/state.service.ts @@ -610,7 +610,7 @@ export class StateService< ); } - @withPrototypeForArrayMembers(CipherView) + @withPrototypeForArrayMembers(CipherView, CipherView.fromJSON) async getDecryptedCiphers(options?: StorageOptions): Promise { return ( await this.getAccount(this.reconcileOptions(options, await this.defaultInMemoryOptions())) diff --git a/libs/common/src/services/webCryptoFunction.service.ts b/libs/common/src/services/webCryptoFunction.service.ts index b863f2267c..2e0b920f51 100644 --- a/libs/common/src/services/webCryptoFunction.service.ts +++ b/libs/common/src/services/webCryptoFunction.service.ts @@ -9,7 +9,7 @@ export class WebCryptoFunctionService implements CryptoFunctionService { private crypto: Crypto; private subtle: SubtleCrypto; - constructor(win: Window) { + constructor(win: Window | typeof global) { this.crypto = typeof win.crypto !== "undefined" ? win.crypto : null; this.subtle = !!this.crypto && typeof win.crypto.subtle !== "undefined" ? win.crypto.subtle : null; diff --git a/package-lock.json b/package-lock.json index 49fa99c43c..d1aa452584 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,7 +30,6 @@ "big-integer": "^1.6.51", "bootstrap": "4.6.0", "braintree-web-drop-in": "^1.33.1", - "browser-hrtime": "^1.1.8", "bufferutil": "^4.0.6", "chalk": "^4.1.0", "commander": "^7.2.0", @@ -83,7 +82,7 @@ "@storybook/angular": "^6.5.7", "@storybook/builder-webpack5": "^6.5.7", "@storybook/manager-webpack5": "^6.5.7", - "@types/chrome": "^0.0.139", + "@types/chrome": "^0.0.190", "@types/duo_web_sdk": "^2.7.1", "@types/firefox-webext-browser": "^82.0.0", "@types/inquirer": "^8.2.1", @@ -12504,9 +12503,9 @@ } }, "node_modules/@types/chrome": { - "version": "0.0.139", - "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.139.tgz", - "integrity": "sha512-YZDKFlSVGFp4zldJlO+PUpxMH8N9vLke0fD6K9PA+TzXxPXu8LBLo5X2dzlOs2N/n+uMdI1lw7OPT1Emop10lQ==", + "version": "0.0.190", + "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.190.tgz", + "integrity": "sha512-lCwwIBfaD+PhG62qFB46mIBpD+xBIa+PedNB24KR9YnmJ0Zn9h0OwP1NQBhI8Cbu1rKwTQHTxhs7GhWGyUvinw==", "dev": true, "dependencies": { "@types/filesystem": "*", @@ -52084,9 +52083,9 @@ } }, "@types/chrome": { - "version": "0.0.139", - "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.139.tgz", - "integrity": "sha512-YZDKFlSVGFp4zldJlO+PUpxMH8N9vLke0fD6K9PA+TzXxPXu8LBLo5X2dzlOs2N/n+uMdI1lw7OPT1Emop10lQ==", + "version": "0.0.190", + "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.190.tgz", + "integrity": "sha512-lCwwIBfaD+PhG62qFB46mIBpD+xBIa+PedNB24KR9YnmJ0Zn9h0OwP1NQBhI8Cbu1rKwTQHTxhs7GhWGyUvinw==", "dev": true, "requires": { "@types/filesystem": "*", diff --git a/package.json b/package.json index 82e67080c1..8675da7ba0 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "@storybook/angular": "^6.5.7", "@storybook/builder-webpack5": "^6.5.7", "@storybook/manager-webpack5": "^6.5.7", - "@types/chrome": "^0.0.139", + "@types/chrome": "^0.0.190", "@types/duo_web_sdk": "^2.7.1", "@types/firefox-webext-browser": "^82.0.0", "@types/inquirer": "^8.2.1", @@ -155,7 +155,6 @@ "big-integer": "^1.6.51", "bootstrap": "4.6.0", "braintree-web-drop-in": "^1.33.1", - "browser-hrtime": "^1.1.8", "bufferutil": "^4.0.6", "chalk": "^4.1.0", "commander": "^7.2.0",